mirror of
https://github.com/ruanyf/es6tutorial.git
synced 2025-05-24 18:32:22 +00:00
edit class/decorator
This commit is contained in:
parent
4960a1808f
commit
2ffd5f9e97
@ -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)是一个第三方模块,提供了几个常见的修饰器,通过它可以更好地理解修饰器。
|
||||
|
@ -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参数,可以向该函数传入任意数目的参数。
|
||||
|
@ -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版起就已经支持。
|
||||
|
@ -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的用法介绍
|
||||
|
||||
## 模块
|
||||
|
Loading…
x
Reference in New Issue
Block a user