浅谈ES6

Posted by franki on April 3, 2018

es6


ES6出现了有一段时间,有点属于老生常谈了,毕竟ES7的提案都在成为标准,今天好好谈谈ES6给学习、工作带来的便利!

模板字符串(Template Literals)


模板字符串的出现,比以前的字符串拼接来的简单直接。可以用${name}插入变量。具体表现如下:

var fName = "jogh"; var sName = "bob";

es5:
var a = "my full name is" + fName + ' ' + sName;

es6:
var b = `my full name is ${fName} ${sName}`;

块作用域语法(Syntax Block scoping)


JavaScript 一直被作用域所限制,很多时候我们会把整个 JavaScript 文件包裹在一个空的的立即调用函数表达式(IIFE)。这样做的原因是因为隔离文件中的所有变量,避免变量污染,防止引起变量冲突。

如今,我们有块作用域和两个绑定到块的新变量声明。

let 声明

var 类似,也存在一些差异。由于它是块级作用域,可以在不影响外部变量的情况下声明相同名称的变量。

var name = "car";
{
    let name = "toyota";
    console.log(name); // toyata
}
console.log(name); // car

以上还解决了一个经典的面试题:“输出什么,如何让它按照你的期望运行?”

for (var i = 1; i < 5; i++) {
    setTimeout(() => {console.log(i);}, 1000);
}

在这种情况下会输出“5 5 5 5”,因为变量 i 在每次迭代都会改变。

如果将 var 换成 let ,则所有的内容都会更改。现在,每个循环都会创建一个新的块级作用域,其值为绑定到该循环的值。就好比:

{let i = 1; setTimeout(() => {console.log(i);}, 1000)}
{let i = 2; setTimeout(() => {console.log(i);}, 1000)}
{let i = 3; setTimeout(() => {console.log(i);}, 1000)}
{let i = 4; setTimeout(() => {console.log(i);}, 1000)}

varlet 还有一个区别就是,let 不存在变量提升。

{
    console.log(a); // undefined
    console.log(b); // ReferenceError
    var a = "5";
    let b = "ss";
}

建议使用 let 来替代 var,除非你有特别的需要来提升变量。

const 声明

es5的时候我们要创建一个变量,经常用大写的形式来声明为一个常量,告诉开发者不要随意更改。现在我们有了更好的声明方式,就是用 const 去定义。

{
    const name = "car";
    console.log(name); // car
    name = "toyota"; // TypeError
}

const 并不会让变量不可变,只不过是会锁定其值。如果有复杂的赋值(对象或数组),那么该值仍然可以修改的。

{
    const name = [1, 2, 3, 4];
    const foo = {name: 'franki', age: '20'};
    name.push(5);
    foo.job = "web developer";
    console.log(name); // [1, 2, 3, 4, 5]
    console.log(foo); // {name: 'franki', age: '20', job: 'web developer'}
}

块作用域中函数的问题

函数声明被绑定到块级作用域。

{
    baz();
    function baz() {// do something}
}
baz(); // 报错

特别你在 if 声明函数,问题就来了

if (something) {
    function baz () {//do something}
} else {
    function baz () {// do another thing}
}
baz();

es5语法时无论 something 是什么,都会执行后面的 baz 声明的函数,因为存在变量提升。但是 es6baz 收到了快级作用域的限制。所以会报引用错误(ReferenceError)。

扩展运算符(Spread)


es6 引入了 ... 操作符,主要用途是:将数组或对象传递到新的数据或新对象中,并将多个参数合并到一个数组中。

最常见到的场景:

let a = [5, 6, 7];
let b = [1, 2, 3, 4, ...a];
console.log(b); // [1, 2, 3, 4, 5, 6, 7]

将数组里面的元素作为一组变量传递给函数时非常有用

function foo (a, b, c) {console.log(a, b, c);}
let data = [1, 2, 3];
foo(...data); // 1, 2, 3

还可以将对象扩展,将每个键值导入到新的对象中。

let a = {name: 'franki'};
let b = {age: '20', ...a};
console.log(b); // {age: '20', name: 'franki'}

扩展运算符的作用是创建新的对象或数组,下面的例子 b 指向同一个数组

let a = [1, 2, 3];
let b = [...a];
let c = a;
b.push(4);
console.log(a); // [1, 2, 3]
console.log(b); // [1, 2, 3, 4] 引用不同的数组
c.push(5);
console.log(c, a); // [1, 2, 3, 5] [1, 2, 3, 5] a 和 c 指向同一个地址

当你不知道传递多少个变量到函数里面,把所有的变量收集到数组中非常有效。

function coll (...args) { console.log(args); } // [1, 2, 3]
coll(1, 2, 3);

默认参数


es6 现在函数的的定义可以使用默认参数。缺少或未定义的值将使用默认值进行初始化。需要注意的是 nullfalse 值被强制为 0

function foo (a = 5, b = 10) {console.log(a+b);}
foo(); // 15
foo(7, 12); // 19
foo(undefined, 8); // 13
foo(false); // 10 false is coerced to 0

默认值不仅仅可以是值,还可以是表达式或者函数

function foo (a) { return a*4; }
function bar (x = 2, y = x + 4, z = foo(x)) {
    console.log([x, y, z]);
}
bar(); // [2, 6, 8]
bar(1,2,3); // [1,2,3]
bar(10,undefined,3); // [10, 14, 3]

解构(Destructuring)


解构是拆分等号左侧的数组或对象的过程。数组或对象可以来自变量,函数或等式。

let [ a, b, c ] = [ 6, 2, 9];
console.log(`a=${a}, b=${b}, c=${c}`); //a=6, b=2, c=9
function foo() { return ['car', 'dog', 6 ]; }
let [ x, y, z ] = foo();
console.log(`x=${x}, y=${y}, z=${z}`);  // x=car, y=dog, z=6

通过对象解构,可以在大括号内列出对象的键以提取该键值对。

function bar() { return {a: 1, b: 2, c: 3}; }
let { a, c } = bar();
console.log(a); // 1
console.log(c); // 3
console.log(b); // undefined

有时,你想提取值,但将它们分配给一个新的变量。这是通过在等号左边的 ‘key: variable’ 配对完成的。

function baz() {
    return {
        x: 'car',
        y: 'London',
        z: { name: 'John', age: 21}
    };
}
let { x: vehicle, y: city, z: { name: driver } } = baz();
console.log(
    `I'm going to ${city} with ${driver} in their ${vehicle}.`
); // I'm going to London with John in their car.

另外需要特别注意的是,对象解构允许为多个变量赋值。

let { x: first, x: second } = { x: 4 };
console.log( first, second ); // 4, 4

对象字面量和简写模式


当你从变量创建对象字面量时,ES6 允许你在与变量名称相同的情况下省略该 key 。

let a = 4, b = 7;
let c = { a: a, b: b };
let concise = { a, b };
console.log(c, concise) // {a: 4, b: 7}, {a: 4, b: 7}

动态属性名称

ES6 添加了使用动态分配的 key 来创建或添加属性的功能。

let  city= 'sheffield_';
let a = {
    [ city + 'population' ]: 350000
};
a[ city + 'county' ] = 'South Yorkshire';
console.log(a); // {sheffield_population: 350000, sheffield_county: 'South Yorkshire' }

箭头函数(Arrow Functions)


箭头函数有两个主要方面:解构和 this 绑定。

它们可以具有比传统函数更简单的结构,因为它们不需要 function关键字,并且它们会自动返回箭头之后的任何内容。

var foo = function( a, b ) {
    return a * b;
}
let bar = ( a, b ) => a * b;

对于箭头函数最有用的地方之一是在 .map,.forEach 或 .sort 之类的数组函数中。

let arr = [ 5, 6, 7, 8, 'a' ];
let b = arr.map( item => item + 3 );
console.log(b); // [ 8, 9, 10, 11, 'a3' ]