你不知道的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();//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]]是一种不同于类的设计模式,它是一种委托行为的设计模式。
 
 

不知道的JavaScript的更多相关文章

  1. js值----你所不知道的JavaScript系列(6)

    1.数组 在 JavaScript 中,数组可以容纳任何类型的值,可以是字符串.数字.对象(object),甚至是其他数组(多维数组就是通过这种方式来实现的) .----<你所不知道的JavaS ...

  2. js类型----你所不知道的JavaScript系列(5)

    ECMAScirpt 变量有两种不同的数据类型:基本类型,引用类型.也有其他的叫法,比如原始类型和对象类型等. 1.内置类型 JavaScript 有七种内置类型: • 空值(null) • 未定义( ...

  3. 闭包----你所不知道的JavaScript系列(4)

    一.闭包是什么? · 闭包就是可以使得函数外部的对象能够获取函数内部的信息. · 闭包是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分. · 闭包就 ...

  4. let和const----你所不知道的JavaScript系列(2)

    let 众所周知,在ES6之前,声明变量的关键字就只有var.var 声明变量要么是全局的,要么是函数级的,而无法是块级的. var a=1; console.log(a); console.log( ...

  5. LHS 和 RHS----你所不知道的JavaScript系列(1)

      变量的赋值操作会执行两个动作, 首先编译器会在当前作用域中声明一个变量(如果之前没有声明过), 然后在运行时引擎会在作用域中查找该变量, 如果能够找到就会对它赋值.----<你所不知道的Ja ...

  6. Lodash 严重安全漏洞背后 你不得不知道的 JavaScript 知识

    摘要: 详解原型污染. 原文:Lodash 严重安全漏洞背后 你不得不知道的 JavaScript 知识 作者:Lucas HC Fundebug经授权转载,版权归原作者所有. 可能有信息敏感的同学已 ...

  7. 【转载】14个你可能不知道的 JavaScript 调试技巧

    了解你的工具可以极大的帮助你完成任务.尽管 JavaScript 的调试非常麻烦,但在掌握了技巧 (tricks) 的情况下,你依然可以用尽量少的的时间解决这些错误 (errors) 和问题 (bug ...

  8. 14 个你可能不知道的 JavaScript 调试技巧

    了解你的工具可以极大的帮助你完成任务.尽管 JavaScript 的调试非常麻烦,但在掌握了技巧 (tricks) 的情况下,你依然可以用尽量少的的时间解决这些错误 (errors) 和问题 (bug ...

  9. 你所不知道的JavaScript数组

    相信每一个 javascript 学习者,都会去了解 JS 的各种基本数据类型,数组就是数据的组合,这是一个很基本也十分简单的概念,他的内容没多少,学好它也不是件难事情.但是本文着重要介绍的并不是我们 ...

  10. 10个你可能不知道的JavaScript小技巧

    1.变量转换 看起来很简单,但据我所看到的,使用构造函数,像Array()或者Number()来进行变量转换是常用的做法.始终使用原始数据类型(有时也称为字面量)来转换变量,这种没有任何额外的影响的做 ...

随机推荐

  1. 关于android的nfc问题

    最近在研究android的nfc问题 首先再网上有很多关于android nfc 读写的问题,但是大多数都是关于Mifare Classic类型卡的读写,我百试不得骑解,于是自己写了一些程序供大家参考 ...

  2. 一个问题:关于类型转换Type Cast(汇编讲解 as 语法)

    问题如下: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34   ...

  3. EF连接MySQL数据Web.Config配置

    EF连接MySQL数据Web.Config配置 <?xml version="1.0" encoding="utf-8"?> <configu ...

  4. android设置背景图片透明

    设置Activiyt为透明可以在Activity中引用系统透明主题android:theme="@android:style/Theme.Translucent" 设置背景图片透明 ...

  5. VC++ WIN32 sdk实现按钮自绘详解.

    网上找了很多,可只是给出代码,没有详细解释,不便初学者理解.我就抄回冷饭.把这个再拿出来说说. 实例图片:    首先建立一个标准的Win32 Application 工程.选择a simple Wi ...

  6. Spring Boot Mongodb

    Spring注解学习,有助于更好的理解下面代码: @ConditionOnClass表明该@Configuration仅仅在一定条件下才会被加载,这里的条件是Mongo.class位于类路径上 @En ...

  7. App 运营 推广相关

    基本要素 1.定位和产品 2.取个好名字,一目了然+下载冲动 3.设计一个好图标,有感性和直观的认识 4.做好产品的说明.关键字,截图(前1-2行是重点) 5.做市场的排名(相关因素如下)   (1) ...

  8. linux shell编程指南第十八章------控制流结构

    在书写正确脚本前,大概讲一下退出状态.任何命令进行时都将返回一个退出状态.如 果要观察其退出状态,使用最后状态命令: $ echo $? 主要有4种退出状态.前面已经讲到了两种,即最后命令退出状态$ ...

  9. HDU--杭电--4504--威威猫系列故事——篮球梦--DP

    威威猫系列故事——篮球梦 Time Limit: 300/100 MS (Java/Others)    Memory Limit: 65535/32768 K (Java/Others) Total ...

  10. IDFA的值什么时候会发生改变

    在何种情况下 , 应用的IDFA值会发生改变? 近期工作中须要获得一个能够唯一地标示每个不同应用的ID,之前的苹果UDID已经不让使用了. 那么我们须要使用新的IDFA来引用.可是在某些情况下这个ID ...