diff --git a/docs/class.md b/docs/class.md index 9d869e1..70f0800 100644 --- a/docs/class.md +++ b/docs/class.md @@ -13,7 +13,7 @@ function Point(x,y){ } Point.prototype.toString = function () { - return '('+this.x+', '+this.y+')'; + return '(' + this.x + ', ' + this.y + ')'; } ``` @@ -41,6 +41,18 @@ class Point { Point类除了构造方法,还定义了一个toString方法。注意,定义“类”的方法的时候,前面不需要加上function这个保留字,直接把函数定义放进去了就可以了。 +ES6的类,完全可以看作构造函数的另一种写法。 + +```javascript +Class Point{ + // ... +} + +typeof Point // "function" +``` + +上面代码表明,类的数据类型就是函数。 + 构造函数的prototype属性,在ES6的“类”上面继续存在。事实上,除了constructor方法以外,类的方法都定义在类的prototype属性上面。 ```javascript @@ -161,7 +173,7 @@ p1.__proto__ === p2.__proto__ 上面代码中,p1和p2都是Point的实例,它们的原型都是Point,所以\_\_proto\_\_属性是相等的。 -这也意味着,可以通过\_\_proto\_\_属性为Class添加方法。 +这也意味着,可以通过实例的\_\_proto\_\_属性为Class添加方法。 ```javascript var p1 = new Point(2,3); @@ -183,11 +195,8 @@ p3.printName() // "Oops" 由于本质上,ES6的Class只是ES5的构造函数的一层包装,所以函数的许多特性都被Class继承,包括name属性。 ```javascript - class Point {} - Point.name // "Point" - ``` name属性总是返回紧跟在class关键字后面的类名。 @@ -197,23 +206,19 @@ name属性总是返回紧跟在class关键字后面的类名。 与函数一样,Class也可以使用表达式的形式定义。 ```javascript - const MyClass = class Me { getClassName() { return Me.name; } }; - ``` 上面代码使用表达式定义了一个类。需要注意的是,这个类的名字是MyClass而不是Me,Me只在Class的内部代码可用,指代当前类。 ```javascript - let inst = new MyClass(); inst.getClassName() // Me Me.name // ReferenceError: Me is not defined - ``` 上面代码表示,Me只在Class内部有定义。 @@ -221,9 +226,7 @@ Me.name // ReferenceError: Me is not defined 如果Class内部没用到的话,可以省略Me,也就是可以写成下面的形式。 ```javascript - const MyClass = class { /* ... */ }; - ``` **(6)不存在变量提升** @@ -231,23 +234,18 @@ const MyClass = class { /* ... */ }; Class不存在变量提升(hoist),这一点与ES5完全不同。 ```javascript - new Foo(); // ReferenceError - class Foo {} - ``` 上面代码中,Foo类使用在前,定义在后,这样会报错,因为ES6不会把变量声明提升到代码头部。这种规定的原因与下文要提到的继承有关,必须保证子类在父类之后定义。 ```javascript - { let Foo = class {}; class Bar extends Foo { } } - ``` 如果存在Class的提升,上面代码将报错,因为let命令也是不提升的。 @@ -263,36 +261,31 @@ class Foo {} Class之间可以通过extends关键字,实现继承,这比ES5的通过修改原型链实现继承,要清晰和方便很多。 ```javascript - class ColorPoint extends Point {} - ``` 上面代码定义了一个ColorPoint类,该类通过extends关键字,继承了Point类的所有属性和方法。但是由于没有部署任何代码,所以这两个类完全一样,等于复制了一个Point类。下面,我们在ColorPoint内部加上代码。 ```javascript - class ColorPoint extends Point { constructor(x, y, color) { - super(x, y); // 等同于parent.constructor(x, y) + super(x, y); // 调用父类的constructor(x, y) this.color = color; } toString() { - return this.color + ' ' + super.toString(); // 等同于parent.toString() + return this.color + ' ' + super.toString(); // 调用父类的toString() } } - ``` 上面代码中,constructor方法和toString方法之中,都出现了super关键字,它指代父类的实例(即父类的this对象)。 -子类必须在constructor方法中调用super方法,否则新建实例时会报错。 +子类必须在constructor方法中调用super方法,否则新建实例时会报错。这是因为子类没有自己的this对象,而是继承父类的this对象,然后对其进行加工。如果不调用super方法,子类就得不到this对象。 ```javascript - class Point { /* ... */ } class ColorPoint extends Point { @@ -301,23 +294,21 @@ class ColorPoint extends Point { } let cp = new ColorPoint(); // ReferenceError - ``` +上面代码中,ColorPoint继承了父类Point,但是它的构造函数没有调用super方法,导致新建实例时报错。 + 如果子类没有定义constructor方法,这个方法会被默认添加,代码如下。也就是说,不管有没有显式定义,任何一个子类都有constructor方法。 ```javascript - constructor(...args) { super(...args); } - ``` -另一个需要注意的地方是,在子类的构造函数中,只有调用super之后,才可以使用this关键字,否则会报错。这是因为没有调用父类的构造函数,就无法子类实例的构建。 +另一个需要注意的地方是,在子类的构造函数中,只有调用super之后,才可以使用this关键字,否则会报错。这是因为子类实例的构建,是基于对父类实例加工,只有super方法才能返回父类实例。 ```javascript - class Point { constructor(x, y) { this.x = x; @@ -332,7 +323,6 @@ class ColorPoint extends Point { this.color = color; // 正确 } } - ``` 上面代码中,子类的constructor方法没有调用super之前,就使用this关键字,结果报错,而放在super方法之后就是正确的。 @@ -340,12 +330,10 @@ class ColorPoint extends Point { 下面是生成子类实例的代码。 ```javascript - let cp = new ColorPoint(25, 8, 'green'); cp instanceof ColorPoint // true cp instanceof Point // true - ``` 上面代码中,实例对象cp同时是ColorPoint和Point两个类的实例,这与ES5的行为完全一致。 @@ -359,85 +347,61 @@ cp instanceof Point // true (2)子类prototype属性的`__proto__`属性,表示方法的继承,总是指向父类的prototype属性。 ```javascript +class A { +} class B extends A { } B.__proto__ === A // true B.prototype.__proto__ === A.prototype // true - ``` 上面代码中,子类A的`__proto__`属性指向父类B,子类A的prototype属性的__proto__属性指向父类B的prototype属性。 -第一条继承链,实质如下。 +这两条继承链,可以这样理解:作为一个对象,子类(B)的原型(`__proto__属性`)是父类(A);作为一个构造函数,子类(B)的原型(prototype属性)是父类的实例。 ```javascript - -class B extends A { - constructor() { - return A.call(this); - } -} - -// 等同于 - -class B extends A { - constructor() { - return B.__proto__.call(this); - } -} - -``` - -第二条继承链,实质如下。 - -```javascript - B.prototype = new A(); // 等同于 B.prototype.__proto__ = A.prototype; - ``` -此外,还有三种特殊情况。 +此外,考虑三种特殊情况。第一种特殊情况,子类继承Object类。 ```javascript - class A extends Object { } A.__proto__ === Object // true A.prototype.__proto__ === Object.prototype // true - ``` -第一种特殊情况,子类A继承Object。这种情况下,A其实就是构造函数Object的复制,A的实例就是Object的实例。 +这种情况下,A其实就是构造函数Object的复制,A的实例就是Object的实例。 + +第二种特性情况,不存在任何继承。 ```javascript - class A { } A.__proto__ === Function.prototype // true A.prototype.__proto__ === Object.prototype // true - ``` -第二种特殊情况,A作为一个基类(即不存在任何继承),就是一个普通函数,所以直接继承`Funciton.prototype`。但是,A调用后返回一个空对象(即Object实例),所以`A.prototype.__proto__`指向构造函数(Object)的prototype属性。 +这种情况下,A作为一个基类(即不存在任何继承),就是一个普通函数,所以直接继承`Funciton.prototype`。但是,A调用后返回一个空对象(即Object实例),所以`A.prototype.__proto__`指向构造函数(Object)的prototype属性。 +第三种特殊情况,子类继承null。 ```javascript - class A extends null { } A.__proto__ === Function.prototype // true A.prototype.__proto__ === null // true - ``` -第三种特殊情况,与第二种情况非常像。A也是一个普通函数,所以直接继承`Funciton.prototype`。但是,A调用后返回的对象不继承任何方法,所以它的`__proto__`指向`Function.prototype`,即实质上执行了下面的代码。 +这种情况与第二种情况非常像。A也是一个普通函数,所以直接继承`Funciton.prototype`。但是,A调用后返回的对象不继承任何方法,所以它的`__proto__`指向`Function.prototype`,即实质上执行了下面的代码。 ```javascript class C extends null { @@ -450,46 +414,39 @@ class C extends null { Object.getPrototypeOf方法可以用来从子类上获取父类。 ```javascript - Object.getPrototypeOf(ColorPoint) === Point // true - ``` ### 实例的\_\_proto\_\_属性 -父类和子类的\_\_proto\_\_属性,指向是不一样的。 +父类实例和子类实例的\_\_proto\_\_属性,指向是不一样的。 ```javascript - var p1 = new Point(2, 3); var p2 = new ColorPoint(2, 3, 'red'); p2.__proto__ === p1.__proto // false p2.__proto__.__proto__ === p1.__proto__ // true - ``` -通过子类的\_\_proto\_\_属性,可以修改父类。 +通过子类实例的\_\_proto\_\_属性,可以修改父类实例的行为。 ```javascript - p2.__proto__.__proto__.printName = function () { console.log('Ha'); }; p1.printName() // "Ha" - ``` 上面代码在ColorPoint的实例p2上向Point类添加方法,结果影响到了Point的实例p1。 -### 构造函数的继承 +### 原生构造函数的继承 下面是一个继承原生的Array构造函数的例子。 ```javascript - class MyArray extends Array { constructor(...args) { super(...args); @@ -498,20 +455,17 @@ class MyArray extends Array { var arr = new MyArray(); arr[1] = 12; - ``` 上面代码定义了一个MyArray类,继承了Array构造函数,因此就可以从MyArray生成数组的实例。这意味着,ES6可以自定义原生数据结构(比如Array、String等)的子类,这是ES5无法做到的。 -上面这个例子也说明,extends关键字不仅可以用来继承类,还可以用来继承构造函数。下面是一个自定义Error子类的例子。 +上面这个例子也说明,extends关键字不仅可以用来继承类,还可以用来继承原生的构造函数。下面是一个自定义Error子类的例子。 ```javascript - class MyError extends Error { } throw new MyError('Something happened!'); - ``` ## class的取值函数(getter)和存值函数(setter) @@ -519,7 +473,6 @@ throw new MyError('Something happened!'); 与ES5一样,在Class内部可以使用get和set关键字,对某个属性设置存值函数和取值函数。 ```javascript - class MyClass { get prop() { return 'getter'; @@ -536,7 +489,6 @@ inst.prop = 123; inst.prop // 'getter' - ``` 上面代码中,prop属性有对应的存值函数和取值函数,因此赋值和读取行为都被自定义了。 @@ -546,7 +498,6 @@ inst.prop 如果某个方法之前加上星号(*),就表示该方法是一个Generator函数。 ```javascript - class Foo { constructor(...args) { this.args = args; @@ -563,7 +514,6 @@ for (let x of new Foo('hello', 'world')) { } // hello // world - ``` 上面代码中,Foo类的Symbol.iterator方法前有一个星号,表示该方法是一个Generator函数。Symbol.iterator方法返回一个Foo类的默认遍历器,for...of循环会自动调用这个遍历器。 @@ -573,7 +523,6 @@ for (let x of new Foo('hello', 'world')) { 类相当于实例的原型,所有在类中定义的方法,都会被实例继承。如果在一个方法前,加上static关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就称为“静态方法”。 ```javascript - class Foo { static classMethod() { return 'hello'; @@ -585,7 +534,6 @@ Foo.classMethod() // 'hello' var foo = new Foo(); foo.classMethod() // TypeError: undefined is not a function - ``` 上面代码中,Foo类的classMethod方法前有static关键字,表明该方法是一个静态方法,可以直接在Foo类上调用(`Foo.classMethod()`),而不是在Foo类的实例上调用。如果在实例上调用静态方法,会抛出一个错误,表示不存在该方法。 @@ -593,7 +541,6 @@ foo.classMethod() 父类的静态方法,可以被子类继承。 ```javascript - class Foo { static classMethod() { return 'hello'; @@ -604,7 +551,6 @@ class Bar extends Foo { } Bar.classMethod(); // 'hello' - ``` 上面代码中,父类Foo有一个静态方法,子类Bar可以调用这个方法。 @@ -612,7 +558,6 @@ Bar.classMethod(); // 'hello' 静态方法也是可以从super对象上调用的。 ```javascript - class Foo { static classMethod() { return 'hello'; @@ -626,7 +571,145 @@ class Bar extends Foo { } Bar.classMethod(); +``` +## 修饰器 + +修饰器(Decorator)用于修改类的行为。这是ES7的一个[提案](https://github.com/wycats/javascript-decorators),目前Babel转码器已经支持。 + +```javascript +function testable(target) { + target.isTestable = true; +} + +@testable +class MyTestableClass () {} + +console.log(MyTestableClass.isTestable) // true +``` + +上面代码中,`@testable`就是一个修饰器。它修改了MyTestableClass这个类的行为,为它加上了静态属性isTestable。 + +修饰器函数的参数,就是所要修饰的目标对象。比如上面代码中,testable函数的参数target,就是所要修饰的对象。如果希望修饰器的行为,能够根据目标对象的不同而不同,就要在外面再封装一层函数。 + +```javascript +function testable(isTestable) { + return function(target) { + target.isTestable = isTestable; + } +} + +@testable(true) class MyTestableClass () {} +console.log(MyTestableClass.isTestable) // true + +@testable(false) class MyClass () {} +console.log(MyClass.isTestable) // false +``` + +上面代码中,修饰器testable可以接受参数,这就等于可以修改修饰器的行为。 + +如果想要为类的实例添加方法,可以在修饰器函数中,为目标类的prototype属性添加方法。 + +```javascript +function testable(target) { + target.prototype.isTestable = true; +} + +@testable +class MyTestableClass () {} + +let obj = new MyClass(); + +console.log(obj.isTestable) // true +``` + +上面代码中,修饰器函数testable是在目标类的prototype属性添加属性,因此就可以在类的实例上调用添加的属性。 + +下面是另外一个例子。 + +```javascript +// mixins.js +export function mixins(...list) { + return function (target) { + Object.assign(target.prototype, ...list) + } +} + +// main.js +import { mixins } from './mixins' + +const Foo = { + foo() { console.log('foo') } +} + +@mixins(Foo) +class MyClass {} + +let obj = new MyClass() + +obj.foo() // 'foo' +``` + +上面代码通过修饰器mixins,可以为类添加指定的方法。 + +修饰器可以用`Object.assign()`模拟。 + +```javascript +const Foo = { + foo() { console.log('foo') } +} + +class MyClass {} + +Object.assign(MyClass.prototype, Foo); + +let obj = new MyClass(); +obj.foo() // 'foo' +``` + +修饰器不仅可以修饰类,还可以修饰类的属性。 + +```javascript +class Person { + @readonly + name() { return `${this.first} ${this.last}` } +} +``` + +上面代码中,修饰器readonly用来修饰”类“的name方法。 + +此时,修饰器函数一共可以接受三个参数,第一个参数是所要修饰的目标对象,第二个参数是所要修饰的属性名,第三个参数是该属性的描述对象。 + +```javascript +readonly(Person.prototype, 'name', descriptor); + +function readonly(target, name, descriptor){ + // descriptor对象原来的值如下 + // { + // value: specifiedFunction, + // enumerable: false, + // configurable: true, + // writable: true + // }; + descriptor.writable = false; + return descriptor; +} + +Object.defineProperty(Person.prototype, 'name', descriptor); +``` + +上面代码说明,修饰器(readonly)会修改属性的描述对象(descriptor),然后被修改的描述对象再用来定义属性。下面是另一个例子。 + +```javascript +class Person { + @nonenumerable + get kidCount() { return this.children.length; } +} + +function nonenumerable(target, name, descriptor) { + descriptor.enumerable = false; + return descriptor; +} ``` ## Module diff --git a/docs/function.md b/docs/function.md index e9f2434..ed88a00 100644 --- a/docs/function.md +++ b/docs/function.md @@ -674,7 +674,7 @@ function f(x){ 上面代码中,函数f的最后一步是调用函数g,这就叫尾调用。 -以下两种情况,都不属于尾调用。 +以下三种情况,都不属于尾调用。 ```javascript // 情况一 @@ -687,9 +687,21 @@ function f(x){ function f(x){ return g(x) + 1; } + +// 情况三 +function f(x){ + g(x); +} ``` -上面代码中,情况一是调用函数g之后,还有别的操作,所以不属于尾调用,即使语义完全一样。情况二也属于调用后还有操作,即使写在一行内。 +上面代码中,情况一是调用函数g之后,还有别的操作,所以不属于尾调用,即使语义完全一样。情况二也属于调用后还有操作,即使写在一行内。情况三等同于下面的代码。 + +```javascript +function f(x){ + g(x); + return undefined; +} +``` 尾调用不一定出现在函数尾部,只要是最后一步操作即可。 diff --git a/docs/intro.md b/docs/intro.md index 9e42ad7..6200135 100644 --- a/docs/intro.md +++ b/docs/intro.md @@ -38,7 +38,9 @@ ECMA的第39号技术专家委员会(Technical Committee 39,简称TC39)负 各大浏览器的最新版本,对ES6的支持可以查看[kangax.github.io/es5-compat-table/es6/](http://kangax.github.io/es5-compat-table/es6/)。随着时间的推移,支持度已经越来越高了,ES6的大部分特性都实现了。 -Node.js和io.js(一个部署新功能更快的Node分支)对ES6的支持度,比浏览器更高。通过它们,可以体验更多ES6的特性。建议使用版本管理工具[nvm](https://github.com/creationix/nvm),来安装Node.js和io.js。不过,nvm不支持Windows系统,下面的操作可以改用[nvmw](https://github.com/hakobera/nvmw)或[nvm-windows](https://github.com/coreybutler/nvm-windows)代替。 +Node.js和io.js(一个部署新功能更快的Node分支)是JavaScript语言的服务器运行环境。它们对ES6的支持度,比浏览器更高。通过它们,可以体验更多ES6的特性。 + +建议使用版本管理工具[nvm](https://github.com/creationix/nvm),来安装Node.js和io.js。不过,nvm不支持Windows系统,下面的操作可以改用[nvmw](https://github.com/hakobera/nvmw)或[nvm-windows](https://github.com/coreybutler/nvm-windows)代替。 安装nvm需要打开命令行窗口,运行下面的命令。 @@ -105,6 +107,13 @@ $ node --v8-options | grep harmony 上面命令的输出结果,会因为版本的不同而有所不同。 +我写了一个[ES-Checker](https://github.com/ruanyf/es-checker)模块,用来检查各种运行环境对ES6的支持情况。访问[ruanyf.github.io/es-checker](http://ruanyf.github.io/es-checker),可以看到您的浏览器支持ES6的程度。运行下面的命令,可以查看本机支持ES6的程度。 + +```bash +$ npm install -g es-checker +$ es-checker +``` + ## Babel转码器 [Babel](https://babeljs.io/)是一个广泛使用的ES6转码器,可以ES6代码转为ES5代码,从而在浏览器或其他环境执行。这意味着,你可以用ES6的方式编写程序,又不用担心现有环境是否支持。它的安装命令如下。 diff --git a/docs/let.md b/docs/let.md index 1bb0407..e79d9d2 100644 --- a/docs/let.md +++ b/docs/let.md @@ -58,12 +58,10 @@ a[6](); // 6 let不像var那样,会发生“变量提升”现象。 ```javascript - function do_something() { console.log(foo); // ReferenceError let foo = 2; } - ``` 上面代码在声明foo之前,就使用这个变量,结果会抛出一个错误。 @@ -71,12 +69,10 @@ function do_something() { 这也意味着typeof不再是一个百分之百安全的操作。 ```javascript - if (1) { typeof x; // ReferenceError let x; } - ``` 上面代码中,由于块级作用域内typeof运行时,x还没有值,所以会抛出一个ReferenceError。 @@ -84,14 +80,12 @@ if (1) { 只要块级作用域内存在let命令,它所声明的变量就“绑定”(binding)这个区域,不再受外部的影响。 ```javascript - var tmp = 123; if (true) { tmp = 'abc'; // ReferenceError let tmp; } - ``` 上面代码中,存在全局变量tmp,但是块级作用域内let又声明了一个局部变量tmp,导致后者绑定这个块级作用域,所以在let声明变量前,对tmp赋值会报错。 @@ -101,7 +95,6 @@ ES6明确规定,如果区块中存在let和const命令,这个区块对这些 总之,在代码块内,使用let命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”(temporal dead zone,简称TDZ)。 ```javascript - if (true) { // TDZ开始 tmp = 'abc'; // ReferenceError @@ -113,7 +106,6 @@ if (true) { tmp = 123; console.log(tmp); // 123 } - ``` 上面代码中,在let命令声明变量tmp之前,都属于变量tmp的“死区”。 @@ -121,13 +113,11 @@ if (true) { 有些“死区”比较隐蔽,不太容易发现。 ```javascript - function bar(x=y, y=2) { return [x, y]; } bar(); // 报错 - ``` 上面代码中,调用bar函数之所以报错,是因为参数x默认值等于另一个参数y,而此时y还没有声明,属于”死区“。 @@ -135,7 +125,6 @@ bar(); // 报错 需要注意的是,函数的作用域是其声明时所在的作用域。如果函数A的参数是函数B,那么函数B的作用域不是函数A。 ```javascript - let foo = 'outer'; function bar(func = x => foo) { @@ -144,7 +133,6 @@ function bar(func = x => foo) { } bar(); - ``` 上面代码中,函数bar的参数func,默认是一个匿名函数,返回值为变量foo。这个匿名函数的作用域就不是bar。这个匿名函数声明时,是处在外层作用域,所以内部的foo指向函数体外的声明,输出outer。它实际上等同于下面的代码。 @@ -167,7 +155,6 @@ bar(); let不允许在相同作用域内,重复声明同一个变量。 ```javascript - // 报错 { let a = 10; @@ -179,13 +166,11 @@ let不允许在相同作用域内,重复声明同一个变量。 let a = 10; let a = 1; } - ``` 因此,不能在函数内部重新声明参数。 ```javascript - function func(arg) { let arg; // 报错 } @@ -195,7 +180,6 @@ function func(arg) { let arg; // 不报错 } } - ``` ## 块级作用域 @@ -259,7 +243,6 @@ function f() { console.log('I am outside!'); } const也用来声明变量,但是声明的是常量。一旦声明,常量的值就不能改变。 ```javascript - const PI = 3.1415; PI // 3.1415 @@ -268,7 +251,6 @@ PI // 3.1415 const PI = 3.1; PI // 3.1415 - ``` 上面代码表明改变常量的值是不起作用的。需要注意的是,对常量重新赋值不会报错,只会默默地失败。 @@ -276,24 +258,20 @@ PI // 3.1415 const的作用域与let命令相同:只在声明所在的块级作用域内有效。 ```javascript - if (true) { const MAX = 5; } // 常量MAX在此处不可得 - ``` const命令也不存在提升,只能在声明的位置后面使用。 ```javascript - if (true) { console.log(MAX); // ReferenceError const MAX = 5; } - ``` 上面代码在常量MAX声明之前就调用,结果报错。 @@ -301,20 +279,17 @@ if (true) { const声明的常量,也与let一样不可重复声明。 ```javascript - var message = "Hello!"; let age = 25; // 以下两行都会报错 const message = "Goodbye!"; const age = 30; - ``` 由于const命令只是指向变量所在的地址,所以将一个对象声明为常量必须非常小心。 ```javascript - const foo = {}; foo.prop = 123; @@ -322,7 +297,6 @@ foo.prop // 123 foo = {} // 不起作用 - ``` 上面代码中,常量foo储存的是一个地址,这个地址指向一个对象。不可变的只是这个地址,即不能把foo指向另一个地址,但对象本身是可变的,所以依然可以为其添加新属性。 @@ -360,6 +334,27 @@ var constantize = (obj) => { }; ``` +## 跨模块常量 + +上面说过,const声明的常量只在当前代码块有效。如果想设置跨模块的常量,可以采用下面的写法。 + +```javascript +// constants.js 模块 +export const A = 1; +export const B = 3; +export const C = 4; + +// test1.js 模块 +import * as constants from './constants'; +console.log(constants.A); // 1 +console.log(constants.B); // 3 + +// test2.js 模块 +import {A, B} from './constants'; +console.log(A); // 1 +console.log(B); // 3 +``` + ## 全局对象的属性 全局对象是最顶层的对象,在浏览器环境指的是window对象,在Node.js指的是global对象。在JavaScript语言中,所有全局变量都是全局对象的属性。 diff --git a/docs/reference.md b/docs/reference.md index f4ab860..e5f903e 100644 --- a/docs/reference.md +++ b/docs/reference.md @@ -113,6 +113,7 @@ - Dave Herman, [Static module resolution](http://calculist.org/blog/2012/06/29/static-module-resolution/): ES6模块的静态化设计思想 - Axel Rauschmayer, [ECMAScript 6: new OOP features besides classes](http://www.2ality.com/2014/12/es6-oop.html) - Axel Rauschmayer, [Classes in ECMAScript 6 (final semantics)](http://www.2ality.com/2015/02/es6-classes-final.html): Class语法的详细介绍和设计思想分析 +- Maximiliano Fierro, [Declarative vs Imperative](http://elmasse.github.io/js/decorators-bindings-es7.html): Decorators介绍 ## 工具