- 原文作者:Addy Osmani
- 原文链接: es6-equivalents-in-es5
- 译者:snow
- 喜欢理由:帮助我们更好的了解 ES6 语法
箭头函数
与函数表达式相比,箭头函数表达式(也称为胖箭头函数)的语法更简介,并且不会创建自己的
this
。
箭头函数相当于匿名函数。
ES6:
[1, 2, 3].map(n => n * 2);
// -> [ 2, 4, 6 ]
ES5 实现:
[1, 2, 3].map(function(n) { return n * 2; }, this);
// -> [ 2, 4, 6 ]
ES6:
var evens = [2, 4, 6, 8, 10];
// 表达式正文
var odds = evens.map(v => v + 1);
var nums = evens.map((v, i) => v + i);
console.log(odds);
// -> [3, 5, 7, 9, 11]
console.log(nums);
// -> [2, 5, 8, 11, 14]
// 声明式正文
var fives = [];
nums = [1, 2, 5, 15, 25, 32];
nums.forEach(v => {
if (v % 5 === 0)
fives.push(v);
});
console.log(fives);
// -> [5, 15, 25]
// 作用域中的 this
var bob = {
_name: 'Bob',
_friends: [],
printFriends() {
this._friends.forEach(f =>
console.log(this._name + ' knows ' + f));
}
}
ES5:
'use strict';
var evens = [2, 4, 6, 8, 10];
// 表达式正文
var odds = evens.map(function (v) {
return v + 1;
}, this);
var nums = evens.map(function (v, i) {
return v + i;
}, this);
console.log(odds);
// -> [3, 5, 7, 9, 11]
console.log(nums);
// -> [2, 5, 8, 11, 14]
var fives = [];
nums = [1, 2, 5, 15, 25, 32];
// 声明式正文
nums.forEach(function (v) {
if (v % 5 === 0) {
fives.push(v);
}
}, this);
console.log(fives);
// -> [5, 15, 25]
// Lexical this
var bob = {
_name: 'Bob',
_friends: [],
printFriends: function printFriends() {
this._friends.forEach(function (f) {
return console.log(this._name + ' knows ' + f);
}, this);
}
};
块级作用域函数
块作用域绑定提供了函数和顶级作用域以外的作用域。
这确保你的变量不会超出他们定义的范围内。
ES6:
// let 声明一个局部块作用域,在 ES6 中可以任意的初始化一个值
'use strict';
var a = 5;
var b = 10;
if (a === 5) {
let a = 4; // 作用域在 if 块中
var b = 1; // 作用域在函数内部
console.log(a); // 4
console.log(b); // 1
}
console.log(a); // 5
console.log(b); // 1
ES5:
'use strict';
var a = 5;
var b = 10;
if (a === 5) {
// 在实现上更像下面这样
(function () {
var a = 4;
b = 1;
console.log(a); // 4
console.log(b); // 1
})();
}
console.log(a); // 5
console.log(b); // 1
ES6:
// const 在 ES6 中创建只读的属性常量
'use strict';
// 将 favorite 定义为常量并且赋值为 7
const favorite = 7;
// 试图覆盖常量
try {
favorite = 15;
} catch (err) {
console.log('my favorite number is still: ' + favorite);
// 仍然会输出 7
}
ES5:
'use strict';
// 将 favorite 定义为一个不可写的“常量”,并将其赋值为 7。
Object.defineProperties(window, {
favorite: {
value: 7,
enumerable: true
}
});
// 属性描述默认为 false,并且 const 是可枚举的
var favorite = 7;
// 试图覆盖常量
favorite = 15;
// 仍然会输出 7
console.log('my favorite number is still: ' + favorite);
模版字符串
ES6 模版字符串是可以包含嵌入表达式的字符串,有时也被叫做插值表达式。
ES6:
// 表达式占位符的基本用法
var person = 'Addy Osmani';
console.log(`Yo! My name is ${person}!`);
// 表达式也可以用在对象中
var user = {name: 'Caitlin Potter'};
console.log(`Thanks for getting this into V8, ${user.name}.`);
// 插值表达式:作用之一可以用来计算
var a = 50;
var b = 100;
console.log(`The number of JS frameworks is ${a + b} and not ${2 * a + b}.`);
// 多行字符串不需要换行符
console.log(`string text line 1
string text line 2`);
// 函数内部表达式
function fn() { return 'result'; }
console.log(`foo ${fn()} bar`);
ES5:
'use strict';
// 表达式占位符的基本用法
var person = 'Addy Osmani';
console.log('Yo! My name is ' + person + '!');
// 表达式也可以用在对象中
var user = { name: 'Caitlin Potter' };
console.log('Thanks for getting this into V8, ' + user.name + '.');
// 插值表达式:作用之一可以用来计算
var a = 50;
var b = 100;
console.log('The number of JS frameworks is ' + (a + b) + ' and not ' + (2 * a + b) + '.');
// 多行字符串
console.log('string text line 1\nstring text line 2');
// 或者下面这种写法
console.log('string text line 1\n\
string text line 2');
// 函数内部表达式
function fn() {
return 'result';
}
console.log('foo ' + fn() + ' bar');
计算属性
计算属性名允许你基于表达式在对象文本中指定属性
ES6:
var prefix = 'foo';
var myObject = {
[prefix + 'bar']: 'hello',
[prefix + 'baz']: 'world'
};
console.log(myObject['foobar']);
// -> hello
console.log(myObject['foobaz']);
// -> world
ES5:
'use strict';
var prefix = 'foo';
var myObject = {};
myObject[prefix + 'bar'] = 'hello';
myObject[prefix + 'baz'] = 'world';
console.log(myObject['foobar']);
// -> hello
console.log(myObject['foobaz']);
// -> world
解构赋值
解构赋值语法是一个
JavaScript
表达式,它可以使用数组映射和对象文本构造的语法从数组和对象中提取值,对变量进行赋值。
ES6:
var {foo, bar} = {foo: 'lorem', bar: 'ipsum'};
// foo => lorem and bar => ipsum
ES5:
'use strict';
var _ref = { foo: 'lorem', bar: 'ipsum' };
// foo => lorem and bar => ipsum
var foo = _ref.foo;
var bar = _ref.bar;
ES3:
with({foo: 'lorem', bar: 'ipsum'}) {
// foo => lorem and bar => ipsum
}
ES6:
var [a, , b] = [1,2,3];
ES6 (shimming using Symbol.iterator
):
'use strict';
var _slicedToArray = function (arr, i) {
if (Array.isArray(arr)) {
return arr;
} else {
var _arr = [];
for (var _iterator = arr[Symbol.iterator](), _step; !(_step = _iterator.next()).done;) {
_arr.push(_step.value);
if (i && _arr.length === i) {
break;
}
}
return _arr;
}
};
var _ref = [1, 2, 3];
var _ref2 = _slicedToArray(_ref, 3);
var a = _ref2[0];
var b = _ref2[2];
ES5:
String.prototype.asNamedList = function () {
return this.split(/\s*,\s*/).map(function (name, i) {
return name ? ('var ' + name + '=slice(' + i + ', ' + (i + 1) + ')[0]') : '';
}).join(';');
};
with([1,2,3]) {
eval('a, , b'.asNamedList());
}
默认参数
默认参数允许函数具有可选参数,而不需要检查参数的长度或是否未定义。
ES6:
function greet(msg='hello', name='world') {
console.log(msg,name);
}
greet();
// -> hello world
greet('hey');
// -> hey world
ES5:
'use strict';
function greet() {
// 如果像这样访问 arguments[0],则可以简单地进行访问 msg 变量名
var msg = arguments[0] === undefined ? 'hello' : arguments[0];
var name = arguments[1] === undefined ? 'world' : arguments[1];
console.log(msg, name);
}
function greet(msg, name) {
(msg === undefined) && (msg = 'hello');
(name === undefined) && (name = 'world');
console.log(msg,name);
}
// 对未定义的参数进行检查的基本方法
function greet(msg, name) {
console.log(
defaults(msg, 'hello'),
defaults(name, 'world')
);
}
greet();
// -> hello world
greet('hey');
// -> hey world
ES6:
function f(x, y=12) {
// y 的指是 12 如果没有接收(或者接收的是 undefined )
return x + y;
}
f(3) === 15;
ES5:
'use strict';
function f(x, y) {
if (y === undefined) {
y = 12;
}
return x + y;
}
f(3) === 15;
Iterators 和 For-Of 循环
遍历器是可以遍历容器的对象。这是一种使类工作在
for..of
循环的有用方法。
接口类似于遍历器接口。
迭代一个for..of
循环的形式如下。
ES6:
// 当前环境,将从数组中获取一个遍历器,并对其进行循环,从中获取值
for (let element of [1, 2, 3]) {
console.log(element);
}
// => 1 2 3
ES6 (without using for-of
, if Symbol
is supported):
'use strict';
for (var _iterator = [1, 2, 3][Symbol.iterator](), _step; !(_step = _iterator.next()).done;) {
var element = _step.value;
console.log(element);
}
// => 1 2 3
ES5 (approximates):
// 使用 forEach()
// 不需要在包含的范围中声明索引和元素变量。它们被作为参数提供给遍历器,并被限定在遍历的范围内。
var a = [1,2,3];
a.forEach(function (element) {
console.log(element);
});
// => 1 2 3
// 使用 for 循环
var a = [1,2,3];
for (var i = 0; i < a.length; ++i) {
console.log(a[i]);
}
// => 1 2 3
注意
Symbol
的使用。ES5 需要一个正确的Symbol polyfill
才能正常使用。
Class
class
实现了 ES6 规范草案中描述的类语法和语义。
class
是复用代码最好的方法。
一些 JS 库提供了类和继承,但它们并不相互兼容。
ES6:
class Hello {
constructor(name) {
this.name = name;
}
hello() {
return 'Hello ' + this.name + '!';
}
static sayHelloAll() {
return 'Hello everyone!';
}
}
class HelloWorld extends Hello {
constructor() {
super('World');
}
echo() {
alert(super.hello());
}
}
var hw = new HelloWorld();
hw.echo();
alert(Hello.sayHelloAll());
ES5 ( 类似功能 ):
function Hello(name) {
this.name = name;
}
Hello.prototype.hello = function hello() {
return 'Hello ' + this.name + '!';
};
Hello.sayHelloAll = function () {
return 'Hello everyone!';
};
function HelloWorld() {
Hello.call(this, 'World');
}
HelloWorld.prototype = Object.create(Hello.prototype);
HelloWorld.prototype.constructor = HelloWorld;
HelloWorld.sayHelloAll = Hello.sayHelloAll;
HelloWorld.prototype.echo = function echo() {
alert(Hello.prototype.hello.call(this));
};
var hw = new HelloWorld();
hw.echo();
alert(Hello.sayHelloAll());
更详细的介绍可以查看 Babel
Modules
模块功能大部分是实现了,一些加载api仍然在改进中。
模块试图解决依赖关系和部署中的许多问题,允许用户使用显式导出创建模块,从这些模块中导入特定的导出名称,并保持这些名称的独立性。
app.js - ES6
import math from 'lib/math';
console.log('2π = ' + math.sum(math.pi, math.pi));
app.js - ES5
var math = require('lib/math');
console.log('2π = ' + math.sum(math.pi, math.pi));
lib/math.js - ES6
export function sum(x, y) {
return x + y;
}
export var pi = 3.141593;
lib/math.js - ES5
exports.sum = sum;
function sum(x, y) {
return x + y;
}
var pi = exports.pi = 3.141593;
lib/mathplusplus.js - ES6
export * from 'lib/math';
export var e = 2.71828182846;
export default function(x) {
return Math.exp(x);
}
lib/mathplusplus.js - ES5
var Math = require('lib/math');
var _extends = function (target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i];
for (var key in source) {
target[key] = source[key];
}
}
return target;
};
var e = exports.e = 2.71828182846;
exports['default'] = function (x) {
return Math.exp(x);
};
module.exports = _extends(exports['default'], exports);
数字字面量
ES6:
var binary = [
0b0,
0b1,
0b11
];
console.assert(binary === [0, 1, 3]);
var octal = [
0o0,
0o1,
0o10,
0o77
];
console.assert(octal === [0, 1, 8, 63]);
ES5:
'use strict';
var binary = [0, 1, 3];
console.assert(binary === [0, 1, 3]);
var octal = [0, 1, 8, 63];
console.assert(octal === [0, 1, 8, 63]);
属性赋值方法
对象中支持方法语法, 比如说
toString()
ES6:
var object = {
value: 42,
toString() {
return this.value;
}
};
console.log(object.toString() === 42);
// -> true
ES5:
'use strict';
var object = {
value: 42,
toString: function toString() {
return this.value;
}
};
console.log(object.toString() === 42);
// -> true
对象属性的简介表示
在对象中的属性名和属性值相同时可以忽略属性值。
ES6:
function getPoint() {
var x = 1;
var y = 10;
return {x, y};
}
console.log(getPoint() === {
x: 1,
y: 10
});
ES5:
'use strict';
function getPoint() {
var x = 1;
var y = 10;
return { x: x, y: y };
}
console.log(getPoint() === {
x: 1,
y: 10
});
Rest 参数
rest
参数允许函数在不使用arguments
对象的情况下具有可变数量的参数。
rest
参数是数组的一个实例,因此所有的数组方法都可以使用。
ES6:
function f(x, ...y) {
// y 是个数组
return x * y.length;
}
console.log(f(3, 'hello', true) === 6);
// -> true
ES5:
'use strict';
function f(x) {
var y = [];
y.push.apply(y, arguments) && y.shift();
// y 是个数组
return x * y.length;
}
console.log(f(3, 'hello', true) === 6);
// -> true
扩展运算符
扩展运算符是和
rest
参数相反的。
它允许将数组展开为多个形式的参数。
ES6:
function add(a, b) {
return a + b;
}
let nums = [5, 4];
console.log(add(...nums));
ES5:
'use strict';
var _toArray = function (arr) {
return Array.isArray(arr) ? arr : [].slice.call(arr);
};
function add(a, b) {
return a + b;
}
var nums = [5, 4];
console.log(add.apply(null, _toArray(nums)));
ES6:
function f(x, y, z) {
return x + y + z;
}
// 传递数组的每一个参数
f(...[1,2,3]) === 6;
ES5:
'use strict';
function f(x, y, z) {
return x + y + z;
}
// 传递数组的每一个参数
f.apply(null, [1, 2, 3]) === 6;
Proxy
ES6:
var target = function () {
return 'I am the target';
};
var handler = {
apply: function (receiver, ...args) {
return 'I am the proxy';
}
};
var p = new Proxy(target, handler);
console.log(p() === 'I am the proxy');
// -> true
ES5:
在 ES5 中没有
proxy
, 没有类似的方法去拦截。
类数组
Array.from 使有着单一的参数类数组或者列表(像:
arguments
,NodeList
,DOMTokenList
(当做classList
),NamedNodeMap
(属性使用))返回一个新的数组实例。
ES6:
var listFriends = function() {
var friends = Array.from(arguments);
friends.forEach(friend => {
console.log(friend);
});
};
listFriends('ann', 'bob');
// -> 'ann'
// -> 'bob'
var divs = document.querySelectorAll('div');
Array.from(divs).forEach(node => {
console.log(node);
});
// -> ...
// -> ...
ES5:
var listFriends = function() {
var friends = [].slice.call(arguments)
friends.forEach(function(friend) {
console.log(friend);
});
};
listFriends('ann', 'bob');
// -> 'ann'
// -> 'bob'
var divsArray = [].slice.call(document.querySelectorAll('div'));
divsArray.forEach(function(node) {
console.log(node);
});
// -> ...
// -> ...
参考
- Babel
- JS Rocks
- ES6 Features
- ES6 Feature Proposals
- ECMAScript 6 support in Mozilla
重点
如果有错误或者错别字,还请给我留言指出,谢谢。
我们下期见。