diff --git a/docs/class.md b/docs/class.md index cddb775..f435b14 100644 --- a/docs/class.md +++ b/docs/class.md @@ -844,6 +844,20 @@ console.log(MyTestableClass.isTestable) // true 上面代码中,`@testable`就是一个修饰器。它修改了MyTestableClass这个类的行为,为它加上了静态属性isTestable。 +基本上,修饰器的行为就是下面这样。 + +```javascript +@decorator +class A {} + +// 等同于 + +class A {} +A = decorator(A) || A; +``` + +也就是说,修饰器本质上就是能在编译时执行的函数。 + 修饰器函数可以接受三个参数,依次是目标函数、属性名和该属性的描述对象。后两个参数可省略。上面代码中,testable函数的参数target,就是所要修饰的对象。如果希望修饰器的行为,能够根据目标对象的不同而不同,就要在外面再封装一层函数。 ```javascript @@ -983,6 +997,64 @@ class Person { 除了注释,修饰器还能用来类型检查。所以,对于Class来说,这项功能相当有用。从长期来看,它将是JavaScript代码静态分析的重要工具。 + +### 为什么修饰器不能用于函数? + +修饰器只能用于类和类的方法,不能用于函数,因为存在函数提升。 + +```javascript +var counter = 0; + +var add = function () { + counter++; +}; + +@add +function foo() { +} +``` + +上面的代码,意图是执行后,counter等于1,但是实际上结果是couter等于0。因为函数提升,使得实际执行的代码是下面这样。 + +```javascript +var counter; +var add; + +@add +function foo() { +} + +counter = 0; + +add = function () { + counter++; +}; +``` + +下面是另一个例子。 + +```javascript +var readOnly = require("some-decorator"); + +@readOnly +function foo() { +} +``` + +上面代码也有问题,因为实际执行是下面这样。 + +```javascript +var readOnly; + +@readOnly +function foo() { +} + +readOnly = require("some-decorator"); +``` + +总之,由于存在函数提升,使得修饰器不能用于函数。类是不会提升的,所以就没有这方面的问题。 + ### core-decorators.js [core-decorators.js](https://github.com/jayphelps/core-decorators.js)是一个第三方模块,提供了几个常见的修饰器,通过它可以更好地理解修饰器。 diff --git a/docs/function.md b/docs/function.md index d798dcb..e830d42 100644 --- a/docs/function.md +++ b/docs/function.md @@ -150,7 +150,6 @@ foo() 另一个需要注意的地方是,参数默认值所处的作用域,不是全局作用域,而是函数作用域。 ```javascript - var x = 1; function foo(x, y = x) { @@ -158,20 +157,17 @@ function foo(x, y = x) { } foo(2) // 2 - ``` -上面代码中,参数y的默认值等于x,由于处在函数作用域,所以x等于参数x,而不是全局变量x。 +上面代码中,参数y的默认值等于x,由于处在函数作用域,所以y等于参数x,而不是全局变量x。 参数变量是默认声明的,所以不能用let或const再次声明。 ```javascript - function foo(x = 5) { let x = 1; // error const x = 2; // error } - ``` 上面代码中,参数变量x是默认声明的,在函数体中,不能用let或const再次声明,否则会报错。 @@ -179,7 +175,6 @@ function foo(x = 5) { 参数默认值可以与解构赋值,联合起来使用。 ```javascript - function foo({x, y = 5}) { console.log(x, y); } @@ -187,7 +182,6 @@ function foo({x, y = 5}) { foo({}) // undefined, 5 foo({x: 1}) // 1, 5 foo({x: 1, y: 2}) // 1, 2 - ``` 上面代码中,foo函数的参数是一个对象,变量x和y用于解构赋值,y有默认值5。 @@ -197,19 +191,17 @@ foo({x: 1, y: 2}) // 1, 2 ES6引入rest参数(形式为“...变量名”),用于获取函数的多余参数,这样就不需要使用arguments对象了。rest参数搭配的变量是一个数组,该变量将多余的参数放入数组中。 ```javascript - function add(...values) { - let sum = 0; + let sum = 0; - for (var val of values) { - sum += val; - } + for (var val of values) { + sum += val; + } - return sum; + return sum; } add(2, 5, 3) // 10 - ``` 上面代码的add函数是一个求和函数,利用rest参数,可以向该函数传入任意数目的参数。 diff --git a/docs/object.md b/docs/object.md index dc15a38..a4919a8 100644 --- a/docs/object.md +++ b/docs/object.md @@ -520,7 +520,7 @@ fproxy.foo; // 'Hello, foo' 拦截`for (var x in proxy)`,返回一个遍历器。 -**6)hasOwn(target, propKey)** +**(6)hasOwn(target, propKey)** 拦截`proxy.hasOwnProperty('foo')`,返回一个布尔值。 @@ -842,7 +842,6 @@ Reflect.defineProperty(obj, name, desc); Object.observe方法用来监听对象(以及数组)的变化。一旦监听对象发生变化,就会触发回调函数。 ```javascript - var user = {}; Object.observe(user, function(changes){ changes.forEach(function(change) { @@ -853,7 +852,6 @@ Object.observe(user, function(changes){ user.firstName = 'Michael'; user.lastName = 'Jackson'; user.fullName // 'Michael Jackson' - ``` 上面代码中,Object.observer方法监听user对象。一旦该对象发生变化,就自动生成fullName属性。 @@ -861,7 +859,6 @@ user.fullName // 'Michael Jackson' 一般情况下,Object.observe方法接受两个参数,第一个参数是监听的对象,第二个函数是一个回调函数。一旦监听对象发生变化(比如新增或删除一个属性),就会触发这个回调函数。很明显,利用这个方法可以做很多事情,比如自动更新DOM。 ```javascript - var div = $("#foo"); Object.observe(user, function(changes){ @@ -870,7 +867,6 @@ Object.observe(user, function(changes){ div.text(fullName); }); }); - ``` 上面代码中,只要user对象发生变化,就会自动更新DOM。如果配合jQuery的change方法,就可以实现数据对象与DOM对象的双向自动绑定。 @@ -878,7 +874,6 @@ Object.observe(user, function(changes){ 回调函数的changes参数是一个数组,代表对象发生的变化。下面是一个更完整的例子。 ```javascript - var o = {}; function observer(changes){ @@ -891,20 +886,17 @@ function observer(changes){ } Object.observe(o, observer); - ``` 参照上面代码,Object.observe方法指定的回调函数,接受一个数组(changes)作为参数。该数组的成员与对象的变化一一对应,也就是说,对象发生多少个变化,该数组就有多少个成员。每个成员是一个对象(change),它的name属性表示发生变化源对象的属性名,oldValue属性表示发生变化前的值,object属性指向变动后的源对象,type属性表示变化的种类。基本上,change对象是下面的样子。 ```javascript - var change = { object: {...}, type: 'update', name: 'p2', oldValue: 'Property 2' } - ``` Object.observe方法目前共支持监听六种变化。 @@ -919,9 +911,7 @@ Object.observe方法目前共支持监听六种变化。 Object.observe方法还可以接受第三个参数,用来指定监听的事件种类。 ```javascript - Object.observe(o, observer, ['delete']); - ``` 上面的代码表示,只在发生delete事件时,才会调用回调函数。 @@ -929,9 +919,7 @@ Object.observe(o, observer, ['delete']); Object.unobserve方法用来取消监听。 ```javascript - Object.unobserve(o, observer); - ``` 注意,Object.observe和Object.unobserve这两个方法不属于ES6,而是属于ES7的一部分。不过,Chrome浏览器从33版起就已经支持。 diff --git a/docs/reference.md b/docs/reference.md index 1177c0a..47abdda 100644 --- a/docs/reference.md +++ b/docs/reference.md @@ -28,8 +28,8 @@ ## 语法点 -- Kyle Simpson, [For and against `let`](http://davidwalsh.name/for-and-against-let): 讨论let命令的作用域 -- kangax, [Why `typeof` is no longer “safe”](http://es-discourse.com/t/why-typeof-is-no-longer-safe/15): 讨论在块级作用域内,let命令的变量声明和赋值的行为 +- Kyle Simpson, [For and against let](http://davidwalsh.name/for-and-against-let): 讨论let命令的作用域 +- kangax, [Why typeof is no longer “safe”](http://es-discourse.com/t/why-typeof-is-no-longer-safe/15): 讨论在块级作用域内,let命令的变量声明和赋值的行为 - Axel Rauschmayer, [Variables and scoping in ECMAScript 6](http://www.2ality.com/2015/02/es6-scoping.html): 讨论块级作用域与let和const的行为 - Nick Fitzgerald, [Destructuring Assignment in ECMAScript 6](http://fitzgeraldnick.com/weblog/50/): 详细介绍解构赋值的用法 - Nicholas C. Zakas, [Understanding ECMAScript 6 arrow functions](http://www.nczonline.net/blog/2013/09/10/understanding-ecmascript-6-arrow-functions/) @@ -122,6 +122,7 @@ - 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和Mixin介绍 - Addy Osmani, [Exploring ES2016 Decorators](https://medium.com/google-developers/exploring-es7-decorators-76ecb65fb841): Decorator的深入介绍 +- Sebastian McKenzie, [Allow decorators for functions as well](https://github.com/wycats/javascript-decorators/issues/4): 为什么修饰器不能用于函数 - Maximiliano Fierro, [Traits with ES7 Decorators](http://cocktailjs.github.io/blog/traits-with-es7-decorators.html): Trait的用法介绍 ## 模块