ES6带来了Javascript
新的变化,新的语法特性,使得之以前版本中需要特性方式才能解决的问题一些问题得到了简化和支持,很大程度上提高了开发效率,扩展了Javascript
功能呢。
1. let/const
ES6中增加了两个新的变量声明let
和const
。ES6之前Javascript是没有明确的块级作用域,{}
代码块并不能作为一个完整的作用域来使用(当然函数存在其作用域),使用var
声明的变量在外部也可以引用
{
va a = 1;
}
console.log(a); // 1
let
和const
的引入使得我们可以产生局部的作用域
{
let a = 1;
console.log('inner:a=' + a); // inner:a=1
}
console.log('outer:a=' + a); // Reference Error
let
和const
存在以下的特性
- 必须要先声明后使用,Javascript不会将
let
/const
声明的变量进行提升,因此也不能使用typeof
的值是否是undefined
来判断变量是否已经声明。- 在作用域范围内,不能重复声明相同的变量名
// 变量声明无法提前
{
console.log(a); // ReferenceError
// typeof a === 'undefined' // ReferenceError
let a = 1;
}
// 同名变量只能声明一次
{
let a = 1;
let a = 2; // SyntaxError: Identifier 'a' has already been declared
}
let
和const
声明的区别在于,const
通常选择对常量进行修饰,其自带类似Java
中final
的效果,一旦声明以后变量的引用不能发生变化,变量引用的内容本身可以发生变化
// 声明一个数字字面量
const a = 1;
a = 2; // TypeError: Assignment to constant variable.
// 声明一个对象的引用
const a = {};
a.a = 1; // 可以正常运行
2. 分离/剩余操作符
ES6引入了分离/剩余...
,它有两个用途:
- 在对变量赋值的时候可以用来对
Javascript
中iterable
的对象或者iterator
进行展开- 在方法声明中变量中使用,可以用来接受剩余参数
// 用于展开
var a = 'abcdef';
var b = [...a];
console.log(b); // [a,b,c,d,e,f]
// 可以使用展开效果替代数组的concat
var arr1 = [1,2,3];
var arr2 = [4,5,6];
var res = [...arr1, ...arr2]; // [1,2,3,4,5,6];
// 用与函数接受剩余参数
function f(a, ...b) {
console.log(a);
console.log(b);
}
f(1,2,3,4,5); // 1, [2,3,4,5]
3. 函数默认值
ES6中函数声明时可以给形参添加默认值,以替代在函数体中对参数进行默认值设定的方式
// ES6之前
function f(a) {
var tempA = a || 'test'; // 设置默认值
console.log('a:'+a);
}
f(); // a:test
// ES6
function f(a='test'){
console.log('a:'+a);
}
f(); // a: test
默认值的设置除了使用字面量,也可以使用表达式,但是使用表达式需要注意表达式中变量的作用域和顺序
function f(a=b, b=1) {
console.log(a,b);
}
f(); // ReferenceError: b is not defined
// 另一种情况
var b = 2;
function f(a=b, b=1) { // 因为形参中存在b,所以这里的a不能正确赋值
console.log(a,b);
}
f(); // ReferenceError: b is not defined
使用展开/剩余操作符的形参无法使用这种方式设置默认值
function f(...a = [1,2,3]) {} // SyntaxError: Unexpected token =
4. 对象解构
ES6中的数组和对象,增加了解构的方式来对其进行拆分,将对象或数组的值赋值给某个变量,省略掉中间变量,简化取值写法
// 对数组解构
var [a,b] = [1,2,3,4];
console.log(a); // 1
console.log(b); // 2
// 对对象解构
var {x: vx, y: vy} = {x:1, y: 2};
console.log(vx); // 1;
console.log(vy); // 2;
console.log(x); // ReferenceError
console.log(y); // ReferenceError
其中对于对象的解构需要注意,其中x: vx
的意思是,声明一个变量vx
,并将对象中x
的值赋值给变量vx
,也就是说:
左侧是指向对象中的属性,:
右侧是声明的新的变量,即我们希望赋予的结果,也就是source: target
结构
当然如果对象中属新名和我们希望声明的变量名一致,我们可以使用简写
var {x, y} = {x: 1, y: 2}; // 相当于{x: x, y: y} = {x: 1, y: 2};
解构对象如果不存在某属性,那么赋值变量的值就为undefined
var {x, y, z} = {x: 1, y: 2};
console.log(z); // undefined
解构的过程可以使用剩余表达式,也可以设置默认值
// 可以和剩余表达式结合
var [a,...b] = [1,2,3,4];
console.log(a); // 1
console.log(b); // [2,3,4];
// 设置解构默认值
var {x , y: y=2} = {x: 1}; // 可以简写为 var {x, y=2} = {x:1}
console.log(y); // 2
// 省略中间变量交换变量的值
var x = 1;
var y = 2;
[x, y] = [y, x];
console.log(x); // 2
console.log(y); // 1
5. 字面量简写
ES6中创建对象的时候,可以使用属性和方法有一种简写的方式
var x = 1;
// ES6之前写法
var o = {
x: x,
f: function() {}
}
// ES6提供简写
var o = {
x, // 相当于 x: x
f() {} // 将:function直接省略
};
6. 计算属性名
ES6之前如果属性名是一个变量,我们很难通过一般的手段给对象增加变量属性(应该只能通过Object.defineProperty定义属性),ES6中增加了[]
的语法来满足需求
var x = 'vx';
var o = {
[x]: 1,
[x+ 'f']() {}
}
console.log(o); // {vx: 1, vxf: function}
7. 原型对象关联
ES6新增Object.setPrototypeOf(target, source)
方法来进行对象的原型对象__proto__
的关联操作
// ES6之前
var o1 = {};
var o2 = {
__proto__: o1
}
// ES6
Object.setPrototypeOf(o2, o1);
如果对象中使用了方法简写声明,可以使用super
参数来获取该对象的原型对象
var o1 = {
f() { console.log('o1'); }
}
var o2 = {
f() {
super.f();
console.log('o2');
}
}
Object.setPrototypeOf(o2, o1);
o2.f(); // o1, o2
8. 模板语法
ES6使用模板语法,可以替代字符串拼接的方式来构建含有变量的字符串。模板语法使用了反引号作为标志,使用${}
的插值方式获取变量引用
// ES6之前只能使用+号进行字符串拼接
var name = 'patrick';
console.log('Hi, ' + name + ' welcome!');
// ES6之后使用模板语法
console.log(`Hi, ${name} welcome!`);
同时相比于字符串拼接,模板语法可以使用多行的写法,而字符串拼接不可以
var name = 'patrick';
console.log(`Hi, ${name}
welcome`);
模板语法除此以外还有一个比较有意思的用途,我们可以定义一个特殊的函数,来获取模板语法中的不可变部分和插值部分
var name = 'patrick';
function f(strings, ...values){
console.log(strings);
console.log(values);
}
f`Hi, ${name} welcome` // ["Hi, ", " welcome", raw: Array(2)], ['patrick']
其中strings
中存在一个属性raw
,其表示的是原本的字符串内容,相当于String.raw
的结果
console.log('Hello, \r\n patrick welcome');
console.log(String.raw`Hello, \r\n patrick welcome`);
9. 箭头函数
ES6的箭头函数简化了匿名函数的写法,箭头函数由三部分组成:参数,箭头,函数体。在函数体部分,如果不使用{}
则会将表达式结果作为匿名函数返回值,使用{}
可以支持多行语句
function f(callback) {
var v = callback();
console.log('v:' + v);
}
f(() => 1);
f(() => {
console.log('callback');
return 1;
})
如果在对象方法属性中使用了箭头函数,且在箭头函数中使用了this
关键字,则需要注意this
关键字的作用域上下文。
var a = 'outer';
var o = {
a: 'inner',
f: () => {
console.log(this.a);
}
}
o.f(); // outer
o.f.apply({a: 'apply'}); // outer
10. for of 循环
ES6中增加了for of
循环来对所有的iteratable
对象(对象中有[Symbol.iterator]
属性)进行遍历,其中内置对象中String
, Array
, Generator
,Collections/TypedArray
已经实现了[Symbol.iterator]
属性,可直接遍历
for(let s of 'abc') {
console.log(s); // a b c
}
11. 正则表达式
ES6中增加了两个新的正则表达式标志符,使用u
来扩展字符串中unicode
字符的匹配,使用y
来扩展处理匹配位置(操作reg.lastIndex
来实现),并提供了reg.flags
来获取正则表达式使用到的修饰符。
12. 数字字面量
ES6中对八进制的数字字面量提供了0o
的方式,替代了以前八进制的写法0
console.log(0o17); // 15
13. Unicode处理
ES6中对于BMP(0x0000 -> 0xFFFF)以外的Unicode提供了新的支持方式\u{}
,并在对含有unicode字符串进行长度判断时,要注意unicode字符所占有长度可能会带来的问题
14. Symbol
ES6新的基本类型symbol
,可以用来定义常量取代代码中的Magic Number。通过Symbol(str)
创建,也可以通过Symbol.for(str)
创建,两者区别在于后者会去查找是否已经使用Symbol.for
创建相同key
的symbol对象
,已经创建则返回改symbol
,否则创建新的symbol
对象
var s1 = Symbol('key');
var s2 = Symbol('key');
var s3 = Symbol.for('key');
var s4 = Symbol.for('key');
console.log(s1 === s2); // false;
console.log(s1 === s3); // false
console.log(s3 === s4); // true
可以通过Symbol.keyFor()
的方法获取到使用Symbol.for
创建的Symbol
所使用的字符串key
var key = Symbol.keyFor(Symbol.for('my key'));
console.log(key); // my key
参考:
《你不知道的Javascript(ES6&Beyond)》