ES6常用新特性——读《Understanding ECMAScript 6》总结

现在ES6在很多项目中大量使用。最近我也花时间看了一下《Understanding ECMAScript6》的中文电子书。在这里总结了一些在实际开发中常用的新特性。

块级作用域

在ES6之前,JS只有一种变量声明方式——使用 var 关键字声明的变量。这种声明变量的方式,无论其实际声明位置在何处,都会被视为声明于所在函数的顶部(如果声明不在任意函数内,则视为在全局作用域的顶部)。这就是所谓的变量提升 hoisting )。
ES6 引入了块级作用域,让变量的生命周期更加可控。

块级声明

块级声明也就是让所声明的变量在指定块的作用域外无法被访问。块级作用域(又被称为词法作用域)在如下情况被创建:

  1. 在一个函数内部

  2. 在一个代码块(由一对花括号包裹)内部

let声明

let声明会将变量的作用域限制在当前代码块中。由于 let 声明并不会被提升到当前代码块的顶部,因此你需要手动将 let 声明放置到顶部,以便让变量在整个代码块内部可用。例如:

function getValue(condition) {
    if (condition) {
        let value = "blue";
        // 其他代码
        return value;
    } else {
        // value 在此处不可用
        return null;
    }
    // value 在此处不可用
}

注意事项
如果一个标识符已经在代码块内部被定义,那么在此代码块内使用同一个标识符进行 let 声明就会导致抛出错误。例如:

var count = 30;

let count = 40; // 语法错误

另一方面,在嵌套的作用域内使用 let 声明一个同名的新变量,则不会抛出错误,以下代码对此进行了演示:

var count = 30;

// 不会抛出错误
if (condition) {
    let count = 40;
    // 其他代码
}

常量声明

在 ES6 中里也可以使用 const 语法进行声明。使用 const 声明的变量会被认为是常量( constant )。const 用法与 let 类似,但有一个重要的区别,const 声明的变量的值在被设置完成后就不能再被改变。正因为如此,所有的 const 变量都需要在声明时进行初始化

const maxItems = 30; // 有效的常量

const name; // 语法错误:未进行初始化


const minItems = 5;

minItems = 6; //抛出错误

模板字符串

模板字符串(template string)是增强版的字符串,使用反引号( ` )来包裹普通字符串。它可以当作普通字符串使用,也可以用来定义多行字符串,或者在字符串中嵌入变量。

// 普通字符串
let message = `Hello world!`;
console.log(message);  // Hello world!

//在字符串中包含反引号,只需使用反斜杠( \ )转义即可
let message = `\`Hello\` world!`;
console.log(message);  // `Hello` world!

// 多行字符串(只需在想要的位置包含换行即可)
let message = `Multiline
string`;

console.log(message);           // "Multiline
                                //  string"
console.log(message.length);    // 16

//反引号之内的所有空白符都是字符串的一部分,因此需要留意缩进。
let message = `Multiline
               string`;
               
console.log(message);           // "Multiline
                                //                 string"
console.log(message.length);    // 31


// 字符串中嵌入变量
var name = "Bob", time = "today";
console.log(`Hello ${name}, how are you ${time}?`)  // Hello Bob, how are you today?

替换位

模板字符串替换位的标识是 ${} 。大括号内部可以放入任意的JavaScript表达式,比如:变量名、运算、函数调用,以及引用对象属性。

//普通变量名
var name = "Nicholas";
var message = `Hello, ${name}.`;
console.log(message);  // Hello, Nicholas.

//计算
var x = 1;
var y = 2;
console.log(`${x} + ${y} = ${x + y}`)  // 1 + 2 = 3
console.log(`${x} + ${y * 2} = ${x + y * 2}`)  // 1 + 4 = 5

//函数调用
function fn() {
  return "Hello World";
}
console.log(`foo ${fn()} bar`)  // foo Hello World bar

//对象属性
var obj = {x: 1, y: 2};
console.log(`${obj.x + obj.y}`)

函数

函数参数的默认值

在 ES5 或更早的版本中,我们可能会使用下述模式来创建带有参数默认值的函数:

function add(x, y) {
    x = x || 20;
    y = y || 30;
    return x + y;
}

console.log(add()); // 50

这种写法有一个缺点:如果参数x或者y赋值了,但是对应的布尔值为false,则该赋值不起作用。
在这种情况下,更安全的替代方法是使用typeof来检测参数的类型,示例如下:

function add(x, y) {
    x = (typeof x !== "undefined") ? x : 20;
    y = (typeof y !== "undefined") ? x : 30;
    
    //...
}

下面来看看ES6函数参数默认值的写法:

function add(x = 20, y = 30) {
    return x + y;
}

可以看到,ES6 的写法比 ES5 简洁许多,而且非常自然。

rest参数和扩展运算符

关于这两部分内容可以看这里

箭头函数

ES6 最有意思的一个新部分就是箭头函数( arrow function )。箭头函数使用“箭头”(=>)来定义。
先来看看箭头函数与传统的函数写法的区别:

// ES6
var f = () => 5;
// ES5
var f = function () { return 5 };

// ES6
var sum = (num1, num2) => num1 + num2;
// ES5
var sum = function(num1, num2) {
  return num1 + num2;
};

如果箭头函数的代码块部分多于一条语句,就要使用大括号将它们括起来,并且使用return语句返回。

const foo = () => {
   const a = 20;
   const b = 30;
   return a + b;
}

箭头函数的一个用处是简化回调函数。

// ES5
[1,2,3].map(function (x) {
  return x * x;
});

// ES6
[1,2,3].map(x => x * x);

箭头函数可以替换函数表达式,但是不能替换函数声明

在使用箭头函数时要注意如下几点:

  • 不能更改this :this的值在函数内部不能被修改,在函数的整个生命周期内其值会
    保持不变。

  • 没有arguments对象:既然箭头函数没有arguments绑定,你必须依赖于具名参数或
    剩余参数来访问函数的参数。

  • 不能被使用new调用: 箭头函数没有[[Construct]]方法,因此不能被用为构造函
    数,使用new调用箭头函数会抛出错误。

  • 没有原型: 既然不能对箭头函数使用new,那么它也不需要原型,也就是没有
    prototype属性。

对象字面量语法的扩展

属性和方法的简写

// 属性的简写
function f(x, y) {
  return {x, y};
}

// 等同于
function f(x, y) {
  return {x: x, y: y};
}

f(1, 2) // Object {x: 1, y: 2}


// 方法的简写
var o = {
  method() {
    return "Hello!";
  }
};

// 等同于
var o = {
  method: function() {
    return "Hello!";
  }
};

需计算属性名

JavaScript语言定义对象的属性,有两种方法。

var person = {},
    lastName = "last name";

// 方法一    
person["first name"] = "Nicholas"; 
// 方法二  
person[lastName] = "Zakas";

console.log(person["first name"]); // "Nicholas"
console.log(person[lastName]); // "Zakas"

但是,如果使用字面量方式定义对象(使用大括号),在ES5中只能使用方法一定义属性。

var person = {
    "first name": "Nicholas"
};

console.log(person["first name"]); // "Nicholas"

在ES6中,需计算属性名是对象字面量语法的一部分,它用的也是方括号表示法。

var lastName = "last name";

var person = {
    "first name": "Nicholas",
    [lastName]: "Zakas"
};

console.log(person["first name"]); // "Nicholas"
console.log(person[lastName]); // "Zakas"


// 方括号内也可以是表达式
var suffix = " name";

var person = {
    ["first" + suffix]: "Nicholas",
    ["last" + suffix]: "Zakas"
};

console.log(person["first name"]); // "Nicholas"
console.log(person["last name"]); // "Zakas"


// 也可以用来表示方法名
var obj = {
  ['h' + 'ello']() {
    console.log('hi');
  }
};

obj.hello() // hi

解构赋值

解构赋值也是ES6中非常常用的一个特性。
按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring)。

对象解构

对象解构语法在赋值语句的左侧使用了对象字面量,例如:

let node = {
    type: "Identifier",
    name: "foo"
};

let { type, name } = node;

console.log(type); // "Identifier"
console.log(name); // "foo"

代码中,node.type的值被存储到type本地变量中,node.name的值则存储到name变量中。

当使用解构赋值语句时,如果所指定的本地变量在对象中没有找到同名属性,那么该变量会被赋值为undefined。例如:

let node = {
    type: "Identifier",
    name: "foo"
};

let { type, name, value } = node;

console.log(type); // "Identifier"
console.log(name); // "foo"
console.log(value); // undefined

我们可以选择性地定义一个默认值,以便在指定属性不存在时使用该值。就像这样:

let node = {
    type: "Identifier",
    name: "foo"
};

let { type, name, value = true } = node;

console.log(type); // "Identifier"
console.log(name); // "foo"
console.log(value); // true

上面的示例都使用了对象中的属性名作为本地变量的名称。但ES6允许我们在给本地变量赋值时使用一个不同的名称。就像这样:

let node = {
    type: "Identifier",
    name: "foo"
};

let { type: localType, name: localName } = node;

console.log(localType); // "Identifier"
console.log(localName); // "foo"


// 我们也可以给变量别名加默认值
let node = {
    type: "Identifier"
};

let { type: localType, name: localName = "bar" } = node;

console.log(localType); // "Identifier"
console.log(localName); // "bar"

数组结构

数组解构的语法看起来与对象解构非常相似,只是将对象字面量替换成了数组字面量。直接看例子:

let colors = [ "red", "green", "blue" ];

let [ firstColor, secondColor ] = colors;

console.log(firstColor); // "red"
console.log(secondColor); // "green"


// 也可以在解构模式中忽略一些项
let colors = [ "red", "green", "blue" ];

let [ , , thirdColor ] = colors;

console.log(thirdColor); // "blue"


// 也可以添加默认值
let colors = [ "red" ];

let [ firstColor, secondColor = "green" ] = colors;

console.log(firstColor); // "red"
console.log(secondColor); // "green"

字符串结构

字符串也可以进行结构赋值。

const [a, b, c, d, e] = 'hello';

console.log(a) // "h"
console.log(b) // "e"
console.log(c) // "l"
console.log(d) // "l"
console.log(e) // "o"

参数结构

function add([x, y]){
  return x + y;
}

add([1, 2]); // 3


// 参数解构也可以有默认
function move({x = 0, y = 0} = {}) {
  return [x, y];
}

move({x: 3, y: 8}); // [3, 8]
move({x: 3}); // [3, 0]
move({}); // [0, 0]
move(); // [0, 0]

模块

模块功能主要由两个命令构成:exportimportexport命令用于规定模块的对外接口,import命令用于输入其他模块提供的功能。

export 命令

// 导出数据
export var color = "red";
export let name = "Nicholas";
export const magicNumber = 7;

// 导出函数
export function sum(num1, num2) {
    return num1 + num1;
}

// 导出类
export class Rectangle {
    constructor(length, width) {
        this.length = length;
        this.width = width;
    }
}


// export还可以像下面这样写,放在大括号内统一导出
export var color = "red";
export let name = "Nicholas";
export const magicNumber = 7;

export {color, name, magicNumber};


// 重命名导出
function sum(num1, num2) {
    return num1 + num1;
}
export {sum as add} // 这里sum函数被作为add导出

import 命令

// 导入单个
import { color } from "./example.js";

// 导入多个
import { color, name, sum } from "./example.js";

// 重命名导入
import { color as redColor } from "./example.js";

// 整体导入
import * as example from "./example.js";

导出/导入默认值

导出默认值要使用default关键字

// 导出默认值一共有三种写法

// 第一种
export default function(num1, num2) {
    return num1 + num2;
}

// 第二种
function sum(num1, num2) {
    return num1 + num2;
}
export default sum;

// 第三种
function sum(num1, num2) {
    return num1 + num2;
}
export { sum as default };

导入默认值得方式也有所不同

import sum from "./example.js"; // 与前面不同的是,这里没有了大括号。

console.log(sum(1, 2)); // 3

后记

上面只是总结得只是一部分ES6的常用特性,其实还有PromiseClass等,因篇幅原因,这些可能留到以后再写。

    原文作者:funnyF2E
    原文地址: https://segmentfault.com/a/1190000010596969
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞