你不知道的JavaScript上卷笔记

前言

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

作用域和闭包

作用域是什么

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

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

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

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

词法作用域

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

  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.如下示例  

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

  上述代码输出的不是undefined,而是2。因为上述代码需要经过编译器的编译,编译过程中首先会进行变量声明,然后再由引擎进行变量赋值,所以,上述变量声明虽然写在第二行,但是声明过程是首先执行的

  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.词法作用域是一套引擎如何寻找变量以及会在何处找到变量的规则。词法作用域最重要的特征是它的定义过程发生中代码的书写阶段

  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();//

    上述示例中,函数调用应用了默认绑定,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]]是一种不同于类的设计模式,它是一种委托行为的设计模式。

你不知道的JavaScript上卷笔记的更多相关文章

  1. 【你不知道的javaScript 上卷 笔记3】javaScript中的声明提升表现

    console.log( a ); var a = 2; 执行输出undefined a = 2; var a; console.log( a ); 执行输出2 说明:javaScript 运行时在编 ...

  2. 【你不知道的javaScript 上卷 笔记7】javaScript中对象的[[Prototype]]机制

    [[Prototype]]机制 [[Prototype]]是对象内部的隐试属性,指向一个内部的链接,这个链接的作用是:如果在对象上没有找到需要的属性或者方法引用,引擎就 会继续在 [[Prototyp ...

  3. 【你不知道的javaScript 上卷 笔记6】javaScript中的对象相关内容

    一.创建一个对象的语法 var myObj = { key: value // ... };//字面量 var myObj = new Object(); //new myObj.key = valu ...

  4. 【你不知道的javaScript 上卷 笔记5】javaScript中的this词法

    function foo() { console.log( a ); } function bar() { var a = 3; foo(); } var a = 2; bar(); 上面这段代码为什 ...

  5. 【你不知道的javaScript 上卷 笔记4】javaScript 中闭包的一些运用

    什么是闭包 闭包是javaScript语言的一种特性,在 javaScript 中以函数作为承接单元.当函数可以记住并访问所在的词法作用域时,就产生了闭包,即使函数是在当前词法作用域之外执行. fun ...

  6. 【你不知道的javaScript 上卷 笔记2】 javaScript 的作用域规则

    一.什么是词法作用域? 词法作用域是在定义词法阶段的作用域,就是由代码变量和作用域块写在哪里决定的,基本上词法分析器在处理代码时会保持作用域不变. 二.词法作用域特点 完全由写代码期间函数所声明的位置 ...

  7. 【你不知道的javaScript 上卷 笔记1】 javaScript 是如何工作的?

    一.什么是作用域? 作用域是用来存储变量以及方便寻找变量的一套规则. 二.javaScript 编译过程(编译发生在代码执行前的几微妙) 分词/词法分析(Tokenizing/Lexing)-> ...

  8. 《你不知道的 JavaScript 上卷》 学习笔记

    第一部分: 作用域和闭包 一.作用域 1. 作用域:存储变量并且查找变量的规则 2. 源代码在执行之前(编译)会经历三个步骤: 分词/此法分析:将代码字符串分解成有意义的代码块(词法单元) 解析/语法 ...

  9. 读《你不知道的JavaScript(上卷)》后感-作用域闭包(二)

    github原文 一. 序言 最近我在读一本书:<你不知道的JavaScript>,这书分为上中卷,内容非常丰富,认真细读,能学到非常多JavaScript的知识点,希望广大的前端同胞们, ...

随机推荐

  1. 原码、反码、补码和移码事实上非常easy

    近期在备战软考,复习到计算机组成原理的时候,看到书中关于原码.反码.补码和移码的定义例如以下(n是机器字长): 原码: 反码: 补码: 移码: 看完这些定义以后,我的脑袋瞬间膨胀到原来的二倍!这样变态 ...

  2. 事务管理在三层架构中应用以及使用ThreadLocal再次重构

    本篇将详细讲解如何正确地在实际开发中编写事务处理操作,以及在事务处理的过程中使用ThreadLocal的方法. 在前面两篇博客中已经详细地介绍和学习了DbUtils这个Apache的工具类,那么在本篇 ...

  3. C++ 需要返回值的函数却没有返回值的情况 单例模式

    昨天在看前些天写的代码,发现一个错误. #include <iostream> using namespace std; class singleton { public: static ...

  4. QDockWidget嵌套布局详解-实现Visual Studio布局

    概述 许多工程软件,如Qt Creator,VS,matlab等,都是使用dock布局窗口,这样用户可以自定义界面,自由组合窗口. Qt的嵌套布局由QDockWidget完成,用Qt Creator拖 ...

  5. js正则验证手机号码有效性

    验证130-139,150-159,180-189号码段的手机号码 <script type="text/javascript"> [-]{})|([-]{})|([- ...

  6. 注解在android中的使用

    注解在android程序中的使用 何为注解: 在Java其中,注解又叫做"元数据",它为我们在源码中加入信息提供了一种形式化的方法.让我们能在以后的某个时间方便的使用这些数据.更确 ...

  7. Indy10.2.5的危险做法

    为了排查一个Bug今天无意看了看Indy源码,结果吓了一跳.TIdIOHandler.ReadLongWord函数用于读取通讯数据并转换成LongWord类型返回,它做用了一种危险的做法可能会导致数据 ...

  8. Windows 7如何建立一个FTP的快捷方式

    原来,使用Windows XP的时候,在IE6的地址栏里输入FTP服务器的地址,就可以打开一个资源管理器的界面来管理文件.但是,随着IE的版本的提升或是装了Windows 7,原来的这种方法就不能用了 ...

  9. unity3d游戏开发学习之使用3dmax创建导弹模型

    在着手研究Unity3D的游戏开发时,3D模型能够考虑从unity的assets store去获取,也能够从网上搜索下载,同一时候咱们也能够尝试下自己动手去做一些简单的模型. 这里就依据unity3d ...

  10. 【自由谈】城域网IPv6过渡技术——MAP技术(4)

    本节接着回答MAP技术的第三个问题:“MAP-BR的Pool是如何实现?可靠性如何提升?” 在MAP域中通过将多个MAP-BR放在同一个Pool内实现负载分担和保护倒换的.同一个Pool中的每个MAP ...