大话 JavaScript(Speaking JavaScript):第一章到第五章

发布时间:2024年01月12日

第一部分:JavaScript 快速入门

原文:I. JavaScript Quick Start

译者:飞龙

协议:CC BY-NC-SA 4.0

这部分是 JavaScript 的一个独立快速介绍。你可以在不阅读本书中的其他内容的情况下理解它,本书的其他部分也不依赖于它的内容。然而,阅读本书的提示在阅读本书的提示中适用。

第一章:基本 JavaScript

原文:1. Basic JavaScript

译者:飞龙

协议:CC BY-NC-SA 4.0

本章是关于“基本 JavaScript”,这是我为 JavaScript 的一个子集选择的名称,尽可能简洁,同时仍然能让你高效地工作。当你开始学习 JavaScript 时,我建议你在学习其他语言之前先在其中编程一段时间。这样,你就不必一次学习所有内容,这可能会让人困惑。

背景

本节简要介绍了 JavaScript 的背景,以帮助你理解它为什么是这样的。

JavaScript 与 ECMAScript

ECMAScript是 JavaScript 的官方名称。之所以需要一个新名称,是因为Java有商标(最初由 Sun 持有,现在由 Oracle 持有)。目前,Mozilla 是少数几家被允许正式使用JavaScript名称的公司之一,因为它很久以前就获得了许可证。对于常见用法,以下规则适用:

  • JavaScript意味着编程语言。

  • ECMAScript是语言规范的官方名称。因此,每当提到语言的版本时,人们都说ECMAScript。JavaScript 的当前版本是 ECMAScript 5;ECMAScript 6 目前正在开发中。

影响和语言的性质

JavaScript 的创造者 Brendan Eich 别无选择,只能很快地创建这种语言(否则,Netscape 可能会采用其他更糟糕的技术)。他从几种编程语言中借鉴了一些东西:Java(语法,原始值与对象),Scheme 和 AWK(一级函数),Self(原型继承),以及 Perl 和 Python(字符串,数组和正则表达式)。

JavaScript 在 ECMAScript 3 之前没有异常处理,这就解释了为什么语言经常自动转换值并经常悄悄失败:最初它无法抛出异常。

一方面,JavaScript 有一些怪癖,缺少相当多的功能(块作用域变量,模块,支持子类等)。另一方面,它有几个强大的功能,可以让你解决这些问题。在其他语言中,你学习语言特性。在 JavaScript 中,你经常学习模式而不是语言特性。

鉴于它的影响,毫不奇怪 JavaScript 可以实现一种混合了函数式编程(高阶函数;内置的mapreduce等)和面向对象编程(对象,继承)的编程风格。

语法

本节解释了 JavaScript 的基本语法原则。

语法概述

一些语法的例子:

// Two slashes start single-line comments

var x;  // declaring a variable

x = 3 + y;  // assigning a value to the variable `x`

foo(x, y);  // calling function `foo` with parameters `x` and `y`
obj.bar(3);  // calling method `bar` of object `obj`

// A conditional statement
if (x === 0) {  // Is `x` equal to zero?
    x = 123;
}

// Defining function `baz` with parameters `a` and `b`
function baz(a, b) {
    return a + b;
}

注意等号的两种不同用法:

  • 单个等号(=)用于将值赋给变量。

  • 三个等号(===)用于比较两个值(参见相等运算符)。

语句与表达式

要理解 JavaScript 的语法,你应该知道它有两个主要的语法类别:语句和表达式:

  • 语句“做事情”。程序是一系列语句。这是一个语句的例子,它声明(创建)一个变量foo

    var foo;
    
  • 表达式产生值。它们是函数参数,赋值的右侧等等。这是一个表达式的例子:

    3 * 7
    

语句和表达式之间的区别最好通过 JavaScript 有两种不同的if-then-else的方式来说明——作为语句:

var x;
if (y >= 0) {
    x = y;
} else {
    x = -y;
}

或作为一个表达式:

var x = y >= 0 ? y : -y;

你可以将后者用作函数参数(但不能使用前者):

myFunction(y >= 0 ? y : -y)

最后,无论 JavaScript 在哪里期望一个语句,你也可以使用一个表达式;例如:

foo(7, 1);

整行是一个语句(所谓的表达式语句),但函数调用foo(7, 1)是一个表达式。

分号

在 JavaScript 中,分号是可选的。但是,我建议始终包括它们,因为否则 JavaScript 可能会错误猜测语句的结束。详细信息请参见自动分号插入

分号终止语句,但不终止块。有一种情况下,您会在块后看到一个分号:函数表达式是以块结尾的表达式。如果这样的表达式出现在语句的最后,它后面会跟着一个分号:

// Pattern: var _ = ___;
var x = 3 * 7;
var f = function () { };  // function expr. inside var decl.

注释

JavaScript 有两种注释:单行注释和多行注释。单行注释以//开头,并在行尾终止:

x++; // single-line comment

多行注释由/**/界定:

/* This is
 a multiline
 comment.
 */

变量和赋值

在 JavaScript 中,变量在使用之前被声明:

var foo;  // declare variable `foo`

赋值

您可以声明一个变量并同时赋值:

var foo = 6;

您也可以给现有变量赋值:

foo = 4;  // change variable `foo`

复合赋值运算符

有复合赋值运算符,比如+=。以下两个赋值是等价的:

x += 1;
x = x + 1;

标识符和变量名

标识符是在 JavaScript 中扮演各种语法角色的名称。例如,变量的名称是标识符。标识符区分大小写。

大致而言,标识符的第一个字符可以是任何 Unicode 字母、美元符号($)或下划线(_)。随后的字符还可以是任何 Unicode 数字。因此,以下都是合法的标识符:

arg0
_tmp
$elem
π

以下标识符是保留字——它们是语法的一部分,不能用作变量名(包括函数名和参数名):

argumentsbreakcasecatch
classconstcontinuedebugger
defaultdeletedoelse
enumexportextendsfalse
finallyforfunctionif
implementsimportininstanceof
interfaceletnewnull
packageprivateprotectedpublic
returnstaticsuperswitch
thisthrowtruetry
typeofvarvoidwhile

以下三个标识符不是保留字,但您应该将它们视为保留字:

| Infinity |
| NaN |
| undefined |

最后,您还应该避免使用标准全局变量的名称(参见第二十三章)。您可以将它们用于局部变量而不会破坏任何东西,但您的代码仍然会变得混乱。

JavaScript 有许多我们从编程语言中期望的值:布尔值、数字、字符串、数组等等。JavaScript 中的所有值都有属性。每个属性都有一个(或名称)和一个。您可以将属性视为记录的字段。您可以使用点(.)运算符来读取属性:

value.propKey

例如,字符串'abc'具有属性length

> var str = 'abc';
> str.length
3

前面的也可以写成:

> 'abc'.length
3

点运算符也用于给属性赋值:

> var obj = {};  // empty object
> obj.foo = 123; // create property `foo`, set it to 123
123
> obj.foo
123

您也可以用它来调用方法:

> 'hello'.toUpperCase()
'HELLO'

在上面的例子中,我们已经在值'hello'上调用了方法toUpperCase()

原始值与对象

JavaScript 在值之间做了一个相当武断的区分:

  • 原始值是布尔值、数字、字符串、nullundefined

  • 所有其他值都是对象

两者之间的一个主要区别是它们的比较方式;每个对象都有唯一的标识,并且只有(严格)等于自身:

> var obj1 = {};  // an empty object
> var obj2 = {};  // another empty object
> obj1 === obj2
false
> obj1 === obj1
true

相反,所有编码相同值的原始值都被视为相同:

> var prim1 = 123;
> var prim2 = 123;
> prim1 === prim2
true

接下来的两节将更详细地解释原始值和对象。

原始值

以下是所有原始值(或简称原始值):

原始值具有以下特征:

按值比较

“内容”进行比较:

> 3 === 3
true
> 'abc' === 'abc'
true

始终不可变

属性无法更改,添加或删除:

> var str = 'abc';

> str.length = 1; // try to change property `length`
> str.length      // ? no effect
3

> str.foo = 3; // try to create property `foo`
> str.foo      // ? no effect, unknown property
undefined

(读取未知属性始终返回undefined。)

对象

所有非原始值都是对象。最常见的对象类型是:

  • 普通对象,可以通过对象字面量创建(参见单个对象):

    {
        firstName: 'Jane',
        lastName: 'Doe'
    }
    

前面的对象有两个属性:属性firstName的值为'Jane',属性lastName的值为'Doe'

  • 数组,可以通过数组字面量创建(参见数组):

    [ 'apple', 'banana', 'cherry' ]
    

前面的数组有三个元素,可以通过数字索引访问。例如,'apple’的索引是 0。

  • 正则表达式,可以通过正则表达式字面量创建(参见正则表达式):

    /^a+b+$/
    

对象具有以下特征:

按引用比较

进行身份比较;每个值都有自己的身份:

> {} === {}  // two different empty objects
false

> var obj1 = {};
> var obj2 = obj1;
> obj1 === obj2
true

默认可变

通常可以自由更改,添加和删除属性(参见单个对象):

> var obj = {};
> obj.foo = 123; // add property `foo`
> obj.foo
123

undefined 和 null

大多数编程语言都有表示缺少信息的值。JavaScript 有两个这样的“非值”,undefinednull

  • undefined表示“没有值”。未初始化的变量是undefined

    > var foo;
    > foo
    undefined
    

缺少参数是undefined

    > function f(x) { return x }
    > f()
    undefined
    ```

如果读取不存在的属性,将得到`undefined````js
    > var obj = {}; // empty object
    > obj.foo
    undefined
    ```

+   `null`表示“没有对象”。每当期望对象时(参数,对象链中的最后一个等),它被用作非值。

### 警告

`undefined``null`没有属性,甚至没有标准方法,如`toString()`。

#### 检查 undefined 或 null

函数通常允许您通过`undefined``null`指示缺少值。您可以通过显式检查来做相同的事情:

```js
if (x === undefined || x === null) {
    ...
}

您还可以利用undefinednull都被视为false的事实:

if (!x) {
    ...
}

警告

false0NaN''也被视为false(参见真值和假值)。

使用 typeof 和 instanceof 对值进行分类

有两个用于对值进行分类的运算符:typeof主要用于原始值,而instanceof用于对象。

typeof看起来像这样:

typeof value

它返回描述value“类型”的字符串。以下是一些示例:

> typeof true
'boolean'
> typeof 'abc'
'string'
> typeof {} // empty object literal
'object'
> typeof [] // empty array literal
'object'

以下表列出了typeof的所有结果:

操作数结果
undefined'undefined'
null'object'
布尔值'boolean'
数字值'number'
字符串值'string'
函数'function'
所有其他正常值'object'
(引擎创建的值)JavaScript 引擎允许创建值,其typeof返回任意字符串(与此表中列出的所有结果都不同)。

typeof null返回'object'是一个无法修复的错误,因为这会破坏现有的代码。这并不意味着null是一个对象。

instanceof看起来像这样:

value instanceof Constr

如果value是由构造函数Constr创建的对象,则返回true(参见构造函数:对象的工厂)。以下是一些示例:

> var b = new Bar();  // object created by constructor Bar
> b instanceof Bar
true

> {} instanceof Object
true
> [] instanceof Array
true
> [] instanceof Object  // Array is a subconstructor of Object
true

> undefined instanceof Object
false
> null instanceof Object
false

布尔值

原始布尔类型包括值truefalse。以下运算符产生布尔值:

  • 二进制逻辑运算符:&&(与),||(或)

  • 前缀逻辑运算符:!(非)

  • 比较运算符:

  • 相等运算符:===!====!=

  • 排序运算符(用于字符串和数字):>, >=, <, <=

真值和假值

每当 JavaScript 期望布尔值(例如if语句的条件)时,可以使用任何值。它将被解释为truefalse。以下值被解释为false

  • undefinednull

  • 布尔值:false

  • 数字:-0NaN

  • 字符串:''

所有其他值(包括所有对象!)都被认为是true。被解释为false的值称为假值,被解释为true的值称为真值Boolean()作为函数调用,将其参数转换为布尔值。您可以使用它来测试值的解释方式:

> Boolean(undefined)
false
> Boolean(0)
false
> Boolean(3)
true
> Boolean({}) // empty object
true
> Boolean([]) // empty array
true

二进制逻辑运算符

JavaScript 中的二进制逻辑运算符是短路的。也就是说,如果第一个操作数足以确定结果,第二个操作数将不会被评估。例如,在以下表达式中,函数foo()永远不会被调用:

false && foo()
true  || foo()

此外,二进制逻辑运算符返回它们的操作数之一,这些操作数可能是布尔值也可能不是。使用真值检查来确定哪一个:

和(&&)

如果第一个操作数为假值,则返回它。否则,返回第二个操作数:

> NaN && 'abc'
NaN
> 123 && 'abc'
'abc'

或(||)

如果第一个操作数为真值,则返回它。否则,返回第二个操作数:

> 'abc' || 123
'abc'
> '' || 123
123

相等运算符

JavaScript 有两种相等性:

  • 普通,或“宽松”,(不)相等:==!=

  • 严格(不)相等:===!==

普通相等性认为太多的值是相等的(详细内容在普通(宽松)相等性(==,!=)中有解释),这可能会隐藏错误。因此,建议始终使用严格相等性。

数字

JavaScript 中的所有数字都是浮点数:

> 1 === 1.0
true

特殊数字包括以下内容:

NaN(“不是一个数字”)

一个错误值:

> Number('xyz')  // 'xyz' can’t be converted to a number
NaN

Infinity

也是大多数错误值:

> 3 / 0
Infinity
> Math.pow(2, 1024)  // number too large
Infinity

Infinity大于任何其他数字(除了NaN)。同样,-Infinity小于任何其他数字(除了NaN)。这使得这些数字在作为默认值时非常有用(例如,当你正在寻找最小值或最大值时)。

运算符

JavaScript 有以下算术运算符(参见算术运算符):

  • 加法:number1 + number2

  • 减法:number1 - number2

  • 乘法:number1 * number2

  • 除法:number1 / number2

  • 余数:number1 % number2

  • 增量:++variable, variable++

  • 递减:--variable, variable--

  • 否定:-value

  • 转换为数字:+value

全局对象Math(参见Math)通过函数提供更多的算术运算。

JavaScript 还有位操作的运算符(例如,位与;参见位运算符)。

字符串

字符串可以直接通过字符串字面量创建。这些字面量由单引号或双引号括起来。反斜杠(\)转义字符并产生一些控制字符。以下是一些例子:

'abc'
"abc"

'Did she say "Hello"?'
"Did she say \"Hello\"?"

'That\'s nice!'
"That's nice!"

'Line 1\nLine 2'  // newline
'Backlash: \\'

单个字符通过方括号访问:

> var str = 'abc';
> str[1]
'b'

属性length计算字符串中的字符数:

> 'abc'.length
3

与所有原始值一样,字符串是不可变的;如果要更改现有字符串,需要创建一个新字符串。

字符串运算符

字符串通过加号(+)操作符进行连接,如果其中一个操作数是字符串,则将另一个操作数转换为字符串:

> var messageCount = 3;
> 'You have ' + messageCount + ' messages'
'You have 3 messages'

要在多个步骤中连接字符串,使用+=操作符:

> var str = '';
> str += 'Multiple ';
> str += 'pieces ';
> str += 'are concatenated.';
> str
'Multiple pieces are concatenated.'

字符串方法

字符串有许多有用的方法(参见字符串原型方法)。以下是一些例子:

> 'abc'.slice(1)  // copy a substring
'bc'
> 'abc'.slice(1, 2)
'b'

> '\t xyz  '.trim()  // trim whitespace
'xyz'

> 'mj?lnir'.toUpperCase()
'MJ?LNIR'

> 'abc'.indexOf('b')  // find a string
1
> 'abc'.indexOf('x')
-1

语句

JavaScript 中的条件和循环在以下部分介绍。

条件

if语句有一个then子句和一个可选的else子句,根据布尔条件执行:

if (myvar === 0) {
    // then
}

if (myvar === 0) {
    // then
} else {
    // else
}

if (myvar === 0) {
    // then
} else if (myvar === 1) {
    // else-if
} else if (myvar === 2) {
    // else-if
} else {
    // else
}

我建议始终使用大括号(它们表示零个或多个语句的块)。但如果一个子句只是一个语句,你不必这样做(对于控制流语句forwhile也是如此):

if (x < 0) return -x;

以下是一个switch语句。fruit的值决定执行哪个case

switch (fruit) {
    case 'banana':
        // ...
        break;
    case 'apple':
        // ...
        break;
    default:  // all other cases
        // ...
}

case后的“操作数”可以是任何表达式;它通过===switch的参数进行比较。

循环

for循环的格式如下:

for (??init??; ??condition??; ??post_iteration??)
    ?statement?

init在循环开始时执行。在每次循环迭代之前检查condition;如果变为false,则终止循环。post_iteration在每次循环迭代后执行。

此示例在控制台上打印数组arr的所有元素:

for (var i=0; i < arr.length; i++) {
    console.log(arr[i]);
}

while循环在其条件成立时继续循环其主体:

// Same as for loop above:
var i = 0;
while (i < arr.length) {
    console.log(arr[i]);
    i++;
}

do-while循环在其条件成立时继续循环其主体。由于条件跟随主体,因此主体始终至少执行一次:

do {
    // ...
} while (condition);

在所有循环中:

  • break离开循环。

  • continue开始新的循环迭代。

函数

定义函数的一种方式是通过函数声明

function add(param1, param2) {
    return param1 + param2;
}

前面的代码定义了一个函数add,它有两个参数param1param2,并返回这两个参数的总和。这是如何调用该函数的:

> add(6, 1)
7
> add('a', 'b')
'ab'

定义add()的另一种方式是通过将函数表达式分配给变量add

var add = function (param1, param2) {
    return param1 + param2;
};

函数表达式产生一个值,因此可以直接用于将函数作为参数传递给其他函数:

someOtherFunction(function (p1, p2) { ... });

函数声明被提升

函数声明是提升的-完整地移动到当前范围的开头。这允许您引用稍后声明的函数:

function foo() {
    bar();  // OK, bar is hoisted
    function bar() {
        ...
    }
}

请注意,虽然var声明也被提升(参见变量被提升),但是它们执行的赋值不会:

function foo() {
    bar();  // Not OK, bar is still undefined
    var bar = function () {
        // ...
    };
}

特殊变量参数

您可以使用任意数量的参数调用 JavaScript 中的任何函数;语言永远不会抱怨。但是,它将使所有参数通过特殊变量arguments可用。arguments看起来像一个数组,但没有数组方法:

> function f() { return arguments }
> var args = f('a', 'b', 'c');
> args.length
3
> args[0]  // read element at index 0
'a'

参数太多或太少

让我们使用以下函数来探索 JavaScript 中如何处理太多或太少的参数(函数toArray()显示在将参数转换为数组中):

function f(x, y) {
    console.log(x, y);
    return toArray(arguments);
}

将忽略额外的参数(除了arguments):

> f('a', 'b', 'c')
a b
[ 'a', 'b', 'c' ]

缺少参数将获得值undefined

> f('a')
a undefined
[ 'a' ]
> f()
undefined undefined
[]

可选参数

以下是为参数分配默认值的常见模式:

function pair(x, y) {
    x = x || 0;  // (1)
    y = y || 0;
    return [ x, y ];
}

在第(1)行,||运算符返回x,如果它是真值(不是nullundefined等)。否则,它将返回第二个操作数:

> pair()
[ 0, 0 ]
> pair(3)
[ 3, 0 ]
> pair(3, 5)
[ 3, 5 ]

强制参数个数

如果要强制执行arity(特定数量的参数),可以检查arguments.length

function pair(x, y) {
    if (arguments.length !== 2) {
        throw new Error('Need exactly 2 arguments');
    }
    ...
}

将参数转换为数组

arguments不是数组,它只是类似数组(参见类似数组对象和通用方法)。它有一个length属性,您可以通过方括号中的索引访问其元素。但是,您无法删除元素或调用其中任何数组方法。因此,有时需要将arguments转换为数组,这就是以下函数所做的事情(它在类似数组对象和通用方法中有解释):

function toArray(arrayLikeObject) {
    return Array.prototype.slice.call(arrayLikeObject);
}

异常处理

处理异常的最常见方法(参见第十四章)如下:

function getPerson(id) {
    if (id < 0) {
        throw new Error('ID must not be negative: '+id);
    }
    return { id: id }; // normally: retrieved from database
}

function getPersons(ids) {
    var result = [];
    ids.forEach(function (id) {
        try {
            var person = getPerson(id);
            result.push(person);
        } catch (exception) {
            console.log(exception);
        }
    });
    return result;
}

try子句包围关键代码,如果在try子句内抛出异常,则执行catch子句。使用前面的代码:

> getPersons([2, -5, 137])
[Error: ID must not be negative: -5]
[ { id: 2 }, { id: 137 } ]

严格模式

严格模式(参见严格模式)启用更多警告,并使 JavaScript 成为一种更干净的语言(非严格模式有时被称为“松散模式”)。要打开它,请首先在 JavaScript 文件或<script>标记中键入以下行:

'use strict';

您还可以为每个函数启用严格模式:

function functionInStrictMode() {
    'use strict';
}

变量作用域和闭包

在 JavaScript 中,你在使用变量之前通过var声明变量:

> var x;
> x = 3;
> y = 4;
ReferenceError: y is not defined

你可以使用单个var语句声明和初始化多个变量:

var x = 1, y = 2, z = 3;

但我建议每个变量使用一个语句(原因在Syntax中有解释)。因此,我会重写上一个语句为:

var x = 1;
var y = 2;
var z = 3;

由于提升(参见变量被提升),通常最好在函数的开头声明变量。

变量是函数作用域的

变量的作用域总是整个函数(而不是当前的块)。例如:

function foo() {
    var x = -512;
    if (x < 0) {  // (1)
        var tmp = -x;
        ...
    }
    console.log(tmp);  // 512
}

我们可以看到变量tmp不仅限于从第(1)行开始的块;它存在直到函数的结束。

变量被提升

每个变量声明都是提升的:声明被移动到函数的开头,但它所做的赋值保持不变。例如,考虑下面函数中第(1)行的变量声明:

function foo() {
    console.log(tmp); // undefined
    if (false) {
        var tmp = 3;  // (1)
    }
}

在内部,前面的函数是这样执行的:

function foo() {
    var tmp;  // hoisted declaration
    console.log(tmp);
    if (false) {
        tmp = 3;  // assignment stays put
    }
}

闭包

每个函数都与包围它的函数的变量保持连接,即使它离开了被创建的作用域。例如:

function createIncrementor(start) {
    return function () {  // (1)
        start++;
        return start;
    }
}

从第(1)行开始的函数离开了它被创建的上下文,但仍然连接到start的一个活动版本:

> var inc = createIncrementor(5);
> inc()
6
> inc()
7
> inc()
8

闭包是一个函数加上与其周围作用域的变量的连接。因此,createIncrementor()返回的是一个闭包。

IIFE 模式:引入新的作用域

有时你想引入一个新的变量作用域——例如,防止一个变量成为全局的。在 JavaScript 中,你不能使用块来做到这一点;你必须使用一个函数。但是有一种使用函数的块状方式的模式。它被称为IIFE立即调用函数表达式,发音为“iffy”):

(function () {  // open IIFE
    var tmp = ...;  // not a global variable
}());  // close IIFE

确保按照前面的示例精确地输入(除了注释)。IIFE 是一个在定义后立即调用的函数表达式。在函数内部,存在一个新的作用域,防止tmp成为全局的。请参阅IIFE 引入新的作用域了解 IIFE 的详细信息。

IIFE 的用例:通过闭包无意中共享

闭包保持与外部变量的连接,有时这并不是你想要的:

var result = [];
for (var i=0; i < 5; i++) {
    result.push(function () { return i });  // (1)
}
console.log(result1; // 5 (not 1)
console.log(result3; // 5 (not 3)

第(1)行返回的值始终是i的当前值,而不是函数创建时的值。循环结束后,i的值为 5,这就是为什么数组中的所有函数都返回该值。如果你想让第(1)行的函数接收当前i值的快照,你可以使用 IIFE:

for (var i=0; i < 5; i++) {
    (function () {
        var i2 = i; // copy current i
        result.push(function () { return i2 });
    }());
}

对象和构造函数

本节涵盖了 JavaScript 的两种基本面向对象的机制:单个对象和构造函数(它们是对象的工厂,类似于其他语言中的类)。

单个对象

像所有的值一样,对象都有属性。实际上,你可以把对象看作是一组属性,其中每个属性都是一个(键,值)对。键是一个字符串,值是任何 JavaScript 值。

在 JavaScript 中,你可以直接通过对象字面量创建普通对象:

'use strict';
var jane = {
    name: 'Jane',

    describe: function () {
        return 'Person named '+this.name;
    }
};

前面的对象有属性namedescribe。你可以读取(“获取”)和写入(“设置”)属性:

> jane.name  // get
'Jane'
> jane.name = 'John';  // set
> jane.newProperty = 'abc';  // property created automatically

describe这样的函数值属性被称为方法。它们使用this来引用调用它们的对象:

> jane.describe()  // call method
'Person named John'
> jane.name = 'Jane';
> jane.describe()
'Person named Jane'

in运算符检查一个属性是否存在:

> 'newProperty' in jane
true
> 'foo' in jane
false

如果读取一个不存在的属性,你会得到值undefined。因此,前面的两个检查也可以这样执行:

> jane.newProperty !== undefined
true
> jane.foo !== undefined
false

delete运算符移除一个属性:

> delete jane.newProperty
true
> 'newProperty' in jane
false

任意属性键

属性键可以是任何字符串。到目前为止,我们已经在对象文字和点运算符之后看到了属性键。但是,只有在它们是标识符时,才能以这种方式使用它们(参见Identifiers and Variable Names)。如果要使用其他字符串作为键,必须在对象文字中对其进行引用,并使用方括号来获取和设置属性:

> var obj = { 'not an identifier': 123 };
> obj['not an identifier']
123
> obj['not an identifier'] = 456;

方括号还允许您计算属性的键:

> var obj = { hello: 'world' };
> var x = 'hello';

> obj[x]
'world'
> obj['hel'+'lo']
'world'

提取方法

如果提取一个方法,它将失去与对象的连接。单独使用时,该函数不再是一个方法,this 的值为 undefined(在严格模式下)。

例如,让我们回到之前的对象 jane

'use strict';
var jane = {
    name: 'Jane',

    describe: function () {
        return 'Person named '+this.name;
    }
};

我们想从 jane 中提取方法 describe,将其放入变量 func 中,并调用它。但是,这样做不起作用:

> var func = jane.describe;
> func()
TypeError: Cannot read property 'name' of undefined

解决方案是使用所有函数都具有的 bind() 方法。它创建一个新函数,其 this 始终具有给定值:

> var func2 = jane.describe.bind(jane);
> func2()
'Person named Jane'

方法内的函数

每个函数都有自己的特殊变量 this。如果在方法内部嵌套函数,这是不方便的,因为您无法从函数中访问方法的 this。以下是一个示例,我们调用 forEach 以使用函数遍历数组:

var jane = {
    name: 'Jane',
    friends: [ 'Tarzan', 'Cheeta' ],
    logHiToFriends: function () {
        'use strict';
        this.friends.forEach(function (friend) {
            // `this` is undefined here
            console.log(this.name+' says hi to '+friend);
        });
    }
}

调用 logHiToFriends 会产生一个错误:

> jane.logHiToFriends()
TypeError: Cannot read property 'name' of undefined

让我们看看修复这个问题的两种方法。首先,我们可以将 this 存储在不同的变量中:

logHiToFriends: function () {
    'use strict';
    var that = this;
    this.friends.forEach(function (friend) {
        console.log(that.name+' says hi to '+friend);
    });
}

或者,forEach 有一个第二个参数,允许您为 this 提供一个值:

logHiToFriends: function () {
    'use strict';
    this.friends.forEach(function (friend) {
        console.log(this.name+' says hi to '+friend);
    }, this);
}

在 JavaScript 中,函数表达式经常用作函数调用中的参数。当您从这些函数表达式之一引用 this 时,一定要小心。

构造函数:对象的工厂

到目前为止,您可能认为 JavaScript 对象 是从字符串到值的映射,这是 JavaScript 对象文字所暗示的概念,它看起来像其他语言的映射/字典文字。但是,JavaScript 对象还支持一项真正面向对象的功能:继承。本节并未完全解释 JavaScript 继承的工作原理,但它向您展示了一个简单的模式,以便您开始。如果您想了解更多,请参阅第十七章。

除了作为“真正的”函数和方法外,函数在 JavaScript 中还扮演另一个角色:如果通过 new 运算符调用,它们将成为 构造函数——对象的工厂。因此,构造函数在其他语言中是类的粗略类比。按照惯例,构造函数的名称以大写字母开头。例如:

// Set up instance data
function Point(x, y) {
    this.x = x;
    this.y = y;
}
// Methods
Point.prototype.dist = function () {
    return Math.sqrt(this.x*this.x + this.y*this.y);
};

我们可以看到构造函数有两个部分。首先,函数 Point 设置实例数据。其次,属性 Point.prototype 包含一个具有方法的对象。前者数据对每个实例都是特定的,而后者数据在所有实例之间共享。

要使用 Point,我们通过 new 运算符调用它:

> var p = new Point(3, 5);
> p.x
3
> p.dist()
5.830951894845301

pPoint 的一个实例:

> p instanceof Point
true

数组

数组是可以通过从零开始的整数索引访问的元素序列。

数组文字

数组文字对于创建数组很方便:

> var arr = [ 'a', 'b', 'c' ];

前面的数组有三个元素:字符串 'a''b''c'。您可以通过整数索引访问它们:

> arr[0]
'a'
> arr[0] = 'x';
> arr
[ 'x', 'b', 'c' ]

length 属性指示数组有多少个元素。您可以使用它来追加元素和删除元素:

> var arr = ['a', 'b'];
> arr.length
2

> arr[arr.length] = 'c';
> arr
[ 'a', 'b', 'c' ]
> arr.length
3

> arr.length = 1;
> arr
[ 'a' ]

in 运算符也适用于数组:

> var arr = [ 'a', 'b', 'c' ];
> 1 in arr // is there an element at index 1?
true
> 5 in arr // is there an element at index 5?
false

请注意,数组是对象,因此可以具有对象属性:

> var arr = [];
> arr.foo = 123;
> arr.foo
123

数组方法

数组有许多方法(参见Array Prototype Methods)。以下是一些示例:

> var arr = [ 'a', 'b', 'c' ];

> arr.slice(1, 2)  // copy elements
[ 'b' ]
> arr.slice(1)
[ 'b', 'c' ]

> arr.push('x')  // append an element
4
> arr
[ 'a', 'b', 'c', 'x' ]

> arr.pop()  // remove last element
'x'
> arr
[ 'a', 'b', 'c' ]

> arr.shift()  // remove first element
'a'
> arr
[ 'b', 'c' ]

> arr.unshift('x')  // prepend an element
3
> arr
[ 'x', 'b', 'c' ]

> arr.indexOf('b')  // find the index of an element
1
> arr.indexOf('y')
-1

> arr.join('-')  // all elements in a single string
'x-b-c'
> arr.join('')
'xbc'
> arr.join()
'x,b,c'

遍历数组

有几种用于遍历元素的数组方法(参见Iteration (Nondestructive))。最重要的两个是 forEachmap

forEach 遍历数组并将当前元素及其索引传递给函数:

[ 'a', 'b', 'c' ].forEach(
    function (elem, index) {  // (1)
        console.log(index + '. ' + elem);
    });

前面的代码产生以下输出:

0\. a
1\. b
2\. c

请注意,第(1)行中的函数可以忽略参数。例如,它可能只有参数elem

map通过将函数应用于现有数组的每个元素来创建一个新数组:

> [1,2,3].map(function (x) { return x*x })
[ 1, 4, 9 ]

正则表达式

JavaScript 内置支持正则表达式(第十九章是教程,更详细地解释了它们的工作原理)。它们由斜杠分隔:

/^abc$/
/[A-Za-z0-9]+/

方法 test(): 是否有匹配项?

> /^a+b+$/.test('aaab')
true
> /^a+b+$/.test('aaa')
false

方法 exec(): 匹配和捕获组

> /a(b+)a/.exec('_abbba_aba_')
[ 'abbba', 'bbb' ]

返回的数组包含索引 0 处的完整匹配项,索引 1 处的第一个组的捕获,依此类推。还有一种方法(在RegExp.prototype.exec: Capture Groups中讨论)可以重复调用此方法以获取所有匹配项。

方法 replace(): 搜索和替换

> '<a> <bbb>'.replace(/<(.*?)>/g, '[$1]')
'[a] [bbb]'

replace的第一个参数必须是带有/g标志的正则表达式;否则,只会替换第一个匹配项。还有一种方法(如在String.prototype.replace: Search and Replace中讨论的)可以使用函数来计算替换。

数学

Math(参见第二十一章)是一个具有算术函数的对象。以下是一些示例:

> Math.abs(-2)
2

> Math.pow(3, 2)  // 3 to the power of 2
9

> Math.max(2, -1, 5)
5

> Math.round(1.9)
2

> Math.PI  // pre-defined constant for π
3.141592653589793

> Math.cos(Math.PI)  // compute the cosine for 180°
-1

标准库的其他功能

JavaScript 的标准库相对简陋,但还有更多可以使用的东西:

Date(第二十章)

一个日期的构造函数,其主要功能是解析和创建日期字符串以及访问日期的组件(年、小时等)。

JSON(第二十二章)

一个具有解析和生成 JSON 数据功能的对象。

console.* 方法(参见控制台 API

这些特定于浏览器的方法不是语言本身的一部分,但其中一些也适用于 Node.js。

第二部分:背景

原文:II. Background

译者:飞龙

协议:CC BY-NC-SA 4.0

这部分解释了 JavaScript 的历史和性质。它对语言进行了广泛的初步介绍,并解释了它存在的背景(不过不涉及太多技术细节)。

这部分不是必读的;你可以在没有阅读它的情况下理解本书的其余部分。

第二章 为什么选择 JavaScript?

原文:2. Why JavaScript?

译者:飞龙

协议:CC BY-NC-SA 4.0

有很多编程语言。为什么你要使用 JavaScript?本章将从七个重要方面来看,这些方面在你选择编程语言时很重要,并且认为 JavaScript 总体上做得很好:

  1. 它是免费提供的吗?

  2. 它是一种优雅的编程语言吗?

  3. 在实践中有用吗?

  4. 它有好的工具,特别是好的集成开发环境(IDE)吗?

  5. 它对你想做的事情来说足够快吗?

  6. 它被广泛使用吗?

  7. 它有未来吗?

JavaScript 是免费提供的吗?

JavaScript 可以说是最开放的编程语言:它的规范 ECMA-262 是 ISO 标准。许多独立方实现都紧密遵循这一规范。其中一些实现是开源的。此外,语言的演变由 TC39 委员会负责,该委员会由包括所有主要浏览器供应商在内的几家公司组成。其中许多公司通常是竞争对手,但他们为了语言的利益而共同合作。

JavaScript 优雅吗?

是和不是。我用不同范式的几种编程语言写了大量代码。因此,我很清楚 JavaScript 并不是优雅的巅峰。然而,它是一种非常灵活的语言,有一个相当优雅的核心,并且使你能够使用面向对象编程和函数式编程的混合。

JavaScript 引擎之间的语言兼容性曾经是一个问题,但现在不再是了,部分得益于测试 262 套件,该套件检查引擎是否符合 ECMAScript 规范。相比之下,浏览器和 DOM 的差异仍然是一个挑战。这就是为什么通常最好依赖框架来隐藏这些差异。

JavaScript 有用吗?

世界上最美丽的编程语言是无用的,除非它能让你编写你需要的程序。

图形用户界面

在图形用户界面领域,JavaScript 受益于成为HTML5的一部分。在本节中,我使用 HTML5 这个术语来表示“浏览器平台”(HTML、CSS 和浏览器 JavaScript API)。HTML5 得到了广泛的部署,并不断取得进展。它正在慢慢地成为一个完整的层,用于编写功能齐全的跨平台应用程序;类似于 Java 平台,它几乎像一个嵌入式操作系统。HTML5 的卖点之一是它让你编写跨平台的图形用户界面。这些总是一种妥协:你放弃了一些质量,以换取不受限于单一操作系统。过去,“跨平台”意味着 Windows、Mac OS 或 Linux。但现在我们有了两个额外的交互平台:Web 和移动。使用 HTML5,你可以通过诸如PhoneGapChrome AppsTideSDK等技术来针对所有这些平台。

此外,一些平台将 Web 应用程序作为本地应用程序或允许你本地安装它们——例如 Chrome OS、Firefox OS 和 Android。

其他补充 JavaScript 的技术

除了 HTML5 之外,还有更多的技术可以补充 JavaScript,使语言更有用:

JavaScript 有大量的库,可以让你完成各种任务,从解析 JavaScript(通过Esprima)到处理和显示 PDF 文件(通过PDF.js)。

Node.js

Node.js 平台让你编写服务器端代码和 shell 脚本(构建工具、测试运行器等)。

JSON(JavaScript 对象表示法,在第二十二章中介绍)

JSON 是一种根植于 JavaScript 的数据格式,在 Web 上交换数据变得流行(例如网络服务的结果)。

NoSQL 数据库(例如CouchDBMongoDB

这些数据库紧密集成了 JSON 和 JavaScript。

JavaScript 有好的工具吗?

JavaScript 正在获得更好的构建工具(例如Grunt)和测试工具(例如mocha)。Node.js 使得可以通过 shell 运行这些类型的工具(不仅仅在浏览器中)。在这个领域的一个风险是分裂,因为我们逐渐得到了太多这样的工具。

JavaScript 的 IDE 空间仍处于萌芽阶段,但正在迅速成长。网络开发的复杂性和动态性使得这个空间成为创新的肥沃土壤。两个开源的例子是BracketsLight Table

此外,浏览器正变得越来越强大的开发环境。特别是 Chrome 最近取得了令人印象深刻的进展。有趣的是看到 IDE 和浏览器在未来将整合到多大程度。

JavaScript 足够快吗?

JavaScript 引擎取得了巨大的进步,从缓慢的解释器发展为快速的即时编译器。它们现在已经足够快,适用于大多数应用。此外,已经在开发新的想法,使 JavaScript 足够快以适用于其余的应用:

  • asm.js是 JavaScript 的(非常静态的)子集,在当前引擎上运行速度很快,大约相当于编译后的 C++的 70%。例如,它可以用于实现网络应用的性能关键算法部分。它还被用于将基于 C++的游戏移植到网络平台上。

  • ParallelJS可以并行化使用新数组方法mapParfilterParreducePar(现有数组方法mapfilterreduce的可并行化版本)的 JavaScript 代码。为了使并行化工作,回调必须以特殊的方式编写;主要限制是不能改变在回调中未创建的数据。

JavaScript 被广泛使用吗?

通常广泛使用的语言有两个好处。首先,这样的语言有更好的文档和支持。其次,更多的程序员知道它,这在你需要雇佣某人或者寻找基于该语言的工具的客户时非常重要。

JavaScript 被广泛使用,并获得了前述两个好处:

  • 如今,JavaScript 的文档和支持以各种形式呈现:书籍、播客、博客文章、电子邮件通讯、论坛等等。第三十三章指引您前往重要资源。

  • JavaScript 开发人员需求量大,但他们的人数也在不断增加。

JavaScript 有未来吗?

有几件事表明 JavaScript 有一个光明的未来:

  • 语言正在稳步发展;ECMAScript 6 看起来不错。

  • 有许多与 JavaScript 相关的创新(例如前述的 asm.js 和 ParallelJS,微软的 TypeScript 等)。

  • JavaScript 作为一个不可或缺的部分所在的网络平台正在迅速成熟。

  • JavaScript 得到了众多公司的支持,没有单个人或公司控制它。

结论

考虑到使一种语言具有吸引力的前述因素,JavaScript 的表现非常出色。它当然并不完美,但目前很难超越它,而且情况只会变得更好。

第三章:JavaScript 的本质

原文:3. The Nature of JavaScript

译者:飞龙

协议:CC BY-NC-SA 4.0

JavaScript 的本质可以总结如下:

它是动态的

许多东西都可以改变。例如,你可以自由地添加和删除对象的属性(字段)。而且你可以直接创建对象,而不需要先创建对象工厂(例如类)。

它是动态类型的

变量和对象属性始终可以保存任何类型的值。

它是功能性的和面向对象的

JavaScript 支持两种编程语言范式:函数式编程(一流函数、闭包、通过bind()进行部分应用、数组的内置map()reduce()等)和面向对象编程(可变状态、对象、继承等)。

它默默失败

直到 ECMAScript 3,JavaScript 才没有异常处理。这就解释了为什么语言经常默默失败并自动转换参数和操作数的值:它最初无法抛出异常。

它部署为源代码

JavaScript 始终以源代码部署,并由 JavaScript 引擎编译。源代码具有灵活的交付格式和抽象引擎之间的差异的好处。为了保持文件大小小,使用了两种技术:压缩(主要是 gzip)和最小化(通过重命名变量、删除注释等使源代码更小;有关详细信息,请参见第三十二章)。

它是 Web 平台的一部分

JavaScript 是 Web 平台(HTML5 API、DOM 等)的一个重要组成部分,以至于很容易忘记前者也可以在没有后者的情况下使用。然而,JavaScript 在非浏览器环境中的使用越多(如 Node.js),它就越明显。

怪癖和非正统特性

一方面,JavaScript 有一些怪癖和缺失的功能(例如,它没有块作用域变量,没有内置模块,也不支持子类化)。因此,在其他语言中学习语言特性的地方,你需要在 JavaScript 中学习模式和解决方法。另一方面,JavaScript 包括非正统的特性(如原型继承和对象属性)。这些也需要学习,但更像是一种特性而不是错误。

请注意,JavaScript 引擎已经变得非常智能,并在幕后修复了一些怪癖。例如:

  • 就规范而言,JavaScript 没有整数,只有浮点数。在内部,大多数引擎尽可能使用整数。

  • 可以说,JavaScript 中的数组太灵活了:它们不是元素的索引序列,而是从数字到元素的映射。这样的映射可以有空洞:数组“内部”没有关联值的索引。再次,引擎通过使用优化表示来帮助数组不具有空洞。

优雅的部分

但 JavaScript 也有许多优雅的部分。Brendan Eich 最喜欢的是:1

  • 一流函数

  • 闭包

  • 原型

  • 对象字面量

  • 数组字面量

最后两个项目,对象字面量和数组字面量,让你可以从对象开始,并在后来引入抽象(比如构造函数,JavaScript 中类的类比)。它们还支持 JSON(见第二十二章)。

请注意,优雅的部分可以帮助你解决怪癖。例如,它们允许你在语言内部实现块作用域、模块和继承 API。

影响

JavaScript 受到了几种编程语言的影响(如[图 3-1](ch03.html#fig3-1 “图 3-1: 影响 JavaScript 的编程语言。”)所示):

  • Java 是 JavaScript 语法的榜样。它还导致 JavaScript 将值分为原始值和对象,并引入了Date构造函数(这是java.util.Date的一个移植)。

  • AWK 启发了 JavaScript 的函数。实际上,关键字function来自 AWK。

  • Scheme 是 JavaScript 拥有一流函数(它们被视为值并且可以作为参数传递给函数)和闭包(见第十六章)的原因。

  • Self 对 JavaScript 不寻常的对象导向风格负有责任;它支持对象之间的原型继承。

  • Perl 和 Python 影响了 JavaScript 对字符串、数组和正则表达式的处理。

  • 除了实际的语言之外,HyperTalk 影响了 JavaScript 如何集成到 Web 浏览器中。这导致 HTML 标签具有事件处理属性,如onclick

影响 JavaScript 的编程语言。图 3-1。影响 JavaScript 的编程语言。


1 Brendan Eich,“JavaScript 简史”,2010 年 7 月 21 日,bit.ly/1lKkI0M

第四章:JavaScript 的创建方式

原文:4. How JavaScript Was Created

译者:飞龙

协议:CC BY-NC-SA 4.0

了解 JavaScript 的创建原因和方式有助于我们理解它的特点。

1993 年,NCSA 的 Mosaic 是第一个广受欢迎的 Web 浏览器。1994 年,成立了一家名为网景的公司,以利用新兴的万维网的潜力。网景创建了专有的 Web 浏览器 Netscape Navigator,在 1990 年代占主导地位。许多最初的 Mosaic 作者继续在 Navigator 上工作,但两者故意没有共享代码。

网景很快意识到 Web 需要变得更加动态。即使你只是想检查用户在表单中输入的正确值,也需要将数据发送到服务器以便提供反馈。1995 年,网景聘请了 Brendan Eich,并承诺让他在浏览器中实现 Scheme(一种 Lisp 方言)。2在他开始之前,网景与硬件和软件公司 Sun(后来被 Oracle 收购)合作,将其更静态的编程语言 Java 包含在 Navigator 中。因此,网景内部激烈争论的一个问题是为什么 Web 需要两种编程语言:Java 和一种脚本语言。脚本语言的支持者提出了以下解释:3

我们旨在为 Web 设计师和兼职程序员提供“粘合语言”,他们正在从图像、插件和 Java 小程序等组件构建 Web 内容。我们认为 Java 是由高价程序员使用的“组件语言”,而粘合程序员——Web 页面设计师——将组件组装起来,并使用[一种脚本语言]自动化它们的交互。

当时,网景管理层决定脚本语言必须具有类似于 Java 的语法。这排除了采用现有的语言,如 Perl、Python、TCL 或 Scheme。为了捍卫 JavaScript 的想法,网景需要一个原型。艾奇在 1995 年 5 月的 10 天内写了一个原型。JavaScript 的第一个代号是 Mocha,由马克·安德森创造。后来,网景营销部门出于商标原因和因为几个产品的名称已经带有前缀“Live”,将其更改为 LiveScript。1995 年 11 月底,Navigator 2.0B3 发布,包括原型,继续在早期没有进行重大更改的情况下存在。1995 年 12 月初,Java 的势头增长,Sun 授权商标 Java 给网景。语言再次更名为最终名称 JavaScript。?


2 Brendan Eich,“流行程度”,2008 年 4 月 3 日,bit.ly/1lKl6fG

3 Naomi Hamilton,“编程语言 A-Z:JavaScript”,Computerworld,2008 年 7 月 30 日,bit.ly/1lKldIe

? Paul Krill,“JavaScript 创造者思考过去和未来”,InfoWorld,2008 年 6 月 23 日,bit.ly/1lKlpXO;Brendan Eich,“JavaScript 简史”,2010 年 7 月 21 日,bit.ly/1lKkI0M

第五章 标准化:ECMAScript

原文:5. Standardization: ECMAScript

译者:飞龙

协议:CC BY-NC-SA 4.0

JavaScript 推出后,微软在 Internet Explorer 3.0(1996 年 8 月)中实现了相同的语言,但名称不同,称为 JScript。为了限制微软,网景决定标准化 JavaScript,并要求标准组织 Ecma International 托管标准。ECMA-262 的规范工作始于 1996 年 11 月。由于 Sun(现在是 Oracle)对 Java 一词拥有商标,因此标准化的官方名称不能是 JavaScript。因此,选择了 ECMAScript,源自 JavaScriptEcma。但是,该名称仅用于指代语言的版本(其中一个指的是规范)。每个人仍然称该语言为 JavaScript

ECMA-262 由 Ecma 的 技术委员会 39(TC39)管理和发展。其成员是微软、Mozilla 和 Google 等公司,它们指定员工参与委员会工作;例如 Brendan Eich、Allen Wirfs-Brock(ECMA-262 的编辑)和 David Herman。为了推进 ECMAScript 的设计,TC39 在开放渠道(如邮件列表 es-discuss)上举行讨论,并定期举行会议。会议由 TC39 成员和受邀专家参加。2013 年初,与会人数从 15 到 25 人不等。

以下是 ECMAScript 版本(或 ECMA-262 的 版本)及其主要特性的列表:

ECMAScript 1(1997 年 6 月)

第一版

ECMAScript 2(1998 年 8 月)

编辑更改以使 ECMA-262 与标准 ISO/IEC 16262 保持一致

ECMAScript 3(1999 年 12 月)

do-while,正则表达式,新的字符串方法(concatmatchreplaceslice,使用正则表达式的split等),异常处理等

ECMAScript 4(2008 年 7 月放弃)

ECMAScript 4 被开发为 JavaScript 的下一个版本,原型是用 ML 编写的。然而,TC39 无法就其功能集达成一致。为防止僵局,委员会于 2008 年 7 月底举行会议,并达成了一致,总结在 四点 中:

  1. 开发了 ECMAScript 3 的增量更新(成为 ECMAScript 5)。

  2. 开发了一个比 ECMAScript 4 更少,但比 ECMAScript 3 的增量更新更多的主要新版本。新版本的代号是 Harmony,因为它的构思是在一个和谐的会议中产生的。Harmony 将分为 ECMAScript 6 和 ECMAScript 7。

  3. ECMAScript 4 中将被删除的特性包括包,命名空间和早期绑定。

  4. 其他想法将与 TC39 共识开发。

因此,ECMAScript 4 的开发人员同意使 Harmony 比 ECMAScript 4 更少激进,而 TC39 的其他成员同意继续推动事情向前发展。

ECMAScript 5(2009 年 12 月)

添加了严格模式,获取器和设置器,新的数组方法,对 JSON 的支持等(参见 第二十五章)

ECMAScript 5.1(2011 年 6 月)

编辑更改以使 ECMA-262 与国际标准 ISO/IEC 16262:2011 的第三版保持一致

ECMAScript 6

目前正在开发中,预计将在 2014 年底得到批准。大多数引擎可能会在批准时支持最重要的 ECMAScript 6 特性。完整支持可能需要更长时间。

达成共识并创建标准并不总是容易的,但由于前述各方的协作努力,JavaScript 是一种真正开放的语言,由多个供应商实现,具有非常高的兼容性。这种兼容性是通过非常详细但具体的规范实现的。例如,规范经常使用伪代码,并且它由一个测试套件test262补充,该测试套件检查 ECMAScript 实现的兼容性。有趣的是,ECMAScript 并不由万维网联盟(W3C)管理。TC39 和 W3C 在 JavaScript 和 HTML5 之间存在重叠时进行合作。

文章来源:https://blog.csdn.net/wizardforcel/article/details/135524712
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。