From 01b0e12ba27b5069248fd09235e9a7143915ee30 Mon Sep 17 00:00:00 2001 From: Irvin Date: Wed, 16 Feb 2022 02:02:49 +0800 Subject: remove `notranslate` class in zh-CN --- .../a_re-introduction_to_javascript/index.html | 164 ++++++++++----------- 1 file changed, 82 insertions(+), 82 deletions(-) (limited to 'files/zh-cn/web/javascript/a_re-introduction_to_javascript/index.html') diff --git a/files/zh-cn/web/javascript/a_re-introduction_to_javascript/index.html b/files/zh-cn/web/javascript/a_re-introduction_to_javascript/index.html index 7c567f3902..b49d5c9024 100644 --- a/files/zh-cn/web/javascript/a_re-introduction_to_javascript/index.html +++ b/files/zh-cn/web/javascript/a_re-introduction_to_javascript/index.html @@ -63,7 +63,7 @@ translation_of: Web/JavaScript/A_re-introduction_to_JavaScript

根据语言规范,JavaScript 采用“遵循 IEEE 754 标准的双精度 64 位格式”("double-precision 64-bit format IEEE 754 values")表示数字。——在JavaScript(除了{{jsxref("BigInt")}})当中,并不存在整数/整型(Integer)。因此在处理如下的场景时候,您一定要小心:

-
console.log(3 / 2);             // 1.5,not 1
+
console.log(3 / 2);             // 1.5,not 1
 console.log(Math.floor(3 / 2)); // 1
 
@@ -71,67 +71,67 @@ console.log(Math.floor(3 / 2)); // 1

当然,您也需要小心这种情况:

-
0.1 + 0.2 = 0.30000000000000004
+
0.1 + 0.2 = 0.30000000000000004
 

在具体实现时,整数值通常被视为32位整型变量,在个别实现(如某些浏览器)中也以32位整型变量的形式进行存储,直到它被用于执行某些32位整型不支持的操作,这是为了便于进行位操作。

JavaScript 支持标准的算术运算符,包括加法、减法、取模(或取余)等等。还有一个之前没有提及的内置对象 {{jsxref("Math")}}(数学对象),用以处理更多的高级数学函数和常数:

-
Math.sin(3.5);
+
Math.sin(3.5);
 var circumference = 2 * Math.PI * r;
 

你可以使用内置函数 {{jsxref("Global_Objects/parseInt", "parseInt()")}} 将字符串转换为整型。该函数的第二个可选参数表示字符串所表示数字的基(进制):

-
parseInt("123", 10); // 123
+
parseInt("123", 10); // 123
 parseInt("010", 10); // 10
 

一些老版本的浏览器会将首字符为“0”的字符串当做八进制数字,2013 年以前的 JavaScript 实现会返回一个意外的结果:

-
parseInt("010");  //  8
+
parseInt("010");  //  8
 parseInt("0x10"); // 16

这是因为字符串以数字 0 开头,{{jsxref("Global_Objects/parseInt", "parseInt()")}}函数会把这样的字符串视作八进制数字;同理,0x开头的字符串则视为十六进制数字。

如果想把一个二进制数字字符串转换成整数值,只要把第二个参数设置为 2 就可以了:

-
parseInt("11", 2); // 3
+
parseInt("11", 2); // 3
 

JavaScript 还有一个类似的内置函数 {{jsxref("Global_Objects/parseFloat", "parseFloat()")}},用以解析浮点数字符串,与{{jsxref("Global_Objects/parseInt", "parseInt()")}}不同的地方是,parseFloat() 只应用于解析十进制数字。

一元运算符 + 也可以把数字字符串转换成数值:

-
+ "42";   // 42
+
+ "42";   // 42
 + "010";  // 10
 + "0x10"; // 16

如果给定的字符串不存在数值形式,函数会返回一个特殊的值 {{jsxref("NaN")}}(Not a Number 的缩写):

-
parseInt("hello", 10); // NaN
+
parseInt("hello", 10); // NaN
 

要小心NaN:如果把 NaN 作为参数进行任何数学运算,结果也会是 NaN

-
NaN + 5; //NaN
+
NaN + 5; //NaN
 

可以使用内置函数 isNaN() 来判断一个变量是否为 NaN

-
isNaN(NaN); // true
+
isNaN(NaN); // true
 

JavaScript 还有两个特殊值:Infinity(正无穷)和 -Infinity(负无穷):

-
1 / 0; //  Infinity
+
1 / 0; //  Infinity
 -1 / 0; // -Infinity
 

可以使用内置函数 isFinite() 来判断一个变量是否是一个有穷数, 如果类型为Infinity, -InfinityNaN则返回false

-
isFinite(1/0); // false
+
isFinite(1/0); // false
 isFinite(Infinity); // false
 isFinite(-Infinity); // false
 isFinite(NaN); // false
@@ -153,12 +153,12 @@ Number.isFinite("0"); // false

通过访问字符串的 length(编码单元的个数)属性,可以得到它的长度。

-
"hello".length; // 5
+
"hello".length; // 5
 

这是我们第一次碰到 JavaScript 对象。我们有没有提过你可以像  {{jsxref("Object", "object", "", 1)}}  一样使用字符串?是的,字符串也有 {{jsxref("String", "methods", "#Methods", 1)}}(方法)能让你操作字符串和获取字符串的信息。

-
"hello".charAt(0); // "h"
+
"hello".charAt(0); // "h"
 "hello, world".replace("world", "mars"); // "hello, mars"
 "hello".toUpperCase(); // "HELLO"
 
@@ -176,7 +176,7 @@ Number.isFinite("0"); // false

也可以使用 Boolean() 函数进行显式转换:

-
Boolean(''); // false
+
Boolean(''); // false
 Boolean(234); // true
 
@@ -190,12 +190,12 @@ Boolean(234); // true

let 语句声明一个块级作用域的本地变量,并且可选的将其初始化为一个值。

-
let a;
+
let a;
 let name = 'Simon';

下面是使用  let 声明变量作用域的例子:

-
// myLetVariable 在这里 *不能* 被引用
+
// myLetVariable 在这里 *不能* 被引用
 
 for (let myLetVariable = 0; myLetVariable < 5; myLetVariable++) {
   // myLetVariable 只能在这里引用
@@ -205,18 +205,18 @@ for (let myLetVariable = 0; myLetVariable < 5; myLetVariable++) {
 
 

const 允许声明一个不可变的常量。这个常量在定义域内总是可见的。

-
const Pi = 3.14; // 设置 Pi 的值
+
const Pi = 3.14; // 设置 Pi 的值
 Pi = 1; // 将会抛出一个错误因为你改变了一个常量的值。

var 是最常见的声明变量的关键字。它没有其他两个关键字的种种限制。这是因为它是传统上在 JavaScript 声明变量的唯一方法。使用 var 声明的变量在它所声明的整个函数都是可见的。

-
var a;
+
var a;
 var name = "simon";
 

一个使用  var 声明变量的语句块的例子:

-
// myVarVariable在这里 *能* 被引用
+
// myVarVariable在这里 *能* 被引用
 
 for (var myVarVariable = 0; myVarVariable < 5; myVarVariable++) {
   // myVarVariable 整个函数中都能被引用
@@ -232,19 +232,19 @@ for (var myVarVariable = 0; myVarVariable < 5; myVarVariable++) {
 
 

JavaScript的算术操作符包括 +-*/% ——求余(与模运算相同)。赋值使用 = 运算符,此外还有一些复合运算符,如 +=-=,它们等价于 x = x operator y

-
x += 5; // 等价于 x = x + 5;
+
x += 5; // 等价于 x = x + 5;
 

可以使用 ++-- 分别实现变量的自增和自减。两者都可以作为前缀或后缀操作符使用。

+ 操作符还可以用来连接字符串:

-
"hello" + " world"; // hello world
+
"hello" + " world"; // hello world
 

如果你用一个字符串加上一个数字(或其他值),那么操作数都会被首先转换为字符串。如下所示:

-
"3" + 4 + 5; // 345
+
"3" + 4 + 5; // 345
 3 + 4 + "5"; // 75
 
@@ -252,13 +252,13 @@ for (var myVarVariable = 0; myVarVariable < 5; myVarVariable++) {

JavaScript 中的比较操作使用 <><=>=,这些运算符对于数字和字符串都通用。相等的比较稍微复杂一些。由两个“=(等号)”组成的相等运算符有类型自适应的功能,具体例子如下:

-
123 == "123" // true
+
123 == "123" // true
 1 == true; // true
 

如果在比较前不需要自动类型转换,应该使用由三个“=(等号)”组成的相等运算符:

-
1 === true; //false
+
1 === true; //false
 123 === "123"; // false
 
@@ -270,7 +270,7 @@ for (var myVarVariable = 0; myVarVariable < 5; myVarVariable++) {

JavaScript 的控制结构与其他类 C 语言类似。可以使用 ifelse 来定义条件语句,还可以连起来使用:

-
var name = "kittens";
+
var name = "kittens";
 if (name == "puppies") {
   name += "!";
 } else if (name == "kittens") {
@@ -283,7 +283,7 @@ name == "kittens!!"; // true
 
 

JavaScript 支持 while 循环和 do-while 循环。前者适合常见的基本循环操作,如果需要循环体至少被执行一次则可以使用 do-while

-
while (true) {
+
while (true) {
   // 一个无限循环!
 }
 
@@ -295,41 +295,41 @@ do {
 
 

JavaScript 的 for 循环与 C 和 Java 中的相同:使用时可以在一行代码中提供控制信息。

-
for (var i = 0; i < 5; i++) {
+
for (var i = 0; i < 5; i++) {
   // 将会执行五次
 }
 

JavaScript 也还包括其他两种重要的 for 循环: for...of

-
for (let value of array) {
+
for (let value of array) {
   // do something with value
 }

for...in

-
for (let property in object) {
+
for (let property in object) {
   // do something with object property
 }

&&|| 运算符使用短路逻辑(short-circuit logic),是否会执行第二个语句(操作数)取决于第一个操作数的结果。在需要访问某个对象的属性时,使用这个特性可以事先检测该对象是否为空:

-
var name = o && o.getName();
+
var name = o && o.getName();
 

或用于缓存值(当错误值无效时):

-
var name = cachedName || (cachedName = getName());
+
var name = cachedName || (cachedName = getName());
 

类似地,JavaScript 也有一个用于条件表达式的三元操作符:

-
var allowed = (age > 18) ? "yes" : "no";
+
var allowed = (age > 18) ? "yes" : "no";
 

在需要多重分支时可以使用基于一个数字或字符串的 switch 语句:

-
switch(action) {
+
switch(action) {
     case 'draw':
         drawIt();
         break;
@@ -343,7 +343,7 @@ do {
 
 

如果你不使用 break 语句,JavaScript 解释器将会执行之后 case 中的代码。除非是为了调试,一般你并不需要这个特性,所以大多数时候不要忘了加上 break。

-
switch(a) {
+
switch(a) {
     case 1: // 继续向下
     case 2:
         eatIt();
@@ -355,7 +355,7 @@ do {
 
 

default 语句是可选的。switchcase 都可以使用需要运算才能得到结果的表达式;在 switch 的表达式和 case 的表达式是使用 === 严格相等运算符进行比较的:

-
switch(1 + 3){
+
switch(1 + 3){
     case 2 + 2:
         yay();
         break;
@@ -382,19 +382,19 @@ do {
 
 

有两种简单方法可以创建一个空对象:

-
var obj = new Object();
+
var obj = new Object();
 

和:

-
var obj = {};
+
var obj = {};
 

这两种方法在语义上是相同的。第二种更方便的方法叫作“对象字面量(object literal)”法。这种也是 JSON 格式的核心语法,一般我们优先选择第二种方法。

“对象字面量”也可以用来在对象实例中定义一个对象:

-
var obj = {
+
var obj = {
     name: "Carrot",
     _for: "Max",//'for' 是保留字之一,使用'_for'代替
     details: {
@@ -406,13 +406,13 @@ do {
 
 

对象的属性可以通过链式(chain)表示方法进行访问:

-
obj.details.color; // orange
+
obj.details.color; // orange
 obj["details"]["size"]; // 12
 

下面的例子创建了一个对象原型,Person,和这个原型的实例,You

-
function Person(name, age) {
+
function Person(name, age) {
   this.name = name;
   this.age = age;
 }
@@ -425,14 +425,14 @@ var You = new Person('You', 24);
 
 

完成创建后,对象属性可以通过如下两种方式进行赋值和访问:

-
// 点表示法(dot notation)
+
// 点表示法(dot notation)
 obj.name = 'Simon';
 var name = obj.name;
 

和:

-
// 括号表示法(bracket notation)
+
// 括号表示法(bracket notation)
 obj['name'] = 'Simon';
 var name = obj['name'];
 // can use a variable to define a key
@@ -441,7 +441,7 @@ obj[user] = prompt('what is its value?')

这两种方法在语义上也是相同的。第二种方法的优点在于属性的名称被看作一个字符串,这就意味着它可以在运行时被计算,缺点在于这样的代码有可能无法在后期被解释器优化。它也可以被用来访问某些以预留关键字作为名称的属性的值:

-
obj.for = 'Simon'; // 语法错误,因为 for 是一个预留关键字
+
obj.for = 'Simon'; // 语法错误,因为 for 是一个预留关键字
 obj["for"] = 'Simon'; // 工作正常
 
@@ -461,7 +461,7 @@ obj["for"] = 'Simon'; // 工作正常

创建数组的传统方法是:

-
var a = new Array();
+
var a = new Array();
 a[0] = "dog";
 a[1] = "cat";
 a[2] = "hen";
@@ -470,13 +470,13 @@ a.length; // 3
 
 

使用数组字面量(array literal)法更加方便:

-
var a = ["dog", "cat", "hen"];
+
var a = ["dog", "cat", "hen"];
 a.length; // 3
 

注意,Array.length 并不总是等于数组中元素的个数,如下所示:

-
var a = ["dog", "cat", "hen"];
+
var a = ["dog", "cat", "hen"];
 a[100] = "fox";
 a.length; // 101
 
@@ -485,40 +485,40 @@ a.length; // 101

如果试图访问一个不存在的数组索引,会得到 undefined

-
typeof(a[90]); // undefined
+
typeof(a[90]); // undefined
 

可以通过如下方式遍历一个数组:

-
for (var i = 0; i < a.length; i++) {
+
for (var i = 0; i < a.length; i++) {
     // Do something with a[i]
 }
 

ES2015 引入了更加简洁的 for...of 循环,可以用它来遍历可迭代对象,例如数组:

-
for (const currentValue of a) {
+
for (const currentValue of a) {
   // Do something with currentValue
 }
 

遍历数组的另一种方法是使用 for...in 循环, 然而这并不是遍历数组元素而是数组的索引。注意,如果哪个家伙直接向 Array.prototype 添加了新的属性,使用这样的循环这些属性也同样会被遍历。所以并不推荐使用这种方法遍历数组:

-
for (var i in a) {
+
for (var i in a) {
   // 操作 a[i]
 }
 

ECMAScript 5 增加了另一个遍历数组的方法,forEach()

-
["dog", "cat", "hen"].forEach(function(currentValue, index, array) {
+
["dog", "cat", "hen"].forEach(function(currentValue, index, array) {
   // 操作 currentValue 或者 array[index]
 });
 

如果想在数组后追加元素,只需要:

-
a.push(item);
+
a.push(item);

除了 forEach()push(),Array(数组)类还自带了许多方法。建议查看 Array 方法的完整文档

@@ -591,7 +591,7 @@ a.length; // 101

学习 JavaScript 最重要的就是要理解对象和函数两个部分。最简单的函数就像下面这个这么简单:

-
function add(x, y) {
+
function add(x, y) {
     var total = x + y;
     return total;
 }
@@ -601,19 +601,19 @@ a.length; // 101
 
 

已命名的参数更像是一个指示而没有其他作用。如果调用函数时没有提供足够的参数,缺少的参数会被 undefined 替代。

-
add(); // NaN
+
add(); // NaN
 // 不能在 undefined 对象上进行加法操作
 

你还可以传入多于函数本身需要参数个数的参数:

-
add(2, 3, 4); // 5
+
add(2, 3, 4); // 5
  // 将前两个值相加,4 被忽略了
 

这看上去有点蠢。函数实际上是访问了函数体中一个名为 arguments 的内部对象,这个对象就如同一个类似于数组的对象一样,包括了所有被传入的参数。让我们重写一下上面的函数,使它可以接收任意个数的参数:

-
function add() {
+
function add() {
     var sum = 0;
     for (var i = 0, j = arguments.length; i < j; i++) {
         sum += arguments[i];
@@ -626,7 +626,7 @@ add(2, 3, 4, 5); // 14
 
 

这跟直接写成 2 + 3 + 4 + 5 也没什么区别。我们还是创建一个求平均数的函数吧:

-
function avg() {
+
function avg() {
     var sum = 0;
     for (var i = 0, j = arguments.length; i < j; i++) {
         sum += arguments[i];
@@ -637,7 +637,7 @@ avg(2, 3, 4, 5); // 3.5

这个就有用多了,但是却有些冗长。为了使代码变短一些,我们可以使用剩余参数来替换arguments的使用。在这方法中,我们可以传递任意数量的参数到函数中同时尽量减少我们的代码。这个剩余参数操作符在函数中以:...variable 的形式被使用,它将包含在调用函数时使用的未捕获整个参数列表到这个变量中。我们同样也可以将 for 循环替换为 for...of 循环来返回我们变量的值。

-
function avg(...args) {
+
function avg(...args) {
   var sum = 0;
   for (let value of args) {
     sum += value;
@@ -654,7 +654,7 @@ avg(2, 3, 4, 5); // 3.5
 

需要注意的是,无论“剩余参数操作符”被放置到函数声明的哪里,它都会把除了自己之前的所有参数存储起来。比如函数:function avg(firstValue, ...args) 会把传入函数的第一个值存入 firstValue,其他的参数存入 args。这是虽然一个很有用的语言特性,却也会带来新的问题。avg() 函数只接受逗号分开的参数列表 -- 但是如果你想要获取一个数组的平均值怎么办?一种方法是将函数按照如下方式重写:

-
function avgArray(arr) {
+
function avgArray(arr) {
     var sum = 0;
     for (var i = 0, j = arr.length; i < j; i++) {
         sum += arr[i];
@@ -666,7 +666,7 @@ avgArray([2, 3, 4, 5]); // 3.5
 
 

但如果能重用我们已经创建的那个函数不是更好吗?幸运的是 JavaScript 允许你通过任意函数对象的 apply() 方法来传递给它一个数组作为参数列表。

-
avg.apply(null, [2, 3, 4, 5]); // 3.5
+
avg.apply(null, [2, 3, 4, 5]); // 3.5
 

传给 apply() 的第二个参数是一个数组,它将被当作 avg() 的参数列表使用,至于第一个参数 null,我们将在后面讨论。这也正说明了一个事实——函数也是对象。

@@ -679,7 +679,7 @@ avgArray([2, 3, 4, 5]); // 3.5

JavaScript 允许你创建匿名函数:

-
var avg = function() {
+
var avg = function() {
     var sum = 0;
     for (var i = 0, j = arguments.length; i < j; i++) {
         sum += arguments[i];
@@ -690,7 +690,7 @@ avgArray([2, 3, 4, 5]); // 3.5
 
 

这个函数在语义上与 function avg() 相同。你可以在代码中的任何地方定义这个函数,就像写普通的表达式一样。基于这个特性,有人发明出一些有趣的技巧。与 C 中的块级作用域类似,下面这个例子隐藏了局部变量:

-
var a = 1;
+
var a = 1;
 var b = 2;
 (function() {
     var b = 3;
@@ -703,7 +703,7 @@ b; // 2
 
 

JavaScript 允许以递归方式调用函数。递归在处理树形结构(比如浏览器 DOM)时非常有用。

-
function countChars(elm) {
+
function countChars(elm) {
     if (elm.nodeType == 3) { // 文本节点
         return elm.nodeValue.length;
     }
@@ -717,7 +717,7 @@ b; // 2
 
 

这里需要说明一个潜在问题——既然匿名函数没有名字,那该怎么递归调用它呢?在这一点上,JavaScript 允许你命名这个函数表达式。你可以命名立即调用的函数表达式(IIFE——Immediately Invoked Function Expression),如下所示:

-
var charsInBody = (function counter(elm) {
+
var charsInBody = (function counter(elm) {
     if (elm.nodeType == 3) { // 文本节点
         return elm.nodeValue.length;
     }
@@ -739,7 +739,7 @@ b; // 2
 
 

在经典的面向对象语言中,对象是指数据和在这些数据上进行的操作的集合。与 C++ 和 Java 不同,JavaScript 是一种基于原型的编程语言,并没有 class 语句,而是把函数用作类。那么让我们来定义一个人名对象,这个对象包括人的姓和名两个域(field)。名字的表示有两种方法:“名 姓(First Last)”或“姓, 名(Last, First)”。使用我们前面讨论过的函数和对象概念,可以像这样完成定义:

-
function makePerson(first, last) {
+
function makePerson(first, last) {
     return {
         first: first,
         last: last
@@ -758,7 +758,7 @@ personFullNameReversed(s); // "Willison, Simon"

上面的写法虽然可以满足要求,但是看起来很麻烦,因为需要在全局命名空间中写很多函数。既然函数本身就是对象,如果需要使一个函数隶属于一个对象,那么不难得到:

-
function makePerson(first, last) {
+
function makePerson(first, last) {
     return {
         first: first,
         last: last,
@@ -776,7 +776,7 @@ s.fullNameReversed(); // Willison, Simon

上面的代码里有一些我们之前没有见过的东西:关键字 this。当使用在函数中时,this 指代当前的对象,也就是调用了函数的对象。如果在一个对象上使用点或者方括号来访问属性或方法,这个对象就成了 this。如果并没有使用“点”运算符调用某个对象,那么 this 将指向全局对象(global object)。这是一个经常出错的地方。例如:

-
s = makePerson("Simon", "Willison");
+
s = makePerson("Simon", "Willison");
 var fullName = s.fullName;
 fullName(); // undefined undefined
 
@@ -785,7 +785,7 @@ fullName(); // undefined undefined

下面使用关键字 this 改进已有的 makePerson函数:

-
function Person(first, last) {
+
function Person(first, last) {
     this.first = first;
     this.last = last;
     this.fullName = function() {
@@ -804,7 +804,7 @@ var s = new Person("Simon", "Willison");
 
 

我们的 Person 对象现在已经相当完善了,但还有一些不太好的地方。每次我们创建一个 Person 对象的时候,我们都在其中创建了两个新的函数对象——如果这个代码可以共享不是更好吗?

-
function personFullName() {
+
function personFullName() {
     return this.first + ' ' + this.last;
 }
 function personFullNameReversed() {
@@ -820,7 +820,7 @@ function Person(first, last) {
 
 

这种写法的好处是,我们只需要创建一次方法函数,在构造函数中引用它们。那是否还有更好的方法呢?答案是肯定的。

-
function Person(first, last) {
+
function Person(first, last) {
     this.first = first;
     this.last = last;
 }
@@ -836,7 +836,7 @@ Person.prototype.fullNameReversed = function() {
 
 

这个特性功能十分强大,JavaScript 允许你在程序中的任何时候修改原型(prototype)中的一些东西,也就是说你可以在运行时(runtime)给已存在的对象添加额外的方法:

-
s = new Person("Simon", "Willison");
+
s = new Person("Simon", "Willison");
 s.firstNameCaps();  // TypeError on line 1: s.firstNameCaps is not a function
 
 Person.prototype.firstNameCaps = function() {
@@ -847,7 +847,7 @@ s.firstNameCaps(); // SIMON
 
 

有趣的是,你还可以给 JavaScript 的内置函数原型(prototype)添加东西。让我们给 String 添加一个方法用来返回逆序的字符串:

-
var s = "Simon";
+
var s = "Simon";
 s.reversed(); // TypeError on line 1: s.reversed is not a function
 
 String.prototype.reversed = function() {
@@ -862,12 +862,12 @@ s.reversed(); // nomiS
 
 

定义新方法也可以在字符串字面量上用(string literal)。

-
"This can now be reversed".reversed(); // desrever eb won nac sihT
+
"This can now be reversed".reversed(); // desrever eb won nac sihT
 

正如我前面提到的,原型组成链的一部分。那条链的根节点是 Object.prototype,它包括 toString() 方法——将对象转换成字符串时调用的方法。这对于调试我们的 Person 对象很有用:

-
var s = new Person("Simon", "Willison");
+
var s = new Person("Simon", "Willison");
 s; // [object Object]
 
 Person.prototype.toString = function() {
@@ -878,7 +878,7 @@ s.toString(); // <Person: Simon Willison>
 
 

你是否还记得之前我们说的 avg.apply() 中的第一个参数 null?现在我们可以回头看看这个东西了。apply() 的第一个参数应该是一个被当作 this 来看待的对象。下面是一个 new 方法的简单实现:

-
function trivialNew(constructor, ...args) {
+
function trivialNew(constructor, ...args) {
     var o = {}; // 创建一个对象
     constructor.apply(o, args);
     return o;
@@ -889,15 +889,15 @@ s.toString(); // <Person: Simon Willison>
 
 

因此,调用

-
var bill = trivialNew(Person, "William", "Orange");
+
var bill = trivialNew(Person, "William", "Orange");

可认为和调用如下语句是等效的

-
var bill = new Person("William", "Orange");
+
var bill = new Person("William", "Orange");

apply() 有一个姐妹函数,名叫 call,它也可以允许你设置 this,但它带有一个扩展的参数列表而不是一个数组。

-
function lastNameCaps() {
+
function lastNameCaps() {
     return this.last.toUpperCase();
 }
 var s = new Person("Simon", "Willison");
@@ -911,7 +911,7 @@ s.lastNameCaps();
 
 

JavaScript 允许在一个函数内部定义函数,这一点我们在之前的 makePerson() 例子中也见过。关于 JavaScript 中的嵌套函数,一个很重要的细节是,它们可以访问父函数作用域中的变量:

-
function parentFunc() {
+
function parentFunc() {
   var a = 1;
 
   function nestedFunc() {
@@ -929,7 +929,7 @@ s.lastNameCaps();
 
 

闭包是 JavaScript 中最强大的抽象概念之一——但它也是最容易造成困惑的。它究竟是做什么的呢?

-
function makeAdder(a) {
+
function makeAdder(a) {
   return function(b) {
     return a + b;
   }
@@ -946,7 +946,7 @@ add20(7); // ?
 
 

这里发生的事情和前面介绍过的内嵌函数十分相似:一个函数被定义在了另外一个函数的内部,内部函数可以访问外部函数的变量。唯一的不同是,外部函数已经返回了,那么常识告诉我们局部变量“应该”不再存在。但是它们却仍然存在——否则 adder 函数将不能工作。也就是说,这里存在 makeAdder 的局部变量的两个不同的“副本”——一个是 a 等于 5,另一个是 a 等于 20。那些函数的运行结果就如下所示:

-
add5(6); // 返回 11
+
add5(6); // 返回 11
 add20(7); // 返回 27
 
-- cgit v1.2.3-54-g00ecf