[书籍精读] 《你不知道的JavaScript(上卷)》精读笔记分享

写在前面

  • 书籍介绍:JavaScript这门语言简单易用,很容易上手,但其语言机制复杂微妙,即便是经验丰富的JavaScript开发人员,若是没有认真学习的话也没法真正理解。本套书直面当前JavaScript开发人员不求甚解的大趋势,深刻理解语言内部的机制,全面介绍了JavaScript中常被人误解和忽视的重要知识点。
  • 个人简评:《你不知道的JavaScript》系列分上中下三卷,这里是上卷,主要讲解做用域、原型等核心概念相关的。该系列书籍本人以为就上卷写的不错,中卷有些冗余,下卷讲ES6比较粗糙。这里推荐你们对上卷进行细读。
  • !!文末有pdf书籍、笔记思惟导图、随书代码打包下载地址,须要请自取!阅读[书籍精读系列]全部文章,请移步:推荐收藏-JavaScript书籍精读笔记系列导航

第一章 做用域是什么

1.1.编译原理

  • 编译三个步骤:一、分词/词法分析;二、解析/语法分析;三、代码生成
  • 分词/词法分析(Tokenizing/Lexing):将由字符组成的字符串分解成(对编程语言来讲)有意义的代码块,这些代码块被称为词法单元(token)
  • 解析/语法分析(Parsing):将词法单元流(数组)转换成一个由元素逐级嵌套所组成的表明了程序语法结构的树
  • 代码生成:将 AST 转换为可执行代码的过程
  • JavaScript大部分状况下编译发生在代码执行前的几微秒
  • JavaScript用尽各类办法去保证性能最佳,好比JIT能够延迟编译设置重编译

1.2.理解做用域

  • 引擎:负责整个JavaScript程序的编译及执行过程
  • 编译器负责语法分析及代码生成等
  • 做用域:负责收集并维护由全部声明的标识符(变量)组成的一系列查询,并实施一套很是严格的规则,肯定当前执行的代码对这些标识符的访问权限
  • var a = 2; 引擎认为这里有两个彻底不一样的声明, 一个由编译器在编译时处理, 另外一个则由引擎在运行时处理
  • 伪代码进行归纳:“为一个变量分配内存, 将其命名为 a, 而后将值 2 保存进这个变量
  • 当变量出如今赋值操做的左侧时进行 LHS 查询, 出如今右侧时进行 RHS 查询
  • RHS 查询与简单地查找某个变量的值别无二致,而 LHS 查询则是试图找到变量的容器自己, 从而能够对其赋值
  • LHS查询,例如a=2,对变量赋值
  • RHS查询,例如console.log(a),获取变量的值
  • 增强理解,LHS赋值操做的目标是谁,RHS谁是赋值操做的源头
  • 在严格模式中LHS查询失败时,并不会建立并返回一个全局变量,引擎会抛出同RHS查询失败时相似的ReferenceError异常

1.3.做用域嵌套

  • 在当前做用域中没法找到某个变量时,引擎就会在外层嵌套的做用域中继续查找,直到找到该变量,或抵达最外层的做用域(也就是全局做用域)为止

1.4.异常

  • 不成功的 RHS 引用会致使抛出 ReferenceError 异常。 不成功的 LHS 引用会致使自动隐式地建立一个全局变量(非严格模式下), 该变量使用 LHS 引用的目标做为标识符, 或者抛出 ReferenceError 异常(严格模式下)

第二章 词法做用域

2.1.词法阶段

  • 词法做用域就是定义在词法阶段的做用域。 换句话说,词法做用域是由你在写代码时将变量和块做用域写在哪里来决定的,所以当词法分析器处理代码时会保持做用域不变(大部分状况下是这样的)
  • 做用域查找会在找到第一个匹配的标识符时中止
  • 词法做用域:定义在词法阶段的做用域,动态做用域:做用域气泡,严格包含的,没有任何函数能够部分地出如今2个父级函数中
  • 不管函数在哪里被调用,也不管它如何被调用,它的词法做用域都只由函数被声明时所处的位置决定
  • 词法做用域是在写代码或者说定义时肯定的,而动态做用域是在运行时肯定的
  • 词法做用域关注函数在何处声明,而动态做用域关注函数从何处调用

2.2.欺骗词法

  • eval和with
  • JavaScript 中的 eval(..) 函数能够接受一个字符串为参数, 并将其中的内容视为好像在书写时就存在于程序中这个位置的代码
  • 在严格模式的程序中, eval(..) 在运行时有其本身的词法做用域, 意味着其中的声明没法修改所在的做用域
  • eval执行字符串,使其能够在运行期修改书写期的词法做用域,相似的还有setTimeout和SetInterval第一个参数传入字符串的状况
  • setTimeout(..) 和setInterval(..) 的第一个参数能够是字符串, 字符串的内容能够被解释为一段动态生成的函数代码
  • 在严格模式中,eval()在运行时有其本身的词法做用域,没法修改所在的做用域
  • new Function(..) 函数的行为也很相似, 最后一个参数能够接受代码字符串,并将其转化为动态生成的函数
  • with 能够将一个没有或有多个属性的对象处理为一个彻底隔离的词法做用域, 所以这个对象的属性也会被处理为定义在这个做用域中的词法标识符
  • 尽管 with 块能够将一个对象处理为词法做用域, 可是这个块内部正常的 var 声明并不会被限制在这个块的做用域中, 而是被添加到 with 所处的函数做用域中
  • with声明其实是根据你传递给它的对象凭空建立了一个全新的词法做用域,这两个机制(eval和with)的反作用是引擎没法在编译中对做用域查找进行优化
  • JavaScript 引擎会在编译阶段进行数项的性能优化。 其中有些优化依赖于可以根据代码的词法进行静态分析, 并预先肯定全部变量和函数的定义位置, 才能在执行过程当中快速找到标识符
  • 最悲观的状况是若是出现了 eval(..) 或 with, 全部的优化可能都是无心义的, 所以最简单的作法就是彻底不作任何优化

第三章 函数做用域和块做用域

3.1.函数中的做用域

  • 函数做用域的含义是指属于这个函数的所有变量均可以在整个函数的范围内使用及复用(包括嵌套的做用域)

3.2.隐藏内部实现

  • 最小受权或最小暴露原则:是指在软件设计中, 应该最小限度地暴露必要内容,而将其余内容都“隐藏” 起来,好比某个模块或对象的 API 设计
  • 变量冲突的一个典型例子存在于全局做用域中。当程序中加载了多个第三方库时,若是它们没有妥善地将内部私有的函数或变量隐藏起来,就会很容易引起冲突

3.3.函数做用域

  • 区分函数声明和函数表达式最简单方法看function关键字出如今声明中的位置(不只仅是一行代码,而是整个声明中的位置) 5
  • 函数声明和函数表达式之间最重要的区别是它们的名称标识符将会绑定在何处
  • (function foo(){ .. }) 做为函数表达式意味着 foo 只能在 .. 所表明的位置中被访问,外部做用域则不行
  • 匿名函数表达式几个缺点须要考虑:1.匿名函数在栈追踪中不会显示出有意义的函数名,使得调试很困难;2.若是没有函数名,当函数须要引用自身时只能使用已通过期的 arguments.callee 引用,好比在递归中;3.匿名函数省略了对于代码可读性/可理解性很重要的函数名。一个描述性的名称可让代码不言自明

3.4.块做用域

  • 变量的声明应该距离使用的地方越近越好,并最大限度地本地化
  • 块做用域是一个用来对以前的最小受权原则进行扩展的工具,将代码从在函数中隐藏信息扩展为在块中隐藏信息
  • 用with从对象中建立出的做用域仅在with声明中而非外部做用域中有效
  • try/catch的catch分句会建立一个块做用域,其中声明的变量仅在catch内部有效
  • let 关键字能够将变量绑定到所在的任意做用域中(一般是 { .. } 内部)
  • for 循环头部的 let 不只将 i 绑定到了 for 循环的块中,事实上它将其从新绑定到了循环的每个迭代中,确保使用上一个循环迭代结束时的值从新进行赋值
  • Tracer,Google维护的项目,正是用来将ES6代码转换成兼容ES6以前的环境
  • IIFE和try/catch均可以用来实现let块做用域,但try/catch性能的确很糟糕
  • var a=2;JavaScript实际上会将其当作两个声明。var a;a=2;第一个定义声明是在编译阶段进行的,第二个赋值会留在原地等待执行阶段

第四章 提高

4.1.先有鸡仍是先有蛋

  • 函数做用域和块做用域的行为是同样的,能够总结为:任何声明在某个做用域内的变量,都将附属于这个做用域

4.2.编译器再度来袭

  • 引擎会在解释 JavaScript 代码以前首先对其进行编译。编译阶段中的一部分工做就是找到全部的声明,并用合适的做用域将它们关联起来
  • 正确的思考思路是,包括变量和函数在内的全部声明都会在任何代码被执行前首先被处理
  • 变量和函数声明从它们在代码中出现的位置被“移动”到了最上面。这个过程就叫做提高
  • 只有声明自己会被提高,但函数会首先被提高,而后才是变量
  • 函数声明后面同名的var声明会被忽略掉

4.3.函数优先

  • 一个值得注意的细节(这个细节能够出如今有多个“重复”声明的代码中)是函数会首先被提高, 而后才是变量
  • 一个普通块内部的函数声明一般会被提高到所在做用域的顶部

第五章 做用域闭包

5.1.启示

  • 闭包是基于词法做用域书写代码时所产生的天然结果,你甚至不须要为了利用它们而有意识地建立闭包

5.2.实质问题

  • 函数在定义时的词法做用域之外的地方被调用,闭包使得函数能够继续访问定义时的词法做用域
  • 若是将(访问它们各自词法做用域的)函数看成第一级的值类型并处处传递,你就会看到闭包在这些函数中的应用,在定时器,事件监听器,Ajax请求,跨窗口通讯,Web workers或者任何其余的异步(或者同步)任务中,只要有用了回调函数,实际上就是在使用闭包
  • 循环和闭包:延迟函数的回调会在循环结束时才执行
  • for/let行为指出变量在循环过程当中不止被声明一次,每次迭代都会声明
  • 模块模式需具有的两个必要条件:一、必须有外部的封闭函数,该函数必须至少被调用一次;二、封闭函数必须返回至少一个内部函数,这样内部函数才能在私有做用域中造成闭包,而且能够访问或修改私有的状态

5.3.如今我懂了

  • 在定时器、事件监听器、Ajax 请求、 跨窗口通讯、 Web Workers 或者任何其余的异步(或者同步)任务中,只要使用了回调函数, 实际上就是在使用闭包

5.4.循环和闭包

  • 咱们使用 IIFE 在每次迭代时都建立一个新的做用域。换句话说,每次迭代咱们都须要一个块做用域

5.5.模块

  • 模块模式须要具有两个必要条件:必须有外部的封闭函数,该函数必须至少被调用一次(每次调用都会建立一个新的模块实例);封闭函数必须返回至少一个内部函数,这样内部函数才能在私有做用域中造成闭包,而且能够访问或者修改私有的状态;
  • 基于函数的模块并非一个能被稳定识别的模式(编译器没法识别),它们的 API 语义只有在运行时才会被考虑进来。所以能够在运行时修改一个模块的API
  • ES6 模块 API 更加稳定(API 不会在运行时改变)

附录

  • 动态做用域并不关心函数和做用域是如何声明以及在何处声明的,只关心它们从何处调用
  • 换句话说,做用域链是基于调用栈的,而不是代码中的做用域嵌套
  • 主要区别:词法做用域是在写代码或者说定义时肯定的,而动态做用域是在运行时肯定的

第二部分 this和对象原型

第一章 关于this

1.1.为何要用this

  • 箭头函数:一、容易让人混淆了this绑定规则和词法做用域规则,二、另外箭头函数是匿名而非具名的

1.2.误解

  • this误解:一、指向自身,误理解成指向函数自身;二、指向函数做用域,误理解成指向函数的做用域
  • 为何须要从函数内部引用函数自身:常见的缘由是递归(从函数内部调用这个函数)或者能够写一个在第一次被调用后本身解除绑定的事件处理器
  • 一种传统的可是如今已经被弃用和批判的用法,是使用 arguments.callee 来引用当前正在运行的函数对象。这是惟一一种能够从匿名函数对象内部引用自身的方法

1.3.this究竟是什么

  • this是在运行时进行绑定的,并非在编写时绑定。它的上下文取决于函数调用时的各类条件
  • this的绑定和函数声明的位置没有关系,只取决于函数的调用方式

第二章 this全面解析

2.1.调用位置

  • 调用位置:寻找函数在代码中被调用的地方(而不是声明的位置)
  • 调用栈:为了到达当前执行位置所调用的全部函数,在JavaScript调试器中能够很方便查看
  • 另外一个查看调用栈的方法是使用浏览器的调试工具。 绝大多数现代桌面浏览器都内置了开发者工具,其中包含 JavaScript 调试器

2.2.绑定规则

  • 默认绑定、隐式绑定、显式绑定、new绑定
  • 默认绑定:常见的独立函数调用,没法应用其余规则时默认规则。但使用严格模式(strict mode)时,不能将全局对象用于默认绑定,所以this会绑定到undefined
  • 隐式绑定:须要考虑调用位置是否有上下文对象,或者说是否被某个对象拥有或包含。当函数引用有上下文对象时,该规则会把函数调用中的this绑定到这个上下文对象
  • 一个最多见的 this 绑定问题就是被隐式绑定的函数会丢失绑定对象,也就是说它会应用默认绑定,从而把 this 绑定到全局对象或者 undefined 上,取决因而否是严格模式
  • 隐式丢失:一、var bar = obj.foo; bar();//会应用默认绑定;二、doFoo(obj.foo);//函数做为参数传递,隐式赋值;三、setTimeout(obj.foo, 100);//函数传入语言内置函数,一样
  • 显式绑定:想在某个对象上强调调用函数,可使用函数call和apply方法。foo.call(obj)调用foo时强制把this绑定到obj上,若是传入原始值,会转换成它的对象形式,如new String(...),new Boolean(...)一般称装箱
  • 也会出现绑定丢失问题,硬绑定:不可能再修改它的this。bind(...)会返回一个硬编码的新函数
  • 因为硬绑定是一种很是经常使用的模式 因此在 ES5 中提供了内置的方法Function.prototype.bind
  • new 绑定:JavaScript中new的机制实际上和面向类的语言彻底不一样,使用new来调用函数时,一般会自动执行下面的操做:一、建立(或者说构造)一个全新的对象;二、这个新对象会被执行[[prototype]]链接;三、新对象会绑定到函数调用的this;四、若是函数没有返回其余对象,那么new表达式中的函数调用会自动返回这个新对象

2.3.优先级

  • 四条规则优先级:一、显式绑定比隐式绑定更高;二、new绑定比隐式绑定更高;三、new修改了硬绑定调用中的this
  • new 和 call/apply 没法一块儿使用, 所以没法经过 new foo.call(obj1) 来直接进行测试
  • MDN 提供的一种bind(..) 实现
  • 之因此要在 new 中使用硬绑定函数,主要目的是预先设置函数的一些参数,这样在使用new 进行初始化时就能够只传入其他的参数。 bind(..) 的功能之一就是能够把除了第一个参数(第一个参数用于绑定 this)以外的其余参数都传给下层的函数(这种技术称为“部分应用”, 是“柯里化” 的一种)
  • 能够按照下面的顺序来进行判断this:1. 函数是否在 new 中调用(new 绑定)?若是是的话 this 绑定的是新建立的对象;2. 函数是否经过 call、apply(显式绑定)或者硬绑定调用?若是是的话,this 绑定的是指定的对象;3. 函数是否在某个上下文对象中调用(隐式绑定)?若是是的话,this 绑定的是那个上下文对象;4. 若是都不是的话,使用默认绑定。若是在严格模式下, 就绑定到 undefined,不然绑定到全局对象;

2.4.绑定例外

  • 若是把null或者undefined做为this的绑定对象传入call,apply或者bind会被忽略,实际应用的是默认绑定规则
  • Object.create(null)和{}很像,但不会建立Object.prototype,因此比{}更空
  • 注意:对于默认绑定来讲,决定 this 绑定对象的并非调用位置是否处于严格模式,而是函数体是否处于严格模式

2.5.this词法

  • ES6 中介绍了一种没法使用这些规则的特殊函数类型:箭头函数
  • 箭头函数不使用 this 的四种标准规则,而是根据外层(函数或者全局)做用域来决定 this

第三章 对象

3.1.语法

  • 对象能够两种形式定义:构造形式和声明形式,即new Object()和{}
  • 惟一的区别是,在文字声明中你能够添加多个键/值对,可是在构造形式中你必须逐个添加属性

3.2.类型

  • JavaScript共六种主要类型(语言类型),string、number、boolean、null、undefined、object,前五种是基本类型
  • JavaScript中万物皆是对象,显然是错误的
  • 有些内置对象的名字看起来和简单基础类型同样,不过实际上它们的关系更复杂
  • 原始值 "I am a string" 并非一个对象, 它只是一个字面量,而且是一个不可变的值。若是要在这个字面量上执行一些操做,好比获取长度、访问其中某个字符等,那须要将其转换为 String 对象
  • 在JavaScript中二进制前三位都为0的会被判断为object类型,null的二进制所有是0,故被断定为Object类型
  • null和undefined没有对应的构造形式,只有文字形式,而Date只有构造形式
  • 对于 Object、 Array、 Function 和 RegExp(正则表达式) 来讲, 不管使用文字形式仍是构造形式,它们都是对象,不是字面量
  • Error 对象不多在代码中显式建立,通常是在抛出异常时被自动建立。也可使用 new Error(..) 这种构造形式来建立,不过通常来讲用不着
  • 数组和普通的对象都根据其对应的行为和用途进行了优化,因此最好只用对象来存储键/值对,只用数组来存储数值下标/值对 14

3.3.内容

  • .a 语法一般被称为“属性访问”, ["a"] 语法一般被称为“键访问”
  • 这两种语法的主要区别在于 . 操做符要求属性名知足标识符的命名规范,而 [".."] 语法 能够接受任意 UTF-8/Unicode 字符串做为属性名
  • 在对象中,属性名永远都是字符串。若是你使用 string(字面量)之外的其余值做为属性名,那它首先会被转换为一个字符串
  • 数组也是对象,因此虽然每一个下标都是整数,你仍然能够给数组添加属性。虽然添加了命名属性(不管是经过 . 语法仍是 [] 语法), 数组的 length 值并未发生变化
  • 咱们还不肯定“复制” 一个函数意味着什么。有些人会经过 toString() 来序列化一个函数的源代码(可是结果取决于 JavaScript 的具体实现, 并且不一样的引擎对于不一样类型的函数处理方式并不彻底相同)
  • 对象属性描述符:value(值)、writable(是否可修改)、enumrable(是否可枚举)、configurable(是否可配置)、get、set
  • 无论是否是处于严格模式, 尝试修改一个不可配置的属性描述符都会出错。注意:如你所见,把 configurable 修改为false 是单向操做, 没法撤销!
  • 除了没法修改, configurable:false 还会禁止删除这个属性
  • 不变性:一、对象常量 writable: false和configurable: false;二、禁止扩展:Object.preventExtensions(obj);三、密封:Object.seal在现有对象上调用Object.preventExtensions,并把现有属性标记configuable: false;四、冻结Object.freeze在现有对象上调用Object.seal并把数据访问属性标记writable: false
  • 存在性:in操做符会检查属性是否在对象及其[[prototype]]原型链中,hasOwnProperty只会检查属性是否在对象中,不会检查[[prototype]]链,object.keys和object.getOwnPropertyNames都只会查找对象直接包含的属性

3.4.遍历

  • 遍历:forEach遍历并忽略回调函数返回值,没法break跳出;every一直运行到回调函数返回false,some一直运行到回调函数返回true;for...of遍历值而不是属性
  • every(..) 和 some(..) 中特殊的返回值和普通 for 循环中的 break 语句相似,它们会提早终止遍历

第四章 混合对象“类”

4.1.类理论

  • 面向对象编程强调的是数据和操做数据的行为本质上是互相关联的(固然,不一样的数据有不一样的行为),所以好的设计就是把数据以及和它相关的行为打包(或者说封装)起来
  • 类的另外一个核心概念是多态,这个概念是说父类的通用行为能够被子类用更特殊的行为重写
  • 面向类的设计模式:实例化(instantiation) 继承(inheritance)(相对)多态(polymophism)

4.2.类的机制

  • 类的机制:许多面向类的语言中,“标准库”会提供stack类,是一种"栈"数据结构
  • Stack类内部会有一些变量来存储数据,同时提供一些公有的可访问行为(方法),从而让你的代码能够和(隐藏的)数据进行交互(好比添加、删除数据)
  • 蓝图---建筑,类比于 类---实例

4.3.类的继承

  • 对于真正的类来讲,构造函数是属于类的,而JavaScript是相反的,实际上类是属于构造函数的,类的继承其实就是复制
  • 多态是一个很是普遍的话题,咱们如今所说的“相对” 只是多态的一个方面:任何方法均可以引用继承层次中高层的方法(不管高层的方法名和当前方法名是否相同)
  • 多态的另外一个方面是,在继承链的不一样层次中一个方法名能够被屡次定义,当调用方法时会自动选择合适的定义
  • 须要注意,子类获得的仅仅是继承自父类行为的一份副本。子类对继承到的一个方法进行“重写”,不会影响父类中的方法,这两个方法互不影响,所以才能使用相对多态引用访问父类中的方法
  • 多重继承意味着全部父类的定义都会被复制到子类中

4.4.混入

  • 在继承或者实例化时, JavaScript 的对象机制并不会自动执行复制行为。简单来讲, JavaScript 中只有对象,并不存在能够被实例化的“类”
  • 显式混入mixin,没法(用标准、可靠的方法)真正的复制,只能复制对共享函数对象的引用
  • 隐式混入call,经过this绑定实现
  • 混入模式(不管显式仍是隐式) 能够用来模拟类的复制行为,可是一般会产生丑陋而且脆弱的语法,好比显式伪多态(OtherObj.methodName.call(this, ...)), 这会让代码更加难懂而且难以维护

第五章 原型

5.1.[[Prototype]]

  • Object的原理:全部普通的[prototype]链最终都会指向内置的Object.prototype
  • constructor是一个很是不可靠而且不安全的引用,尽可能避免使用这些引用
  • 调用Object.create会凭空建立一个“新”对象并把新对象内部的[prototype]关联到你指定的对象

5.2.“类”

  • a instanceof Foo,在a的整条[prototype]链中是否有Foo.prototype指向的对象
  • JavaScript和面向类的语言不一样,它并无类来做为对象的抽象模式或者说蓝图,JavaScript中只有对象
  • 继承意味着复制操做,JavaScript(默认)并不会复制对象属性,相反,JavaScript会在两个对象之间建立一个关联,这样一个对象就能够经过委托访问另外一个对象的属性和函数

5.3.技术

  • 在JavaScript中,对于“构造函数”最准确的解释是全部带new的函数调用
  • 实际上,构造函数和你程序中其余函数没有任何区别,当在普通函数调用前加上new关键词以后,就会把这个函数调用变成一个“构造函数调用”
  • new会劫持全部普通函数并用构造函数的形式来调用它
  • 奇怪的__prototype__(在ES6以前并非标准),属性引用了内部(prototype)对象

5.4.对象关联

  • 原型链:若是在对象上没有找到须要的属性或者方法引用,引擎就会继续在[prototype]关联的对象上进行查找,同理,若是在后者中也没找到,就到须要的引用就会继续查找它的prototype,以此类推
  • Object.create会建立一个新对象(bar)并把它关联到咱们指定的对象(foo)
  • 能够充分发挥[prototype]机制的威力(委托)而且避免没必要要的麻烦(好比使用new的构造函数调用会生成.prototype和.constructor引用)
  • Object.create(null)会建立一个拥有空[prototype]连接的对象,这个对象没法委托

第六章 行为委托

6.1.面向委托的设计

  • 回顾:[Prototype]机制就是指对象中的一个内部连接引用另外一个对象
  • JavaScript中这个机制的本质就是对象之间的关联关系
  • 把思路从类和继承的设计模式转换到委托行为的设计模式

6.2.类与对象

  • 委托行为意味着某些对象(XYZ)在找不到属性或者方法引用时会把这个请求委托给另外一个对象(Task),这是一种极其强大的设计模式和父类、子类、继承、多态彻底不一样
  • 对象关联风格的代码相较于对象与类风格更加简洁,由于只关注一件事,对象之间的关系

6.4.更好的语法

  • ES6中的class仍然是经过[prototype]机制实现的
  • 匿名函数没有name标识符,会致使:一、自我引用(递归,事件绑定等)更难;二、调用栈更难追踪;三、代码(稍微)更难理解
  • 鸭子类型:辨别特性,很脆弱的设计

6.5.内省

  • 内省:检查实例的类型,主要目的是经过建立方式来判断对象的结构和功能
  • instanceof:由于Foo.prototype在a1的[prototype]链上,因此instanceof操做告诉咱们a1是Foo类的一个实例。从语法角度上说:instanceof彷佛是检查a1和Foo的关系,但实际上它想说的是a1和Foo.prototype(引用的对象)是互相关联的
  • 行为委托认为对象之间是兄弟关系,互相委托,而不是父类和子类的关系。咱们能够选择在JavaScript中努力实现类机制,也能够拥抱更天然的[prototype]委托机制

写在后面

  • pdf书籍、笔记思惟导图、随书代码打包下载地址:后面补上
  • 纸质书京东购买地址:https://u.jd.com/FwSmuH(推荐购买纸质书来学习)
相关文章
相关标签/搜索