mirror of
https://github.com/ruanyf/es6tutorial.git
synced 2025-05-24 18:32:22 +00:00
edit object/reflect
This commit is contained in:
parent
0dbfa7a33b
commit
e673693006
@ -250,7 +250,27 @@ class Foo {}
|
|||||||
|
|
||||||
如果存在Class的提升,上面代码将报错,因为let命令也是不提升的。
|
如果存在Class的提升,上面代码将报错,因为let命令也是不提升的。
|
||||||
|
|
||||||
**(7)严格模式**
|
**(7)存取函数**
|
||||||
|
|
||||||
|
Class支持set和get方法,设置赋值函数和取值函数,拦截属性的存取行为。
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
class Jedi {
|
||||||
|
constructor(options = {}) {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
set(key, val) {
|
||||||
|
this[key] = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
get(key) {
|
||||||
|
return this[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**(8)严格模式**
|
||||||
|
|
||||||
类和模块的内部,默认就是严格模式,所以不需要使用`use strict`指定运行模式。考虑到未来所有的代码,其实都是运行在模块之中,所以ES6实际上把整个语言升级到了严格模式。
|
类和模块的内部,默认就是严格模式,所以不需要使用`use strict`指定运行模式。考虑到未来所有的代码,其实都是运行在模块之中,所以ES6实际上把整个语言升级到了严格模式。
|
||||||
|
|
||||||
@ -741,12 +761,10 @@ import { stat, exists, readFile } from 'fs';
|
|||||||
ES6允许将独立的JS文件作为模块,也就是说,允许一个JavaScript脚本文件调用另一个脚本文件。该文件内部的所有变量,外部无法获取,必须使用export关键字输出变量。下面是一个JS文件,里面使用export关键字输出变量。
|
ES6允许将独立的JS文件作为模块,也就是说,允许一个JavaScript脚本文件调用另一个脚本文件。该文件内部的所有变量,外部无法获取,必须使用export关键字输出变量。下面是一个JS文件,里面使用export关键字输出变量。
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
|
|
||||||
// profile.js
|
// profile.js
|
||||||
export var firstName = 'Michael';
|
export var firstName = 'Michael';
|
||||||
export var lastName = 'Jackson';
|
export var lastName = 'Jackson';
|
||||||
export var year = 1958;
|
export var year = 1958;
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
上面代码是profile.js文件,保存了用户信息。ES6将其视为一个模块,里面用export命令对外部输出了三个变量。
|
上面代码是profile.js文件,保存了用户信息。ES6将其视为一个模块,里面用export命令对外部输出了三个变量。
|
||||||
@ -754,14 +772,12 @@ export var year = 1958;
|
|||||||
export的写法,除了像上面这样,还有另外一种。
|
export的写法,除了像上面这样,还有另外一种。
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
|
|
||||||
// profile.js
|
// profile.js
|
||||||
var firstName = 'Michael';
|
var firstName = 'Michael';
|
||||||
var lastName = 'Jackson';
|
var lastName = 'Jackson';
|
||||||
var year = 1958;
|
var year = 1958;
|
||||||
|
|
||||||
export {firstName, lastName, year};
|
export {firstName, lastName, year};
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
上面代码在export命令后面,使用大括号指定所要输出的一组变量。它与前一种写法(直接放置在var语句前)是等价的,但是应该优先考虑使用这种写法。因为这样就可以在脚本尾部,一眼看清楚输出了哪些变量。
|
上面代码在export命令后面,使用大括号指定所要输出的一组变量。它与前一种写法(直接放置在var语句前)是等价的,但是应该优先考虑使用这种写法。因为这样就可以在脚本尾部,一眼看清楚输出了哪些变量。
|
||||||
@ -769,7 +785,6 @@ export {firstName, lastName, year};
|
|||||||
使用export命令定义了模块的对外接口以后,其他JS文件就可以通过import命令加载这个模块(文件)。
|
使用export命令定义了模块的对外接口以后,其他JS文件就可以通过import命令加载这个模块(文件)。
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
|
|
||||||
// main.js
|
// main.js
|
||||||
|
|
||||||
import {firstName, lastName, year} from './profile';
|
import {firstName, lastName, year} from './profile';
|
||||||
@ -777,7 +792,6 @@ import {firstName, lastName, year} from './profile';
|
|||||||
function sfirsetHeader(element) {
|
function sfirsetHeader(element) {
|
||||||
element.textContent = firstName + ' ' + lastName;
|
element.textContent = firstName + ' ' + lastName;
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
上面代码属于另一个文件main.js,import命令就用于加载profile.js文件,并从中输入变量。import命令接受一个对象(用大括号表示),里面指定要从其他模块导入的变量名。大括号里面的变量名,必须与被导入模块(profile.js)对外接口的名称相同。
|
上面代码属于另一个文件main.js,import命令就用于加载profile.js文件,并从中输入变量。import命令接受一个对象(用大括号表示),里面指定要从其他模块导入的变量名。大括号里面的变量名,必须与被导入模块(profile.js)对外接口的名称相同。
|
||||||
@ -785,15 +799,12 @@ function sfirsetHeader(element) {
|
|||||||
如果想为输入的变量重新取一个名字,import语句中要使用as关键字,将输入的变量重命名。
|
如果想为输入的变量重新取一个名字,import语句中要使用as关键字,将输入的变量重命名。
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
|
|
||||||
import { lastName as surname } from './profile';
|
import { lastName as surname } from './profile';
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
ES6支持多重加载,即所加载的模块中又加载其他模块。
|
ES6支持多重加载,即所加载的模块中又加载其他模块。
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
|
|
||||||
import { Vehicle } from './Vehicle';
|
import { Vehicle } from './Vehicle';
|
||||||
|
|
||||||
class Car extends Vehicle {
|
class Car extends Vehicle {
|
||||||
@ -803,11 +814,22 @@ class Car extends Vehicle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export { Car }
|
export { Car }
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
上面的模块先加载Vehicle模块,然后在其基础上添加了move方法,再作为一个新模块输出。
|
上面的模块先加载Vehicle模块,然后在其基础上添加了move方法,再作为一个新模块输出。
|
||||||
|
|
||||||
|
如果在一个模块之中,先输入后输出同一个模块,import语句可以与export语句写在一起。
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
export { es6 as default } from './someModule';
|
||||||
|
|
||||||
|
// 等同于
|
||||||
|
import { es6 } from './someModule';
|
||||||
|
export default es6;
|
||||||
|
```
|
||||||
|
|
||||||
|
上面代码中,export和import语句可以结合在一起,写成一行。但是从可读性考虑,不建议采用这种写法,h应该采用标准写法。
|
||||||
|
|
||||||
**(2)模块的整体输入,module命令**
|
**(2)模块的整体输入,module命令**
|
||||||
|
|
||||||
export命令除了输出变量,还可以输出方法或类(class)。下面是一个circle.js文件,它输出两个方法area和circumference。
|
export命令除了输出变量,还可以输出方法或类(class)。下面是一个circle.js文件,它输出两个方法area和circumference。
|
||||||
|
@ -345,7 +345,6 @@ arr1.push(...arr2);
|
|||||||
扩展运算符还可以用于数组的赋值。
|
扩展运算符还可以用于数组的赋值。
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
|
|
||||||
var a = [1];
|
var a = [1];
|
||||||
var b = [2, 3, 4];
|
var b = [2, 3, 4];
|
||||||
var c = [6, 7];
|
var c = [6, 7];
|
||||||
@ -353,15 +352,17 @@ var d = [0, ...a, ...b, 5, ...c];
|
|||||||
|
|
||||||
d
|
d
|
||||||
// [0, 1, 2, 3, 4, 5, 6, 7]
|
// [0, 1, 2, 3, 4, 5, 6, 7]
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
上面代码其实也提供了,将一个数组拷贝进另一个数组的便捷方法。
|
上面代码其实也提供了,将一个数组拷贝进另一个数组的便捷方法。
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const arr2 = [...arr1];
|
||||||
|
```
|
||||||
|
|
||||||
扩展运算符也可以与解构赋值结合起来,用于生成数组。
|
扩展运算符也可以与解构赋值结合起来,用于生成数组。
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
|
|
||||||
const [first, ...rest] = [1, 2, 3, 4, 5];
|
const [first, ...rest] = [1, 2, 3, 4, 5];
|
||||||
first // 1
|
first // 1
|
||||||
rest // [2, 3, 4, 5]
|
rest // [2, 3, 4, 5]
|
||||||
@ -381,7 +382,6 @@ rest // ["bar"]
|
|||||||
const [first, ...rest] = ["foo", "bar", "baz"];
|
const [first, ...rest] = ["foo", "bar", "baz"];
|
||||||
first // "foo"
|
first // "foo"
|
||||||
rest // ["bar","baz"]
|
rest // ["bar","baz"]
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
如果将扩展运算符用于数组赋值,只能放在参数的最后一位,否则会报错。
|
如果将扩展运算符用于数组赋值,只能放在参数的最后一位,否则会报错。
|
||||||
|
182
docs/object.md
182
docs/object.md
@ -772,7 +772,14 @@ Proxy可以理解成在目标对象之前,架设一层“拦截”,外界对
|
|||||||
ES6原生提供Proxy构造函数,用来生成Proxy实例。
|
ES6原生提供Proxy构造函数,用来生成Proxy实例。
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
|
var proxy = new Proxy(target, handler)
|
||||||
|
```
|
||||||
|
|
||||||
|
Proxy对象的使用方法,都是上面这种形式。`new Proxy()`表示生成一个Proxy实例,它的target参数表示所要拦截的目标对象,handler参数也是一个对象,用来定制拦截行为。
|
||||||
|
|
||||||
|
下面是一个使用实例。
|
||||||
|
|
||||||
|
```javascript
|
||||||
var proxy = new Proxy({}, {
|
var proxy = new Proxy({}, {
|
||||||
get: function(target, property) {
|
get: function(target, property) {
|
||||||
return 35;
|
return 35;
|
||||||
@ -782,17 +789,21 @@ var proxy = new Proxy({}, {
|
|||||||
proxy.time // 35
|
proxy.time // 35
|
||||||
proxy.name // 35
|
proxy.name // 35
|
||||||
proxy.title // 35
|
proxy.title // 35
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
作为构造函数,Proxy接受两个参数。第一个参数是所要代理的目标对象(上例是一个空对象),即如果没有Proxy的介入,操作原来要访问的就是这个对象;第二个参数是一个设置对象,对于每一个被代理的操作,需要提供一个对应的处理函数,该函数将拦截对应的操作。比如,上面代码中,设置对象有一个get方法,用来拦截对目标对象属性的访问请求。get方法的两个参数分别是目标对象和所要访问的属性。可以看到,由于拦截函数总是返回35,所以访问任何属性都得到35。
|
上面代码中,作为构造函数,Proxy接受两个参数。第一个参数是所要代理的目标对象(上例是一个空对象),即如果没有Proxy的介入,操作原来要访问的就是这个对象;第二个参数是一个设置对象,对于每一个被代理的操作,需要提供一个对应的处理函数,该函数将拦截对应的操作。比如,上面代码中,设置对象有一个get方法,用来拦截对目标对象属性的访问请求。get方法的两个参数分别是目标对象和所要访问的属性。可以看到,由于拦截函数总是返回35,所以访问任何属性都得到35。
|
||||||
|
|
||||||
注意,要使得Proxy起作用,必须针对Proxy实例(上例是proxy对象)进行操作,而不是针对目标对象(上例是空对象)进行操作。
|
注意,要使得Proxy起作用,必须针对Proxy实例(上例是proxy对象)进行操作,而不是针对目标对象(上例是空对象)进行操作。
|
||||||
|
|
||||||
|
一个技巧是将Proxy对象,设置到`object.proxy`属性,从而可以在object对象上调用。
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var object = { proxy: new Proxy(target, handler) }.
|
||||||
|
```
|
||||||
|
|
||||||
Proxy实例也可以作为其他对象的原型对象。
|
Proxy实例也可以作为其他对象的原型对象。
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
|
|
||||||
var proxy = new Proxy({}, {
|
var proxy = new Proxy({}, {
|
||||||
get: function(target, property) {
|
get: function(target, property) {
|
||||||
return 35;
|
return 35;
|
||||||
@ -800,41 +811,72 @@ var proxy = new Proxy({}, {
|
|||||||
});
|
});
|
||||||
|
|
||||||
let obj = Object.create(proxy);
|
let obj = Object.create(proxy);
|
||||||
|
|
||||||
obj.time // 35
|
obj.time // 35
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
上面代码中,proxy对象是obj对象的原型,obj对象本身并没有time属性,所有根据原型链,会在proxy对象上读取该属性,导致被拦截。
|
上面代码中,proxy对象是obj对象的原型,obj对象本身并没有time属性,所有根据原型链,会在proxy对象上读取该属性,导致被拦截。
|
||||||
|
|
||||||
对于没有设置拦截的操作,则直接落在目标对象上,按照原先的方式产生结果。
|
同一个拦截器函数,可以设置拦截多个操作。
|
||||||
|
|
||||||
Proxy支持的拦截操作一览。
|
```javascript
|
||||||
|
var handler = {
|
||||||
|
get: function(target, name) {
|
||||||
|
if (name === 'prototype') return Object.prototype;
|
||||||
|
return 'Hello, '+ name;
|
||||||
|
},
|
||||||
|
apply: function(target, thisBinding, args) { return args[0]; },
|
||||||
|
construct: function(target, args) { return args[1]; }
|
||||||
|
};
|
||||||
|
|
||||||
- defineProperty(target, propKey, propDesc):返回一个布尔值,拦截Object.defineProperty(proxy, propKey, propDesc)
|
var fproxy = new Proxy(function(x,y) {
|
||||||
- deleteProperty(target, propKey) :返回一个布尔值,拦截delete proxy[propKey]
|
return x+y;
|
||||||
- enumerate(target):返回一个遍历器,拦截for (x in proxy)
|
}, handler);
|
||||||
- get(target, propKey, receiver):返回类型不限,拦截对象属性的读取
|
|
||||||
- getOwnPropertyDescriptor(target, propKey) :返回属性的描述对象,拦截Object.getOwnPropertyDescriptor(proxy, propKey)
|
fproxy(1,2); // 1
|
||||||
- getPrototypeOf(target) :返回一个对象,拦截Object.getPrototypeOf(proxy)
|
new fproxy(1,2); // 2
|
||||||
- has(target, propKey):返回一个布尔值,拦截propKey in proxy
|
fproxy.prototype; // Object.prototype
|
||||||
- isExtensible(target):返回一个布尔值,拦截Object.isExtensible(proxy)
|
fproxy.foo; // 'Hello, foo'
|
||||||
- ownKeys(target):返回一个数组,拦截Object.getOwnPropertyPropertyNames(proxy)、Object.getOwnPropertyPropertySymbols(proxy)、Object.keys(proxy)
|
```
|
||||||
- preventExtensions(target):返回一个布尔值,拦截Object.preventExtensions(proxy)
|
|
||||||
- set(target, propKey, value, receiver):返回一个布尔值,拦截对象属性的设置
|
Proxy支持的拦截操作一览。对于没有设置拦截的操作,则直接落在目标对象上,按照原先的方式产生结果。
|
||||||
- setPrototypeOf(target, proto):返回一个布尔值,拦截Object.setPrototypeOf(proxy, proto)
|
|
||||||
|
(1)get(target, propKey, receiver):拦截对象属性的读取,比如`proxy.foo`和`proxy['foo']`,返回类型不限。最后一个参数receiver可选,当target对象设置了propKey属性的get函数时,receiver对象会绑定get函数的this对象。
|
||||||
|
|
||||||
|
(2)set(target, propKey, value, receiver):拦截对象属性的设置,比如`proxy.foo = v`或`proxy['foo'] = v`,返回一个布尔值。
|
||||||
|
|
||||||
|
(3)has(target, propKey):拦截`propKey in proxy`的操作,返回一个布尔值。
|
||||||
|
|
||||||
|
(4)deleteProperty(target, propKey) :拦截`delete proxy[propKey]`的操作,返回一个布尔值。
|
||||||
|
|
||||||
|
(5)enumerate(target):拦截`for (var x in proxy)`,返回一个遍历器。
|
||||||
|
|
||||||
|
(6)hasOwn(target, propKey):拦截`proxy.hasOwnProperty('foo')`,返回一个布尔值。
|
||||||
|
|
||||||
|
(7)ownKeys(target):拦截`Object.getOwnPropertyNames(proxy)`、`Object.getOwnPropertySymbols(proxy)`、`Object.keys(proxy)`,返回一个数组。该方法返回对象所有自身的属性,而`Object.keys()`仅返回对象可遍历的属性。
|
||||||
|
|
||||||
|
(8)getOwnPropertyDescriptor(target, propKey) :拦截`Object.getOwnPropertyDescriptor(proxy, propKey)`,返回属性的描述对象。
|
||||||
|
|
||||||
|
(9)defineProperty(target, propKey, propDesc):拦截`Object.defineProperty(proxy, propKey, propDesc)`、`Object.defineProperties(proxy, propDescs)`,返回一个布尔值。
|
||||||
|
|
||||||
|
(10)preventExtensions(target):拦截`Object.preventExtensions(proxy)`,返回一个布尔值。
|
||||||
|
|
||||||
|
(11)getPrototypeOf(target) :拦截`Object.getPrototypeOf(proxy)`,返回一个对象。
|
||||||
|
|
||||||
|
(12)isExtensible(target):拦截`Object.isExtensible(proxy)`,返回一个布尔值。
|
||||||
|
|
||||||
|
(13)setPrototypeOf(target, proto):拦截`Object.setPrototypeOf(proxy, proto)`,返回一个布尔值。
|
||||||
|
|
||||||
如果目标对象是函数,那么还有两种额外操作可以拦截。
|
如果目标对象是函数,那么还有两种额外操作可以拦截。
|
||||||
|
|
||||||
- apply方法:拦截Proxy实例作为函数调用的操作,比如proxy(···)、proxy.call(···)、proxy.apply(···)。
|
(14)apply(target, object, args):拦截Proxy实例作为函数调用的操作,比如`proxy(...args)`、`proxy.call(object, ...args)`、`proxy.apply(...)`。
|
||||||
- construct方法:拦截Proxy实例作为构造函数调用的操作,比如new proxy(···)。
|
|
||||||
|
|
||||||
### get
|
(15)construct(target, args, proxy):拦截Proxy实例作为构造函数调用的操作,比如new proxy(...args)。
|
||||||
|
|
||||||
|
### get()
|
||||||
|
|
||||||
get方法用于拦截某个属性的读取操作。上文已经有一个例子,下面是另一个拦截读取操作的例子。
|
get方法用于拦截某个属性的读取操作。上文已经有一个例子,下面是另一个拦截读取操作的例子。
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
|
|
||||||
var person = {
|
var person = {
|
||||||
name: "张三"
|
name: "张三"
|
||||||
};
|
};
|
||||||
@ -851,7 +893,6 @@ var proxy = new Proxy(person, {
|
|||||||
|
|
||||||
proxy.name // "张三"
|
proxy.name // "张三"
|
||||||
proxy.age // 抛出一个错误
|
proxy.age // 抛出一个错误
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
上面代码表示,如果访问目标对象不存在的属性,会抛出一个错误。如果没有这个拦截函数,访问不存在的属性,只会返回undefined。
|
上面代码表示,如果访问目标对象不存在的属性,会抛出一个错误。如果没有这个拦截函数,访问不存在的属性,只会返回undefined。
|
||||||
@ -859,7 +900,6 @@ proxy.age // 抛出一个错误
|
|||||||
利用proxy,可以将读取属性的操作(get),转变为执行某个函数。
|
利用proxy,可以将读取属性的操作(get),转变为执行某个函数。
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
|
|
||||||
var pipe = (function () {
|
var pipe = (function () {
|
||||||
var pipe;
|
var pipe;
|
||||||
return function (value) {
|
return function (value) {
|
||||||
@ -884,12 +924,11 @@ var reverseInt = function (n) { return n.toString().split('').reverse().join('')
|
|||||||
|
|
||||||
pipe(3) . double . pow . reverseInt . get
|
pipe(3) . double . pow . reverseInt . get
|
||||||
// 63
|
// 63
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
上面代码设置Proxy以后,达到了将函数名链式使用的效果。
|
上面代码设置Proxy以后,达到了将函数名链式使用的效果。
|
||||||
|
|
||||||
### set
|
### set()
|
||||||
|
|
||||||
set方法用来拦截某个属性的赋值操作。假定Person对象有一个age属性,该属性应该是一个不大于200的整数,那么可以使用Proxy对象保证age的属性值符合要求。
|
set方法用来拦截某个属性的赋值操作。假定Person对象有一个age属性,该属性应该是一个不大于200的整数,那么可以使用Proxy对象保证age的属性值符合要求。
|
||||||
|
|
||||||
@ -923,7 +962,7 @@ person.age = 300 // 报错
|
|||||||
|
|
||||||
上面代码中,由于设置了存值函数set,任何不符合要求的age属性赋值,都会抛出一个错误。利用set方法,还可以数据绑定,即每当对象发生变化时,会自动更新DOM。
|
上面代码中,由于设置了存值函数set,任何不符合要求的age属性赋值,都会抛出一个错误。利用set方法,还可以数据绑定,即每当对象发生变化时,会自动更新DOM。
|
||||||
|
|
||||||
### apply
|
### apply()
|
||||||
|
|
||||||
apply方法拦截函数的调用、call和apply操作。
|
apply方法拦截函数的调用、call和apply操作。
|
||||||
|
|
||||||
@ -945,12 +984,11 @@ p() === 'I am the proxy';
|
|||||||
|
|
||||||
上面代码中,变量p是Proxy的实例,当它作为函数调用时(p()),就会被apply方法拦截,返回一个字符串。
|
上面代码中,变量p是Proxy的实例,当它作为函数调用时(p()),就会被apply方法拦截,返回一个字符串。
|
||||||
|
|
||||||
### ownKeys
|
### ownKeys()
|
||||||
|
|
||||||
ownKeys方法用来拦截Object.keys()操作。
|
ownKeys方法用来拦截Object.keys()操作。
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
|
|
||||||
let target = {};
|
let target = {};
|
||||||
|
|
||||||
let handler = {
|
let handler = {
|
||||||
@ -963,7 +1001,6 @@ let proxy = new Proxy(target, handler);
|
|||||||
|
|
||||||
Object.keys(proxy)
|
Object.keys(proxy)
|
||||||
// [ 'hello', 'world' ]
|
// [ 'hello', 'world' ]
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
上面代码拦截了对于target对象的Object.keys()操作,返回预先设定的数组。
|
上面代码拦截了对于target对象的Object.keys()操作,返回预先设定的数组。
|
||||||
@ -973,7 +1010,6 @@ Object.keys(proxy)
|
|||||||
Proxy.revocable方法返回一个可取消的Proxy实例。
|
Proxy.revocable方法返回一个可取消的Proxy实例。
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
|
|
||||||
let target = {};
|
let target = {};
|
||||||
let handler = {};
|
let handler = {};
|
||||||
|
|
||||||
@ -984,17 +1020,23 @@ proxy.foo // 123
|
|||||||
|
|
||||||
revoke();
|
revoke();
|
||||||
proxy.foo // TypeError: Revoked
|
proxy.foo // TypeError: Revoked
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Proxy.revocable方法返回一个对象,该对象的proxy属性是Proxy实例,revoke属性是一个函数,可以取消Proxy实例。上面代码中,当执行revoke函数之后,再访问Proxy实例,就会抛出一个错误。
|
Proxy.revocable方法返回一个对象,该对象的proxy属性是Proxy实例,revoke属性是一个函数,可以取消Proxy实例。上面代码中,当执行revoke函数之后,再访问Proxy实例,就会抛出一个错误。
|
||||||
|
|
||||||
## Reflect
|
## Reflect
|
||||||
|
|
||||||
|
### 概述
|
||||||
|
|
||||||
Reflect对象与Proxy对象一样,也是ES6为了操作对象而提供的新API。Reflect对象的设计目的有这样几个。
|
Reflect对象与Proxy对象一样,也是ES6为了操作对象而提供的新API。Reflect对象的设计目的有这样几个。
|
||||||
|
|
||||||
- 将Object对象的一些明显属于语言层面的方法,放到Reflect对象上。现阶段,某些方法同时在Object和Reflect对象上部署。
|
(1) 将Object对象的一些明显属于语言层面的方法,放到Reflect对象上。现阶段,某些方法同时在Object和Reflect对象上部署,未来的新方法将只部署在Reflect对象上。
|
||||||
- Reflect对象的方法与Proxy对象的方法一一对应,这是为了让Proxy对象可以方便地有一个原始方法,作为修改行为基础。
|
|
||||||
|
(2) 修改某些Object方法的返回结果,让其变得更合理。比如,`Object.defineProperty(obj, name, desc)`在无法定义属性时,会抛出一个错误,而`Reflect.defineProperty(obj, name, desc)`则会返回false。
|
||||||
|
|
||||||
|
(3) 让Object操作都变成函数行为。某些Object操作是命令式,比如`name in obj`和`delete obj[name]`,而`Reflect.has(obj, name)`和`Reflect.deleteProperty(obj, name)`让它们变成了函数行为。
|
||||||
|
|
||||||
|
(4)Reflect对象的方法与Proxy对象的方法一一对应,只要是Proxy对象的方法,就能在Reflect对象上找到对应的方法。这就让Proxy对象可以方便地调用对应的Reflect方法,完成默认行为,作为修改行为的基础。
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
Proxy(target, {
|
Proxy(target, {
|
||||||
@ -1010,22 +1052,19 @@ Proxy(target, {
|
|||||||
|
|
||||||
上面代码中,Proxy方法拦截target对象的属性赋值行为。它采用Reflect.set方法将值赋值给对象的属性,然后再部署额外的功能。
|
上面代码中,Proxy方法拦截target对象的属性赋值行为。它采用Reflect.set方法将值赋值给对象的属性,然后再部署额外的功能。
|
||||||
|
|
||||||
下面的例子中,Reflect对象方法基本与Object对象的对应方法一致。
|
下面是get方法的例子。
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
var O = {a: 1};
|
var loggedObj = new Proxy(obj, {
|
||||||
Object.defineProperty(O, 'b', {value: 2});
|
get: function(target, name) {
|
||||||
O[Symbol('c')] = 3;
|
console.log("get", target, name);
|
||||||
|
return Reflect.get(target, name);
|
||||||
Reflect.ownKeys(O); // ['a', 'b', Symbol(c)]
|
}
|
||||||
|
});
|
||||||
function C(a, b){
|
|
||||||
this.c = a + b;
|
|
||||||
}
|
|
||||||
var instance = Reflect.construct(C, [20, 22]);
|
|
||||||
instance.c; // 42
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### 方法
|
||||||
|
|
||||||
Reflect对象的方法清单如下。
|
Reflect对象的方法清单如下。
|
||||||
|
|
||||||
- Reflect.getOwnPropertyDescriptor(target,name)
|
- Reflect.getOwnPropertyDescriptor(target,name)
|
||||||
@ -1048,17 +1087,54 @@ Reflect对象的方法清单如下。
|
|||||||
- Reflect.apply(target,thisArg,args)
|
- Reflect.apply(target,thisArg,args)
|
||||||
- Reflect.construct(target,args)
|
- Reflect.construct(target,args)
|
||||||
|
|
||||||
上面这些方法中,有几点需要注意。
|
上面这些方法的作用,大部分与Object对象的同名方法的作用都是相同的。下面是对其中几个方法的解释。
|
||||||
|
|
||||||
- Reflect.get(target,name,receiver) 查找target对象的name属性。如果name属性部署了读取函数,则读取函数的this绑定receiver。
|
(1)Reflect.get(target,name,receiver)
|
||||||
|
|
||||||
- Reflect.set(target, name, value, receiver) 设置target对象的name属性等于value。如果name属性设置了赋值函数,则赋值函数的this绑定receiver。
|
查找并返回target对象的name属性,如果没有该属性,则返回undefined。
|
||||||
|
|
||||||
- Reflect.construct(target, args)等同于`new target(...args)`。
|
如果name属性部署了读取函数,则读取函数的this绑定receiver。
|
||||||
|
|
||||||
- Reflect.apply(fun,thisArg,args)等同于`Function.prototype.apply.call(fun,thisArg,args)`。
|
```javascript
|
||||||
|
var obj = {
|
||||||
|
get foo() { return this.bar(); },
|
||||||
|
bar: function() { ... }
|
||||||
|
}
|
||||||
|
|
||||||
- Reflect.set()、Reflect.defineProperty()、Reflect.freeze()、Reflect.seal()和Reflect.preventExtensions()返回一个布尔值,表示操作是否成功。它们对应的Object方法,失败时都会抛出错误。
|
// 下面语句会让 this.bar()
|
||||||
|
// 变成调用 wrapper.bar()
|
||||||
|
Reflect.get(obj, "foo", wrapper);
|
||||||
|
```
|
||||||
|
|
||||||
|
(2)Reflect.set(target, name, value, receiver)
|
||||||
|
|
||||||
|
设置target对象的name属性等于value。如果name属性设置了赋值函数,则赋值函数的this绑定receiver。
|
||||||
|
|
||||||
|
(3)Reflect.has(obj, name)
|
||||||
|
|
||||||
|
等同于`name in obj`。
|
||||||
|
|
||||||
|
(4)Reflect.deleteProperty(obj, name)
|
||||||
|
|
||||||
|
等同于`delete obj[name]`。
|
||||||
|
|
||||||
|
(5)Reflect.construct(target, args)
|
||||||
|
|
||||||
|
等同于`new target(...args)`,这提供了一种不使用new,来调用构造函数的方法。
|
||||||
|
|
||||||
|
(6)Reflect.getPrototypeOf(obj)
|
||||||
|
|
||||||
|
读取对象的\_\_proto\_\_属性,等同于`Object.getPrototypeOf(obj)`。
|
||||||
|
|
||||||
|
(7)Reflect.setPrototypeOf(obj, newProto)
|
||||||
|
|
||||||
|
设置对象的\_\_proto\_\_属性。注意,Object对象没有对应这个方法的方法。
|
||||||
|
|
||||||
|
(8)Reflect.apply(fun,thisArg,args)
|
||||||
|
|
||||||
|
等同于`Function.prototype.apply.call(fun,thisArg,args)`。一般来说,如果要绑定一个函数的this对象,可以这样写`fn.apply(obj, args)`,但是如果函数定义了自己的apply方法,就只能写成`Function.prototype.apply.call(fn, obj, args)`,采用Reflect对象可以简化这种操作。
|
||||||
|
|
||||||
|
另外,需要注意的是,Reflect.set()、Reflect.defineProperty()、Reflect.freeze()、Reflect.seal()和Reflect.preventExtensions()返回一个布尔值,表示操作是否成功。它们对应的Object方法,失败时都会抛出错误。
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
// 失败时抛出错误
|
// 失败时抛出错误
|
||||||
|
@ -61,6 +61,10 @@
|
|||||||
- Axel Rauschmayer, [Symbols in ECMAScript 6](http://www.2ality.com/2014/12/es6-symbols.html): Symbol简介
|
- Axel Rauschmayer, [Symbols in ECMAScript 6](http://www.2ality.com/2014/12/es6-symbols.html): Symbol简介
|
||||||
- Axel Rauschmayer, [Meta programming with ECMAScript 6 proxies](http://www.2ality.com/2014/12/es6-proxies.html): Proxy详解
|
- Axel Rauschmayer, [Meta programming with ECMAScript 6 proxies](http://www.2ality.com/2014/12/es6-proxies.html): Proxy详解
|
||||||
- Daniel Zautner, [Meta-programming JavaScript Using Proxies](http://dzautner.com/meta-programming-javascript-using-proxies/): 使用Proxy实现元编程
|
- Daniel Zautner, [Meta-programming JavaScript Using Proxies](http://dzautner.com/meta-programming-javascript-using-proxies/): 使用Proxy实现元编程
|
||||||
|
- Tom Van Cutsem, [Harmony-reflect](https://github.com/tvcutsem/harmony-reflect/wiki): Reflect对象的设计目的
|
||||||
|
- Tom Van Cutsem, [Proxy Traps](https://github.com/tvcutsem/harmony-reflect/blob/master/doc/traps.md):Proxy拦截操作一览
|
||||||
|
- Tom Van Cutsem, [Reflect API](https://github.com/tvcutsem/harmony-reflect/blob/master/doc/api.md)
|
||||||
|
- Tom Van Cutsem, [Proxy Handler API](https://github.com/tvcutsem/harmony-reflect/blob/master/doc/handler_api.md)
|
||||||
|
|
||||||
## Symbol
|
## Symbol
|
||||||
|
|
||||||
|
273
docs/style.md
273
docs/style.md
@ -1,6 +1,6 @@
|
|||||||
# 编程风格
|
# 编程风格
|
||||||
|
|
||||||
本章探讨如何将ES6的新语法,运用到编码实践之中,与传统的JavaScript语法结合在一起,以及如何形成良好的编码风格。
|
本章探讨如何将ES6的新语法,运用到编码实践之中,与传统的JavaScript语法结合在一起,写出合理的、易于阅读和维护的代码。多家公司和组织已经公开了它们的风格规范,具体可参阅[jscs.info](http://jscs.info/),下面的内容主要参考了[Airbnb](http://jscs.info/)的JavaScript风格规范。
|
||||||
|
|
||||||
## 块级作用域
|
## 块级作用域
|
||||||
|
|
||||||
@ -9,7 +9,6 @@
|
|||||||
ES6提出了两个新的声明变量的命令:let和const。其中,let完全可以取代var,因为两者语义相同,而且let没有副作用。
|
ES6提出了两个新的声明变量的命令:let和const。其中,let完全可以取代var,因为两者语义相同,而且let没有副作用。
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
if(true) {
|
if(true) {
|
||||||
@ -19,7 +18,6 @@ if(true) {
|
|||||||
for (let i = 0; i < 10; i++) {
|
for (let i = 0; i < 10; i++) {
|
||||||
console.log(i);
|
console.log(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
上面代码如果用var替代let,实际上就声明了一个全局变量,这显然不是本意。变量应该只在其声明的代码块内有效,var命令做不到这一点。
|
上面代码如果用var替代let,实际上就声明了一个全局变量,这显然不是本意。变量应该只在其声明的代码块内有效,var命令做不到这一点。
|
||||||
@ -27,14 +25,12 @@ for (let i = 0; i < 10; i++) {
|
|||||||
var命令存在变量提升效用,let命令没有这个问题。
|
var命令存在变量提升效用,let命令没有这个问题。
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
if(true) {
|
if(true) {
|
||||||
console.log(x); // ReferenceError
|
console.log(x); // ReferenceError
|
||||||
let x = 'hello';
|
let x = 'hello';
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
上面代码如果使用var替代let,console.log那一行就不会报错,而是会输出undefined,因为变量声明提升到代码块的头部。这违反了变量先声明后使用的原则。
|
上面代码如果使用var替代let,console.log那一行就不会报错,而是会输出undefined,因为变量声明提升到代码块的头部。这违反了变量先声明后使用的原则。
|
||||||
@ -46,7 +42,6 @@ if(true) {
|
|||||||
在let和const之间,建议优先使用const,尤其是在全局环境,不应该设置变量,只应设置常量。这符合函数式编程思想,有利于将来的分布式运算。
|
在let和const之间,建议优先使用const,尤其是在全局环境,不应该设置变量,只应设置常量。这符合函数式编程思想,有利于将来的分布式运算。
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
|
|
||||||
// bad
|
// bad
|
||||||
var a = 1, b = 2, c = 3;
|
var a = 1, b = 2, c = 3;
|
||||||
|
|
||||||
@ -57,7 +52,6 @@ const c = 3;
|
|||||||
|
|
||||||
// best
|
// best
|
||||||
const [a, b, c] = [1, 2, 3];
|
const [a, b, c] = [1, 2, 3];
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
const声明常量还有两个好处,一是阅读代码的人立刻会意识到不应该修改这个值,二是防止了无意间修改变量值所导致的错误。
|
const声明常量还有两个好处,一是阅读代码的人立刻会意识到不应该修改这个值,二是防止了无意间修改变量值所导致的错误。
|
||||||
@ -75,7 +69,6 @@ V8引擎只在严格模式之下,支持let和const。结合前两点,这实
|
|||||||
静态字符串一律使用单引号或反引号,不使用双引号。动态字符串使用反引号。
|
静态字符串一律使用单引号或反引号,不使用双引号。动态字符串使用反引号。
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
|
|
||||||
// bad
|
// bad
|
||||||
const a = "foobar";
|
const a = "foobar";
|
||||||
const b = 'foo' + a + 'bar';
|
const b = 'foo' + a + 'bar';
|
||||||
@ -87,7 +80,56 @@ const c = `foobar`;
|
|||||||
const a = 'foobar';
|
const a = 'foobar';
|
||||||
const b = `foo${a}bar`;
|
const b = `foo${a}bar`;
|
||||||
const c = 'foobar';
|
const c = 'foobar';
|
||||||
|
```
|
||||||
|
|
||||||
|
## 解构赋值
|
||||||
|
|
||||||
|
使用数组成员对变量赋值,优先使用解构赋值。
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const arr = [1, 2, 3, 4];
|
||||||
|
|
||||||
|
// bad
|
||||||
|
const first = arr[0];
|
||||||
|
const second = arr[1];
|
||||||
|
|
||||||
|
// good
|
||||||
|
const [first, second] = arr;
|
||||||
|
```
|
||||||
|
|
||||||
|
函数的参数如果是对象的成员,优先使用解构赋值。
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// bad
|
||||||
|
function getFullName(user) {
|
||||||
|
const firstName = user.firstName;
|
||||||
|
const lastName = user.lastName;
|
||||||
|
}
|
||||||
|
|
||||||
|
// good
|
||||||
|
function getFullName(obj) {
|
||||||
|
const { firstName, lastName } = obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
// best
|
||||||
|
function getFullName({ firstName, lastName }) {
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
如果函数返回多个值,优先使用对象的解构赋值,而不是数组的解构赋值。这样便于以后添加返回值,以及更改返回值的顺序。
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// bad
|
||||||
|
function processInput(input) {
|
||||||
|
return [left, right, top, bottom];
|
||||||
|
}
|
||||||
|
|
||||||
|
// good
|
||||||
|
function processInput(input) {
|
||||||
|
return { left, right, top, bottom };
|
||||||
|
}
|
||||||
|
|
||||||
|
const { left, right } = processInput(input);
|
||||||
```
|
```
|
||||||
|
|
||||||
## 对象
|
## 对象
|
||||||
@ -95,7 +137,6 @@ const c = 'foobar';
|
|||||||
单行定义的对象,最后一个成员不以逗号结尾。多行定义的对象,最后一个成员以逗号结尾。
|
单行定义的对象,最后一个成员不以逗号结尾。多行定义的对象,最后一个成员以逗号结尾。
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
|
|
||||||
// bad
|
// bad
|
||||||
const a = { k1: v1, k2: v2, };
|
const a = { k1: v1, k2: v2, };
|
||||||
const b = {
|
const b = {
|
||||||
@ -109,13 +150,11 @@ const b = {
|
|||||||
k1: v1,
|
k1: v1,
|
||||||
k2: v2,
|
k2: v2,
|
||||||
};
|
};
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
对象尽量静态化,一旦定义,就不得随意添加新的属性。如果添加属性不可避免,要使用Object.assign方法。
|
对象尽量静态化,一旦定义,就不得随意添加新的属性。如果添加属性不可避免,要使用Object.assign方法。
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
|
|
||||||
// bad
|
// bad
|
||||||
const a = {};
|
const a = {};
|
||||||
a.x = 3;
|
a.x = 3;
|
||||||
@ -127,27 +166,108 @@ Object.assign(a, { x: 3 });
|
|||||||
// good
|
// good
|
||||||
const a = { x: null };
|
const a = { x: null };
|
||||||
a.x = 3;
|
a.x = 3;
|
||||||
|
```
|
||||||
|
|
||||||
|
如果对象的属性名是动态的,可以在创造对象的时候,使用属性表达式定义。
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// bad
|
||||||
|
const obj = {
|
||||||
|
id: 5,
|
||||||
|
name: 'San Francisco',
|
||||||
|
};
|
||||||
|
obj[getKey('enabled')] = true;
|
||||||
|
|
||||||
|
// good
|
||||||
|
const obj = {
|
||||||
|
id: 5,
|
||||||
|
name: 'San Francisco',
|
||||||
|
[getKey('enabled')]: true,
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
上面代码中,对象obj的最后一个属性名,需要计算得到。这时最好采用属性表达式,在新建obj的时候,将该属性与其他属性定义在一起。这样一来,所有属性就在一个地方定义了。
|
||||||
|
|
||||||
|
另外,对象的属性和方法,尽量采用简洁表达法,这样易于描述和书写。
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var ref = 'some value';
|
||||||
|
|
||||||
|
// bad
|
||||||
|
const atom = {
|
||||||
|
ref: ref,
|
||||||
|
|
||||||
|
value: 1,
|
||||||
|
|
||||||
|
addValue: function (value) {
|
||||||
|
return atom.value + value;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// good
|
||||||
|
const atom = {
|
||||||
|
ref,
|
||||||
|
|
||||||
|
value: 1,
|
||||||
|
|
||||||
|
addValue(value) {
|
||||||
|
return atom.value + value;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
## 数组
|
||||||
|
|
||||||
|
使用扩展运算符(...)拷贝数组。
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// bad
|
||||||
|
const len = items.length;
|
||||||
|
const itemsCopy = [];
|
||||||
|
let i;
|
||||||
|
|
||||||
|
for (i = 0; i < len; i++) {
|
||||||
|
itemsCopy[i] = items[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
// good
|
||||||
|
const itemsCopy = [...items];
|
||||||
|
```
|
||||||
|
|
||||||
|
使用Array.from方法,将类似数组的对象转为数组。
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const foo = document.querySelectorAll('.foo');
|
||||||
|
const nodes = Array.from(foo);
|
||||||
```
|
```
|
||||||
|
|
||||||
## 函数
|
## 函数
|
||||||
|
|
||||||
使用匿名函数的场合,一律改为使用箭头函数。
|
立即执行函数可以写成箭头函数的形式。
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
|
(() => {
|
||||||
// bad
|
console.log('Welcome to the Internet.');
|
||||||
arr.reduce(function(x, y) { return x + y; }, 0);
|
})();
|
||||||
|
|
||||||
// good
|
|
||||||
arr.reduce((x, y) => x + y, 0);
|
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
箭头函数取代Function.prototype.bind,不应再用 self / _this / that 绑定 this。
|
那些需要使用函数表达式的场合,尽量用箭头函数代替。因为这样更简洁,而且绑定了this。
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
|
// bad
|
||||||
|
[1, 2, 3].map(function (x) {
|
||||||
|
return x * x;
|
||||||
|
});
|
||||||
|
|
||||||
|
// good
|
||||||
|
[1, 2, 3].map((x) => {
|
||||||
|
return x * x;
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
箭头函数取代Function.prototype.bind,不应再用self/\_this/that绑定 this。
|
||||||
|
|
||||||
|
```javascript
|
||||||
// bad
|
// bad
|
||||||
const self = this;
|
const self = this;
|
||||||
const boundMethod = function(...params) {
|
const boundMethod = function(...params) {
|
||||||
@ -159,13 +279,11 @@ const boundMethod = method.bind(this);
|
|||||||
|
|
||||||
// best
|
// best
|
||||||
const boundMethod = (...params) => method.apply(this, params);
|
const boundMethod = (...params) => method.apply(this, params);
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
所有配置项都应该集中在一个对象,放在最后一个参数,布尔值不可以直接作为参数。
|
所有配置项都应该集中在一个对象,放在最后一个参数,布尔值不可以直接作为参数。
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|
||||||
// bad
|
// bad
|
||||||
function divide(a, b, option = false ) {
|
function divide(a, b, option = false ) {
|
||||||
}
|
}
|
||||||
@ -173,7 +291,35 @@ function divide(a, b, option = false ) {
|
|||||||
// good
|
// good
|
||||||
function divide(a, b, { option = false } = {}) {
|
function divide(a, b, { option = false } = {}) {
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
不要在函数体内使用arguments变量,使用rest运算符(...)代替。因为rest运算符显式表明你想要获取参数,而且arguments是一个类似数组的对象,而rest运算符可以提供一个真正的数组。
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// bad
|
||||||
|
function concatenateAll() {
|
||||||
|
const args = Array.prototype.slice.call(arguments);
|
||||||
|
return args.join('');
|
||||||
|
}
|
||||||
|
|
||||||
|
// good
|
||||||
|
function concatenateAll(...args) {
|
||||||
|
return args.join('');
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
使用默认值语法设置函数参数的默认值。
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// bad
|
||||||
|
function handleThings(opts) {
|
||||||
|
opts = opts || {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// good
|
||||||
|
function handleThings(opts = {}) {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Map结构
|
## Map结构
|
||||||
@ -181,7 +327,6 @@ function divide(a, b, { option = false } = {}) {
|
|||||||
注意区分Object和Map,只有模拟实体对象时,才使用Object。如果只是需要key:value的数据结构,使用Map。因为Map有内建的遍历机制。
|
注意区分Object和Map,只有模拟实体对象时,才使用Object。如果只是需要key:value的数据结构,使用Map。因为Map有内建的遍历机制。
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
|
|
||||||
let map = new Map(arr);
|
let map = new Map(arr);
|
||||||
|
|
||||||
for (let key of map.keys()) {
|
for (let key of map.keys()) {
|
||||||
@ -195,12 +340,60 @@ for (let value of map.values()) {
|
|||||||
for (let item of map.entries()) {
|
for (let item of map.entries()) {
|
||||||
console.log(item[0], item[1]);
|
console.log(item[0], item[1]);
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Class
|
||||||
|
|
||||||
|
总是用class,取代需要prototype操作。因为class的写法更简洁,更易于理解。
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// bad
|
||||||
|
function Queue(contents = []) {
|
||||||
|
this._queue = [...contents];
|
||||||
|
}
|
||||||
|
Queue.prototype.pop = function() {
|
||||||
|
const value = this._queue[0];
|
||||||
|
this._queue.splice(0, 1);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// good
|
||||||
|
class Queue {
|
||||||
|
constructor(contents = []) {
|
||||||
|
this._queue = [...contents];
|
||||||
|
}
|
||||||
|
pop() {
|
||||||
|
const value = this._queue[0];
|
||||||
|
this._queue.splice(0, 1);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
使用extends实现继承,因为这样更简单,不会有破坏instanceof运算的危险。
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// bad
|
||||||
|
const inherits = require('inherits');
|
||||||
|
function PeekableQueue(contents) {
|
||||||
|
Queue.apply(this, contents);
|
||||||
|
}
|
||||||
|
inherits(PeekableQueue, Queue);
|
||||||
|
PeekableQueue.prototype.peek = function() {
|
||||||
|
return this._queue[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
// good
|
||||||
|
class PeekableQueue extends Queue {
|
||||||
|
peek() {
|
||||||
|
return this._queue[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## 模块
|
## 模块
|
||||||
|
|
||||||
使用import取代require。
|
首先,Module语法是JavaScript模块的标准写法,坚持使用这种写法。使用import取代require。
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
// bad
|
// bad
|
||||||
@ -215,7 +408,6 @@ import { func1, func2 } from 'moduleA';
|
|||||||
使用export取代module.exports。
|
使用export取代module.exports。
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
|
|
||||||
// commonJS的写法
|
// commonJS的写法
|
||||||
var React = require('react');
|
var React = require('react');
|
||||||
|
|
||||||
@ -237,5 +429,34 @@ const Breadcrumbs = React.createClass({
|
|||||||
});
|
});
|
||||||
|
|
||||||
export default Breadcrumbs
|
export default Breadcrumbs
|
||||||
|
```
|
||||||
|
|
||||||
|
不要在模块输入中使用通配符。因为这样可以确保你的模块之中,有一个默认输出(export default)。
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// bad
|
||||||
|
import * as myObject './importModule';
|
||||||
|
|
||||||
|
// good
|
||||||
|
import myObject from './importModule';
|
||||||
|
```
|
||||||
|
|
||||||
|
如果模块默认输出一个函数,函数名的首字母应该小写。
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
function makeStyleGuide() {
|
||||||
|
}
|
||||||
|
|
||||||
|
export default makeStyleGuide;
|
||||||
|
```
|
||||||
|
|
||||||
|
如果模块默认输出一个对象,对象名的首字母应该大写。
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const StyleGuide = {
|
||||||
|
es6: {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default StyleGuide;
|
||||||
```
|
```
|
||||||
|
Loading…
x
Reference in New Issue
Block a user