【自种树自乘凉】ES6 新特征总结

这是我参与8月更文挑战的第4天,活动详情查看:8月更文挑战前端

要知道,作前端开发,ES6 是必学的。这篇文章适合初步入门和复习,结合阮一峰老师的教程可以更深刻的学习 ES6。es6

目录:面试

  1. 块级做用域、块级变量 let、块级常量 const
  2. 箭头函数
  3. 参数处理(默认参数/...)
  4. 模板字面量(模板字符串)
  5. 对象的扩展
  6. 解构赋值
  7. 模块(import/export)
  8. 类(class/extends)
  9. Promise
  10. End 感谢阅读

一. 块级做用域、变量 let、常量 const

  1. 由一对 { } 界定的语句叫作块语句,这在其余编程语言中叫作复合语句

通常理解的块:express

  • 块:由 {} 包括住
  • if(){}:是一个块
  • for(){}:是一个块

这些也可理解成块:编程

  • 一个函数:函数做用域
  • script 标签:全局做用域
  1. JS 中用 var 声明的变量是没有块级做用域的,只有函数做用域和全局做用域
var x = 1;
{
  var x = 2;
}
console.log(x);
复制代码
  • 会输出 2,由于块中的 var 语句与块前面的 var 语句做用域相同
  • 在 C 或 Java 中,这段代码会输出 1
  • 这代码证实了 var 没有块做用域
  1. 相比之下,使用 let 和 const 声明的变量是有块级做用域的
let x = 1;
{
  let x = 2;
}
console.log(x); 
复制代码
  • 输出 1
  • x 被限制在块级做用域中
  • 这里将 let 换成 const 结果也同样
  1. 经典的例子(背住):
var a = [];
for (var i = 0; i < 10; i++) {
      a[i] = function () {console.log(i);};
}
a[0]();                // 10
a[1]();                // 10
a[6]();                // 10

/********************/

var a = [];
for (let i = 0; i < 10; i++) {
      a[i] = function () {console.log(i);};
}
a[0]();                // 0
a[1]();                // 1
a[6]();                // 6
复制代码
  • 第一个中的 i 是没有块级做用域的,全局只有一个变量 i,每次循环的 {} 中的代码都指向同一个 i
  • 第二个中的 i 是有块级做用域的,当前的i只在本轮循环有效,每次循环的 {} 中的代码都指向不一样的 i
  • 值得注意的是:for的 () 是一个父做用域,{} 是一个子做用域
  • 由于当前的 i 只在本轮循环有效,因此 let 定义的 i 在每次循环都会从新定义一遍,每次都是一个新变量
  1. 补充:

使用 function 时也有块级做用域:数组

foo('outside');  // TypeError: foo is not a function
{
  function foo(location) {
   console.log('foo is called ' + location);
  }
  foo('inside'); // 正常工做而且打印 'foo is called inside' 
}
复制代码
  1. var 变量提高,let 变量暂时性死区
console.log(foo); //输出 undefined,不会报错
var foo = 2; 

console.log(bar); //报错 ReferenceError 
let bar = 2;
复制代码
  • var 会发生变量提高现象,将变量提高到函数顶部或全局顶部
  • let 没有这种状况,必须在 let 声明后调用该变量 (const 和 let 同样)
  • let 和 const 的这种现象称为暂时性死区
  • 从块开始到 let 声明之间是“暂存死区”
  1. let 错误

在同一个做用域中用 let 重复定义一个变量将引发 TypeErrorpromise

if (x) {
  let foo;
  let foo; // TypeError thrown
}
复制代码
  1. const 的概念
  • 必须在声明的同一语句中指定它的值
  • const 声明建立一个值的只读引用,也就是说只能经过 const 定义的变量来读这个值,不能修改这个值,可是若是这个值自己发生了变化,那么 const 定义的变量所对应的值也就跟着变化,好比当引动内容是对象的状况下。
  • 一个常量不能和它所在做用域内的其余变量或函数拥有相同的名称。
// 下面错误,常量要求有一个初始值
const FOO;

// 下面正确,常量能够定义成对象
const MY_OBJECT = {"key": "value"};
// 下面错误,改变常量会失败
MY_OBJECT = {"OTHER_KEY": "value"};
// 对象属性并不在保护的范围内,下面会成功执行(对象自己发生变化)
MY_OBJECT.key = "otherValue";

// 定义一个常量
const MY_FAV = 20;
// 不能同名,会出错
var MY_FAV = 20; 
// 也会报错
let MY_FAV = 20;
复制代码

二. 箭头函数

基础语法:markdown

// 通常语法:
(参数1, 参数2, …, 参数N) => { 函数声明 }

// 至关于:(参数1, 参数2, …, 参数N) =>{ return 表达式; }
(参数1, 参数2, …, 参数N) => 表达式(单一)

// 当只有一个参数时,圆括号是可选的:
(单一参数) => {函数声明}
单一参数 => {函数声明}

// 没有参数的函数应该写成一对圆括号
() => {函数声明}
复制代码

高级语法:编程语言

注意: 字面量通常指 [1, 2, 3] 或者 {name: "mdn"} 这种简洁的构造方式ide

// 加括号的函数体返回对象字面表达式:
参数=> ({foo: bar})

// 支持剩余参数和默认参数
(参数1, 参数2, ...rest) => {函数声明}
(参数1 = 默认值1,参数2, …, 参数N = 默认值N) => {函数声明}

// 一样支持参数列表解构
let f = ([a, b] = [1, 2], {x: c} = {x: a + b}) => a + b + c;
f();  // 6
复制代码

例子:

箭头函数比较适用于本来须要匿名函数的地方,好比用在数组内置方法map、filter、forEach、reduce的回调函数中

var elements = [
  'Hydrogen',
  'Helium',
  'Lithium',
  'Beryllium'
];

elements.map(function(element) { 
  return element.length; 
}); // 返回数组:[8, 6, 7, 9]

// 上面的普通函数能够改写成以下的箭头函数
elements.map((element) => {
  return element.length;
}); // [8, 6, 7, 9]
复制代码

重点注意:

this 对象的指向是可变的,可是在箭头函数中,它是固定的。箭头函数体内的 this 对象,就是定义时所在的对象,而不是使用时所在的对象。

三. 参数处理

  1. 给函数设置默认参数值

在ES6之前,咱们想要给参数设置默认值得这么作:

function multiply(a, b) {
  // JavaScript 中函数的参数默认是 undefined
  // 没有值传入或者 undefined 被传入给 b 时给 b 一个默认值
  b = (typeof b !== 'undefined') ?  b : 1;
  return a * b;
}

multiply(5, 2); // 10
multiply(5);    // 5
复制代码

如今学习了ES6以后就变得很简单:

function multiply(a, b = 1) {
  return a * b;
}

multiply(5, 2); // 10
multiply(5);    // 5
复制代码

注意参数的传递是从左到右的:

function f(x = 1, y) { 
  return [x, y]; 
}

f(); // [1, undefined]
f(2); // [2, undefined]
复制代码
  1. 剩余参数(...)

剩余参数语法容许咱们将一个不定数量的参数表示为一个数组。

语法:

function(a, b, ...theArgs) {
   ...
}
复制代码
  • 必须是函数的最后一个参数以...为前缀
  • 它是由剩余实参组成的 Array 数组
  • 这个例子中的 theArgs 收集第三个以及以后的全部实参

实例:

function fun1(...theArgs) {
  alert(theArgs.length); // 可使用全部数组属性方法
}
fun1();  // 弹出 "0", 由于 theArgs 没有元素
fun1(5); // 弹出 "1", 由于 theArgs 只有一个元素
复制代码
  1. 展开运算符(...)

展开语法, 能够在函数调用/数组构造时, 将数组表达式或者 string 在语法层面展开;还能够在构造字面量对象时, 将对象表达式按 key-value 的方式展开。

注意: 字面量通常指 [1, 2, 3] 或者 {name: "mdn"} 这种简洁的构造方式

  • 在函数调用时使用展开语法:
function sum(x, y, z) {
  return x + y + z;
}
const numbers = [1, 2, 3];
console.log(sum(...numbers));//6

/****下例表示能够屡次使用****/

function myFunction(v, w, x, y, z) { }
var args = [0, 1];
myFunction(-1, ...args, 2, ...[3]);
复制代码
  • 构造字面量数组时使用展开语法:
/****构造字面量数组或链接数组****/
var parts = ['shoulders', 'knees']; 
var lyrics = ['head', ...parts, 'and', 'toes']; 
// ["head", "shoulders", "knees", "and", "toes"]
var arr = [...parts, ...lyrics];

/****支持浅拷贝(一维数组)****/
var arr = [1, 2, 3];
var arr2 = [...arr];
arr2.push(4); 
// arr2 此时变成 [1, 2, 3, 4]
// arr 不受影响
复制代码
  • 构造字面量对象时使用展开语法:
var obj1 = { foo: 'bar', x: 42 };
var obj2 = { foo: 'baz', y: 13 };

var clonedObj = { ...obj1 };
// 浅拷贝
// 克隆后的对象: { foo: "bar", x: 42 }

var mergedObj = { ...obj1, ...obj2 };
// 合并后的对象: { foo: "baz", x: 42, y: 13 }
复制代码

四. 模板字面量(模板字符串)

由反引号 `` 来代替单引号 '' 或者双引号 ""

  • 单行字符串:
`string text`
复制代码
  • 多行字符串(注意回车也包含在内)

再也不用 "+" 或 "\" 链接两行字符串

`string text line 1 string text line 2`

console.log(`string text line 1 string text line 2`);
复制代码
  • 插入表达式:

再也不用 "+" 链接表达式

`string text ${expression} string text`

var a = 5;
var b = 10;
console.log(`a + b is ${a + b}`);
复制代码
  • 带标签的模板字符串:

标签使咱们能够用函数来解析模板字符串。

直接看例子:

var person = 'Mike';
var age = 28;

function myTag(strings, personExp, ageExp) {

  var str0 = strings[0]; // "that "
  var str1 = strings[1]; // " is a "

  var ageStr;
  if (ageExp > 99){
    ageStr = 'centenarian';
  } else {
    ageStr = 'youngster';
  }

  return str0 + personExp + str1 + ageStr;
}

var output = myTag`that ${ person } is a ${ age }`;

console.log(output);
// that Mike is a youngster
复制代码
  • 原始字符串:

在标签函数的第一个参数中,存在一个特殊的属性 raw ,咱们能够经过它来访问模板字符串的原始字符串,而不通过特殊字符的替换。

例子:

function tag(strings) {
  console.log(strings.raw[0]);
}
tag`string text line 1 \n string text line 2`;
// logs "string text line 1 \n string text line 2"
复制代码

五. 对象的扩展

  • 建立对象时能够简化方法和重名的键值对

通常状况:

var name="pan";
var age=20;
var people = {
      name: name,
      age: age,
      getName: function() {
           console.log(this.name)
      }
};
复制代码

ES6简化后:

var name="pan";
var age=20;
var people = {
      name,
      age,
      getName(){
           console.log(this.name)
      }
};
复制代码
  • 计算属性名(属性名能够用表达式)
var i = 0;
var a = {
  ["foo" + ++i]: i,
  ["foo" + ++i]: i,
  ["foo" + ++i]: i
};
console.log(a.foo1); // 1
console.log(a.foo2); // 2
console.log(a.foo3); // 3
复制代码
  • proto

当定义的 属性:值 为 proto:值 时,不会建立名为 proto 的属性。 但当值为对象时,将更改原型(由于 proto 本来指向原型)。 因此不建议使用这个属性名(避免更改 proto 的指向)。

六. 解构赋值

解构赋值语法是一种 JavaScript 表达式,它使得将值从数组,或属性从对象,提取到不一样的变量中,成为可能。

目的就是将数组中的值或对象中的属性方便的提取到其余变量中

  • 解构数组语法:
var x, y;
[x, y] = [1, 2, 3];
console.log(x); // 1
console.log(y); // 2

// 或者:
var [x, y] = [1, 2, 3];
console.log(x); // 1
console.log(y); // 2
复制代码

小技巧(不用临时变量也能交换变量):

var a = 1;
var b = 3;
// 等价于[a,b]=[3,1]
[a, b] = [b, a];
console.log(a); // 3
console.log(b); // 1
复制代码
  • 解构对象语法:
var o = {p: 42, q: true};
var {p, q} = o;

console.log(p); // 42
console.log(q); // true
复制代码

注意:对象解构与数组解构有一个重要的不一样点,数组的元素是按次序排列的,变量的取值由它的位置决定;而对象的属性没有次序,变量必须与属性同名,才能取到正确的值。

七. 模块(import/export)

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

一个模块就是一个独立的文件。该文件内部的全部变量,外部没法获取。若是你但愿外部可以读取模块内部的某个变量,就必须使用 export 关键字输出该变量。下面是一个 JS 文件,里面使用 export 命令输出变量。

  • 导出 export
// profile.js
var firstName = 'Michael';
var lastName = 'Jackson';
var year = 1958;

export {firstName, lastName, year};
复制代码
  • 上面代码是 profile.js 文件,保存了用户信息,
  • ES6 将其视为一个模块,里面用 export 命令对外部输出了三个变量。
  • 除了这种普通变量,输出的变量还能够是一个函数。
  • 另外,export 语句输出的接口,与其对应的值是动态绑定关系,
  • 即经过该接口,能够取到模块内部实时的值。
  • 最后,export/import 命令不能在块级做用域内,能够在全局做用域任何位置。
  • 导入 import

import 语句只能在声明了 type="module" 的 script 的标签中使用。 动态导入:import(),它不须要依赖type="module" 的 script 标签。 按照必定的条件或者按需加载模块的时候,动态 import() 是很是有用的。 而静态的 import 是初始化加载的最优选择。

// main.js
import {firstName, lastName, year} from './profile.js';

function setName(element) {
  element.textContent = firstName + ' ' + lastName;
}
复制代码
  • 上面代码的 import 命令,用于加载 profile.js 文件,并从中输入变量。
  • import 命令接受一对大括号,里面指定要从其余模块导入的变量名。
  • 大括号里面的变量名,必须与被导入模块(profile.js)对外接口的名称相同。

注意:import 语句会执行所加载的模块:

import './lodash.js';
// 执行lodash模块,可是不输入任何值
复制代码

总体导入:

import * as myModule from './my-module.js'; // 不用加大括号 {}
// 将 my-module 里面输出的变量/函数,
// 输入到 myModule 这个对象中做为属性/方法。
// 经过 myModule.属性名 / myModule.方法() 来调用。
复制代码
  • 默认导出/导入默认值:

告别使用 import 命令的时候,用户须要知道所要加载的变量名或函数名的现状:

// 先用 export default 语法默认导出
// 既然是默认输出,那么一个模板只能使用一次
export default function foo() {
  console.log('foo');
}

// 导入默认值
import customName from './default.js'; // 不用加大括号 {}
customName(); // 'foo'
复制代码

八. 类

ES6 中引入的 JavaScript 类实质上是 JavaScript 现有的基于原型的继承的语法糖。类语法不会为 JavaScript 引入新的面向对象的继承模型。

  • 如何建立类:
class Rectangle {
  constructor(height, width) { //构造方法
    this.height = height;
    this.width = width;
  }
}
// 或者
let Rectangle = class {
  constructor(height, width) { //构造方法
    this.height = height;
    this.width = width;
  }
};
复制代码
  • 如何使用类:
class Rectangle {
    // constructor
    constructor(height, width) {
        this.height = height;
        this.width = width;
    }
    // Getter
    get area() {
        return this.calcArea()
    }
    // Method
    calcArea() {
        return this.height * this.width;
    }
}
const square = new Rectangle(10, 10);

console.log(square.area); // 100
复制代码
  • 类的静态方法:

不能经过一个类实例调用静态方法(这里和 Java 语法有区别)

class Point {
    constructor(x, y) {
        this.x = x;
        this.y = y;
    }

    static distance(a, b) { // static 语法
        const dx = a.x - b.x;
        const dy = a.y - b.y;

        return Math.hypot(dx, dy);
    }
}

const p1 = new Point(5, 5);
const p2 = new Point(10, 10);
// 直接经过类名调用
console.log(Point.distance(p1, p2));
复制代码
  • 类的继承(extends)
class Animal { 
  constructor(name) {
    this.name = name;
  }
  
  speak() {
    console.log(this.name + ' makes a noise.');
  }
}

class Dog extends Animal {
   constructor(name) {
    super(name); // 调用超类构造函数
  }

  speak() {
    console.log(this.name + ' barks.');
  }
}

var d = new Dog('Mitzie');
// 'Mitzie barks.'
d.speak();
复制代码
  • 调用父类方法(super)
class Cat { 
  constructor(name) {
    this.name = name;
  }
  
  speak() {
    console.log(this.name + ' makes a noise.');
  }
}

class Lion extends Cat {
  speak() {
    super.speak();
    console.log(this.name + ' roars.');
  }
}
复制代码

九. Promise 对象

这部份内容是重难点,说是面试必考的知识点也不过度,由于它不易简单的进行总结,因此我推荐读到这里的朋友去细读 阮一峰Promise ,进行系统的学习这部份内容。

十. End 感谢阅读

留下宝贵的一赞再走!

img-16259633876812169a61ab3330f5437ee31d7546c0cd7.jpg

相关文章
相关标签/搜索