[关闭]
@seandor 2016-08-01T11:26:14.000000Z 字数 6165 阅读 968

Javascript Fundamentals 笔记 & 总结

javascript

1. Javascript代码放在哪里

<script> </script>
* inside an external file.
<script src="js/script.js"> <script>

尽管JS代码可以放在HTML文档中的不同位置,Javascript引擎会将处在不同位置的JS代码拼接起来并且从上到下顺序执行。浏览器中Javascript引擎是一个单线程的引擎,JS代码一旦执行,就会一次性执行完。

  1. <!-- html code -->
  2. <!doctype html>
  3. <html lang="en">
  4. <head>
  5. <meta charset="utf-8">
  6. <script>
  7. console.log(x);
  8. </script>
  9. </head>
  10. <body>
  11. <h1>Where to place javascript code</h1>
  12. <script src="js/script.js"></script>
  13. <script>
  14. console.log("after hello world!");
  15. </script>
  16. </body>
  17. </html>
  1. // javascript code
  2. var x = "hello, world!";

控制台输出:

  1. hello, world!
  2. after hello world!

2. Javascript 函数和作用域

Javascript函数中的所有参数都是可选的。

  1. function compare(x, y) {}
  2. var a = compare(4, 5);
  3. compare(4, 'a');
  4. compare();
  5. compare(3, 4, 5); //the third arguments is not used in the function, but it's perfectly legal to pass it in.

上面代码中的用法都是合法的。

Javascript中只有两种作用域,Global和Function(aka lexical)。定义在全局下的变量或函数可以在任何地方执行,而定义在函数中的变量或函数只能在函数内部使用。

2.1 Javascript 作用域链(Scope Chain)

Javascript作用域链的工作原理:
被引用(不是定义)的变量首先会在自己所处的作用域中搜索。如果没有找到,就会向它的外部引用(外部环境的引用),如果仍没找到,那么就会向它的外部引用的外部引用去搜索,直到搜索至全局作用域。如果全局作用域中没找到,那么该变量就是未定义的(undefined)。

  1. // Global
  2. var x = 2;
  3. A();
  4. function A() {
  5. var x = 5;
  6. B();
  7. }
  8. function B() {
  9. console.log(x);
  10. }

上面代码的输出结果是2而不是5。因为函数B是在全局作用域中定义的,所以它的外部引用是全局作用域。x会首先函数B中搜索,发现没找到后,就开始在它的外部作用域中搜索,而它的外部作用域是全局作用域而不是函数A作用域。

无论一个函数是在何处执行,就处理变量作用域而言,只和该函数物理上被定义所处的环境有关。

上文提到的Lexical作用域也被称为函数作用域,但Lexical作用域还有一层意思,是指函数对除了Global以外的外部环境的引用。当然除了Global作用域外,也只剩Function作用域了。

  1. function foo() {
  2. var a = 1;
  3. function bar() {
  4. var b = 2;
  5. function barz() {
  6. console.log(a+b);
  7. }
  8. return barz;
  9. }
  10. return bar();
  11. }
  12. var fn = foo();
  13. fn();

对于函数barz来说,它有一个对于外部环境的引用那就是函数bar作用域,所以函数bar作用域是barz的lexical作用域。按照作用域链的工作原理,函数barz中出现的变量a,需要一直向上搜索,直到在函数foo中找到变量a的定义,所以函数foo作用域也是barz的lexical作用域。

3. Javascript 内置类型(Built-in Types)

Javascript中有7中built-in类型:其中6种是原始类型,一种是对象类型。

Javascript中的对象就是一组键值对

  1. person = {
  2. firstname: "Sy",
  3. lastname: "Ling",
  4. contact: {
  5. tel: 10000000,
  6. email: "somebody@xxmail.com"
  7. }
  8. }

Javascript中的原始类型用于表示单一,不可变的值。

3.1 Type Coercion

使用双等号==来比较两个变量的时候,javascript会做自动的类型转换。

使用===表示严格等于,javascript首先会比较两个变量的类型,如果类型不一样,就不会继续比较他们的值而直接返回false。

javascript中会被强制转换为false的一些情况如下代码所示。

  1. if ( false || null || undefined || "" || 0 || NaN ) {
  2. console.log("This line won't ever execute.");
  3. }
  4. else {
  5. console.log("All false.");
  6. }

也可以使用Boolean(undefined)来查看不同类型强制转换为布尔类型后的结果。

Javascript中不要将{放在单独一行。

Javascript中"" || "hello"将返回"hello"而不是true,也就是说这个表达式在进行type coercion之前就返回值了。使用Boolean("" || "hello"),才会返回true。因此在JS代码中常用这样的结构来表示一种逻辑,即如果左边的变量为空,则使用右边的变量。

  1. name = name || name_substitution;

4. Javascript 对象(Object)

4.1 创建对象

使用new Object()创建对象:

  1. var company = new Object();
  2. // 自动创建属性
  3. company.name = "Facebook";
  4. company.ceo = new Object();
  5. company.ceo.firstName = "Mark";
  6. company.ceo.favColor = "blue";
  7. // Note that you can not use dot notation here.
  8. // 只有当属性名为合法的javascript变量名的时候才可以使用dot notation.
  9. company["stock of company"] = 110;

使用Object literal创建对象:

  1. var facebook = {
  2. name: "Facebook",
  3. ceo: {
  4. firstName: "Mark",
  5. favColor: "blue"
  6. },
  7. // 属性加不加双引号都是字符串
  8. "stock of company": 110
  9. }

使用上述两种方法创建的对象的结果是一样的,但使用Object literal要更加简单和直观。

4.2 函数(Function)

Javascript中函数只是regular Object,但是有一些特殊的属性。

在Javascript中函数属于'First-Class Data Types',意思是你能对一个变量或者对象进行的操作都可以对函数进行。

  1. function multiply(x, y) {
  2. return x * y;
  3. }
  4. console.log(multiply(5, 3));
  5. // you can add property to a function
  6. multiply.version = "v.1.0.0"
  7. // function factory
  8. function makeMultiplier(multiplier) {
  9. var myFunc = function (x) {
  10. return multiplier * x;
  11. }
  12. return myFunc;
  13. }
  14. var multiplyBy3 = makeMultiplier(3);
  15. var multiplyBy5 = makeMultiplier(5);
  16. // 将函数作为参数传递
  17. function doOperationOn(x, operation) {
  18. return operation(x);
  19. }
  20. var result = doOperationOn(5, multiplyBy3);

5. 按值传递 & 按引用传递

Javascript中原始类型是按值传递的,对象类型按引用传递。但是从实现原理上理解,它们实际上都是按值传递的。举例如下:

  1. var a = 7;
  2. var b = a;

value

  1. var a = {x:7};
  2. var b = a;

reference

图中可以清晰的看出原始类型和对象类型在内存中的区别,原始类型的变量在内存中存储的是实际的值,而对象类型在内存中存储的却是另一个内存地址(用来存储对象数据)。

6. prototype & 'this'

6.1 prototype与类

引子:

  1. // function constructors
  2. function Circle(radius) {
  3. this.radius = radius;
  4. this.getArea = function () {
  5. return Math.PI * Math.pow(this.radius, 2);
  6. };
  7. }
  8. var myCircle = new Circle(10);

使用上述方法创建的函数对象,每创建一次函数对象内的属性radius和getArea函数都会被创建一次,对于radius来说多次创建是合理的,但是对于getArea函数,只需要创建一次就可以,所有Circle的实例应该共享该函数。

  1. // function constructors
  2. function Circle(radius) {
  3. this.radius = radius;
  4. }
  5. Circle.prototype.getArea = function () {
  6. return Math.PI * Math.pow(this.radius, 2);
  7. }
  8. var myCircle = new Circle(10);

使用这种方法来创建函数构造器就和其他语言中的类非常类似。

6.2 this关键字

首先看下面的例子:

  1. var literalCircle = { // new Object()
  2. radius: 10,
  3. getArea: function () {
  4. // var self = this;
  5. console.log(this);
  6. var increaseRadius = function () {
  7. this.radius = 20;
  8. // self.radius = 20;
  9. };
  10. increaseRadius();
  11. return Math.PI * Math.pow(this.radius, 2);
  12. }
  13. };

控制台打印出this为literalCircle对象本身,为什么this没有指向外部的window对象呢,原因很简单当我们使用Object literal创建对象的时候,和使用new关键字创建对象一样,都会使对象中的this指向新创建的对象本身。

getArea函数内部还有一个函数用来增加radius,但在此使用this并不能实现增加radius的大小。函数内部的函数中使用this会出现bug,this关键字会指向全局的window对象。一个解决办法就是使用局部变量将this存储起来,以便后续使用。

7. 闭包(Closures)

闭包是一个比较抽象的概念,简单来说,闭包指的是当一个函数在它的lexical作用域之外执行时,它仍能记得并使用它的lexical作用域。

  1. function makeMultiplier(multiplier) {
  2. return (
  3. function (x) {
  4. return multiplier * x;
  5. }
  6. );
  7. }
  8. var doubleAll = makeMultiplier(2);
  9. console.log(doubleAll(10)); // its own exec env

最后一句代码中doubleAll函数执行了,但是它是如何获得multiplier的呢,如果没有闭包,它确实无法获得multiplier。当一个函数从另一个函数中返回时,Javascript会在内存中保留被返回函数的lexical作用域环境,所以即使在外部执行该函数,它仍然能够获得multiplier的值。

8. Namespace & IIFE

Javascript语言本身并没有Namespace,但我们可以很容易伪造Namespace(实现和Namespace一样的功能)。伪造Namespace通常使用对象来完成,将私有的变量和方法存储在对象的属性中。但是对于有些变量我们可能只是临时使用并不希望放在对象中,如果不放在对象中,那么又会出现全部变量造成的困扰。

Javascript可以使用一种叫做Immediately Invoked Function Expression(IIFE)来做一种封装。

  1. // IIFE
  2. (function () {
  3. console.log("Hello Coursera!");
  4. })();

IIFE的常用方法是在函数体中创建一个对象,并将这个对象暴露给window对象。这样我们只需要将需要提供给外界的变量或方法作为该对象的属性即可,其他变量则完全处在函数的作用域中。
下面看一个实际的例子:

  1. (function (global) {
  2. // Set up a namespace for our utility
  3. var ajaxUtils = {};
  4. // Returns an HTTP request object
  5. function getRequestObject() {
  6. ...
  7. }
  8. }
  9. // Makes an Ajax GET request to 'requestUrl'
  10. ajaxUtils.sendGetRequest =
  11. ...
  12. };
  13. // Expose utility to the global object
  14. global.$ajaxUtils = ajaxUtils;
  15. })(window);

对于getRequestObject函数完全只在IIFE内部使用,不需要暴露给window对象。

9. Ajax

Ajax的原理和事件相应类似,JS代码是一行一行执行的,如果中间有一行代码使用的Ajax请求,JS引擎会将这个请求和相应的JS处理函数地址发送给HTTP Requestor(负责处理HTTP请求的组件),JS会继续向下执行,不会等待HTTP响应。在某一时刻,JS代码可能已经全部执行完,这时收到服务器的响应,JS引擎会根据处理函数地址去执行响应动作。

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注