ES6 教程

查看目录版

查看原版

ES6 全称 ECMAScript 6.0 ,2015.06 发布。

本教程内容如下:

ES6概览

声明与表达式

ES6 let 与 const

  • let 声明的变量只在 let 命令所在的代码块内有效;
  • const 声明一个只读的常亮,一旦声明,常亮的值就不能改变;
let 与 var 的区别:
  • let 只在代码块内有效,var 在全局范围内有效;

  • let 只能声明一次,var 可以多次声明;

  • let 不存在变量提升,var 会变量提升;

  console.log(a);       //ReferenceError: a is not defined
  let a = "apple";

  console.log(b);       //undefined
  var b = "banana";
const 命令:
  • const 声明一个只读的变量,声明之后不允许改变。意味着,一旦声明必须初始化,否则会报错;
暂时性死区:
  • letconst 都会存在;
  var PI = "a";
  if(true){
    console.log(PI);        //ReferenceError: PI is not defined
    const PI = "3.1415926";
  }
  • ES6 明确规定,代码块内如果存在 let 或者 const ,代码块会对这些声明命令的变量从 块的开始 就形成一个封闭作用域。在代码块内,在声明变量 PI 之前使用它就会报错。

ES6 解构赋值

  • 解构赋值是对赋值运算符的扩展;
  • 它针对数组或对象进行模式匹配,然后对其中的变量进行赋值;
  • 在代码书写上简洁且易读,语义更加清晰明了;也方便了对复杂对象中数据字段的获取;
解构模型

在解构中,有下面两部分参与:

  • 解构的源 :解构赋值表达式的右边部分;
  • 解构的目标 :解构赋值表达式的左边部分;
数组模型的解构(Array)
  • 基本
  let [a, b, c] = [1, 2, 3];
  //    a = 1
  //    b = 2
  //    c = 3
  • 可嵌套
  let [a, [[b], c]] = [1, [[2], 3]];
  //    a = 1
  //    b = 2
  //    c = 3
  • 可忽略
  let [a, , b] = [1, 2, 3];
  //    a = 1
  //    b = 3
  • 不完全解构
  let [a = 1, b] = [];
  //    a = 1
  //    b = undefined
  • 剩余运算符
  let [a, ...b] = [1, 2, 3];
  //    a = 1
  //    b = [2, 3]
  • 字符串等

在数组的解构中,解构的目标若为可遍历对象,皆可进行解构赋值。可遍历对象即实现 Iterator 接口的数据;

  let [a, b, c, d, e] = 'hello';
  //    a = 'h'
  //    b = 'e'
  //    c = 'l'
  //    d = 'l'
  //    e = 'o'
  • 解构默认值
  let [a = 2] = [undefined];
  //    a = 2

当解构模式有匹配结果,且匹配结果是 undefined 时,会触发默认值作为返回结果;

  let [a = 3, b = a] = [];
  //    a = 3, b = 3
  let [a = 3, b = a] = [1];
  //    a = 1, b = 1
  let [a = 3, b = a] = [1, 2]
  //    a = 1, b = 2
  • ab 匹配结果为 undefined ,触发默认值:a = 3; b = a = 3
  • a 正常解构赋值,匹配结果为:a = 1b 匹配结果为 undefined ,触发默认值:b = a = 1
  • ab 正常解构赋值,匹配结果为:a = 1, b = 2
对象模型的解构(Object)
  • 基本
  let { foo, bar } = { foo: 'aaa', bar : 'bbb' };
  //    foo = 'aaa'
  //    bar = 'bbb'

  let { baz : foo } = { baz : 'ddd'};
  //    foo = 'ddd'
  • 可嵌套可忽略
  let obj = { p: ['hello', { y: 'world'}] };
  let { p: [x, { y }] } = obj;
  //    x = 'hello'
  //    y = 'world'

  let obj = { p: ['hello', { y : 'world' }] };
  let { p: [x, { }] } = obj;
  //    x = 'hello'
  • 不完全解构
  let obj = { p: [{y: 'world'}] };
  let { p: [{ y }, x]} = obj;
  //    x = undefined
  //    y = 'world'
  • 剩余运算符
  let {a, b, ...rest} = {a: 10, b: 20, c: 30, d: 40};
  //    a = 10
  //    b = 20
  //    rest = {c: 30, d: 40}
  • 解构默认值
  let {a = 10, b = 5} = {a: 3};
  //    a = 3; b = 5
  let {a: aa = 10,b: bb = 5} = {a: 3}
  //    aa = 3; bb = 5

ES6 Symbol

  • ES6 引入的一种新的原始数据类型,表示独一无二的值。最大的用法是用来定义对象的唯一属性名;
  • 即使参数一样,定义的 Symbol 值也是不相等的;
  • ES6 数据类型除了 Number、String、Boolean、Object、nullundefined ,还新增了 Symbol
基本用法
  • Symbol 函数栈不能使用 new 命令,因为 Symbol 是原始数据类型,不是对象。

  • 可以接收一个 字符串 作为参数,为新创建的 Symbol 提供描述,用来显示在控制台或者作为字符串的时候使用,便于区分;

  • 字符串相同的 Symbol ,值不相同;

  let sy = Symbol("kk");
  console.log(sy);                  // Symbol(kk)
  typeof(sy);                               // "symbol"

  // 参数相同的 Symbol() 返回的值不相等
  let sy1 = Symbol("kk");
  console.log(sy === sy1);  // false
使用场景
作为属性名
  • 由于每一个 Symbol 的值都是不相等的,所以 Symbol 作为对象的属性名,可以保证属性不重名;
  let sy = Symbol("key1");

  // 写法1
  let syObject = {};
  syObject[sy] = "kk";
  console.log(syObject);        // {Symbol(key1): "kk"}

  // 写法2
  let syObject = {
    [sy]: "kk"
  };
  console.log(syObject);        // {Symbol(key1): "kk"}

  // 写法3
  let syObject = {};
  Object.defineProperty(syObject, sy, {value: "kk"});
  console.log(syObject);        // {Symbol(key1): "kk"}
  • Symbol 作为对象属性名时不能用 . 运算符,要用方括号。因为 . 运算符后面是字符串,所以取到的是 字符串sy 属性,而不是 Symbolsy 属性;
  let syObject = {};
  syObject[sy] = "kk";

  syObject[sy];             // "kk"
  syObject.sy;              // undefined
注意点
  • Symbol 值作为属性名时,该属性是公有属性,不是私有属性,可以在类的外部访问;

  • 不会出现在 for … infor … of 的循环中;

  • 也不会被 Object.keys()Object.getOwnPropertyNames() 返回;

  • 如果要读取一个对象的 Symbol 属性,可以通过 Object.getOwnPropertySymbols()Reflect.ownKeys() 取到;

  let syObject = {};
  syObject[sy] = "kk";
  console.log(syObject);                                        // {Symbol(key1): "kk"}

  for (let i in syObject){
    console.log(i);                                                 // 无输出
  }

  Object.keys(syObject);                                        // []
  Object.getOwnPropertySymbols(syObject);       // [Symbol(key1)]
  Reflect.ownKeys(syObject);                                // [Symbol(key1)]
定义常量
  • ES5 中用字符串表示常量,但是字符串不能保证是唯一的,这会引起一些问题:
  const COLOR_RED = "red";
  const COLOR_YELLOW = "yellow";
  const COLOR_BLUE = "blue";
  const MY_BLUE = "blue";

  function getConstantName(color) {
    switch (color) {
      case COLOR_RED : 
        return "COLOR_RED";
      case COLOR_YELLOW:
        return "COLOR_YELLOW";
      case COLOR_BLUE:
        return "COLOR_BLUE";
      case MY_BLUE:
        return "MY_BLUE";
      default:
        throw new Exception("Can't find this color");
    }
  }
  • 如果使用 Symbol 定义常量,就可以保证这一组常量的值都不相等。用 Symbol 来修改上面的例子;
  const COLOR_RED = Symbol("red");
  const COLOR_RED = Symbol("yellow");
  const COLOR_BLUE = Symbol("blue");
  const MY_BLUE = Symbol("blue");

  function getConstantName(color) {
    switch (color) {
      case COLOR_RED : 
        return "COLOR_RED";
      case COLOR_YELLOW:
        return "COLOR_YELLOW";
      case COLOR_BLUE:
        return "COLOR_BLUE";
      case MY_BLUE:
        return "MY_BLUE";
      default:
        throw new Exception("Can't find this color");
    }
  }
Symbol.for()
  • Symbol.for() 类似单例模式;

  • 首先会在全局搜索被 登记Symbol 中是否有该字符串参数作为名称的 Symbol 值,如果有即返回该 Symbol 值;若没有则新建并返回一个以该字符串参数为名称的 Symbol 值,并 登记 在全局环境中供搜索;

  let yellow = Symbol("Yellow");
  let yellow1 = Symbol.for("Yellow");
  console.log(yellow === yellow1);      // false

  let yellow2 = Symbol.for("Yellow");
  console.log(yellow1 === yellow2);     // true
Symbol.keyFor()
  • Symbol.keyFor() 返回一个 已登记Symbol 类型值的 key ,用来检测该字符串参数作为名称的 Symbol 值是否已被 登记
  let yellow = Symbol("Yellow");
  console.log(Symbol.keyFor(yellow));           // undefined

  let yellow1 = Symbol.for("Yellow");
  console.log(Symbol.keyFor(yellow1));      // "Yellow"
关于登记
  • Symbol() 不会登记 字符串参数;
  • Symbol.for()会登记 字符串参数;

内置对象

ES6 Map 与 Set

  • MapSetES6 新增的两种类型;
  • Map 对象保存键值对。任何值(对象或者原始值)都可以作为一个键或一个值;
  • Set 对象保存一个键,允许存储任何类型的唯一值,无论是原始值或是对象引用;
  • MapSet 对象的值都是有序的(FIFO 原则),而 Object 的顺序是按 key 转换之后的顺序排序的,转换的规则是 字符串类型的数字转化成数字,字母按字母表,从小到大排序;
Map 对象
  • Map 保存键值对。任何值(对象或者原始值)都可以作为一个键或一个值;
Maps 和 Objects 的区别
  • 一个 Object 的键只能是字符串或者 Symbol ,但一个 Map 的键可以是任意值;
  • Map 中的键值是有序的(FIFO 原则),而添加到对象中的键则不是;
  • Map 的键值对个数可以从 size 属性获取,而 Object 的键值对个数只能手动计算;
  • Object 都有自己的原型,原型链上的键名有可能和你自己在对象上设置的键名产生冲突;
Map 中的 key
  • key 是字符串
  var myMap = new Map();
  var keyString = "a string";

  myMap.set(keyString, "和键'a string'关联的值");

  myMap.get(keyString);         // "和键'a string'关联的值"
  myMap.get("a string");        // "和键'a string'关联的值"
                                                    // 因为 keyString === 'a string'
  • key 是对象
  var myMap = new Map();
  var keyObj = {};

  myMap.set(keyObj, "和键 keyObj 关联的值");

  myMap.get(keyObj);                // "和键 keyObj 关联的值"
  myMap.get({});                        // undefined, 因为 keyObj !== {}
  • key 是函数
  var myMap = new Map();
  var keyFunc = function(){};

  myMap.set(keyFunc, "和键 keyFunc 关联的值");

  myMap.get(keyFunc);               // "和键 keyFunc 关联的值"
  myMap.get(function() {}); // undefined,因为 keyFunc !== function() {}
  • key 是NaN
  var myMap = new Map();
  myMap.set(NaN, "not a number");

  myMap.get(NaN);                       // "not a number"

  var otherNuN = Number("foo");
  myMap.get(otherNaN);          // "not a number"
                                                    // 虽然 NaN 和任何值甚至它自己都不相等(NaN !== NaN 返回 true),但 NaN 作为 Map 的键来说是没有区别的
Map 的迭代
  • Map 进行遍历,以下两个最高级;

  • for … of

  var myMap = new Map();
  myMap.set(0, "zero");
  myMap.set(1, "one");

  // 将会显示两个 log。一个是 "0 = zero",另一个是 "1 = one"
  for (var [key, value] of myMap) {
    console.log(key + " = " + value);
  }
  for (var [key, value] of myMap.entries()) {
    console.log(key + " = " + value);
  }
  /* 这个 entries() 方法返回一个新的 Iterator 对象,它按插入顺序包含了 Map 对象中每个元素的 [key, value] 数组 */

  // 将会显示两个 log。一个是 "0",另一个是 "1"
  for (var key of myMap.keys()) {
    console.log(key);
  }
  /* 这个 keys() 方法返回一个新的 Iterator 对象,它按插入顺序包含了 Map 对象中每个元素的键 */

  // 将会显示两个 log。一个是 "zero",另一个是 "one"
  for (var value of myMap.values()) {
    console.log(value);
  }
  /* 这个 values() 方法返回一个 Iterator 对象,它按插入顺序包含了 Map 对象中每个元素的值。 */
  • forEach()
  var myMap = new Map();
  myMap.set(0, "zero");
  myMap.set(1, "one");

  // 将会显示两个 log。一个是 "0 = zero",另一个是 "1 = one"
  myMap.forEach(function(value, key){
    console.log(key + " = " + value);
  }, myMap)
Map 对象的操作
  • Map 与 Array 的转换
  var kvArray = [["key1", "value1"], ["key2", "value2"]];

  // Map 构造函数可以将一个 二维键值对数组 转换成一个 Map 对象
  var myMap = new Map(kvArray);

  // Array.from 函数可以将一个 Map 对象转换成一个 二维键值对数组
  var outArray = Array.from(myMap);
  • Map 的克隆
  var myMap1 = new Map([["key1", "value1"], ["key2", "value2"]]);
  var myMap2 = new Map(myMap1);

  console.log(myMap1 === myMap2);
  // false. Map 对象构造函数生成的实例,迭代出新的对象
  • Map 的合并
  var first = new Map([[1, 'one'], [2, 'two'], [3, 'three']]);
  var second = new Map([[1, 'uno'], [2, 'dos']]);

  var merged = new Map([...first, ...second]);
  // 合并两个 Map 对象时,如果有重复的键值,后面的会覆盖前面的。
  // 所以,merged 后的值是 uno, dos, three
Set 对象
  • Set 对象允许你存储任何类型的唯一值,无论是原始值还是对象引用;
Set 中的特殊值

Set 对象存储的值总是唯一的,所以需要判断两个值是否恒等。有几个特殊值需要特殊对待:

  • +0-0 在存储判断唯一性的时候是恒等的,所以不重复;

  • undefinedundefined 是恒等的,所以不重复;

  • NaNNaN 是不恒等的,但是在 Set 中只能存一个,所以不重复;

  let mySet = new Set();

  mySet.add(1);                                 // Set(1) {1}
  mySet.add(5);                                 // Set(2) {1, 5}
  mySet.add(5);                                 // Set(2) {1, 5} 这里体现了值的唯一性
  mySet.add(-0);                                // Set(3) {1, 5, 0} 这里体现了值的 FIFO 原则
  mySet.add(+0);                                // Set(3) {1, 5, 0}

  mySet.add("some text");               // Set(4) {1, 5, 0, "some text"} 这里体现了类型的多样性

  var o = {a: 1, b: 2};
  mySet.add(o);                                 // Set(5) {1, 5, 0, "some text", {...}}
  mySet.add({a: 1, b: 2});          // Set(6) {1, 5, 0, "some text", {...}, {...}} 这里体现了对象之间引用不同于不恒等,即使值相同,Set 也能存储
类型转换
  • Array
  // Array ==> Set
  var mySet = new Set(["value1", "value2", "value3"]);

  // Set ==> Array,用 ...操作符
  var myArray = [...mySet];

  // String ==> Set
  var mySet = new Set('hello');     // Set(4) {"h", "e", "l", "o"}
  // Set ==> String
  // 可以 Set ==> Array ==> String (.toString 或 join 都可以)
  // .toString 是不能将 Set 转换成 String
Set 对象作用
  • 数组去重
  var mySet = new Set([1, 2, 3, 4, 4]);
  [...mySet];                                                   // [1, 2, 3, 4]
  • 并集
  var a = new Set([1, 2, 3]);
  var b = new Set([4, 3, 2]);
  var union = new Set([...a, ...b]);    // {1, 2, 3, 4}
  • 交集
  var a = new Set([1, 2, 3]);
  var b = new Set([4, 3, 2]);
  var insersect = new Set([...a].filter(x => b.has(x)));        // {2, 3}
  • 差集
  var a = new Set([1, 2, 3]);
  var b = new Set([4, 3, 2]);
  var difference = new Set([...a].filter(x => !b.has(x)));  // {1}

ES6 Reflect 与 Proxy

  • ProxyReflectES6 为了操作对象引入的 API
  • Proxy 可以对目标对象的读取、函数调用等操作进行拦截,然后进行操作处理。它不直接操作对象,而是像代理模式,通过对象的代理对象进行操作,在进行这些操作时,可以添加一些需要的额外操作;
  • Reflect 可以用于获取目标对象的行为,它与 Object 类似,但是更易读,为操作对象提供了一种更优雅的方式。它的方法与 Proxy 是对应的;
Proxy
  • 一个 Proxy 对象由两个部分组成:targethandler

  • 在通过 Proxy 构造函数生成对象实例时,需要提供这两个参数;

  • target 即目标对象,handler 是一个对象,声明了代理 target 的指定行为;

  let target = {
    name: 'Tom',
    age: 24
  }
  let handler = {
    get: function(target, key) {
      console.log('getting ' + key);
      return target[key];       // 不是target.key
    },
    set: function(target, key, value) {
      console.log('setting ' + key);
      target[key] = value;
    }
  }

  let proxy = new Proxy(target, handler);
  proxy.name;                   // 实际执行 handler.get
  proxy.age = 25;           // 实际执行 handler.set
  // getting name
  // setting age
  // 25

  // target 可以为空对象
  let targetEpt = {};
  let proxyEpt = new Proxy(targetEpt, handler);
  // 调用 get 方法,此时目标对象为空,没有 name 属性
  proxyEpt.name;
  // 调用 set 方法,向目标对象中添加了 name 属性
  proxyEpt.name = 'Tom';
  // setting name
  // "Tom"
  // 再次调用 get ,此时已经存在 name 属性了
  proxyEpt.name;
  // getting name
  // "Tom"

  // handler 对象也可以为空,相当于不设置拦截操作,直接访问目标对象
  let targetEmpty = {};
  let proxyEmpty = new Proxy(targetEmpty, {});
  proxyEmpty.name = "Tom";
  targetEmpty)                  // {name: "Tom"}
实例方法
Reflect
  • ES6Object 上的一些语言内部的方法移植到了 Reflect 对象上(当前某些方法同时存在 ObjectReflect 对象上,但未来的新方法只会部署在 Reflect 对象上);
  • Reflect 对象对某些方法的返回结果进行了修改,使其更合理;
  • Reflect 对象使用函数的方式实现了 Object 的命令式操作;
实例方法
  // 查找并返回 target 对象的 name 属性
  Reflect.get(target, name, receiver);

  // 将 target 的 name 属性设置为 value。返回值为 boolean,true 表示修改成功,false 表示修改失败。当 target 为不存在的对象时,会报错
  Reflect.set(target, name, value, receiver);

  // 是 name in obj 指令的函数化,用于查找 name 属性在 obj 中是否存在。返回值为 boolean。如果 obj 不是对象则会报错 TypeError
  Reflect.has(obj, name)

  // 是 delete obj[property] 的函数化,用于删除 obj 对象的 property 属性。返回值为 boolean。如果 obj 不是对象则会报错 TypeError
  Reflect.deleteProperty(obj, property)

  // 等同于 new target(...args)
  Reflect.construct(obj, args);

  // 用于读取 obj 的 _proto_ 属性。在 obj 不是对象时会报错。
  Reflect.getPrototypeOf(obj)

  // 用于设置目标对象的 prototype
  Reflect.setPrototypeOf(obj, newProto);

  // 等同于 Function.prototype.apply.call(func, thisArg, args).func 表示目标函数;thisArg 表示目标函数绑定的 this 对象;args 表示目标函数调用时传入的参数列表,可以是数组或类似数组的对象。若目标函数无法调用,会抛出 TypeError
  Reflect.apply(func, thisArg, args);

  // 用于为目标对象定义属性。如果 target 不是对象,会抛出异常
  Reflect.defineProperty(target, propertyKey, attributes)

  // 用于得到 target 对象的 propertyKey 属性的描述。如果 target 不是对象,会抛出错误
  Reflect.getOwnPropertyDescriptor(target, propertyKey)

  // 用于判断 target 对象是否可扩展。返回值为 boolean。如果 target 参数不是对象,会抛出异常;
  Reflect.isExtensible(target)

  // 用于让 target 对象变为不可扩展。如果 target 参数不是对象,会抛出异常
  Reflect.preventExtensions(target)

  // 用于返回 target 对象的所有属性,等同于 Object.getOwnPropertyNames 与 Object.getOwnPropertySymbols 之和
  Reflect.ownKeys(target)
组合使用
  • Reflect 对象的方法与 Proxy 对象的方法是一一对应的。所以 Proxy 对象的方法可以通过调用 Reflect 对象的方法获取默认行为,然后进行额外操作;
  let exam = {
    name: "Tom",
    age: 24
  }
  let handler = {
    get: function(target, key) {
      console.log("getting" + key);
      return Reflect.get(target, key);
    },
    set: function(target, key, value) {
      console.log("setting" + key + " to " + value);
      Reflect.set(target, key, value);
    }
  }

  let proxy = new Proxy(target, handler);
  proxy.name = "Jerry";
  proxy.name;
  // setting name to Jerry
  // getting name
  // "Jerry"

ES6 字符串

子串识别

ES6 之前判断字符串是否包含子串,是用 indexOf 方法,ES6 新增了子串的识别方法。

  • includes() :返回布尔值,判断是否找到参数字符串;

  • startsWith() :返回布尔值,判断参数字符串是否是在原字符串的头部;

  • endsWith() :返回布尔值,判断参数字符串是否是在原字符串的尾部;

  • 以上三个方法都可以接受两个参数 - 需要搜索的字符串,和 可选的搜索起始位置索引;

  let string = "apple,banana,orange";
  string.includes("banana");                // true
  string.startsWith("apple");               // true
  string.endsWith("apple");                 // false
  string.startsWith("banana",6);        // true

注意点

  • 这三个方法只返回布尔值,如果要知道子串的位置,还是得用 indexOflastIndexOf
  • 这三个方法如果传入了正则表达式而不是字符串,就会抛出错误。而 indexOflastIndexOf 这两个方法,它们会将正则表达式转换成字符串并搜索它;
字符串重复
  • repeat() :返回新的字符串,表示将字符串重复指定次数返回;
  console.log("hello,".repeat(2));      // "hello,hello,"
  • 如果参数是小数,向下取整;
  console.log("hello,".repeat(3.2));    // "hello,hello,hello,"
  • 如果参数是 0 至 -1 之间的小数,会进行取整运算,等同于 0 ;
  console.log("hello,".repeat(-0.5));   // ""
  • 如果参数是 NaN ,等同于 0 ;
  console.log("hello,".repeat(NaN));    // ""
  • 如果参数是负数或者 Infinity ,会报错;
  console.log("hello,".repeat(-1));             // RangeError: Invalid count value
  console.log("hello,".repeat(Infinity));   // RangeError: Invalid count value
  • 如果参数的参数是字符串,会先将字符串转化为数字;
  console.log("hello,".repeat("hh"));           // ""
  console.log("hello,".repeat("2"));            // "hello,hello,"
字符串补全
  • padStart :返回新的字符串,表示用参数字符串从 头部 补全原字符串;

  • padEnd :返回新的字符串,表示用参数字符串从 尾部 补全原字符串;

  • 以上两个方法接受两个参数 - 生成字符串的最小长度,和 可选的用来补全的字符串。如果没有第二个参数,默认用空格填充;

  console.log("h".padStart(5,"0"));             // "ooooh"
  console.log("h".padEnd(5,"0"));                   // "hoooo"
  console.log("h".padStart(5));                     // "    h"
  • 如果指定的长度小于或等于原字符串的长度,则返回原字符串;
  console.log("hello".padStart(3,"A"));     // "hello"
  console.log("hello".padStart(5,"A"));     // "hello"
  console.log("hello".padStart(10,"A"));    // "AAAAAhello"
  • 如果原字符串加上补全字符串的长度大于指定长度,则截去超出位数的补全字符串;
  console.log("hello".padStart(10,",world!"));  // "hello,worl"
  • 常用于补全位数;
  console.log("123".padStart(10,"0"));                  // "0000000123"
模板字符串
  • 模板字符串相当于加强版的字符串,用反引号 `
  • 除了作为普通字符串,还可以用来定义多行字符串;
  • 还可以在字符串中加入变量和表达式;
基本用法
  • 普通字符串
  let string = `Hello'\n'world`;
  console.log(string);
  // "Hello"
  // "world"
  • 多行字符串
  let string1 = `Hey,
  can you stop angry now?`;
  console.log(string1);
  // Hey,
  // can you stop angry now?
  • 字符串插入变量和表达式

变量名写在 \${} 中,\${} 中也可以放入 JavaScript 表达式;

  let name = "Mike";
  let age = 27;
  let info = `My Name is ${name}, I am ${age+1} years old next year.`;
  console.log(info);
  // My Name is Mike, I am 28 years old next year.
  • 字符串中调用函数
  function f() {
    return "have fun!";
  }
  let string2 = `Game start,${f()}`;
  console.log(string2);
  // Game start,have fun!
注意要点
  • 模板字符串中的换行和空格都是会被保留的
  let innerHtml = `<ul>
    <li>menu</li>
    <li>mine</li>
  </ul>
  `;
  console.log(innerHtml);
  // 输出
  <ul>
    <li>menu</li>
    <li>mine</li>
  </ul>
模板标签
  • 模板标签,是一个函数的调用,调用的参数是模板字符串;
  alert`Hello world!`;
  // 等价于
  alert('Hello world!');
  • 当模板字符串中带有变量,会将模板字符串参数处理成多个参数;
  function f(stringArr,...values) {
    let result = "";
    for (let i=0,i<stringArr.length;i++) {
      result += stringArr[i];
      if (values[i]) {
        result += values[i];
      }
    }
    return result;
  }
  let name = "Mike";
  let age = 27;
  f`My Name is ${name},I am ${age+1} years old next year.`;
  // "My Name is Mike,I am 28 years old next year."

  f`My Name is ${name},I am ${age+1} years old next year.`;
  // 等价于
  f(['My Name is',',I am',' years old next year.'],'Mike',28);
应用
  • 过滤 HTML 字符串,防止用于输入恶意内容
  function f(stringArr,...values) {
    var result = "";
    for (let i=0,i<stringArr.length;i++) {
      result += stringArr[i];
      if (values[i]) {
        result += String(values[i]).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
      }
    }
    return result;
  }

  var name = '<Amy&Mike>';
  f`<p>Hi, ${name}. I would like send you some message.</p>`;
  // <p>Hi, &lt;Amy&amp;Mike&gt;. I would like send you some message.</p>
  • 国际化处理(转化多国语言)
  i18n`Hello ${name}, you are visitor number ${visitorNumber}`;
  // 你好**,你是第**位访问者

ES6 数值

数值的表示
  • 二进制表示法新写法:前缀 0b0B
  console.log(0b11 === 3);      // true
  console.log(0B11 === 3);      // true
  • 八进制表示法新写法:前缀 0o0O
  console.log(0o11 === 9);      // true
  console.log(0O11 === 9);      // true
常量
  • Number.EPSILON

  • 表示 1 与 大于 1 的最小浮点数之间的差;

  • 它的值接近于 2.2204460492503130808472633361816E-16,或者 2-52。

  • 可用于测试数值是否在误差范围内

  0.1 + 0.2 === 0.3;                    // false
  // 在误差范围内 即视为相等
  equal = (Math.abs(0.1 - 0.3 + 0.2) < Number.EPSILON);     // true
  • 属性特性
  writeable: false
  enumerable: false
  configurable: false
最大/最小安全整数
  • 安全整数

安全整数表示在 JavaScript 中能够精确表示的整数。安全整数的范围在 2 的 -53 次方到 2 的 53 次方之间(不包括两个端点)。超过这个范围的整数无法精确表示;

  • 最大安全整数

安全整数范围的上限,即 2 的 53 次方减 1;

  Number.MAX_SAFE_INTEGER === Math.pow(2, 53) - 1;                          // true
  Number.MAX_SAFE_INTEGER + 1 === Number.MAX_SAFE_INTEGER + 2;  // true
  Number.MAX_SAFE_INTEGER === Number.MAX_SAFE_INTEGER + 1;          // false
  Number.MAX_SAFE_INTEGER -1 === Number.MAX_SAFE_INTEGER - 2;       // false
  • 最小安全整数

安全整数范围的下限,即 2 的 53 次方减 1 的负数

  Number.MIN_SAFE_INTEGER === -(Math.pow(2, 53) - 1);                       // true
  Number.MIN_SAFE_INTEGER - 1 === Number.MIN_SAFE_INTEGER - 2;  // true
  Number.MIN_SAFE_INTEGER === Number.MIN_SAFE_INTEGER - 1;          // false
  Number.MIN_SAFE_INTEGER + 1 === Number.MIN_SAFE_INTEGER + 2;  // false
  • 属性特性
  wirteable: false
  enumerable: false
  configurable: false
方法
  • Number 对象新方法
  // 用于检查一个数值是否为有限的(finite),即不是 Infinity
  Number.isFinite();

  console.log(Number.isFinite(1));              // true
  console.log(Number.isFinite(0.1));            // true

  // NaN 不是有限的
  console.log(Number.isFinite(NaN));            // false

  console.log(Number.isFinite(Infinity));   // false
  console.log(Number.isFinite(-Infinity));// false
  // Number.isFinite 没有隐式的 Number() 类型转换,所有非数值都返回 false
  console.log(Number.isFinite('foo'));      // false
  console.log(Number.isFinite('15'));           // false
  console.log(Number.isFinite(true));           // false
  // 用于检测一个值是否为 NaN
  Number.isNaN();

  console.log(Number.isNaN(NaN));                   // true
  console.log(Number.isNaN('true'/0));      // true

  // 在全局的 isNaN() 中,以下皆返回 true,因为在判断前会将非数值向数值转换
  // 而 Number.isNaN() 不存在隐式的 Number() 类型转换,非 NaN 全部返回 false
  Number.isNaN("NaN");                                      // false
  Number.isNaN(undefined);                              // false
  Number.isNaN({});                                             // false
  Number.isNaN("true");                                     // false
  • 从全局移植到 Number 对象的方法
  // 用于将给定的字符串转化为指定进制的整数
  Number.parseInt();

  // 不指定默认为 10 进制
  Number.parseInt('12.34');         // 12
  Number.parseInt(12.34);               // 12

  // 指定进制
  Number.parseInt('0011', 2);       // 3

  // 与全局的 parseInt() 函数是同一个函数
  Number.parseInt === parseInt; // true
  // 用于把一个字符串解析成浮点数
  Number.parseFloat();

  Number.parseFloat('123.45');                      // 123.45
  Number.parseFloat('123.45abc');                   // 123.45

  // 无法被解析成浮点数时,则返回 NaN
  Number.parseFloat('abc');                             // NaN

  // 与全局的 parseFloat() 方法是同一个方法
  Number.parseFloat === parseFloat;             // true
  // 用于判断给定的参数是否是整数
  Number.isInteger(value);

  Number.isInteger(0);                                      // true
  // JavaScript 内部,整数和浮点数采用的是相同的存储方法,因此 1 与 1.0 被视为相同的值
  Number.isInteger(1);                                      // true
  Number.isInteger(1.0);                                    // true

  Number.isInteger(1.1);                                    // false
  Number.isInteger(Math.PI);                            // false

  // NaN 和正负 Infinity 不是整数
  Number.isInteger(NaN);                                    // false
  Number.isInteger(Infinity);                           // false
  Number.isInteger(-Infinity);                      // false

  Number.isInteger("10");                                   // false
  Number.isInteger(true);                                   // false
  Number.isInteger(false);                              // false
  Number.isInteger([1]);                                    // false

  // 数值的进度超过 53 个二进制位时,由于第 54 位及后面的位被丢弃,会产生误判
  Number.isInteger(1.0000000000000001);     // true

  // 一个数值的绝对值小于 Number.MIN_VALUE(5E-324),即小于 JavaScript 能够分辨的最小值,会被自动转为 0,也会产生误判
  Number.isInteger(5E-324);                             // false
  Number.isInteger(5E-325);                             // true
  // 用于判读整数数值是否在安全范围内
  Number.isSafeInteger();

  Number.isSafeInteger(Number.MIN_SAFE_INTEGER - 1);    // false
  Number.isSafeInteger(Number.MAX_SAFE_INTEGER + 1);    // false
Math 对象的扩展
  • ES6Math 对象上新增了 17 个数学相关的静态方法,这些方法只能在 Math 中调用;

  • 详情参考 ES6 Math 对象的扩展

  // 计算一个数的 立方根
  Math.cbrt(num);

  // 计算两个数以 32 位带符号整数形式相乘的结果,返回的也是一个 32 位带符号整数
  Math.imul(num1, num2);

  // 用于计算所有参数的 平方和的平方根
  Math.hypot(num1, num2, ...);
  Math.hypot(3, 4);         // 5

  // 用于返回数字的 32 位无符号整数形式的前导0的个数
  Math.clz32(num);

  // 用于返回数字的整数部分
  Math.trunc(num);

  // 用于获取数字的 32 位单精度浮点数形式
  Math.fround(num);

  // 判断数字的符号(正、负、0)
  Math.sign(num);

  // 用于计算 e 的 x 次方减 1 的结果,即 Math.exp(x)-1
  Math.expm1(num);

  // 用于计算 1+x 的自然对数,即 Math.log(1+x)
  Math.log1p(num);

  // 用于计算以 10 为底的 x 的对数
  Math.log10(num);

  // 用于计算以 2 为底的 x 的对数
  Math.log2(num);

  // 双曲函数方法
  // 正弦
  Math.sinh(x);

  // 余弦
  Math.cosh(x);

  // 正切
  Math.tanh(x);

  // 反正弦
  Math.asinh(x);

  // 反余弦
  Math.acosh(x);

  // 反正切
  Math.atanh(x);

  // 指数运算符
  // 右结合,从右往左计算
  **
  1 ** 2;               // 2
  2 ** 2 ** 3;  // 256

  let exam = 2;
  exam ** = 2;  // 4

ES6 对象

对象字面量
  • 属性的简洁表示法

ES6 允许对象的属性值直接写变量。这时候属性名时变量名,属性值是变量值;

  const age = 12;;
  const name = "Amy";
  const person = {age, name};
  person                                // {age: 12, name: "Amy"}
  // 等同于
  const person = {age: age, name: name};
  • 方法名也可以简写
  const person = {
    sayHi(){
      console.log("Hi");
    }
  }
  person.sayHi();               // "Hi"
  // 等同于
  const person = {
    sayHi:function(){
      console.log("Hi");
    }
  }
  person.sayHi();               // "Hi"

如果是 Generator 函数,则要在前面加一个星号;

  const obj = {
    * myGenerator(){
      yield 'hello world';
    }
  };
  // 等同于
  const obj = {
    myGenerator: function* (){
      yield 'hello world';
    }
  };
  • 属性名表达式

ES6 允许用表达式作为属性名,但是一定要将表达式放在方括号内;

  const obj = {
    ["he"+"llo"](){
      return "Hi";
    }
  }
  obj.hello();          // "Hi"

注意点: 属性的简洁表示法和属性名表达式 不能 同时使用,会报错;

  const hello = "Hello";
  const obj = {
    [hello]
  };
  obj                               // SyntaxError: Unexpected token }

  const hello = "Hello";
  const obj = {
    [hello+"2"]:"world"
  };
  obj                               // {Hello2: 'world'}
对象的拓展运算符
  • 扩展运算符(...)用于取出参数对象所有可遍历属性然后拷贝到当前对象;

  • 基本用法

  let person = {name: "Amy", age: 15};
  let someone = { ...person };
  someone;                      // {name: "Amy", age: 15}
  • 可用于合并两个对象
  let age = {age: 15};
  let name = {name: "Amy"};
  let person = {...age, ...name};
  person;                           // {age: 15, name: "Amy"}
  • 注意点

自定义属性和拓展运算符对象里面属性相同的时候:后面的属性会把前面的属性覆盖掉

  let person = {name: "Amy", age: 15};
  let someone = { ...person, name: "Mike", age: 17};
  someone;                      // {name: "Mike", age: 17}
  let person = {name: "Amy", age: 15};
  let someone = {name: "Mike", age: 17, ...person};
  someone;                      // {name: "Amy", age: 15}

拓展运算符后面是空对象,没有任何效果也不会报错

  let a = {...{}, a: 1, b: 2};
  a;                                    // {a: 1, b: 2}

拓展运算符后面是 null 或者 undefined,没有任何效果也不会报错

  let b = {...null, ...undefined, a: 1, b: 2};
  b;                                    // {a: 1, b: 2}
对象的新方法
  • Object.assing(target,source_1,…)

用于将源对象的所有可枚举属性复制到目标对象中;

基本用法

  let target = {a: 1};
  let object2 = {b: 2};
  let object3 = {c: 3};
  Object.assign(target,object2,object3);
  // 第一个参数是目标对象,后面的参数是源对象
  target;                                                       // {a: 1, b: 2, c: 3}

  // 如果目标独享和源对象有同名属性,或者多个源对象有同名属性,后面的属性会覆盖前面的属性
  // 如果该函数只有一个参数,当参数为对象时,直接返回该对象;当参数不是对象时,先将该参数转为对象然后返回;
  Object.assign(3);                                 // Number {3}
  typeof Object.assign(3);                  // "object"

  // 因为 null 和 undefined 不能转化为对象,所以会报错
  Object.assign(null);                          // TypeError: Cannot convert undefined or null to object
  Object.assign(undefined);                 // TypeError: Cannot convert undefined or null to object

  // 当 null 和 undefined 为源对象的参数时,会跳过,不报错
  Object.assign(1, undefined);          // Number {1}
  Object.assign({a: 1}, null);          // {a: 1}

  Object.assign(undefined, {a: 1}); // TypeError: Cannot convert undefined or null to object

注意点

assing 的属性拷贝是浅拷贝

  let sourceObj = {a: {b: 1}};
  let targetObj = {c: 3};
  Object.assign(targetObj, sourceObj);
  targetObj.a.b = 2;
  sourceObj.a.b;    // 2

同名属性替换

  let targetObj = {a: {b: 1, c: 2}};
  let sourceObj = {a: {b: "hh"}};
  Object.assign(targetObj, sourceObj);
  targetObj;            // {a: {b: "hh"}}
  // 会直接把 整个a 属性替换掉,而不是替换 a.b属性

数组的替换

  Object.assign([2,3], [5]);        // [5,3]
  // 会将数组处理成对象,所以先将 [2,3] 转化为 {0: 2, 1: 3},然后在进行属性复制。所以源对象的 0 号属性覆盖了目标对象的0 号属性
  • Object.is(value1,value2)

用来比较两个值是否严格相等,与(===)基本类似。

基本用法

  Object.is("q","q");               // true
  Object.is(1,1);                       // true
  Object.is([1],[1]);               // false
  Object.is({q:1},{q:1});       // false

与(===)的区别

  // 一是 +0不等于-0
  Object.is(+0,-0);                 // false
  +0 === -0                                 // true

  // 二是 NaN等于本身
  Object.is(NaN,NaN);               // true
  NaN === NaN                               // false

ES6 数组

数组创建
  • Array.of()

将参数中所有值作为元素形成数组

  console.log(Array.of(1, 2, 3, 4));            // [1, 2, 3, 4]

  // 参数值可为不同类型
  console.log(Array.of(1, '2', true));      // [1, '2', true]

  // 参数为空时返回空数组
  console.log(Array.of());                              // []
  • Array.from()

类数组对象可迭代对象 转化为数组

  // 参数为数组,返回和原数组一样的数组
  console.log(Array.from([1, 2]));              // [1, 2]

  // 参数含空位
  console.log(Array.from(1, , 3));              // [1, undefined, 3]

参数

  Array.from(arrayLike[, mapFn[, thisArg]])

返回值为转换后的数组

arrayLike

想要转化的 类数组对象可迭代对象

  console.log(Array.from([1, 2, 3]));                                   // [1, 2, 3]

mapFn

可选,map 函数,用于对每个元素进行处理,放入数组的是 处理后 的元素

  console.log(Array.from([1, 2, 3], (n) => n * 2));     // [2, 4, 6]

thisArg

可选,用于指定 map 函数执行时的 this 对象

  let map = {
    do: function(n) {
      return n * 2;
    }
  }
  let arrayLike = [1, 2, 3];
  console.log(Array.from(arrayLike, function (n) {
    return this.do(n);
  }, map));                                                                                     // [2, 4, 6]
  • 类数组对象

一个类数组对象必须含有 length 属性,且元素属性名必须是数值或者可转为数值的字符

  let arr = Array.from({
    0: '1',
    1: '2',
    2: 3,
    length: 3
  });
  console.log(arr);                 // ['1', '2', 3]

  // 没有 length 属性,则返回空数组
  let array = Array.from({
    0: '1',
    1: '2',
    2: 3
  });
  console.log(array);               // []

  // 元素属性名不为数值 且无法转换为数值,返回长度为 length ,元素值为 undefined 的数组
  let array1 = Array.from({
    a: 1,
    b: 2,
    length: 2
  });
  console.log(array1);          // [undefined, undefined]
  • 转换可迭代对象

转换 map

  let map = new Map();
  map.set('key0', 'value0');
  map.set('key1', 'value1');
  console.log(Array.from(map));             // [['key0', 'value0'], ['key1', 'value1']]

转换 set

  let arr = [1, 2, 3];
  let set = new Set(arr);
  console.log(Array.from(set));             // [1, 2, 3]

转换 字符串

  let str = 'abc';
  console.log(Array.from(str));             // ["a", "b", "c"]
扩展的方法
  • 查找

find()

查找数组中符合条件的元素,返回第一个元素

  let arr = Array.of(1, 2, 3, 4);
  console.log(arr.find(item => item > 2));                  // 3

  // 数组空位 处理为 undefined
  console.log([, 1].find(n => true));                               // undefined

findIndex()

查找数组中符合条件的元素索引,返回第一个元素索引

  let arr = Array.of(1, 2, 3, 4);
  // 参数1: 回调函数
  // 参数2(可选): 指定回调函数中的 this 值(可参考Array.from 的 thisArg参数)
  console.log(arr.findIndex(item => item === 3));       // 2
  • 填充

fill()

将一定范围索引的数组元素内容 填充 为单个指定的值

  let arr = Array.of(1, 2, 3, 4);
  // 参数1: 用来填充的值
  // 参数2: 被填充的起始索引
  // 参数3(可选): 被填充的结束索引,默认为数组末尾
  // 参数2 <= 填充值索引 < 参数3
  console.log(arr.fill(0, 1, 1));           // [1, 2, 3, 4]
  console.log(arr.fill(0, 1, 2));           // [1, 0, 3, 4]
  console.log(arr.fill(0, 1, 3));           // [1, 0, 0, 4]
  console.log(arr.fill(0, 1));              // [1, 0, 0, 0]

copyWithIn()

将一定范围索引的数组元素 修改 为此数组另一指定范围索引的元素

  // 参数1: 被修改的起始索引
  // 参数2: 被用来覆盖的数据的起始索引
  // 参数3(可选): 被用来覆盖的数组的结束索引,默认为数组末尾
  // 参数2 <= 覆盖值索引 < 参数3
  console.log([1, 2, 3, 4].copyWithIn(0, 2, 4));        // [3, 4, 3, 4]

  // 参数1位负数,表示倒数。最后一个数为 -1
  console.log([1, 2, 3, 4].copyWithIn(-2, 0));          // [1, 2, 1, 2]

  console.log([1, 2, , 4].copyWithIn(0, 2, 4));         // [, 4, , 4]
  • 遍历

entries()

遍历键值对

  for (let [key, value] of ['a', 'b'].entries()) {
    console.log(key, value);
  }
  // 0 "a"
  // 1 "b"

  // 不使用 for...of 循环
  let entries = ['a', 'b'].entries();
  console.log(entries.next().value);        // [0, "a"]
  console.log(entries.next().value);        // [1, "b"]

  // 数组含空位
  consoel.log([...[, 'a'].entries()]);  // [[0, undefined], [1, "a"]]

keys()

遍历键名

  for (let key of ['a', 'b'].keys()) {
    console.log(key);
  }
  // 0
  // 1

  // 数组含空位
  console.log([...[, 'a'].keys()]);         // [0, 1]

values()

遍历键的值

  for (let value of ['a', 'b'].values()) {
    console.log(value);
  }
  // "a"
  // "b"

  // 包含空数组
  console.log([...[, 'a'].values()]);       // [undefined, "a"]
  • 包含

includes()

数组是否包含指定值

注意:SetMaphas 方法区分;Sethas 方法用于查找值;Maphas 方法用于查找键名

  // 参数1: 包含的指定值
  // 参数2(可选): 搜索的起始索引,默认为0
  [1, 2, 3].includes(1);                                // true
  [1, 2, 3].includes(1, 2);                         // false
  [1, NaN, 3].includes(NaN);                        // true
  • 嵌套数组转一维数组

flat()

默认转换一层,即去掉一个中括号

  console.log([1, [2, 3]].flat());                                      // [1, 2, 3]

  // 指定转换的嵌套层数
  console.log([1, [2, [3, [4, 5]]]].flat(2));                   // [1, 2, 3, [4, 5]]

  // 不管嵌套多少层
  console.log([1, [2, [3, [4, 5]]]].flat(Infinity));    // [1, 2, 3, 4, 5]

  // 自动跳过空位
  console.log([1, [2, , 3]].flat());                                    // [1, 2, 3]

flatMap()

先对数组中的每个元素进行了处理,再对数组执行 flat() 方法

  // 参数1: 遍历函数,该遍历函数可接受3个参数:当前元素,当前元素索引,原数组
  // 参数2: 指定遍历函数中 this 的指向
  console.log([1, 2, 3].flatMap(n => [n * 2]));                 // [2, 4, 6]
数组缓冲区
定型数组
扩展运算符
  • 复制数组
  let arr = [1, 2];
  let arr1 = [...arr];
  console.log(arr1);                                            // [1, 2]

  // 数组含空位
  let arr2 = [1, , 3];
  let arr3 = [...arr2];
  console.log(arr3);                                            // [1, undefined, 3]
  • 合并数组
  console.log([...[1, 2], ...[3, 4]]);      // [1, 2, 3, 4]

运算符与语句

ES6 函数

函数参数的扩展
默认参数
  • 基本用法
  function fn(name, age = 17) {
    console.log(name + "," + age);
  }
  fn("Amy",18);             // Amy,18
  fn("Amy","");             // Amy,
  fn("Amy");                    // Amh,17
  • 注意点:使用函数默认参数时,不允许有同名参数
  // 不报错
  function fn(name, name) {
    console.log(name);
  }

  // 报错
  // SyntaxError: Duplicate parameter name not allowed in this context
  function fn(name, name, age=17) {
    console.log(name+","+age);
  }
  • 只有在未传递参数,或者参数为 nudefined 时,才会使用默认参数。null 值被认为是有效的值传递
  function fn(name, age=17) {
    console.log(name+","+age);
  }
  fn("Amy",null);               // Amy,null
  • 函数默认参数存在暂时性死区,在函数参数默认值表达式中,还未初始化赋值的参数值无法作为其他参数的默认值
  function f(x, y=x) {
    console.log(x,y);
  }
  f(1);                                 // 1 1

  function f(x=y) {
    console.log(x);
  }
  f();                                  // ReferenceError: y is not defined
不定参数
  • 不定参数用来表示不确定参数个数,形如,…变量名。由...加上一个具名参数标识符组成

  • 具名参数只能放在参数组的最后,并且有且只有一个不定参数

  function f(...values) {
    console.log(values.length);
  }

  f(1,2);                               // 2
  f(1,2,3,4);                       // 4
箭头函数
  • 箭头函数提供了一种更加简洁的函数书写方式。基本语法是:
  参数 => 函数体
  • 基本语法
  var f = v => v;
  // 等价于
  var f = function(a) {
    return a;
  }
  f(1);                                 // 1
  • 当箭头函数没有参数或者有多个参数,要用 () 括起来
  var f = (a,b) => a+b;
  f(6,2);                           // 8
  • 当箭头函数函数体有多行语句,要用 {} 括起来,表示代码块。当只有一行语句,并且需要返回结果时,可以省略 {} ,结果会自动返回
  var f = (a,b) => {
    let result = a+b;
    return result;
  }
  f(6,2);                           // 8
  • 当箭头函数要返回对象的时候,为了区分与代码块,要用 () 将对象包裹起来
  // 报错
  var f = (id,name) => {id: id, name: name};
  f(6,2);                           // SyntaxError: Unexpected token :

  // 不报错
  var f = (id,name) => ({id: id, name: name});
  f(6,2);                           // {id: 6, name: 2}
  • 注意点:没有 this、super、arguments 和 new.target 绑定
  var func = () => {
    // 箭头函数里没有 this 对象
    // 此时的 this 是外层的 this 对象,即 Window
    console.log(this);
  }
  func(55);                     // Window

  var func = () => {
    console.log(arguments);
  } 
  func(55);                     // ReferenceError: arguments is not defined
  • 箭头函数体中的 this 对象,是定义函数时的对象,而不是使用函数时的对象
  function fn(){
    setTimeout(()=>{
      // 定义时,this 绑定的是 fn 中的 this 对象
      console.log(this.a);
    },0)
  }
  var a = 20;
  // fn 的 this 对象为 {a: 19}
  fn.call({a: 18});         // 18
  • 不可以作为构造函数,也就是不能使用 new 命令,会报错
适合使用的场景
  • ES6 之前, JavaScriptthis 对象一直很令人头大,回调函数,经常看到 var self = this 这样的代码,为了将外部 this 传递到回调函数中。有了箭头函数,就不需要这样了;

回调函数 中的 普通函数this ,指 使用函数时的对象

回调函数 中的 箭头函数this ,指 定义函数时的对象

或者说,要 维护一个 this 的上下文 的时候,使用 箭头函数

  // 回调函数
  var Person = {
    'age': 18,
    'sayHello': function (){
      setTimeout(function(){
        console.log(this.age);
      },0);
    }
  };
  var age = 20;
  Person.sayHello();        // 20

  var Person1 = {
    'age': 18,
    'sayHello': function(){
      setTimeout(()=>{
        console.log(this.age);
      },0);
    }
  }
  var age = 20;
  Person1.sayHello();       // 18
不适合使用的场景
  • 定义函数的方法,且该方法中包含 this

一般函数 中的 普通函数this , 指 定义函数时的对象

一般函数 中的 箭头函数this , 指 外层的 this对象,最外层为全局对象 Window

  var Person = {
    'age': 18,
    'sayHello': function(){
      console.log(this.age);
    }
  }
  var age = 20;
  Person.sayHello();            // 18
                                                // 此时的 this 指 Person 对象

  var Person1 = {
    'age': 18,
    'sayHello': ()=>{
      console.log(this.age);
    }
  }
  var age = 20;
  Person1.sayHello();           // 18
                                                // 此时的 this 指 外层的 Window 全局对象
  • 需要动态 this 的时候
  var button = document.getElementById('userClick');
  button.addEventListener('click',()=>{
    this.classList.toggle('on');
  });
  // button 的监听函数是箭头函数,所以监听函数里的 this 指向的是定义的时候外层的 this 对象,即 Window。
  // 导致无法操作到被点击的按钮对象

ES6 迭代器

Iterator
  • 可迭代的数据结构

Array

String

Map

Set

Dom元素(正在进行中)

  • 普通对象(由 object 创建)不可迭代
for…of 循环
  • 常规数据类型

Array

String

Map

Set

  • 可迭代的数据类型

类数组对象

let、const 和 var

ES6 Class 类

基础用法
  • 类定义

  • 类声明

  • 注意要点

  • 类的主体

属性

静态属性

公共属性

实例属性

name 属性

方法

constructor 方法

返回对象

静态方法

原型方法

实例方法

  • 类的实例化

new

实例化对象

decorator
  • 类修饰

一个参数

多个参数——嵌套实现

  • 方法修饰

三个参数

封装与继承
  • getter / setter
  • extends
  • super
  • 注意要点

ES6 模块

概述
特点
export 与 import
  • 基本用法

  • as 的用法

  • import 命令的特点

只读属性

单例模式

  • export default 命令
复合使用

异步编程

ES6 Promise 对象

Promise 状态
  • 状态的特点
  • 状态的缺点
then 方法
  • then 方法的特点
  • then 方法的注意点
更多文章

ES6 Generator 函数

Generator 函数组成
  • function 后面,函数名之前有个 *
  • 函数内部有 yield 表达式
执行机制
  • next() 方法
函数返回的遍历器对象的方法
  • next 方法
  • return 方法
  • yield* 表达式
使用场景
  • 实现 Iterator

ES6 async 函数

async
  • 语法
  • 返回值
await
  • 语法
  • 返回值