From 5fbf4e195f83ae6d1c013b07d6deabdc0c1e2668 Mon Sep 17 00:00:00 2001 From: "Zhou, Ding a" Date: Wed, 16 Nov 2016 19:30:28 +0800 Subject: [PATCH] progress --- es6 & beyond/ch4.md | 65 +++++++++++++++++++++------------------------ es6 & beyond/ch5.md | 46 ++++++++++++++++---------------- 2 files changed, 54 insertions(+), 57 deletions(-) diff --git a/es6 & beyond/ch4.md b/es6 & beyond/ch4.md index eb71ed2ce..f57c4d5bc 100644 --- a/es6 & beyond/ch4.md +++ b/es6 & beyond/ch4.md @@ -3,11 +3,11 @@ 如果你写过任何数量相当的JavaScript,这就不是什么秘密:异步编程是一种必须的技能。管理异步的主要机制曾经是函数回调。 -然而,ES6增加了一种新特性来帮助你解决仅使用回调来管理异步的重大缺陷:*Promise*。另外,我们可以重温generator(前一章中提到的)来看看一种将两者组合的模式,它是JavaScript中异步流程控制编程向前迈出的重要一步。 +然而,ES6增加了一种新特性:*Promise*,来帮助你解决仅使用回调来管理异步的重大缺陷。另外,我们可以重温generator(前一章中提到的)来看看一种将两者组合的模式,它是JavaScript中异步流程控制编程向前迈出的重要一步。 ## Promises -让我们辨明一些误解:Promise不是回调的替代品。Promise提供了一种可靠的中介机制 —— 也就是,在你的调用代码和将要执行任务的异步代码之间 —— 来管理回调。 +让我们辨明一些误解:Promise不是回调的替代品。Promise提供了一种可信的中介机制 —— 也就是,在你的调用代码和将要执行任务的异步代码之间 —— 来管理回调。 另一种考虑Promise的方式是作为一种事件监听器,你可以在它上面注册监听一个通知你任务何时完成的事件。它是一个仅被触发一次的时间,但不管怎样可以被看作是一个事件。 @@ -15,9 +15,9 @@ Promise可以被链接在一起,它们可以是一系列顺序的、异步完 还有另外一种概念化Promise的方式是,将它看作一个 *未来值*,一个与时间无关的值的容器。无论底层的值是否是最终值,这种容器都可以被同样地推理。观测一个Promise的解析会在这个值准备好的时候将它抽取出来。换言之,一个Promise被认为是一个同步函数返回值的异步版本。 -一个Promise只可能拥有两种解析结果:完成或拒绝,并带有一个可选的信号值。如果一个Promise被完成,这个最终值成为一个完成值。如果它被拒绝,这个最终值称为理由(也就是“拒绝的理由”)。Promise只可能被解析(完成或拒绝)一次。任何其他的完成或拒绝的尝试都会被简单地忽略,一点一个Promise被解析,它就成为一个不可被改变的值(immutable)。 +一个Promise只可能拥有两种解析结果:完成或拒绝,并带有一个可选的信号值。如果一个Promise被完成,这个最终值称为一个完成值。如果它被拒绝,这个最终值称为理由(也就是“拒绝的理由”)。Promise只可能被解析(完成或拒绝)一次。任何其他的完成或拒绝的尝试都会被简单地忽略,一旦一个Promise被解析,它就成为一个不可被改变的值(immutable)。 -显然,有几种不同的方式可以来考虑一个Promise是什么。没有一个角度就它自身来说是完全充分的,但是每一个角度都提供了整体的一个方面。这其中的要点是,它们为仅使用回调的异步提供了一个重大的改进,也就是说它们提供了顺序、可预测性、以及可靠性。 +显然,有几种不同的方式可以来考虑一个Promise是什么。没有一个角度就它自身来说是完全充分的,但是每一个角度都提供了整体的一个方面。这其中的要点是,它们为仅使用回调的异步提供了一个重大的改进,也就是它们提供了顺序、可预测性、以及可信性。 ### 创建与使用 Promises @@ -35,21 +35,21 @@ var p = new Promise( function pr(resolve,reject){ * 如果你不使用参数值,或任何非promise值调用`resolve(..)`,promise就会被完成。 * 如果你调用`resolve(..)`并传入另一个promise,这个promise就会简单地采用 —— 要么立即要么最终地 —— 这个被传入的promise的状态(不是完成就是拒绝)。 -这里是你通常如何使用一个promise来重构一个依赖于回调的函数调用。如果你始于使用一个`ajax(..)`工具,它期预期要调用一个错误优先风格的回调: +这里是你通常如何使用一个promise来重构一个依赖于回调的函数调用。假定你始于使用一个`ajax(..)`工具,它期预期要调用一个错误优先风格的回调: ```js function ajax(url,cb) { - // make request, eventually call `cb(..)` + // 发起请求,最终调用 `cb(..)` } // .. ajax( "http://some.url.1", function handler(err,contents){ if (err) { - // handle ajax error + // 处理ajax错误 } else { - // handle `contents` success + // 处理成功的`contents` } } ); ``` @@ -59,8 +59,7 @@ ajax( "http://some.url.1", function handler(err,contents){ ```js function ajax(url) { return new Promise( function pr(resolve,reject){ - // make request, eventually call - // either `resolve(..)` or `reject(..)` + // 发起请求,最终不是调用 `resolve(..)` 就是调用 `reject(..)` } ); } @@ -69,15 +68,15 @@ function ajax(url) { ajax( "http://some.url.1" ) .then( function fulfilled(contents){ - // handle `contents` success + // 处理成功的 `contents` }, function rejected(reason){ - // handle ajax error reason + // 处理ajax的错误reason } ); ``` -Promise拥有一个接收一个或两个回调函数的方法`then(..)`。第一个函数(如果存在的话)被看作是promise被成功地完成时要调用的处理器。第二个函数(如果存在的话)被看作是promise被明确拒绝时,或者任何错误/异常在解析的过程中被捕捉到时要调用的处理器。 +Promise拥有一个方法`then(..)`,它接收一个或两个回调函数。第一个函数(如果存在的话)被看作是promise被成功地完成时要调用的处理器。第二个函数(如果存在的话)被看作是promise被明确拒绝时,或者任何错误/异常在解析的过程中被捕捉到时要调用的处理器。 如果这两个参数值之一被省略或者不是一个合法的函数 —— 通常你会用`null`来代替 —— 那么一个占位用的默认等价物就会被使用。默认的成功回调将传递它的完成值,而默认的错误回调将传播它的拒绝理由。 @@ -96,8 +95,7 @@ ajax( "http://some.url.1" ) } ) .then( function fulfilled(data){ - // handle data from original promise's - // handlers + // 处理来自于原本的promise的处理器中的数据 } ); ``` @@ -118,16 +116,15 @@ ajax( "http://some.url.1" ) } ) .then( function fulfilled(contents){ - // `contents` comes from the subsequent - // `ajax(..)` call, whichever it was + // `contents` 来自于任意一个后续的 `ajax(..)` 调用 } ); ``` -要注意的是,在第一个`fulfilled(..)`中的一个exception将 *不会* 导致第一个`rejected(..)`被调用,因为这个处理仅会应答第一个原始的promise的解析。取代它的是,第二个`then(..)`调用所针对的第二个promise,将会收到这个拒绝。 +要注意的是,在第一个`fulfilled(..)`中的一个异常(或者promise拒绝)将 *不会* 导致第一个`rejected(..)`被调用,因为这个处理仅会应答第一个原始的promise的解析。取代它的是,第二个`then(..)`调用所针对的第二个promise,将会收到这个拒绝。 -在上面的代码段中,我们没有监听这个拒绝,这意味着它会为了未来的观察而被静静地保持下来。如果你永远不通过调用`then(..)`或`catch(..)`来观察它,那么它将会称为未处理的。有些浏览器的开发者控制台可能会探测到这些未处理的拒绝并报告它们,但是这不是有可靠保证的;你应当总是观察promise拒绝。 +在上面的代码段中,我们没有监听这个拒绝,这意味着它会为了未来的观察而被静静地保持下来。如果你永远不通过调用`then(..)`或`catch(..)`来观察它,那么它将会成为未处理的。有些浏览器的开发者控制台可能会探测到这些未处理的拒绝并报告它们,但是这不是有可靠保证的;你应当总是观察promise拒绝。 -**注意:** 这只是Promise理论和行为的简要概览。要进行深入得多的探索,参见本系列的 *异步与性能* 的第三章。 +**注意:** 这只是Promise理论和行为的简要概览。要进行更加深入的探索,参见本系列的 *异步与性能* 的第三章。 ### Thenables @@ -140,7 +137,7 @@ Thenable基本上是一个一般化的标签,标识着任何由除了`Promise( ```js var th = { then: function thener( fulfilled ) { - // call `fulfilled(..)` once every 100ms forever + // 永远会每100ms调用一次`fulfilled(..)` setInterval( fulfilled, 100 ); } }; @@ -182,7 +179,7 @@ var p2 = new Promise( function pr(resolve){ } ); ``` -**提示:** `Promise.resolve(..)`就是前一节提出的thenable信任问题的解决方案。任何你还不确定是一个可信的promise的值 —— 它甚至可能是一个立即值 —— 都可以通过传入`Promise.resolve(..)`来进行规范化。如果这个值已经是一个可识别的promise或thenable,它的状态/解析结果将简单地被采用,将错误行为与你隔绝开。如果相反它是一个立即值,那么它将会被“包装”进一个纯粹的promise,以此将它的行为规范化为异步的。 +**提示:** `Promise.resolve(..)`就是前一节提出的thenable信任问题的解决方案。任何你还不确定是一个可信promise的值 —— 它甚至可能是一个立即值 —— 都可以通过传入`Promise.resolve(..)`来进行规范化。如果这个值已经是一个可识别的promise或thenable,它的状态/解析结果将简单地被采用,将错误行为与你隔绝开。如果相反它是一个立即值,那么它将会被“包装”进一个纯粹的promise,以此将它的行为规范化为异步的。 `Promise.reject(..)`创建一个立即被拒绝的promise,与它的`Promise(..)`构造器对等品一样: @@ -226,7 +223,7 @@ Promise.all( [p1,p2,v3] ) Promise.all( [p1,p2,v3,p4] ) .then( function fulfilled(vals){ - // never gets here + // 永远不会跑到这里 }, function rejected(reason){ console.log( reason ); // Oops @@ -237,8 +234,8 @@ Promise.all( [p1,p2,v3,p4] ) `Promise.all([ .. ])`等待所有的值完成(或第一个拒绝),而`Promise.race([ .. ])`仅会等待第一个完成或拒绝。考虑如下代码: ```js -// NOTE: re-setup all test values to -// avoid timing issues misleading you! +// 注意:为了避免时间的问题误导你, +// 重建所有的测试值! Promise.race( [p2,p1,v3] ) .then( function fulfilled(val){ @@ -248,7 +245,7 @@ Promise.race( [p2,p1,v3] ) Promise.race( [p2,p4] ) .then( function fulfilled(val){ - // never gets here + // 永远不会跑到这里 }, function rejected(reason){ console.log( reason ); // Oops @@ -280,7 +277,7 @@ step1() .then(step4); ``` -但是,对于表达异步流程控制来说有更好的选项,而且在代码风格上可能比长长的promise链更理想。我们可以使用在第三章中学到的generator来表达我们的异步流程控制。 +但是对于表达异步流程控制来说有更好的选项,而且在代码风格上可能比长长的promise链更理想。我们可以使用在第三章中学到的generator来表达我们的异步流程控制。 要识别一个重要的模式:一个generator可以yield出一个promise,然后这个promise可以使用它的完成值来推进generator。 @@ -309,7 +306,7 @@ function *main() { } ``` -从表面上看,这个代码段要比前一个promise链等价物要更繁冗。但是它提供了更加吸引人的 —— 而且重要的是,更加容易理解和阅读 —— 看起来同步的代码风格(“return”值的`=`赋值操作,等等),对于`try..catch`错误处理可以跨越那些隐藏的异步边界使用来说就更是这样。 +从表面上看,这个代码段要比前一个promise链等价物要更繁冗。但是它提供了更加吸引人的 —— 而且重要的是,更加容易理解和阅读的 —— 看起来同步的代码风格(“return”值的`=`赋值操作,等等),对于`try..catch`错误处理可以跨越那些隐藏的异步边界使用来说就更是这样。 为什么我们要与generator一起使用Promise?不用Promise进行异步generator编码当然是可能的。 @@ -358,24 +355,24 @@ function run(gen) { run( main ) .then( function fulfilled(){ - // `*main()` completed successfully + // `*main()` 成功地完成了 }, function rejected(reason){ - // Oops, something went wrong + // 噢,什么东西搞错了 } ); ``` -实质上,在你程序中的任何拥有多于两个异步步骤的流程控制逻辑的地方,你就可以 *而且应当* 使用一个由运行工具驱动的让出promise的generator来以一种同步的风格表达流程控制。这样做将产生更易于理解和维护的代码。 +实质上,在你程序中的任何拥有多于两个异步步骤的流程控制逻辑的地方,你就可以 *而且应当* 使用一个由运行工具驱动的promise-yielding generator来以一种同步的风格表达流程控制。这样做将产生更易于理解和维护的代码。 这种“让出一个promise推进generator”的模式将会如此常见和如此强大,以至于ES6之后的下一个版本的JavaScript几乎可以确定将会引入一中新的函数类型,它无需运行工具就可以自动地执行。我们将在第八章中讲解`async function`(正如它们期望被称呼的那样)。 ## 复习 -随着JavaScript在它被广泛采用过程中的日益成熟与成长,异步编程越发地成为关注的中心。对于这些异步任务来说回调不完全够用,而且在更精巧的需求面前全面地崩塌。 +随着JavaScript在它被广泛采用过程中的日益成熟与成长,异步编程越发地成为关注的中心。对于这些异步任务来说回调并不完全够用,而且在更精巧的需求面前全面崩塌了。 -可喜的是,ES6增加了Promise来回调的主要缺陷之一:在可预测的行为上缺乏可信性。Promise代表一个潜在异步任务的未来完成值,跨越同步和异步的边界将行为进行了规范化。 +可喜的是,ES6增加了Promise来解决回调的主要缺陷之一:在可预测的行为上缺乏可信性。Promise代表一个潜在异步任务的未来完成值,跨越同步和异步的边界将行为进行了规范化。 -但是,Promise与generator的组合才完全揭示了这样做的好处:将我们的异步流程控制代码重新安排,将难看的回调浆糊(也叫“低于”)弱化并抽象出去。 +但是,Promise与generator的组合才完全揭示了这样做的好处:将我们的异步流程控制代码重新安排,将难看的回调浆糊(也叫“地狱”)弱化并抽象出去。 目前,我们可以在各种异步库的运行器的帮助下管理这些交互,但是JavaScript最终将会使用一种专门的独立语法来支持这种交互模式! diff --git a/es6 & beyond/ch5.md b/es6 & beyond/ch5.md index 1e10a1d1b..5a9a7c3f1 100644 --- a/es6 & beyond/ch5.md +++ b/es6 & beyond/ch5.md @@ -1,28 +1,28 @@ -# You Don't Know JS: ES6 & Beyond -# Chapter 5: Collections +# 你不懂JS:ES6与未来 +# 第五章:集合 -结构化的集合与访问数据对于任何JS程序来说都是一个关键组成部分。从这门语言的最开始到现在,书组和对象一直都是我们创建数据结构的主要机制。当然,许多更高级的数据结构都曾建立在这些之上,作为用户方的库。 +结构化的集合与数据访问对于任何JS程序来说都是一个关键组成部分。从这门语言的最开始到现在,数组和对象一直都是我们创建数据结构的主要机制。当然,许多更高级的数据结构作为用户方的库都曾建立在这些之上。 -到了ES6,最有用(而且最能优化性能)的数据结构抽象中的一些已经作为这门语言的原生组件被加入了进来。 +到了ES6,最有用(而且性能经过优化的!)的数据结构抽象中的一些已经作为这门语言的原生组件被加入了进来。 -我们将通过检视 *TypedArrays* 来开始这一章,技术上讲它与几年前ES5的努力是同一时期的产物,但是仅仅作为WebGL的同伴被标准化了,而不是作为JavaScript自身的一部分。到了ES6,这些东西已经被语言规范直接采纳了,这给予了它们头等的地位。 +我们将通过检视 *类型化数组(TypedArrays)* 来开始这一章,技术上讲它与几年前的ES5是同一时期的产物,但是仅仅作为WebGL的同伴被标准化了,而不是作为JavaScript自身的一部分。到了ES6,这些东西已经被语言规范直接采纳了,这给予了它们头等的地位。 -Map就像对象(键/值对),但是与仅能使用一个字符串作为键不同的是,你可以使用任何值 —— 即使是另一个对象或map!Set与数组很相似(值的列表),但是这些值都是唯一的;如果你添加一个重复的值,它会被忽略。还有与之相对应的weak结构:WeakMap和WeakSet。 +Map就像对象(键/值对),但是与仅能使用一个字符串作为键不同的是,你可以使用任何值 —— 即使是另一个对象或map!Set与数组很相似(值的列表),但是这些值都是唯一的;如果你添加一个重复的值,它会被忽略。还有与之相对应的weak结构(与内存/垃圾回收有关联):WeakMap和WeakSet。 -## TypedArrays +## 类型化数组(TypedArrays) 正如我们在本系列的 *类型与文法* 中讲到过的,JS确实拥有一组内建类型,比如`number`和`string`。看到一个称为“类型化的数组”的特性,可能会诱使你推测它意味着一个特定类型的值的数组,比如一个仅含字符串的数组。 -然而,类型化数组其实更多的是关于使用类数组的语义(索引访问,等等)提供对二进制数据的结构化访问。名称中的“类型”指的是在大量二进制位(比特桶)的类型之上覆盖的“视图”,它实质上是一个映射,控制着这些二进制位是否应当被看作8位有符号整数的数组,还是被看作16位有符号整数的数组,等等。 +然而,类型化数组其实更多的是关于使用类似数组的语义(索引访问,等等)提供对二进制数据的结构化访问。名称中的“类型”指的是在大量二进制位(比特桶)的类型之上覆盖的“视图”,它实质上是一个映射,控制着这些二进制位是否应当被看作8位有符号整数的数组,还是被看作16位有符号整数的数组,等等。 -你怎样才能构建这样的比特桶呢?它被称为一个“缓冲(buffer)”,而你可以用`ArrayBuffer(..)`构造器最直接地构建它: +你怎样才能构建这样的比特桶呢?它被称为一个“缓冲(buffer)”,而你可以用`ArrayBuffer(..)`构造器直接地构建它: ```js var buf = new ArrayBuffer( 32 ); buf.byteLength; // 32 ``` -现在`buf`是一个长度为32字节(256比特)的二进制缓冲,它被与初始化为全`0`。除了检查它的`byteLength`属性,一个缓冲本身不会允许你进行任何操作。 +现在`buf`是一个长度为32字节(256比特)的二进制缓冲,它被预初始化为全`0`。除了检查它的`byteLength`属性,一个缓冲本身不会允许你进行任何操作。 **提示:** 有几种web平台特性都使用或返回缓冲,比如`FileReader#readAsArrayBuffer(..)`,`XMLHttpRequest#send(..)`,和`ImageData`(canvas数据)。 @@ -35,9 +35,9 @@ arr.length; // 16 `arr`是一个256位的`buf`缓冲在16位无符号整数的类型化数组的映射,意味着你得到16个元素。 -### Endianness +### 字节顺序 -明白一个事实非常重要:`arr`是使用JS所运行的平台的字节顺序设定(大端发或小端法)被映射的。如果二进制数据是由一种字节顺序创建,但是在一个拥有相反字节数序的平台被解释时,这就可能是个问题。 +明白一个事实非常重要:`arr`是使用JS所运行的平台的字节顺序设定(大端法或小端法)被映射的。如果二进制数据是由一种字节顺序创建,但是在一个拥有相反字节数序的平台被解释时,这就可能是个问题。 字节顺序指的是一个多字节数字的低位字节(8个比特位的集合) —— 比如我们在早先的代码段中创建的16位无符号整数 —— 是在这个数字的字节序列的左边还是右边。 @@ -45,8 +45,8 @@ arr.length; // 16 但是如果`3085`使用两个8位数字来表示的话,字节顺序就像会极大地影响它在内存中的存储: -* `0000110000001101` / `0c0d` (big endian) -* `0000110100001100` / `0d0c` (little endian) +* `0000110000001101` / `0c0d` (大端法) +* `0000110100001100` / `0d0c` (小端法) 如果你从一个小端法系统中收到表示为`0000110100001100`的`3085`,但是在一个大端法系统中为它上面铺一层视图,那么你将会看到值`3340`(10进制)和`0d0c`(16进制)。 @@ -62,11 +62,11 @@ var littleEndian = (function() { })(); ``` -`littleEndian`将是`true`或`false`;对大多数浏览器来说,它应当返回`true`。这个测试使用`DataView(..)`,它允许更底层,更精细地控制如何从你平在缓冲上的视图中访问二进制位。前面代码段中的`setInt16(..)`方法的第三个参数告诉`DataView`,对于这个操作你想使用什么字节顺序。 +`littleEndian`将是`true`或`false`;对大多数浏览器来说,它应当返回`true`。这个测试使用`DataView(..)`,它允许更底层,更精细地控制如何从你平铺在缓冲上的视图中访问二进制位。前面代码段中的`setInt16(..)`方法的第三个参数告诉`DataView`,对于这个操作你想使用什么字节顺序。 -**警告:** 不要讲一个数组缓冲中底层的二进制存储的字节顺序与一个数字在JS程序中被暴露时如何被表示搞混。举例来说,`(3085).toString(2)`返回`"110000001101"`,它被假定前面有四个`"0"`因而是大端法表现形式。事实上,这个表现形式是基于一个单独的16位视图的,而不是两个8位字节的视图。上面的`DataView`测试是确定你的JS环境的字节顺序的最佳方法。 +**警告:** 不要将一个数组缓冲中底层的二进制存储的字节顺序与一个数字在JS程序中被暴露时如何被表示搞混。举例来说,`(3085).toString(2)`返回`"110000001101"`,它被假定前面有四个`"0"`因而是大端法表现形式。事实上,这个表现形式是基于一个单独的16位视图的,而不是两个8位字节的视图。上面的`DataView`测试是确定你的JS环境的字节顺序的最佳方法。 -### Multiple Views +### 多视图 一个单独的缓冲可以连接多个视图,例如: @@ -83,7 +83,7 @@ view8[1]; // 12 view8[0].toString( 16 ); // "d" view8[1].toString( 16 ); // "c" -// swap (as if endian!) +// 调换(好像字节顺序一样!) var tmp = view8[0]; view8[0] = view8[1]; view8[1] = tmp; @@ -91,7 +91,7 @@ view8[1] = tmp; view16[0]; // 3340 ``` -类型化数组的构造器拥有多种签名。目前我们展示过的只是向它们传递一个既存的缓冲。然而,这种形式还接受两个额外的参数:`byteOffset`和`length`。换句话讲,你可以从`0`意外的位置开始类型化数组视图,也可以使它的长度小于整个缓冲的长度。 +类型化数组的构造器拥有多种签名。目前我们展示过的只是向它们传递一个既存的缓冲。然而,这种形式还接受两个额外的参数:`byteOffset`和`length`。换句话讲,你可以从`0`以外的位置开始类型化数组视图,也可以使它的长度小于整个缓冲的长度。 如果二进制数据的缓冲包含规格不一的大小/位置,这种技术可能十分有用。 @@ -104,7 +104,7 @@ var first = new Uint16Array( buf, 0, 2 )[0], fourth = new Float32Array( buf, 4, 4 )[0]; ``` -### TypedArray Constructors +### 类型化数组构造器 除了前一节我们检视的`(buffer,[offset, [length]])`形式之外,类型化数组的构造器还支持这些形式: @@ -144,7 +144,7 @@ a.join( "-" ); **警告:** 你不能对类型化数组使用没有意义的特定`Array.prototype`方法,比如修改器(`splice(..)`,`push(..)`,等等)和`concat(..)`。 -要小心,在类型化数组中的元素被限制在它被声明的位长度中。如果你有一个`Uint8Array`并试着向它的一个元素赋予某些大于8为的值,那么这个值将被戒断以保持在相应的位长度中。 +要小心,在类型化数组中的元素被限制在它被声明的位长度中。如果你有一个`Uint8Array`并试着向它的一个元素赋予某些大于8为的值,那么这个值将被截断以保持在相应的位长度中。 这可能造成一些问题,例如,如果你试着对一个类型化数组中的所有值求平方。考虑如下代码: @@ -232,7 +232,7 @@ keys[1] === y; // true vals[1]; // "bar" ``` -当然,你不会想亲自管理这些平行数组,所以你可能会顶一个数据解构,使它内部带有自动管理的方法。除了你不得不自己做这些工作,主要的缺陷是访问的时间复杂度不再是O(1),而是O(n)。 +当然,你不会想亲自管理这些平行数组,所以你可能会定义一个数据解构,使它内部带有自动管理的方法。除了你不得不自己做这些工作,主要的缺陷是访问的时间复杂度不再是O(1),而是O(n)。 但在ES6中,不再需要这么做了!使用`Map(..)`就好: @@ -251,7 +251,7 @@ m.get( y ); // "bar" 唯一的缺点是你不能使用`[]`方括号访问语法来设置或取得值。但是`get(..)`和`set(..)`可以完美地取代这种语法。 -要从一个map中产出一个元素,不要使用`delete`操作符,而是使用`delete(..)`方法: +要从一个map中删除一个元素,不要使用`delete`操作符,而是使用`delete(..)`方法: ```js m.set( x, "foo" );