资讯专栏INFORMATION COLUMN

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

jsliang / 3338人阅读

摘要:对象解构对象解构语法在赋值语句的左侧使用了对象字面量,例如代码中,的值被存储到本地变量中,的值则存储到变量中。当使用解构赋值语句时,如果所指定的本地变量在对象中没有找到同名属性,那么该变量会被赋值为。

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

块级作用域

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

块级声明

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

在一个函数内部

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

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等,因篇幅原因,这些可能留到以后再写。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/87176.html

相关文章

  • [译] 前端攻略-从路人甲到英雄无敌二:JavaScript 与不断演化的框架

    摘要:一般来说,声明式编程关注于发生了啥,而命令式则同时关注与咋发生的。声明式编程可以较好地解决这个问题,刚才提到的比较麻烦的元素选择这个动作可以交托给框架或者库区处理,这样就能让开发者专注于发生了啥,这里推荐一波与。 本文翻译自FreeCodeCamp的from-zero-to-front-end-hero-part。 继续译者的废话,这篇文章是前端攻略-从路人甲到英雄无敌的下半部分,在...

    roadtogeek 评论0 收藏0
  • 你该知道的ES6那些事儿

    摘要:最近重构了一个项目,引入了部分用法,最大的感受是让这门语言变得更加严谨,更加方便。通过该方法获得位置后还得比较一次才能判断是否存在。再来看看的写法使用数组来初始化一个,构造器能确保不重复地使用这些值。下面提供链接,供有兴趣的朋友参考。 最近重构了一个SPA项目,引入了部分ES6用法,最大的感受是ES6让javascript这门语言变得更加严谨,更加方便。本篇将结合实战经验,对最常用的部...

    CoffeX 评论0 收藏0
  • Koa / Co / Bluebird or Q / Generators / Promises /

    摘要:经常游荡在的我总能发现许多好问题和好答案。尽管网络上有着各式各样的关于该主题的指导,但涉及到在各种情景下的最佳实践,或者较好实践的方面还是不够清晰。我寄希望于针对我这篇裹脚布式问题的回复可以改变这一现状。我感觉因此收益的绝对不止是我一个人。 经常游荡在 SO 的我总能发现许多好问题和好答案。它们的好不仅仅在于知识的价值,更可贵之处在于如何表达:如何提问/如何回答。不久前我在 SF...

    xingpingz 评论0 收藏0
  • ES6 箭头函数(arrow function)

    摘要:因为箭头函数没有构造方法。因为不能一个箭头函数,所以也没必要有了。的值在这个箭头函数的整个生命周期里面都不变。你必须通过命名参数和剩余参数去获取箭头函数的参数。非箭头函数在非严格模式下面可以有重名参数。 例行声明:接下来的文字内容全部来自 Understanding ECMAScript 6,作者是Nicholas C.Zakas,也就是大名鼎鼎的Professional JavaSc...

    Arno 评论0 收藏0
  • ES6-迭代器(iterator)和生成器(generator)- 迭代器和生成器的高级用法

    摘要:在生成器中使用语句生成器也是函数,所以它也可以使用语句。只是由于生成器本身的特性,其内部的的行为会和一般函数有些差别。 前面2篇系列文章讲解了迭代器和生成器的最常用,最基础的用法;这篇来讨论迭代器和生成器的一些稍稍高级一点的用法: 1: 给迭代器的next()方法传参 2: 从迭代器中抛出错误 3: 在生成器中使用return语句 4: 委托生成器(组合生成器或者生成器组合?) 1: ...

    Edison 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<