• 《你不知道的JavaScript[下卷]》【15】—— ES6语法


    你不知道的Javascript(下卷)笔记

    深入编程

    Javascript 是解释型的,实际上是动态编译程序,然后立即执行编译后的代码。
    编写代码是为了给开发者阅读的,代码应该解释为什么,而非是什么。

    深入 Javascript

    Javascript 中”假”值的列表:

    • “” (空字符串)
    • 0, -0, NaN (无效数字)
    • null、undefined
    • false

    任何不在”假”值列表中的值都是”真”值,例如[],{}等。
    在Jvascript中,闭包最常见的应用是模块模式。模块模式允许你定义外部不可见的私有实现细节(变量、函数),同时也可以提供允许从外部访问的公开API。

    ES6及更新版本

    transpiling:通过transpiling(transformation+compiling,转换+编译)的技术,将ES6代码转化为等价(或近似)的可以在ES5环境下工作的代码。
    let+for

    1
    2
    3
    4
    5
    6
    7
    var funcs = [];
    for(let i = 0; i < 5; i++) {
    funcs.push(function() {
    console.log(i);
    })
    }
    funcs[3](); // 3

    for循环头部的let i 不只是为for循环本身声明了一个i,而是为循环的每一次迭代都重新声明了一个新的i。这意味着loop迭代内部创建的闭包封闭的是每次迭代中的变量。

    spread/rest

    ES6引入的新的运算符…,通常称为spread/rest运算符,取决于在哪/如何被使用。

    1
    2
    3
    4
    // spread
    var a = [2,3,4];
    var b = [1, ...a, 5];
    console.log(b); // [1,2,3,4,5]
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // rest
    function foo(x, y, ...z) {
    console.log(x, y, z)
    }
    foo(1,2,3,4,5) // 1 2 [3, 4, 5]
    // 如果没有明明参数的话,会收集所有的参数
    function foo(...args) {
    console.log(args);
    }
    foo(1,2,3,4,5); // [1,2,3,4,5]

    这种用法最好的一点是,为类数组 arguments 提供了非常可靠的替代形式。

    箭头函数

    箭头函数总是函数表达式,并不存在箭头函数声明,箭头函数是匿名函数表达式,它们没有用于递归或者事件绑定/解绑定的命名引用。
    箭头函数的主要设计目的以特定的方式改变this的行为特性

    1
    2
    3
    4
    5
    6
    7
    8
    9
    var controller = {
    makeRequest: function() {
    var self = this;
    btn.addEventListner('click', function(){
    //
    self.makeRequest(...);
    }, false)
    }
    }

    因为 this 是动态的,通过变量 self 依赖于词法作用域的可预测性。在箭头函数内部,this 绑定不是动态的,是词法的。这是箭头函数的主要设计特性。
    除了词法 this,箭头函数还有词法 arguments,它们没有自己的 arguments 数组,而是继承自父层–词法 super 和 new.target 也是一样。

    代码组织

    模块

    • ES6 使用基于文件的模块,也就是一个文件一个模块。
    • ES6 模块的 API 是静态的。也就是说,需要在模块的公开 API 中静态定义所有最高层导出,之后无法补充。
    • ES6 模块是单例。
    • 模块的公开 API 暴露的属性和方法并不仅仅是普通的值或引用的赋值。他们是到内部模块定义中的标识符的实际绑定(几乎类似于指针)。
    • 导入模块和静态请求加载(如果还没加载的话)这个模块是一样的

    import 和 export 都必须出现在使用它们的最顶层作用域。举例来说,不能把 import 或 export 放在 if 条件中,它必须出现在所有代码块和函数的外面。
    比较下面两段代码:

    1
    2
    3
    4
    function foo() {
    // ...
    }
    export default foo;
    1
    2
    3
    4
    function foo() {
    // ...
    }
    export { foo as default };

    第一段代码中,导出的是此刻到函数表达式的绑定,而不是标识符foo,也就是说,export default… 接受的是一个表达式,如果之后 foo 赋了一个不同的值,模块导入得到的仍然是原来导出的函数,而不是新的值
    第二段代码,默认导出绑定实际上绑定到 foo 标识符而不是它的值,所以如果之后修改了 foo 的值,导入的值也会更新

    模块依赖环

    import 语句使用外部环境(浏览器、Node.js等)提供的独立机制,来实际把模块标识符字符串解析成可用的指令,用于寻找和加载所需的模块,这个机制就是系统模块加载器。浏览器中环境提供的模块加载器会把模块标识符解析为URL,在Node.js这种服务器上就解析为本地文件系统路径。

    // TODO

    笔记-你不知道的JS(下卷)(未完)

    第一部分 JS基础

    第一章与第二章 基础

    1.内置类型:string、number、boolean、null&undefined、object、symbol(ES6),object子类型:object(一般)、array、function、Date。推荐一张思维导图

    2.typeof:用于检查值的类型,而不是变量的类型

    typeof "string": "string"
    typeof 1 : "number"          typeof NAN : number
    typeof true : "boolean"
    typeof null : "object"
    typeof {} : "object"
    typeof [] : "object"
    typeof function(){} : "function"

    3.==与===:==进行类型转换,检查值是否相等;===检查类型和值是否都相同;注意对象之间比较的是引用,引用地址不等时,==与===效果一样

    "1" === "1"    //true
    "1" === 1      //false
    "1" == 1       //true
    new String("1") == new String("1")   //false

    注:==只触发隐式类型转换,与装箱(封箱)无关

    4.装箱(封箱):string对应String,number对应Number,boolean对应Boolean。在调用内置类型方法时,就会自动触发封箱过程,如"1".length、"a".toUpperCase、(3.1415).toFixed(2) 等等。

    typeof "1"      //"string"
    typeof new String("1")       //"object"

    5.var vs let:此处简述主要区别,var函数级作用域(存在函数内作用域提升),let块级作用域(常区别于:if、for、while等块逻辑)

    6.let vs const:此处简述,了解c++指针对此处理解非常简单,当存在变更地址的逻辑时:无论时简单类型(string、number、boolean),还是对象类型(object),只能用let;当只变更值时,简单类型(string、number、boolean)只能用let,对象类型(object),推荐用const(加快编译,当然也可用let);当值和地址都不变时,上述类型都推荐用const。

    7.IIFE(立即调用函数表达式):主要应用于,创建新的变量作用域,保护外部作用域不被污染。其它应用都是无用炫技,但是在let面前这些都是花里胡哨。

    8.闭包与作用域(参见上卷笔记):简单提一句:在声明时可访问的变量,在执行时也可访问

    9.this与原型(参见上卷笔记)

    10.polyfill vs transpile:polyfill相当于打补丁,当代码出现利用ES6特性时,通过增加函数的方式来保证逻辑正常运行,常用ES6-Shim;transpile相当于转译,把遵循ES6(或后续ES7等)写的代码转译成ES5,比如Babel。这两者都是为了旧版浏览器能运行那些遵循新规则的JS代码。

    11.非JS:DOM API(如getElementById),BOM API(如alert、prompt、console.log),此处推荐文章:BOM和DOM详解

     

    第三章

    摘抄三勺鸡汤

    1.学习其它语言的认真的开发者会想要花费精力学习他们使用的语言的方方面面,但JS开发者却通常不会学习这门语言的很多内容(原书的翻译)。(这个的原因有一部分是因为很多开发者都是转型过来,原先做桌面的,做后端的,为了吃饭,转前端了,觉得前端是脚本,没那么复杂,用到哪学到哪,故不会系统且全面的进行学习。70-80的程序员大都如此。)

    2.本书的宗旨是,所有JS开发者都可以,也应该学习这门伟大的语言的方方面面。个人偏见、框架假定(?)和项目的截止日期都不应该成为你从不学习和深入理解JS的借口。(Flag)

    3.“你不知道的JS”既不是批判也不是攻击,这是一种领悟。(多么痛的领悟)

     

    第二部分 ES6新增

    第一章及第二章 语法

    1.再谈块作用域 let:

    //第一种显示声明
    {
      let a;
    }
    //第二种(目前只在for语句出现)
    let(..){..}

    重要:for循环中let i,不是为整个for循环声明一个变量i,而是为每一次迭代声明一个独立的变量i。

    考虑如下代码:

    //let 块作用域
    var funcs = [];
    for (let i=0; i<5; i++){
        funcs.push( function(){
            console.log(i);
        });
    }
    funcs[3]();     // 3
    
    //var 函数作用域
    var funcs = [];
    for (var i=0; i<5; i++){
        funcs.push( function(){
            console.log(i);
        });
    }
    funcs[3]();     // 5

     

    2.spread/rest:常用于形参与实参的传参过程。

    function foo(x,y,z){
       console.log(x,y,z);
    }
    foo( ...[1,2,3] )  // 1,2,3
    
    function foo(x,y,...z){
       console.log(x,y,z);
    }
    foo(1,2,3,4,5)    // 1,2, [3,4,5]

    用于实参数组时,展开数组传参;用于形参时,实参对应剩余部分归集为一数组。用于形参时,...args又称为rest参数,可替换原有繁琐的arguments类数组对象。

     

    3.默认参数值:

    function foo(x = 1,y = 2){
       console.log(x,y);
    }
    foo()   //1,2
    foo(5)  //5,2
    foo(5, undefined)  //5,2
    foo(5, null)    //5,0  null强制转换为0

     

    4.解构

    个人意见除了在import中习惯使用,其余时候基本就是用来缩减一些临时变量的代码,总的来说花里胡哨,包括下一节的简洁属性和简洁方法,老老实实写就ok了,对应上年纪的或者已经习惯旧有写法的人来说,花在这上面熟悉新写法,不如把脑袋里内存用于其它更值得的地方,此处性价比不高,过(鉴定完毕,莫抬杠)。

     

    5.模板字面量:

    let name = "sz";
    let greeting = `hello ${name}`;  //hello sz
    //在模板中可调用函数
    function upper(s){
      s.toUpperCase();
    }
    let greeting = `hello ${upper(name)}`;  //hello SZ
    let more = `hello ${upper(`dear ${name}`)}`;  //hello DEAR SZ
    
    //raw 原始字符串
    console.log(`Hello
    World`);  //Hello 换行 World
    console.log(String.raw`Hello
    World`);  //Hello
    World

     

    6.箭头函数 =>

    重要:箭头函数不只是更短的语法,而是bind(this)。

    非常重要:this在非箭头函数里时动态的,即看调用者是谁;this在箭头函数中是词法的,即词法作用域下的this(一开始较难理解),可简单理解为:取决于它的外层函数,外层函数的this指向谁,它的this也指向谁。

    var controller = {
        foo : function(){
           btn.addEventListener("click", ()=>{
                this.foo();                    //use like controller.foo()   this => controller 
           });
        }
    }
    
    //important
    var controller = {
        foo :  () => {                     //.........change.........
           btn.addEventListener("click", ()=>{
                this.foo();                    //Error 由于词法作用域,this => 全局作用域,没有定义foo
           });
        }
    }
    

     

    7.欠正则表达式、Unicode,Symbol作为一新的原生类型,花里胡哨,除了在声明事件时,用符号来代替字符串常量,目的竟然是为了避免有意无意出现重复值。。。Symbol在迭代器以及内置对象中应用广泛,本身自定义来使用,两字谢谢。

     

    待续。。。

    编辑于 2018-10-06
     
     
     
     
     

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

     

    - 小磊哥 er 的全栈技术 - SegmentFault 思否
    书籍介绍:JavaScript 这门语言简单易用,很容易上手,但其语言机制复杂微妙,即使是经验丰富的 JavaScript 开发人员,如果没有认真学习的话也无法真正理解。
    写在前面
    书籍介绍:JavaScript 这门语言简单易用,很容易上手,但其语言机制复杂微妙,即使是经验丰富的 JavaScript 开发人员,如果没有认真学习的话也无法真正理解。本套书直面当前 JavaScript 开发人员不求甚解的大趋势,深入理解语言内部的机制,全面介绍了 JavaScript 中常被人误解和忽视的重要知识点。
    我的简评:《你不知道的 JavaScript》系列分上中下三卷,这里是下卷,主要讲解 ES6 相关的。该系列书籍本人觉得就上卷写的不错,中卷有些冗余,下卷讲 ES6 比较粗糙。不过有空翻一翻,还是多少有些收获。
    !!文末有 pdf 书籍、笔记思维导图、随书代码打包下载地址,需要请自取!阅读「书籍精读系列」所有笔记,请移步:推荐收藏 - JavaScript 书籍精读笔记系列导航

    第 1 章 ES?现在与未来
    与 ES5 不同,ES6 并不仅仅是为这个语言新增一组 API。它包括一组新的语法形式,其中的一部分可能是要花些时间才能理解和熟悉的。它还包括各种各样的新的组织形式和操作各种数据类型的新的辅助 API
    1.2.transpiling
    transpiling(transformation+compiling,转换 + 编译)的技术。简单的说,其思路是利用专门的工具把你的 ES6 代码转换为等价(或近似)的可以在 ES5 环境下工作的代码
    并非所有的 ES6 新特性都需要使用 transpiler,还有 polyfill(也称为 shim)这种模式。在可能的情况下,polyfill 会为新环境中的行为定义在旧环境中的等价行为。语法不能 polyfill,而 API 通常可以

    第 2 章 语法
    2.1. 块作用域声明
    JavaScript 中变量作用域的基本单元一直是 function。如果需要创建一个块作用域,最普遍的方法除了普通的函数声明之外,就是立即调用函数表达式(IIFE)
    let 声明:可以创建绑定到任意块的声明。只需要一对 {..} 就可以创建一个作用域;过早访问 let 声明的引用导致的这个 ReferenceError 严格说叫做临时死亡区错误(TDZ)-- 你在访问一个已经声明但没有初始化的变量;声明时没有赋值的变量会自动赋值为 undefined,所以 let b;就等价于 let b = undefined;;未声明的变量,所以 typeof 是检查它是否存在的唯一安全的方法为什么我坚持认为应该把所有的 let 声明放在其所在作用域的最前面了,这样就完全避免了不小心过早访问的问题
    const 声明:const ,用于创建常量;它是一个设定了初始值之后就只读的变量;const 声明必须要有显式的初始化;如果这个值是复杂值,比如对象或者数组,其内容仍然是可以修改的;是否使用 const,理论上说,引擎更容易了解这个变量的值 / 类型永远不会改变,那么它就可以取消某些可能的追踪;
    块作用域函数:从 ES6 开始,块内声明的函数,其作用域在这个块内
    2.2.spread/rest
    当... 用在数组之前时(实际上是任何 iterable),它会把这个变量 “展开” 为各个独立的值
    ... 的另外一种常见用法基本上可以被看作反向的行为;... 把一系列值收集到一起成为一个数组
    2.3. 默认参数值
    可能 JavaScript 最常见的一个技巧就是关于设定函数参数默认值的
    只能通过传入比 “期望” 更少的参数来省略最后的若干参数(例如,后侧的),而无法省略位于参数列表中间或者起始处的参数
    一个很重要的需要记住的 JavaScript 设计原则:undefined 意味着缺失。也就是说,undefined 和缺失是无法区别的,至少对于函数参数来说是如此
    默认值表达式:默认值表达式是惰性求值的,这意味着它们只在需要的时候运行 -- 也就是说,是在参数的值省略或者为 undefined 的时候;Function.prototype 本身就是一个没有操作的空函数
    2.4. 解构
    可以把将数组或者对象属性中带索引的值手动赋值看作结构化赋值。专门语法,专用于数组解构和对象解构
    不只是声明:除非需要把所有的赋值表达式都当作声明,否则不应该在赋值中混入声明。不然会出现语法错误
    重复赋值:对象解构形式允许多次列出同一个源属性(持有值类型任意);记住:解构的目的不只是为了打字更少,而是为了可读性更强
    2.5. 太多,太少,刚刚好
    如果为比解构 / 分解出来的值更多的值赋值,那么就像期望的一样,多余的值会被赋为 undefined
    默认值赋值:使用与前面默认函数参数值类似的 = 语法,解构的两种形式都可以提供一个用来赋值的默认值
    嵌套解构:可以把嵌套解构当作一种展开对象名字空间的简单方法
    解构参数:解构赋值 / 默认值会被放在参数列表中,而重组的过程会被放在函数体的 return 语句中
    2.6. 对象字面量扩展
    简洁属性
    简洁方法:严格说来,ES5 定义了 getter/setter 字面量形式,但是没怎么被使用,主要是因为缺少 transpiler 来处理这个新语法(实际上也是 ES5 新增的唯一主要新语法)
    计算属性名:对象字面定义属性名位置的[..]中可以放置任意合法表达式;计算属性名最常见的用法可能就是和 Symbols 共同使用;
    设定[[Prototype]]
    super 对象
    2.7. 模板字面量
    这个特性的名称非常具有误导性,其根据你对单词模板 (template) 的理解而定
    插入字符串字面量中的换行(新行)会在字符串值中被保留
    插入表达式:在插入字符串字面量的 ${..} 内可以出现任何合法的表达式,包括函数调用、在线函数表达式调用,甚至其他插入字符串字面量;插入字符串字面量在它出现的词法作用域内,没有任何形式的动态作用域;
    标签模板字面量:重新命名这个特性来明确表达其功能:标签字符串字面量;哪些实际应用(用来把数字格式化为美元表示法;其他应用包括全球化、本地化等的特殊处理)
    2.8. 箭头函数
    箭头函数定义包括一个参数列表(零个或多个参数,如果参数个数不是一个的话要用 (..) 包围起来),然后是标识 =>,函数体放在最后
    箭头函数总是函数表达式;并不存在箭头函数声明
    箭头函数是匿名函数表达式 -- 他们没有用于递归或者事件绑定 / 解绑定的命名引用
    我认为 => 箭头函数转变带来的可读性与被转化函数的长度负相关。这个函数越长,=> 带来的好处就越小;函数越短,=> 带来的好处就越大
    不只是更短的语法,而是 this:对=>的关注多数都在于从代码中去掉 function、return 和 {..} 节省了那些宝贵的键盘输入;对=>的关注多数都在于从代码中去掉 function、return 和 {..} 节省了那些宝贵的键盘输入;在箭头函数内部,this 绑定不是动态的,而是词法的;底线:=>是关于 this、arguments 和 super 的词法绑定。这个 ES6 的特性设计用来修正一些常见的问题,而不是 bug、巧合或者错误
    2.9.for...of 循环
    for..of 循环的值必须是一个 iterable,或者说它必须可以转换 / 封箱到一个 iterable 对象的值。iterable 就是一个能够产生迭代器供循环使用的对象
    JavaScript 中默认为(或提供)iterable 的标准内建值包括:Arrays、Strings、Generators、Collections/TypedArrays
    for..of 循环也可以通过 break、continue、return(如果在函数中的话)提前终止,并抛出异常
    2.10. 正则表达式
    一个事实:JavaScript 中的正则表达式很长时间以来基本上没有什么变化
    Unicode 标识:在 ES6 之前,正则表达式只能基于 PMB 字符匹配,这意味着那些扩展字符会被当作两个独立的字符来匹配;在 ES6 中,u 标识符表示正则表达式用 Unicode(UTF-16)字符来解释处理字符串,把这样的扩展字符串当作单个实体来匹配;影响的是匹配部分的长度;还有一点需要注意,u 标识符使得 + 和 * 这样的量词把整个 Unicode 码点作为单个字符而应用,而不仅仅是应用于字符的低位(也就是符号的最右部分);
    定点标识:ES6 正则表达式中另外一个新增的标签模式是 y,通常称为 “定点(sticky)模式”;定点主要是指在正则表达式的起点有一个虚拟的锚点,只从正则表达式的 lastIndex 属性指定的位置开始匹配;
    正则表达式 flags:ES6 之前,想要通过检查一个正则表达式对象来判断它应用了哪些标识,需要把它从 source 属性的内容中解析出来;在 ES6 中,现在可以用新的 flags 属性直接得到这些值;ES6 的另一个调整是如果把标识传给已有的正则表达式,RegExp(..) 构造器现在支持 flags;
    2.11. 数字字面量扩展
    虽然可以用不同进制形式指定数字,但是数字的数字值还是保存的值,并且默认的输出解释形式总是十进制
    2.12.Unicode
    Unicode 字符范围从 0x0000 到 0xFFFF,包含可能看到和接触到的所有(各种语言的)标准打印字符。这组字符称为基本多语言平面(BMP)。BMP 甚至包含了像雪人这样的有趣的符号
    现在有了可以用于作 Unicode 转义(在字符串和正则表达式中)的新形式,称为 Unicode 码点转义
    支持 Unicode 的字符:默认情况下,JavaScript 字符串运算和方法不能感知字符串中俄 astral 符号;如何精确计算这样的字符串长度。[...gclef].length
    字符定位:原生的 ES6 对此的答案是 charAt,但不支持 astral 字符的原子性,也不会考虑组合符号的因素;ES6 提供了 codePointAt;组合 String.fromCodePoint 和 codePointAt 来获得支持 Unicode 的 charAt 的更简单且更优的方法;
    Unicode 标识符名
    2.13. 符号
    ES6 为 JavaScript 引入了一个新的原生类型:Symbol。它和其他原生类型不一样,symbol 没有字面量形式
    以下几点需要注意:不能也不应该对 Symbol() 使用 new。它并不是一个构造器,也不会创建一个对象;传给 Symbol() 的参数是可选的。如果传入了的话,应该是一个为这个 symbol 的用途给出用户友好描述的字符串;typeof 的输出是一个新的值 ("symbol"),这是识别 symbol 的首选方法;
    符号本身的内部值 - 称为它的名称(name)- 是不在代码中出现且无法获得的。可以把这个符号值想象为一个自动生成的、(在应用内部)唯一的字符串值
    符号的主要意义是创建一个类似字符串的不会与其他任何值冲突的值
    如果需要把符号值作为一个真正的字符串使用,那么它就需要用 String() 或者 toString() 进行显式类型转换,因为不允许隐式地把符号转换为字符串
    符号注册:Symbol.for() 在全局符号注册表中搜索,来查看是否有描述文字相同的符号已经存在,如果有的话就返回它;换句话说,全局注册表把符号值本身根据其描述文字作为单例处理;具有讽刺意义的是,基本上符号的目的是为了取代应用中的 magic 字符串(赋予特殊意义的任意字符串);可以使用 Symbol.keyFor() 提取注册符号的描述文本(键值);
    作为对象属性的符号:如果把符号用作对象的属性 / 键值,那么它会以一种特殊的方式存储,使得这个属性不出现在对这个对象的一般属性枚举中;要取得对象的符号属性,使用 Object.getOwnPropertySymbols;
    2.14. 小结
    这些新语法形式中大多数的设计目的都是消除常见编程技巧中的痛点,比如为函数设定默认值以及把参数的 “其余” 部分收集到数组中
    而像=>箭头函数这样的特性看起来是为了使代码更简洁的语法,实际上它有非常特别的行为特性,应该只在适当的时候使用

    第 3 章 代码组织
    3.1. 迭代器
    迭代器(iterator)是一个结构化的模式,用于从源以一次一个的方式提取数据
    迭代器是一种有序的、连续的、基于拉取的用于消耗数据的组织方式
    接口:IteratorResult 接口指定了从任何迭代器操作返回的值必须是下面这种形式的对象:{value: .., done: true/false}
    next() 迭代:迭代器的 next(..) 方法可以接受一个或多个可选参数。绝大多数内置迭代器没有利用这个功能
    可选的 return 和 throw:多数内置迭代器都没有实现可选的迭代器接口 - return (..) 和 throw(..);return(..) 被定义为向迭代器发送一个信号,表明消费者代码已经完毕,不会再从其中提取任何值;throw(..) 用于向迭代器报告一个异常 / 错误,迭代器针对这个信号的反应可能不同于针对 return(..) 意味着的完成信号;
    自定义迭代器:这种特定的用法强调了迭代器可以作为一个模式来组织功能,而不仅仅是数据
    3.2. 生成器
    ES6 引入了一个全新的某种程度上说是奇异的函数形式,称为生成器
    生成器可以在执行当中暂停自身,可以立即恢复执行也可以过一段时间之后恢复执行。所以显然它并不像普通函数那样保证运行到完毕
    在执行当中的每次暂停 / 恢复循环都提供了一个双向信息传递的机会,生成器可以返回一个值,恢复它的控制代码也可以发回一个值
    语法:执行生成器,比如 foo(5, 10),并不实际在生成器中运行代码。相反,它会产生一个迭代器控制这个生成器执行其代码;生成器还有一个可以在其中使用的新关键字,用来标示暂停点:yield;一个永不结束的循环就意味着一个永不结束的生成器,这是完全有效的,有时候完全就是你所需要的;yield 严格上说不是一个运算符,尽管像 yield 1 这样使用它的时候确实看起来像是运算符;yield.. 表达式和赋值表达式行为上的类似性有一定概念上的合理性;因为 yield 关键字的优先级很低,几乎 yield.. 之后的任何表达式都会首先计算,然后再通过 yield 发送;和 = 赋值一样,yield 也是 “右结合” 的,也就是说多个 yield 表达式连续出现等价于用 (..) 从右向左分组;yield * 可以调用另外一个生成器(通过委托到其迭代器),所以它也是可以通过调用自身执行某种生成器递归;
    迭代器控制:ES6 之后 的一个早期提案会通过生成器内部一个独立的元属性,支持访问传入最初 next(..) 调用的值;可以把生成器看作是值的产生器,其中每次迭代就是产生一个值来消费;从更通用的意义上来说,可能更合理的角度是把生成器看作一个受控的、可传递的代码执行;
    提前完成:生成器上附着的迭代器支持可选的 return(..) 和 throw(..) 方法。这两种方法都有立即终止一个暂停的生成器的效果;return(..) 除了可以手动调用,还可以在每次迭代的末尾被任何消耗迭代器的 ES6 构件自动调用,比如 for...of 循环和 spread 运算符;这个功能的目的是通知生成器如果控制代码不再在它上面迭代,那么它可能就会执行清理任务(释放资源,重置状态等);不要把 yield 语句放在 finally 子句内部,虽然这是有效且合法的,但确实是一个可怕的思路。它会延后你的 return(..) 调用的完成;和 return(..) 不同,迭代器的 throw(..) 方法从来不会被自动调用;
    错误处理:生成器的错误处理可以表达为 try...catch,它可以在由内向外和由外向内两个方向工作
    Transpile 生成器:一个练习巩固了生成器实际上就是状态机逻辑的简化语法这个概念
    生成器使用:两个主要模式(产生一系列值;顺序执行的任务队列;)
    3.3. 模块
    在所有 JavaScript 代码中,唯一最重要的代码组织模式是模块
    旧方法:传统的模块模式基于一个带有内部变量和函数的外层函数,以及一个被返回的 “public API”;其中常见的是异步模块定义(AMD),还有一种是通用模块定义(UMD);
    前进:对于 ES6 来说,我们不再需要依赖于封装函数和闭包提供模块支持;ES6 模块和过去我们处理模块的方式之间的显著概念区别;ES6 模块将会为代码组织提供完整支持,包括封装、控制公开 API 以及引用依赖导入;长远来看,ES6 模块从本质上说必然会取代之前所有的模块格式和标准,即使是 CommonJS,因为 ES6 模块是建立在语言的语法支持基础上的;模块 transpiler / 转换工具是必不可少的。不管你是在编写普通模块、AMD、UMD、CommonJS 还是 ES6,这些工具都不得不解析转化为对代码运行的所有环境都适用的形式;
    新方法:支撑 ES6 模块的两个主要新关键字是 import 和 export;import 和 export 都必须出现在使用它们的最顶层作用域。它们必须出现在所有代码块和函数的外面;没有用 export 标示的一切都在模块作用域内部保持私有。在模块内没有全局作用域;模块还能访问 window 和所有的 “全局” 变量,只是不作为词法上的顶层作用域;模块导出不是像你熟悉的赋值运算符 = 那样只是值或者引用的普通赋值。实际上,导出的是对这些东西(变量等)的绑定(类似于指针);默认导出把一个特定导出绑定设置为导入模块时的默认导出。绑定的名称就是 default;每个模块只能有一个 default;JavaScript 引擎无法静态分析平凡对象的内容,这意味着它无法对静态 import 进行性能优化。让每个成员独立且显式地导出的优点是引擎可以对其进行静态分析和优化;
    模块依赖环:首先必须声明,尽量避免故意设计带有环形依赖的系统;本质上说,相互导入,加上检验两个 import 语句的有效性的静态验证,虚拟组合了两个独立的模块空间(通过绑定),这样 foo(..)可以调用 bar(..),反过来也是一样;import 语句的静态加载语义意味着可以确保通过 import 相互依赖的 “foo” 和“bar”在其中任何一个运行之前,二者都会被加载、解析和编译;
    模块加载:import 语句使用外部环境(浏览器、Node.js 等)提供的独立机制,来实际把模块标识符字符串解析成可用的指令,用于寻找和加载所需的模块。这个机制就是系统模块加载器;如果在浏览器中,环境提供的默认模块加载器会把模块标识符解析为 URL。如果在像 Node.js 这样的服务器上就解析为本地文件系统路径;模块加载器本身不是由 ES6 指定的。他是独立的、平行的标准,目前由 WHATWG 浏览器标准工作组管理;
    3.4. 类
    class:新的 ES6 类机制的核心是关键字 class,表示一个块,其内容定义了一个函数原型的成员;ES6 class 本身并不是一个真正的实体,而是一个包裹着其他像函数和属性这样的具体实体并把他们组合到一起的元概念
    extends 和 super:ES6 还通过面向类的常用术语 extends 提供了一个语法糖,用来在两个函数原型之间建立[[Prototype]]委托链接 - 通常被误称为 “继承” 或者令人迷惑的标识为“原型继承”;在构造器中,super 自动指向“父构造器”在方法中,super 会指向“父对象”,这样就可以访问其属性 / 方法了;
    new.target:新概念,称为元属性;new target 是一个新的在所有函数中都可用的 “魔法” 值,尽管在一般函数中它通常是 undefined;在任何构造器中,new.target 总是指向 new 实际上直接调用的构造器,即使构造器是在父类中且通过子类构造器用 super(..)委托调用;除了访问静态 / 方法之外,类构造器中的 new.target 元属性没有什么其他用处;
    static:直接添加到这个类的函数对象上,而不是在这个函数对象的 prototype 对象上
    3.5. 小结
    迭代器提供了对数组或运算的顺序访问。可以通过像for..of和...这些新语言特性来消耗迭代器
    生成器是支持本地暂停 / 恢复的函数。通过迭代器来控制。它们可以用于编程(也是交互地,通过yield/next消息传递)生成供迭代消耗的值
    模块支持对实现细节的私有封装,并提供公开导出 API。模块定义是基于文件的单例实例,在编译时静态决议
    类对基于原型的编码提供了更清晰的语法。新增的 super 也解决了[[prototype]]链中相对引用的棘手问题
    第 4 章 异步流控制
    管理异步的主要机制一直以来都是函数回调
    ES6 增加了一个新的特性来帮助解决只用回调实现异步的严重缺陷:Promise

    4.1.Promise
    介绍:一些错误观念:Promise 不是对回调的替代。Promise 在回调代码和将要执行这个任务的异步代码之间提供了一种可靠的中间机制来管理回调;可以把 Promise 链接到一起,这就把一系列异步完成的步骤串联了起来;Promise 链提供了一个近似的异步控制流;还有一种定义 Promise 的方式,就是把它看作一个未来值,对一个值得独立于时间的封装容器;换句话说,Promise 可以被看作是同步函数返回值的异步版本;Promise 的决议结果只有两种可能:完成或拒绝,附带一个可选的单个值;Promise 只能被决议(完成或者拒绝)一次;
    构造和使用 Promise:提供给构造器 Promise(..) 的两个参数都是函数,一般称为 resolve(..) 和 reject(..);Promise 有一个 then(..) 方法,接受一个或两个回调函数作为参数;默认的成功回调把完成值传出,默认的出错回调会传递拒绝原因值;如果永远不通过 then(..) 或 catch(..) 调用来观察的话,它就会一直保持未处理状态;
    Thenable:Promise(..) 构造器的真正实例是 Promise。但还有一些类 promise 对象,称为 thenable,一般来说,它们也可以用 Promise 机制解释;任何提供了 then(..) 函数的对象(或函数)都被认为是 thenable;一般来说,如果从某个其他系统接收到一个自称 promise 或者 thenable 的东西,不应该盲目信任它;避免把可能被误认为 thenable 的值直接用于 Promise 机制;
    Promise API:Promise API 还提供了一些静态方法与 Promise 一起工作;Promise.resolve(..) 创建了一个决议到传入值的 promise;resolve(..) 和 Promise.resolve(..) 可以接受 promise 并接受它的状态 / 决议,而reject(..)和Promise.reject(..)并不区分接收的值是什么;Promise.all([..])等待所有都完成(或者第一个拒绝),而Promise.race([..])等待第一个完成或者拒绝;Promise.all([])将会立即完成(没有完成值),Promise.race([])将会永远挂起。因此建议,永远不要用空数组使用这些方法;
    4.2. 生成器 + Promise
    这个重要的模式需要理解一下:生成器可以 yield 一个 promise,然后这个 promise 可以被绑定,用其完成值来恢复这个生成器的运行
    Promise 是一种把普通回调或者 chunk 控制反转反转回来的可靠系统
    把 Promise 的可信任性与生成器的同步代码组合在一起有效解决了回调所有的重要缺陷
    本质上说,只要代码中出现超过两个异步步骤的流控制逻辑,都可以也应该使用由 run 工具驱动的 promise-yield 生成器以异步风格表达控制流
    4.3. 小结
    ES6 新增了 Promise 来弥补回调的主要缺陷之一:缺少对可预测行为方式的保证。Promise 代表了来自可能异步的任务的未来完成值,跨越同步和异步边界对行为进行规范化
    Promise 与生成器的结合完全实现了重新安排异步流控制代码来消除丑陋的回调乱炖(或称地狱)
    第 5 章 集合
    Map 就像是一个对象(键 / 值对),但是键值并非只能为字符串,而是可以使用任何值 - 甚至是另一个对象或 map
    Set 与数组(值的序列)类似,但是其中的值是唯一的;如果新增的值是重复的,就会被忽略。
    还有相应的弱(与内存 / 垃圾回收相关)版本:WeakMap 和 WeakSet
    5.1.TypedArray
    实际上带类型的数组更多是为了使用类数组语义(索引访问等)结构化访问二进制数据
    大小端:理解下面这点很重要:arr 的映射是按照运行 JavaScript 的平台的大小端设置(大端或小端)进行的;大小端的意思是多字节数字(比如前面代码片段中创建的 16 位无符号整型)中低字节(8 位)位于这个数字字节表示中德右侧还是左侧;目前 Web 上最常用的是小端表示方式,但是肯定存在不采用这种方式的浏览器;
    多视图:单个 buffer 可以关联多个视图
    带类数组构造器:带类型数组构造器的实例几乎和普通原生数组完全一样。一些区别包括具有固定的长度以及值都属于某种 “类型”;不能对 TypedArray 使用不合理的 Array.prototype 方法,比如修改器 (splice(..)、push(..)等) 和concat(..);要清楚 TypedArray 中德元素是限制在声明的位数大小中的;要解决平方值溢出的局限,可以使用TypedArray.from(..)函数;
    5.2.Map
    了解对象是创建无序键 / 值对数据结构,也称为映射(map)的主要机制
    对象作为映射的主要缺点是不能使用非字符串值作为键
    唯一的缺点就是不能使用方括号 [ ] 语法设置和获取值,但完全可以使用get(..)和set(..)方法完美代替
    Map 值
    Map 键:map 的本质是允许你把某些额外的信息(值)关联到一个对象(键)上,而无需把这个信息放入对象本身;对于 map 来说,尽管可以使用任意类型的值作为键,但通常我们会使用对象,因为字符串或者其他基本类型已经可以作为普通对象的键使用;
    5.3.WeakMap
    WeakMap 是 map 的变体,二者的多数外部行为都是一样的,区别在于内部内存分配(特别是其 GC)的工作方式
    WeakMap(只)接受对象作为键。这些对象是被弱持有的,也就是说如果对象本身被垃圾回收的话,在 WeakMap 中的这个项目也会被移除
    WeakMap 没有 size 属性或 clear() 方法,也不会暴露任何键、值或项目上的迭代器
    需要注意的是,WeakMap 只是弱持有它的键,而不是值
    5.4.Set
    set 是一个值的集合,其中的值唯一(重复会被忽略)
    除了会把 - 0 和 0 当作是一样的而不加区别之外,has(..)中的比较算法和Object.is(..)几乎一样
    Set 迭代器:set 固有的唯一性是它最有用的特性;set 的唯一性不允许强制转换,所以 1 和 “1” 被认为是不同的值;
    5.5.WeakSet
    就像 WeakMap 弱持有它的键(对其值是强持有的)一样,WeakSet 对其值也是弱持有的(这里并没有键)
    WeakSet 的值必须是对象,而并不像 set 一样可以是原生类型值
    5.6. 小结
    TypedArray 提供了对二进制数据 buffer 的各种整型类型 “视图”,比如 8 位无符号整型和 32 位浮点型。对二进制数据的数组访问使得运算更容易表达和维护,从而可以更容易操纵视频、音频、canvas 数据等这样的复杂数据
    Map 是键 / 值对,其中的键不只是字符串 / 原生类型,也可以是对象。Set 是成员值(任意类型)唯一的列表
    WeakMap 也是 map,其中的键(对象)是弱持有的,因此当它是对这个对象的最后一个引用的时候,GC(垃圾回收)可以回收这个项目。WeakSet 也是 set,其中的值是弱持有的,也就是如果其中的项目是对这个对象最后一个引用的时候,GC 可以移除它
    第 6 章 新增 API
    6.1.Array
    各种 JavaScript 用户库扩展最多的特性之一就是数组(Array)类型
    1. 静态函数 Array.of(..):Array(..) 构造器有一个众所周知的陷阱,就是如果只传入一个参数,并且这个参数是数字的话,那么不会构造一个值为这个数字的单个元素的数组,而是构造一个空数组,其 length 属性为这个数字
    2. 静态函数 Array.from(..):JavaScript 中的 “类(似)数组对象” 是指一个有 length 属性,具体说是大于等于 0 的整数值的对象;普遍的需求就是把他们转为真正的数组,Array.prototype.slice.call(arrLike);新的 ES6 Array.from(..)方法都是更好理解、更优雅、更简洁的替代方法;
    3. 创建数组和子类型:of(..) 和 from(..) 都使用访问它们的构造器来构造数组
    4. 原型方法 copyWidthin(..):Array.copyWithin(..) 是一个新的修改器方法,所有数组都支持;它从一个数组中复制一部分到同一个数组的另一个位置,覆盖这个位置所有原来的值;copyWithin(..) 方法不会增加数组的长度。到达数组结尾复制就会停止;
    5. 原型方法 fill(..):用指定值完全(或部分)填充已存在的数组;可选接收参数 start 和 end,它们指定了数组要填充的子集位置;
    6. 原型方法 find(..):在数组中搜索一个值得最常用方法一直是 indexOf(..) 方法,这个方法返回找到值的索引,如果没有找到就返回 - 1;indexOf(..) 需要严格匹配 ===;ES6 的 find(..) 一旦回到返回 true / 真值,会返回实际的数组值;
    7. 原型方法 findIndex(..):如果需要严格匹配的索引值,那么使用 indexOf(..),如果需要自定义匹配的索引值,那么使用 findIndex(..)
    8. 原型方法 entries()、values()、keys():它提供了同样的迭代器方法 entries()、values() 和 keys(),从这个意义上说,他是一个集合
    6.2.Object
    1. 静态函数 Object.is(..):静态函数 Object.is(..) 执行比 === 比较更严格的值比较;Object.is(..) 调用底层的 SameValue 算法;如果需要严格识别 NaN 或者 - 0,那么应该选择 Object.is(..);
    2. 静态函数 Object.getOwnPropertySymbols(..):它直接从对象上取得所有的符号属性
    3. 静态函数 Object.setPrototypeOf(..):设置对象的[[Prototype]]用于行为委托
    4. 静态函数 Object.assign(..):很多 JavaScript 库 / 框架提供了用于把一个对象的属性复制 / 混合到另一个对象中德工具;ES6 新增了 Object.assign(..),对于每个源来说,它的可枚举和自己拥有的(也就是不是 “继承来的”)键值,包括符号都会通过简单 = 赋值被复制;不可枚举的属性和非自有的属性都被排除在赋值过程之外;Object.create(..) 是 ES5 工具,创建一个[[Prototype]]链接的空对象;
    6.3.Math
    更优化地执行这些计算,或者执行比手动版本精度更高的计算
    6.4.Number
    两个新增内容就是指向已有的全局函数的引用:Number.parseInt(..) 和 Number.parseFloat(..)
    1. 静态属性:新增了一些辅助数字常量:Number.EPSOLON、Number.MAX_SAFE_INTEGER、Number.MIN_SAFE_INTEGER
    2. 静态属性 Number.isNaN(..):标准全局工具 isNaN(..) 自出现以来就是有缺陷的,它对非数字的东西都返回 true,而不是只对真实的 NaN 值返回 true,因为它把参数强制转换为数字类型(可能会错误地导致 NaN)。ES6 增加了一个修正工具 Number.isNaN,可以按照期望工作
    3. 静态属性 Number.isFinite(..):标准的全局 isFinite(..) 会对参数进行强制类型转换,但是 Number.isFinite(..) 会略去这种强制行为
    4. 整型相关静态函数:JavaScript 的数字值永远都是浮点数(IEE-754);检查一个值得小数部分是否非 0,x===Math.floor(x),ES6 新增了 Number.isInteger(..);JavaScript 代码本身不会因为只使用整数而运行得更快,但是,只有在使用整数时,引擎才可以采用优化技术(比如 asm.js);
    6.5. 字符串
    1.Unicode 函数:String.fromCodePoint(..)、String.codePointAt(..)和 String.normalize(..)新增这些函数是为了提高 JavaScript 字符串值 uiUnicode 的支持;字符串原型方法 normalize(..)用于执行 Unicode 规范化,或者把字符用 “合并符” 连接起来或者把合并的字符解开;
    2. 静态函数 String.raw(..):String.raw(..) 工具作为内置标签函数提供,与模板字符串字面值一起使用,用于获得不应用任何转义序列的原始字符串
    3. 原型函数 repeat(..):重复字符串
    4. 字符串检查函数:又新增了 3 个用于搜索 / 检查的新方法:startsWidth、endsWidth 和 includes
    第 7 章 元编程
    写在前面
    元编程是指操作目标是程序本身的行为特性的编程。换句话说,它是程序的编程的编程
    元编程关注以下一点或几点:代码查看自身,代码修改自身,代码修改默认语言特性,以便影响其他代码
    元编程的目标是利用语言自身的内省能力是代码的其余部分更具描述性、表达性和灵活性
    7.1. 函数名称
    默认情况下,name 属性不可写,但可配置,也就是说如果需要的话,可使用 Object.defineProperty(..) 来手动修改
    7.2. 元属性
    元属性以属性访问的形式提供特殊的其他方法无法获取的元信息
    对于所有的元编程技术都要小心,不要编写过于机灵的代码,让未来的你或者其他代码维护者难以理解
    7.3. 公开符号
    除了在自己的程序中定义符号之外,JavaScript 预先定义了一些内置符号,称为公开符号
    Symbol.iterator 表示任意对象上的一个专门位置(属性),语言机制自动在这个位置上寻找一个方法,这个方法构造一个迭代器来消耗这个对象的值
    最常见的一个元编程任务,就是在一个值上进行内省来找出它是什么种类,这通常是为了确定其上适合执行何种运算。最常用的内省技术是 toString() 和 instanceof
    符号 @@species,这个符号控制要生成新实例时,类的内置方法使用哪一个构造器
    抽象类型转换运算 ToPrimitive,它用在对象为了某个操作必须被强制转换为一个原生类型值得时候。ES6 之前,没有办法控制这个行为
    ES6 中,在任意对象值上作为属性的符号 @@toPrimitivesymbol 都可以通过指定一个方法来定制这个 ToPrimitive 强制转换
    对于正则表达式对象,有 4 个公开符号可以被覆盖:@@match、@@search、@@split、@@replace
    符号 @@isConcatSpreadable 可以被定义为任意对象的布尔型属性(Symbol.isConcattSpreadable),用来指示如果把它传给一个数组的 concat(..) 是否应该将其展开
    符号 @@unscopables 可以被定义为任意对象的对象属性(Symbol.unscopables),用来指示使用 with 语句时哪些属性可以或部可以暴露为词法变量
    strict 模式下不允许 with 语句,因此应当被认为是语言的过时特性。不要使用它
    7.4. 代理
    ES6 种新增的最明显的元编程特性之一是 Proxy(代理) 特性
    代理是一种由你创建的特殊的对象,它 “封装” 另一个普通对象 -- 或者说挡在这个普通对象的前面
    代理局限性:可以在对象上执行的很广泛的一组基本操作都可以通过这些元编程处理函数 trap;代理处理函数总会有一些不变形(invariant),亦即不能被覆盖的行为;这些不变性限制了自定义代理行为的能力,但它们的目的只是为了防止你创建诡异或罕见(或者不一致)的行为;
    可取消代理:想要创建一个在你想要停止它作为代理时便可以被停用的代理。解决方案是创建可取消代理;可取消代理用 Proxy.revocable(..) 创建,这是一个普通函数,而不像 Proxy(..) 一样是构造器;一旦可取消代理被取消,任何对它的访问(触发它的任意 trap)都会抛出 TypeError;可取消代理的一个可能应用场景是,在你的应用中把代理分发到第三方,其中管理你的模型数据,而不是给出真实模型本身的引用;
    使用代理:代理成为了代码交互的主要对象,而实际目标对象保持隐藏 / 被保护的状态
    7.5.Reflect API
    Reflect 对象是一个平凡对象(就像 Math),不像其他内置原生值一样是函数 / 构造器
    它持有对应于各种可控的元编程任务的静态函数
    有一个区别是如果第一个参数(目标对象)不是对象的话,Object.* 相应工具会试图把它类型转换为一个对象
    Reflect 的元编程能力提供了模拟各种语法特性的编程等价物,把之前隐藏的抽象操作暴露出来。可以利用这些能力扩展功能和 API,以实现领域特定语言(DSL)
    属性排序:在 ES6 之前,一个对象键 / 属性的列出顺序是依赖于具体实现,并未在规范中定义;对于 ES6 来说,拥有属性的列出顺序是是由[[OwnPropertyKeys]]算法定义的;其顺序为(首先,按照数字上升排序,枚举所有整数索引拥有的属性;然后,按照创建顺序枚举其余的拥有的字符串属性名;最后,按照创建顺序枚举拥有的符号属性;)
    7.6. 特性测试
    特性测试,就是一种由你运行的用来判断一个特性是否可用的测试
    JavaScript 中最常用的特性测试是检查一个 API 是否存在,如果不存在的话,定义一个 polyfill
    7.7. 尾递归调用
    通常,在一个函数内部调用另一个函数的时候,会分配第二个栈帧来独立管理第二个函数调用的变量 / 函数
    当考虑递归编程的时候(一个函数重复调用自身)- 或者两个或多个函数彼此调用形成递归 - 调用栈的深度很容易达到成百上千,甚至更多
    JavaScript 引擎不得不设置一个武断的限制来防止这种编程技术引起浏览器和设备内存消耗尽而崩溃。“RangeError:Maximum call stack size exceeded”
    调用栈深度限制不由规范控制。它是依赖于具体实现的,并且根据浏览器和设备不同而有所不同
    尾调用优化(Tail Call Optimization, TCO) - 于是额外的栈帧分配是不需要的。引擎不需要对下一个函数调用创建一个新的栈帧,只需复用已有的栈帧
    第 8 章 ES6 之后
    在新特性还没有被需要支持的所有浏览器都实现的情况下,transpiler 和 polyfill 是我们迁移到新特性的桥梁
    8.1. 异步函数
    async function 本质上就是生成器 + Promise+run(..) 模式的语法糖;它们底层的运作方式是一样的
    警告:async function 有一个没有解决的问题,因为它只返回一个 promise,所以没有办法从外部取消一个正在运行的 async function 实例;Promise 是不可取消的(至少在编写本部分的时候是如此);
    8.2.Object.observe
    可能在后 ES6,我们将会看到通过工具 Object.observe(..) 直接添加到语言中的支持。
    本质上说,这个思路就是你可以建立一个侦听者(listener)来观察对象的改变,然后在每次变化发生时调用一个回调
    可以观察的改变有 6 种类型:add、update、delete、reconfigure、setPrototype、preventExtensions
    1. 自定义改变事件:除了前面 6 类内置改变事件,也可以侦听和发出自定义改变事件
    2. 结束观测
    8.3. 幂运算符
    有提案提出为 JavaScript 新增一个运算符用于执行幂运算,就像 Math.pow(..) 一样
    8.4. 对象属性与...
    ... 运算符展开和收集数组的用法很直观,那么对于对象呢
    8.5.Array#includes
    出现了一个获得了大量支持的提案,提出增加一个真正返回布尔值的数组搜索方法 includes(..)
    Array.includes(..) 使用的匹配逻辑能够找到 Nan 值,但是无法区分 - 0 和 0
    8.6.SIMD
    SIMD API 暴露了可以同时对多个数字值运算的各种底层(CPU)指令
    8.7.WebAssembly(WASM)
    最近(以及不久的将来)JavaScript 语言设计修改上的最大压力之一就是需要成为更适合从其他语言(比如 C/C++、ClojureScript)变换 / 交叉编译的目标语言
    ASM.js 是合法 JavaScript 的一个子集,这个子集最严格地限制了那些使得 JavaScript 引擎难以优化的行为。结果就是兼容 ASM.js 的代码运行在支持 ASM 的引擎上时效率有巨大的提升,几乎与原生优化的等价 C 程序相当
    WASM 提出了一种代码的高度压缩 AST(语法树)二进制表示格式,然后可以直接向 JavaScript 引擎发出指令,而它的基础结构,不需要通过 JavaScript 解析,甚至不需要符合 JavaScript 的规则
    写在后面
    pdf 书籍、笔记思维导图、随书代码打包下载地址:https://pan.baidu.com/s/1ei5ya29OlvFPlMV53I8ZXA(提取码:dcqo)
    纸质书京东购买地址:https://u.jd.com/FwSmuH(推荐购买纸质书来学习)
    全文完
    本文由 简悦 SimpRead 优化,用以提升阅读体验
    使用了 全新的简悦词法分析引擎 beta,点击查看详细说明
    写在前面
    第 1 章 ES?现在与未来
    1.2.transpiling
    第 2 章 语法
    2.1. 块作用域声明
    2.2.spread/rest
    2.3. 默认参数值
    2.4. 解构
    2.5. 太多,太少,刚刚好
    2.6. 对象字面量扩展
    2.7. 模板字面量
    2.8. 箭头函数
    2.9.for...of 循环
    2.10. 正则表达式
    2.11. 数字字面量扩展
    2.12.Unicode
    2.13. 符号
    2.14. 小结
    第 3 章 代码组织
    3.1. 迭代器
    3.2. 生成器
    3.3. 模块
    3.4. 类
    3.5. 小结
    第 4 章 异步流控制
    4.1.Promise
    4.2. 生成器 + Promise
    4.3. 小结
    第 5 章 集合
    5.1.TypedArray
    5.2.Map
    5.3.WeakMap
    5.4.Set
    5.5.WeakSet
    5.6. 小结
    第 6 章 新增 API
    6.1.Array
    6.2.Object
    6.3.Math
    6.4.Number
    6.5. 字符串
    第 7 章 元编程
    写在前面
    7.1. 函数名称
    7.2. 元属性
    7.3. 公开符号
    7.4. 代理
    7.5.Reflect API
    7.6. 特性测试
    7.7. 尾递归调用
    第 8 章 ES6 之后
    8.1. 异步函数
    8.2.Object.observe
    8.3. 幂运算符
    8.4. 对象属性与...
    8.5.Array#includes
    8.6.SIMD
    8.7.WebAssembly(WASM)
    写在后面


    作者:小磊哥er
    链接:https://segmentfault.com/a/1190000022663208
    来源:SegmentFault 思否
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

     
  • 相关阅读:
    在VMware中为Red Hat配置静态ip并可访问网络-Windows下的VMware
    03-nginx虚拟主机配置
    解决nginx: [emerg] bind() to [::]:80 failed (98: Address already in use)
    02-nginx信号量
    RedHat Linux设置yum软件源为本地ISO
    01-nginx介绍及编译安装
    Linux.负载均衡
    01-MySQL优化大的思路
    10 华电内部文档搜索系统 search02
    10 华电内部文档搜索系统 search03
  • 原文地址:https://www.cnblogs.com/cx2016/p/13206135.html
Copyright © 2020-2023  润新知