1
0
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:
Ruan Yifeng 2015-07-03 16:50:09 +08:00
parent 0dbfa7a33b
commit e673693006
5 changed files with 417 additions and 94 deletions

View File

@ -250,7 +250,27 @@ class Foo {}
如果存在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实际上把整个语言升级到了严格模式。
@ -741,12 +761,10 @@ import { stat, exists, readFile } from 'fs';
ES6允许将独立的JS文件作为模块也就是说允许一个JavaScript脚本文件调用另一个脚本文件。该文件内部的所有变量外部无法获取必须使用export关键字输出变量。下面是一个JS文件里面使用export关键字输出变量。
```javascript
// profile.js
export var firstName = 'Michael';
export var lastName = 'Jackson';
export var year = 1958;
```
上面代码是profile.js文件保存了用户信息。ES6将其视为一个模块里面用export命令对外部输出了三个变量。
@ -754,14 +772,12 @@ export var year = 1958;
export的写法除了像上面这样还有另外一种。
```javascript
// profile.js
var firstName = 'Michael';
var lastName = 'Jackson';
var year = 1958;
export {firstName, lastName, year};
```
上面代码在export命令后面使用大括号指定所要输出的一组变量。它与前一种写法直接放置在var语句前是等价的但是应该优先考虑使用这种写法。因为这样就可以在脚本尾部一眼看清楚输出了哪些变量。
@ -769,7 +785,6 @@ export {firstName, lastName, year};
使用export命令定义了模块的对外接口以后其他JS文件就可以通过import命令加载这个模块文件
```javascript
// main.js
import {firstName, lastName, year} from './profile';
@ -777,7 +792,6 @@ import {firstName, lastName, year} from './profile';
function sfirsetHeader(element) {
element.textContent = firstName + ' ' + lastName;
}
```
上面代码属于另一个文件main.jsimport命令就用于加载profile.js文件并从中输入变量。import命令接受一个对象用大括号表示里面指定要从其他模块导入的变量名。大括号里面的变量名必须与被导入模块profile.js对外接口的名称相同。
@ -785,15 +799,12 @@ function sfirsetHeader(element) {
如果想为输入的变量重新取一个名字import语句中要使用as关键字将输入的变量重命名。
```javascript
import { lastName as surname } from './profile';
```
ES6支持多重加载即所加载的模块中又加载其他模块。
```javascript
import { Vehicle } from './Vehicle';
class Car extends Vehicle {
@ -803,11 +814,22 @@ class Car extends Vehicle {
}
export { Car }
```
上面的模块先加载Vehicle模块然后在其基础上添加了move方法再作为一个新模块输出。
如果在一个模块之中先输入后输出同一个模块import语句可以与export语句写在一起。
```javascript
export { es6 as default } from './someModule';
// 等同于
import { es6 } from './someModule';
export default es6;
```
上面代码中export和import语句可以结合在一起写成一行。但是从可读性考虑不建议采用这种写法h应该采用标准写法。
**2模块的整体输入module命令**
export命令除了输出变量还可以输出方法或类class。下面是一个circle.js文件它输出两个方法area和circumference。

View File

@ -345,7 +345,6 @@ arr1.push(...arr2);
扩展运算符还可以用于数组的赋值。
```javascript
var a = [1];
var b = [2, 3, 4];
var c = [6, 7];
@ -353,15 +352,17 @@ var d = [0, ...a, ...b, 5, ...c];
d
// [0, 1, 2, 3, 4, 5, 6, 7]
```
上面代码其实也提供了,将一个数组拷贝进另一个数组的便捷方法。
```javascript
const arr2 = [...arr1];
```
扩展运算符也可以与解构赋值结合起来,用于生成数组。
```javascript
const [first, ...rest] = [1, 2, 3, 4, 5];
first // 1
rest // [2, 3, 4, 5]
@ -381,7 +382,6 @@ rest // ["bar"]
const [first, ...rest] = ["foo", "bar", "baz"];
first // "foo"
rest // ["bar","baz"]
```
如果将扩展运算符用于数组赋值,只能放在参数的最后一位,否则会报错。

View File

@ -772,7 +772,14 @@ Proxy可以理解成在目标对象之前架设一层“拦截”外界对
ES6原生提供Proxy构造函数用来生成Proxy实例。
```javascript
var proxy = new Proxy(target, handler)
```
Proxy对象的使用方法都是上面这种形式。`new Proxy()`表示生成一个Proxy实例它的target参数表示所要拦截的目标对象handler参数也是一个对象用来定制拦截行为。
下面是一个使用实例。
```javascript
var proxy = new Proxy({}, {
get: function(target, property) {
return 35;
@ -782,17 +789,21 @@ var proxy = new Proxy({}, {
proxy.time // 35
proxy.name // 35
proxy.title // 35
```
作为构造函数Proxy接受两个参数。第一个参数是所要代理的目标对象上例是一个空对象即如果没有Proxy的介入操作原来要访问的就是这个对象第二个参数是一个设置对象对于每一个被代理的操作需要提供一个对应的处理函数该函数将拦截对应的操作。比如上面代码中设置对象有一个get方法用来拦截对目标对象属性的访问请求。get方法的两个参数分别是目标对象和所要访问的属性。可以看到由于拦截函数总是返回35所以访问任何属性都得到35。
上面代码中,作为构造函数Proxy接受两个参数。第一个参数是所要代理的目标对象上例是一个空对象即如果没有Proxy的介入操作原来要访问的就是这个对象第二个参数是一个设置对象对于每一个被代理的操作需要提供一个对应的处理函数该函数将拦截对应的操作。比如上面代码中设置对象有一个get方法用来拦截对目标对象属性的访问请求。get方法的两个参数分别是目标对象和所要访问的属性。可以看到由于拦截函数总是返回35所以访问任何属性都得到35。
注意要使得Proxy起作用必须针对Proxy实例上例是proxy对象进行操作而不是针对目标对象上例是空对象进行操作。
一个技巧是将Proxy对象设置到`object.proxy`属性从而可以在object对象上调用。
```javascript
var object = { proxy: new Proxy(target, handler) }.
```
Proxy实例也可以作为其他对象的原型对象。
```javascript
var proxy = new Proxy({}, {
get: function(target, property) {
return 35;
@ -800,41 +811,72 @@ var proxy = new Proxy({}, {
});
let obj = Object.create(proxy);
obj.time // 35
```
上面代码中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
- deleteProperty(target, propKey) 返回一个布尔值拦截delete proxy[propKey]
- enumerate(target)返回一个遍历器拦截for (x in proxy)
- get(target, propKey, receiver):返回类型不限,拦截对象属性的读取
- getOwnPropertyDescriptor(target, propKey) 返回属性的描述对象拦截Object.getOwnPropertyDescriptor(proxy, propKey)
- getPrototypeOf(target) 返回一个对象拦截Object.getPrototypeOf(proxy)
- has(target, propKey)返回一个布尔值拦截propKey in proxy
- isExtensible(target)返回一个布尔值拦截Object.isExtensible(proxy)
- ownKeys(target)返回一个数组拦截Object.getOwnPropertyPropertyNames(proxy)、Object.getOwnPropertyPropertySymbols(proxy)、Object.keys(proxy)
- preventExtensions(target)返回一个布尔值拦截Object.preventExtensions(proxy)
- set(target, propKey, value, receiver):返回一个布尔值,拦截对象属性的设置
- setPrototypeOf(target, proto)返回一个布尔值拦截Object.setPrototypeOf(proxy, proto)
var fproxy = new Proxy(function(x,y) {
return x+y;
}, handler);
fproxy(1,2); // 1
new fproxy(1,2); // 2
fproxy.prototype; // Object.prototype
fproxy.foo; // 'Hello, foo'
```
Proxy支持的拦截操作一览。对于没有设置拦截的操作则直接落在目标对象上按照原先的方式产生结果。
1get(target, propKey, receiver):拦截对象属性的读取,比如`proxy.foo``proxy['foo']`返回类型不限。最后一个参数receiver可选当target对象设置了propKey属性的get函数时receiver对象会绑定get函数的this对象。
2set(target, propKey, value, receiver):拦截对象属性的设置,比如`proxy.foo = v``proxy['foo'] = v`,返回一个布尔值。
3has(target, propKey):拦截`propKey in proxy`的操作,返回一个布尔值。
4deleteProperty(target, propKey) :拦截`delete proxy[propKey]`的操作,返回一个布尔值。
5enumerate(target):拦截`for (var x in proxy)`,返回一个遍历器。
6hasOwn(target, propKey):拦截`proxy.hasOwnProperty('foo')`,返回一个布尔值。
7ownKeys(target):拦截`Object.getOwnPropertyNames(proxy)``Object.getOwnPropertySymbols(proxy)``Object.keys(proxy)`,返回一个数组。该方法返回对象所有自身的属性,而`Object.keys()`仅返回对象可遍历的属性。
8getOwnPropertyDescriptor(target, propKey) :拦截`Object.getOwnPropertyDescriptor(proxy, propKey)`,返回属性的描述对象。
9defineProperty(target, propKey, propDesc):拦截`Object.defineProperty(proxy, propKey, propDesc``Object.defineProperties(proxy, propDescs)`,返回一个布尔值。
10preventExtensions(target):拦截`Object.preventExtensions(proxy)`,返回一个布尔值。
11getPrototypeOf(target) :拦截`Object.getPrototypeOf(proxy)`,返回一个对象。
12isExtensible(target):拦截`Object.isExtensible(proxy)`,返回一个布尔值。
13setPrototypeOf(target, proto):拦截`Object.setPrototypeOf(proxy, proto)`,返回一个布尔值。
如果目标对象是函数,那么还有两种额外操作可以拦截。
- apply方法拦截Proxy实例作为函数调用的操作比如proxy(···)、proxy.call(···)、proxy.apply(···)。
- construct方法拦截Proxy实例作为构造函数调用的操作比如new proxy(···)。
14apply(target, object, args)拦截Proxy实例作为函数调用的操作比如`proxy(...args)``proxy.call(object, ...args)``proxy.apply(...)`
### get
15construct(target, args, proxy)拦截Proxy实例作为构造函数调用的操作比如new proxy(...args)。
### get()
get方法用于拦截某个属性的读取操作。上文已经有一个例子下面是另一个拦截读取操作的例子。
```javascript
var person = {
name: "张三"
};
@ -851,7 +893,6 @@ var proxy = new Proxy(person, {
proxy.name // "张三"
proxy.age // 抛出一个错误
```
上面代码表示如果访问目标对象不存在的属性会抛出一个错误。如果没有这个拦截函数访问不存在的属性只会返回undefined。
@ -859,7 +900,6 @@ proxy.age // 抛出一个错误
利用proxy可以将读取属性的操作get转变为执行某个函数。
```javascript
var pipe = (function () {
var pipe;
return function (value) {
@ -884,12 +924,11 @@ var reverseInt = function (n) { return n.toString().split('').reverse().join('')
pipe(3) . double . pow . reverseInt . get
// 63
```
上面代码设置Proxy以后达到了将函数名链式使用的效果。
### set
### set()
set方法用来拦截某个属性的赋值操作。假定Person对象有一个age属性该属性应该是一个不大于200的整数那么可以使用Proxy对象保证age的属性值符合要求。
@ -923,7 +962,7 @@ person.age = 300 // 报错
上面代码中由于设置了存值函数set任何不符合要求的age属性赋值都会抛出一个错误。利用set方法还可以数据绑定即每当对象发生变化时会自动更新DOM。
### apply
### apply()
apply方法拦截函数的调用、call和apply操作。
@ -945,12 +984,11 @@ p() === 'I am the proxy';
上面代码中变量p是Proxy的实例当它作为函数调用时p()就会被apply方法拦截返回一个字符串。
### ownKeys
### ownKeys()
ownKeys方法用来拦截Object.keys()操作。
```javascript
let target = {};
let handler = {
@ -963,7 +1001,6 @@ let proxy = new Proxy(target, handler);
Object.keys(proxy)
// [ 'hello', 'world' ]
```
上面代码拦截了对于target对象的Object.keys()操作,返回预先设定的数组。
@ -973,7 +1010,6 @@ Object.keys(proxy)
Proxy.revocable方法返回一个可取消的Proxy实例。
```javascript
let target = {};
let handler = {};
@ -984,17 +1020,23 @@ proxy.foo // 123
revoke();
proxy.foo // TypeError: Revoked
```
Proxy.revocable方法返回一个对象该对象的proxy属性是Proxy实例revoke属性是一个函数可以取消Proxy实例。上面代码中当执行revoke函数之后再访问Proxy实例就会抛出一个错误。
## Reflect
### 概述
Reflect对象与Proxy对象一样也是ES6为了操作对象而提供的新API。Reflect对象的设计目的有这样几个。
- 将Object对象的一些明显属于语言层面的方法放到Reflect对象上。现阶段某些方法同时在Object和Reflect对象上部署。
- Reflect对象的方法与Proxy对象的方法一一对应这是为了让Proxy对象可以方便地有一个原始方法作为修改行为基础。
1 将Object对象的一些明显属于语言层面的方法放到Reflect对象上。现阶段某些方法同时在Object和Reflect对象上部署未来的新方法将只部署在Reflect对象上。
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)`让它们变成了函数行为。
4Reflect对象的方法与Proxy对象的方法一一对应只要是Proxy对象的方法就能在Reflect对象上找到对应的方法。这就让Proxy对象可以方便地调用对应的Reflect方法完成默认行为作为修改行为的基础。
```javascript
Proxy(target, {
@ -1010,22 +1052,19 @@ Proxy(target, {
上面代码中Proxy方法拦截target对象的属性赋值行为。它采用Reflect.set方法将值赋值给对象的属性然后再部署额外的功能。
下面的例子中Reflect对象方法基本与Object对象的对应方法一致
下面是get方法的例子
```javascript
var O = {a: 1};
Object.defineProperty(O, 'b', {value: 2});
O[Symbol('c')] = 3;
Reflect.ownKeys(O); // ['a', 'b', Symbol(c)]
function C(a, b){
this.c = a + b;
var loggedObj = new Proxy(obj, {
get: function(target, name) {
console.log("get", target, name);
return Reflect.get(target, name);
}
var instance = Reflect.construct(C, [20, 22]);
instance.c; // 42
});
```
### 方法
Reflect对象的方法清单如下。
- Reflect.getOwnPropertyDescriptor(target,name)
@ -1048,17 +1087,54 @@ Reflect对象的方法清单如下。
- Reflect.apply(target,thisArg,args)
- Reflect.construct(target,args)
上面这些方法中,有几点需要注意
上面这些方法的作用大部分与Object对象的同名方法的作用都是相同的。下面是对其中几个方法的解释
- Reflect.get(target,name,receiver) 查找target对象的name属性。如果name属性部署了读取函数则读取函数的this绑定receiver。
1Reflect.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);
```
2Reflect.set(target, name, value, receiver)
设置target对象的name属性等于value。如果name属性设置了赋值函数则赋值函数的this绑定receiver。
3Reflect.has(obj, name)
等同于`name in obj`
4Reflect.deleteProperty(obj, name)
等同于`delete obj[name]`
5Reflect.construct(target, args)
等同于`new target(...args)`这提供了一种不使用new来调用构造函数的方法。
6Reflect.getPrototypeOf(obj)
读取对象的\_\_proto\_\_属性等同于`Object.getPrototypeOf(obj)`
7Reflect.setPrototypeOf(obj, newProto)
设置对象的\_\_proto\_\_属性。注意Object对象没有对应这个方法的方法。
8Reflect.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
// 失败时抛出错误

View File

@ -61,6 +61,10 @@
- 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详解
- 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

View File

@ -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没有副作用。
```javascript
"use strict";
if(true) {
@ -19,7 +18,6 @@ if(true) {
for (let i = 0; i < 10; i++) {
console.log(i);
}
```
上面代码如果用var替代let实际上就声明了一个全局变量这显然不是本意。变量应该只在其声明的代码块内有效var命令做不到这一点。
@ -27,14 +25,12 @@ for (let i = 0; i < 10; i++) {
var命令存在变量提升效用let命令没有这个问题。
```javascript
"use strict";
if(true) {
console.log(x); // ReferenceError
let x = 'hello';
}
```
上面代码如果使用var替代letconsole.log那一行就不会报错而是会输出undefined因为变量声明提升到代码块的头部。这违反了变量先声明后使用的原则。
@ -46,7 +42,6 @@ if(true) {
在let和const之间建议优先使用const尤其是在全局环境不应该设置变量只应设置常量。这符合函数式编程思想有利于将来的分布式运算。
```javascript
// bad
var a = 1, b = 2, c = 3;
@ -57,7 +52,6 @@ const c = 3;
// best
const [a, b, c] = [1, 2, 3];
```
const声明常量还有两个好处一是阅读代码的人立刻会意识到不应该修改这个值二是防止了无意间修改变量值所导致的错误。
@ -75,7 +69,6 @@ V8引擎只在严格模式之下支持let和const。结合前两点这实
静态字符串一律使用单引号或反引号,不使用双引号。动态字符串使用反引号。
```javascript
// bad
const a = "foobar";
const b = 'foo' + a + 'bar';
@ -87,7 +80,56 @@ const c = `foobar`;
const a = 'foobar';
const b = `foo${a}bar`;
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
// bad
const a = { k1: v1, k2: v2, };
const b = {
@ -109,13 +150,11 @@ const b = {
k1: v1,
k2: v2,
};
```
对象尽量静态化一旦定义就不得随意添加新的属性。如果添加属性不可避免要使用Object.assign方法。
```javascript
// bad
const a = {};
a.x = 3;
@ -127,27 +166,108 @@ Object.assign(a, { x: 3 });
// good
const a = { x: null };
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
// bad
arr.reduce(function(x, y) { return x + y; }, 0);
// good
arr.reduce((x, y) => x + y, 0);
(() => {
console.log('Welcome to the Internet.');
})();
```
箭头函数取代Function.prototype.bind不应再用 self / _this / that 绑定 this。
那些需要使用函数表达式的场合尽量用箭头函数代替。因为这样更简洁而且绑定了this。
```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
const self = this;
const boundMethod = function(...params) {
@ -159,13 +279,11 @@ const boundMethod = method.bind(this);
// best
const boundMethod = (...params) => method.apply(this, params);
```
所有配置项都应该集中在一个对象,放在最后一个参数,布尔值不可以直接作为参数。
```bash
// bad
function divide(a, b, option = false ) {
}
@ -173,7 +291,35 @@ function divide(a, b, option = false ) {
// good
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结构
@ -181,7 +327,6 @@ function divide(a, b, { option = false } = {}) {
注意区分Object和Map只有模拟实体对象时才使用Object。如果只是需要key:value的数据结构使用Map。因为Map有内建的遍历机制。
```javascript
let map = new Map(arr);
for (let key of map.keys()) {
@ -195,12 +340,60 @@ for (let value of map.values()) {
for (let item of map.entries()) {
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
// bad
@ -215,7 +408,6 @@ import { func1, func2 } from 'moduleA';
使用export取代module.exports。
```javascript
// commonJS的写法
var React = require('react');
@ -237,5 +429,34 @@ const Breadcrumbs = React.createClass({
});
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;
```