你不知道的JavaScript上卷笔记

你不知道的JavaScript上卷笔记

前言

You don't know JavaScript是github上一个系列文章
 
初看到这一标题的时候,感受怎么老外也搞标题党,用这种冲突性比较强的题目吸引眼球,以至最初真没去看内容。直到出了中文版《你不知道的JavaScript》,一看评价你们都说好,买来一读,内容果真很好,不少地方,让我这个半路转行JavaScript的人豁然开朗。中文版如今出了上卷,中卷应该很快会推出,下卷就要等久一点了javascript

做用域和闭包

做用域是什么

  1.现代JavaScript已经再也不是解释执行的,而是编译执行的。可是与传统的编译语言不一样,它不是提早编译,编译结果不能进行移植。编译过程当中,一样会通过分词/词法分析,解析/语法分析,代码生成三个阶段。html

  2.以var a = 2;语句为例,对这一程序语句对处理,须要通过引擎,编译器,做用域三者的配合。其中,引擎从头至尾负责整个javascript程序的编译和执行过程;编译器负责语法分析和代码生成;做用域负责收集并维护由全部声明的标识符组成的系列查询,并实施一套规则,肯定当前执行的代码对这些标识符的访问权限。java

  3.对于var a = 2;编译器首先查找做用域中是否已经有该名称的变量,而后引擎中执行编译器生成的代码时,会首先查找做用域。若是找到就执行赋值操做,不然就抛出异常git

  4.引擎对变量的查找有两种:LHS查询RHS查询。当变量出现中赋值操做左侧时是LHS查询,出现中右侧是RHS查询es6

词法做用域

  1.词法做用域就是定义在词法阶段的做用域。词法做用域是由你在写代码时将变量和块做用域写在哪里决定的,词法处理器分析代码时会保持做用域不变github

  1. function foo(a){ 
        var b = a * 2; 
        function bar(c){ 
            console.log(a,b,c); 
        } 
        bar(b * 3); 
    } 
    foo(2);     
    这个例子有三级嵌套的做用域
  2. 做用域查找会在找到第一个匹配的标识符时中止
  3. eval和with能够欺骗词法做用域,不推荐使用

函数做用域和块做用域

  1. JavaScript具备基于函数的做用域,属于这个函数的变量均可以在整个函数的范围内使用及复用
  2. (function fun(){})()
    函数表达式和函数声明的区别是看function关键字出如今声明中的位置。若是function是声明中的第一个词,那么就是一个函数声明,不然就是一个函数表达式
  3. with,try/catch具备块做用域,方便好用的实现块级做用域的是es6带来的let关键字

提高

  1.以下示例  web

a=2; 
var a; 
console.log(a); 

  上述代码输出的不是undefined,而是2。由于上述代码须要通过编译器的编译,编译过程当中首先会进行变量声明,而后再由引擎进行变量赋值,因此,上述变量声明虽然写在第二行,可是声明过程是首先执行的ajax

  1. console.log(a); 
    var a = 2; 
    上述代码不是抛ReferenceError异常, 而是输出undefined
  2. 只有声明会被提高,赋值及其余运行逻辑会留在原地 
  3. foo(); 
    function foo(){ 
        console.log(a);//undefined 
        var a = 2; 
    }
    foo();//不是ReferenceError,是TypeError var foo = function bar(){}; foo被提高并分配给所在做用域,因此foo()不会致使ReferenceError,可是foo没有被赋值,对undefined进行函数调用,因此抛出TypeError

做用域闭包

  1. 将内部函数传递到所在词法做用域之外,它都会持有对原始定义做用域的饮用,不管中何处执行这个函数都会使用闭包
  2. 本质上,不管什么时候何地,若是将函数看成第一级的值类型并处处传递,就会看到闭包在这些函数中的应用。在定时器,事件监听器,ajax请求,web workers或者其余任何异步任务中,只要使用了回调函数,实际上就是在使用闭包
  3. 模块的封装利用了闭包,将内部变量隐藏,并返还一个公共api的对象,这一返回的对象对模块的私有变量造成闭包访问。

动态做用域

  1.词法做用域是一套引擎如何寻找变量以及会在何处找到变量的规则。词法做用域最重要的特征是它的定义过程发生中代码的书写阶段segmentfault

  2.动态做用域让做用域做为一个在运行时就被动态肯定的形式,而不是在写代码时进行静态肯定的形式。设计模式

function foo(){ 
    console.log(a);//2 
} 
function bar(){ 
    var a = 3; 
    foo(); 
} 
var a = 2; 
bar(); 

词法做用域让foo()中的a经过RHS引用到了全局做用域中的a,因此输出2;动态做用域不关心函数和做用域如何声明以及在何处声明,只关心从何处调用。换言之,做用域链是基于调用栈的,而不是代码中的做用域嵌套。若是以动态做用域来看,上面代码中执行时会输出3

  3.JavaScript不具有动态做用域,可是this机制中某种程度上很像动态做用域,this关注函数如何调用。

this词法

  1. es6经过箭头函数,将this同词法做用域联系起来了。
  2. 以前若是会遇到this丢失,常见方法时使用本地变量替换this引用 
    var obj = { 
        msg : 'awesome', 
        cool:function(){ 
            setTimeout(function timer(){ 
                console.log(this.msg); 
                },100);     
            } 
        }; 
    var msg = 'not awesome'; 
    obj.cool(); //not awesome 
    
    var obj = { 
        msg : 'awesome',
         cool:function(){ 
        var self = this; setTimeout(function timer(
            console.log(self.msg); 
            },100); 
        }  
    }; 
    var msg = 'not awesome'; 
    obj.cool(); //awesome 
    
    var obj = { 
        msg : 'awesome', 
        cool:function(){ setTimeout(() => { 
            console.log(this.msg); 
            },100); 
        } 
    }; 
    var msg = 'not awesome'; 
    obj.cool(); //awesome 
    
    var obj = { 
        msg : 'awesome', 
        cool:function(){ 
            setTimeout(function timer() { 
                console.log(this.msg); }.bind(this),100); 
                } 
        }; 
    var msg = 'not awesome'; 
    obj.cool(); //awesome            

this和对象原型

this全面解析

  1. 默认绑定。
    function foo(){ 
        console.log(this.a); 
    } 
    var a = 2; foo();//2
    上述示例中,函数调用应用了默认绑定,this指向全局对象
  2. 隐式绑定。调用位置是否有上下文对象,或者说是否被某个对象拥有或者包含。
    function foo(){ 
        console.log(this.a); 
    } 
    var obj={ 
        a:2,
         foo:foo 
    }; 
    obj.foo();//2 
    foo()被调用时,落脚点指向obj对象。当函数引用有上下文对象时,隐式对象将函数调用中的this绑定到上下文对象。
  3. 显示绑定。使用apply,call或者bind显示绑定
  4. new绑定。
    • JavaScript中的构造函数只是一些使用new操做符时被调用的函数。它们并不会属于某个类,也不会实例化一个类。实际上,它们甚至不能说是一种特殊的函数类型,它们只是被new操做符调用的普通函数。
    • 使用new来调用函数,或者说发生构造函数调用,会自动执行下面操做:a)建立一个全新的对象 b)这个新对象会被执行[[原型]]链接 c)这个新对象会绑定到函数调用的this d)若是函数没有返回其余对象,那么new表达式中的函数调用会自动返回这个新对象
  5. 优先级。new绑定 > 显示绑定 > 隐式绑定 > 默认绑定

对象

  1. 属性描述符 
    var obj = {}; 
    Object.defineProperty(
        obj,
        "a",
        { 
            value:2, 
            writable:true, 
            configurable:true, 
            enumerable:true 
        }
    );
    obj.a;//2    
    • writable决定是否能够修改属性的值,writable为false时,对obj.a的修改会静默失败。
    • configurable表示属性是否能够配置,若是为false,再对obj调用defineProperty来修改设置,会抛出TypeError
    • enumerable表示属性是否会出如今对象枚举属性中,若是为false,就不会出如今for...in循环中
  2. 对象常量。结合使用writable:false和configurable:false能够建立一个真正的常量属性(不可修改,不可重定义,不可删除)
  3. 禁止扩展。若是想禁止对一个对象添加新属性而且保留已有属性,可使用Object.preventExtensions()方法
  4. 密封。Object.seal()会建立一个密封的对象,这个方法实际上会在一个现有对象上调用Object.preventExtensions()方法,而且把全部属性标记为configurable:false
  5. 冻结。Object.freeze()会建立一个冻结对象,这个方法会在现有对象上调用Object.seal()方法,并把全部数据访问的属性标记为writable:false
  6. [[get]],[[put]]。
    • var obj = { 
          this._a_ = 2; 
          get a(){ return this._a_; }, 
          set a(val){ this._a_ = val * 2; } 
      }; 
      Object.defineProperty( 
          obj, 
          "b", 
          { 
              get:function(){return this._a_ * 2}, 
              enumerable:true 
          } 
      )

原型

本节为以为书中写的有点绕,不清晰。推荐阅读

  1. constructor, prototype, proto 详解
  2. JavaScript中proto与prototype的关系
  3. How does proto differ from constructor.prototype?
  4. 理解JavaScript面向对象的思路

行为委托

  1. [[prototype]]机制是对象中一个内部连接引用另外一个对象 若是中第一个对象上没找到须要的属性或者方法引用,引擎就会继续在[[prototyoe]]关联的对象上进行查找。同理,若是后者中也没找到须要的引用,就会继续查找它的[[prototype]],以此类推,这一系列对象的连接称为原型链。换言之,这个机制就是对象之间的关联关系。
  2. [[prototype]]是一种不一样于类的设计模式,它是一种委托行为的设计模式。
相关文章
相关标签/搜索