ES6新特性


1. let 关键字

let 关键字用来声明变量,使用 let 声明的变量有几个特点:

  1. 不允许重复声明
    允许在程序的任何位置使用 var 重新声明 JavaScript 变量:

    var x = 10;
    
    // 现在,x 为 10
     
    var x = 6;
    // 现在,x 为 6
    

    在相同的作用域,或在相同的块中,通过 let 重新声明一个 var 变量是不允许的:

    var x = 10;       // 允许
    let x = 6;       // 不允许
    
    {
      var x = 10;   // 允许
      let x = 6;   // 不允许
    }
    

    在相同的作用域,或在相同的块中,通过 let 重新声明一个 let 变量是不允许的:

    let x = 10;       // 允许
    let x = 6;       // 不允许
    
    {
      let x = 10;   // 允许
      let x = 6;   // 不允许
    }
    

    在相同的作用域,或在相同的块中,通过 var 重新声明一个 let 变量是不允许的:

    let x = 10;       // 允许
    var x = 6;       // 不允许
    
    {
      let x = 10;   // 允许
      var x = 6;   // 不允许
    }
    

    在不同的作用域或块中,通过 let 重新声明变量是允许的:

    let x = 6;       // 允许
    
    {
      let x = 7;   // 允许
    }
    
    {
      let x = 8;   // 允许
    }
    

    使用 var 关键字重新声明变量会带来问题。在块中重新声明变量也将重新声明块外的变量:

    var x = 10;
    // 此处 x 为 10
    { 
      var x = 6;
      // 此处 x 为 6
    }
    // 此处 x 为 6
    

    使用 let 关键字重新声明变量可以解决这个问题。在块中重新声明变量不会重新声明块外的变量:

    var x = 10;
    // 此处 x 为 10
    { 
      let x = 6;
      // 此处 x 为 6
    }
    // 此处 x 为 10
    
  2. 块级作用域
    通过 var 关键词声明的变量没有块作用域。在块 {} 内声明的变量可以从块之外进行访问。

    { 
      var x = 10; 
    }
    // 此处可以使用 x
    

    可以使用 let 关键词声明拥有块作用域的变量。在块 {} 内声明的变量无法从块外访问:

    { 
      let x = 10;
    }
    // 此处不可以使用 x
    

    循环作用域
    在循环中使用 var

    var i = 7;
    for (var i = 0; i < 10; i++) {
      // 一些语句
    }
    // 此处,i 为 10
    

    在循环中使用 let

    let i = 7;
    for (let i = 0; i < 10; i++) {
      // 一些语句
    }
    // 此处 i 为 7
    
  3. 不存在变量提升
    通过 var 声明的变量会提升到顶端。您可以在声明变量之前就使用它:

    // 在此处,您可以使用 carName
    console.log(carName);
    var carName;
    

    通过 let 定义的变量不会被提升到顶端。在声明 let 变量之前就使用它会导致 ReferenceError。变量从块的开头一直处于“暂时死区”,直到声明为止:

    // 在此处,您不可以使用 carName
    let carName;
    
  4. 不影响作用域链

    {
    	let str = '孙悟空';
    	function fn() {
    		console.log(str);
    	}
    	fn();
    }
    

应用场景:以后声明变量使用 let 不会错

2. const 关键字

const 关键字用来声明常量,const 声明有以下特点:

  1. 声明必须赋初始值

    const PI = 3.14159265359;
    
  2. 标识符一般为大写

  3. 不允许重复声明
    在程序中的任何位置都允许重新声明 JavaScript var 变量:

    var x = 2;    //  允许
    var x = 3;    //  允许
    x = 4;        //  允许
    

    在同一作用域或块中,不允许将已有的 varlet 变量重新声明或重新赋值给 const

    var x = 2;         // 允许
    const x = 2;       // 不允许
    {
      let x = 2;     // 允许
      const x = 2;   // 不允许
    }
    

    在同一作用域或块中,为已有的 const 变量重新声明声明或赋值是不允许的:

    const x = 2;       // 允许
    const x = 3;       // 不允许
    x = 3;             // 不允许
    var x = 3;         // 不允许
    let x = 3;         // 不允许
    
    {
      const x = 2;   // 允许
      const x = 3;   // 不允许
      x = 3;         // 不允许
      var x = 3;     // 不允许
      let x = 3;     // 不允许
    }
    

    在另外的作用域或块中重新声明 const 是允许的:

    const x = 2;       // 允许
    
    {
      const x = 3;   // 允许
    }
    
    {
      const x = 4;   // 允许
    }
    
  4. 值不允许修改
    关键字 const 有一定的误导性。它没有定义常量值。它定义了对值的常量引用。
    因此,我们不能更改常量原始值:

    const PI = 3.141592653589793;
    PI = 3.14;      // 会出错
    PI = PI + 10;   // 也会出错
    

    但我们可以更改常量对象的属性:

    // 您可以创建 const 对象:
    const car = {type:"porsche", model:"911", color:"Black"};
    
    // 您可以更改属性:
    car.color = "White";
    
    // 您可以添加属性:
    car.owner = "Bill";
    

    但是您无法重新为常量对象赋值:

    const car = {type:"porsche", model:"911", color:"Black"};
    car = {type:"Volvo", model:"XC60", color:"White"};    // ERROR
    

    我们也可以更改常量数组的元素:

    // 您可以创建常量数组:
    const cars = ["Audi", "BMW", "porsche"];
    
    // 您可以更改元素:
    cars[0] = "Honda";
    
    // 您可以添加元素:
    cars.push("Volvo");
    

    但是您无法重新为常量数组赋值:

    const cars = ["Audi", "BMW", "porsche"];
    cars = ["Honda", "Toyota", "Volvo"];    // ERROR
    

    注意: 对象属性修改和数组元素变化不会出发 const 错误

  5. 块作用域
    在块作用域内使用 const 声明的变量与 let 变量相似。在本例中,x 在块中声明,不同于在块之外声明的 x

    var x = 10;
    // 此处,x 为 10
    { 
      const x = 6;
      // 此处,x 为 6
    }
    // 此处,x 为 10
    
  6. 不存在变量提升
    通过 var 声明的变量会提升到顶端。您可以在声明变量之前就使用它:

    // 在此处,您可以使用 carName
    console.log(carName);
    var carName;
    

    通过 const 定义的变量不会被提升到顶端。const 变量不能在声明之前使用:

    carName = "Volvo";    // 您不可以在此处使用 carName
    const carName = "Volvo";
    

应用场景:声明对象类型使用 const,非对象类型声明选择 let

3. 解构赋值

ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构赋值。

  1. 数组解构赋值
    数组必须按照顺序解构
    // 常规用法
    const F4 = ['小沈阳','刘能','赵四','宋小宝'];
    let [xiao, liu, zhao, song] = F4;
    console.log(xiao); // 小沈阳
    console.log(liu); // 刘能
    
    // 可设置默认值,当对应的值为undefined时才会使用默认值
    let [a, b, c, d, xie = '谢大脚'] = F4;
    console.log(xie); // 谢大脚
    
    // 可以使用rest操作符来捕获剩余项(必须是最后一个解构项)
    let [d, e, ...f] = F4;
    console.log(d); // 小沈阳
    console.log(e); // 刘能
    console.log(f); // ['赵四','宋小宝']
    
    // 不完全解构
    const [g,,h] = F4;
    console.log(g); // 小沈阳
    console.log(h); // 赵四
    
  2. 对象解构赋值
    // 常规用法
    const sun = {
    	name: '孙悟空',
    	age: '500',
    	fName:null,
    	daYaoGuai: function () {
    		console.log("我可以打妖怪");
    	}
    };
    
    let { name, age, daYaoGuai } = sun;
    console.log(name); // 孙悟空
    console.log(age); // 500
    console.log(daYaoGuai); // 这个方法
    daYaoGuai();  // 我可以打妖怪
    
    // 取别名
    let { name: ming, age, daYaoGuai } = sun;
    console.log(ming); // 孙悟空
    
    // 设置默认值,当定义的变量在对象中不存在时,其默认值才会生效
    let { name, fName: '齐天大圣', age, daYaoGuai } = sun;
    console.log(fName); // 齐天大圣
    
    // 可将剩余属性赋值给一个变量(必须是最后一个解构项)
    let { name, fName: '齐天大圣', ...b } = sun;
    console.log(name); // 孙悟空
    console.log(fName); // 齐天大圣
    console.log(b); // { age: '500',daYaoGuai:这个方法... }
    
  3. 复杂解构
    let sun = {
        name: '孙悟空',
        age: 500,
        brothers: ['猪八戒', '沙僧', '白龙马', '唐僧'],
        history: [
            { name: '白骨精' },
            { name: '黑熊精' },
            { name: '牛魔王' }
        ]
    };
    
    let { brothers: [one, two, three], history: [first, second, {name}] } = sun;
    console.log(one); // 猪八戒
    console.log(two); // 沙僧
    console.log(first); // { name: '白骨精' }
    console.log(second); // { name: '黑熊精' }
    console.log(name); // 牛魔王
    
    // 如果想要继续使用 brothers 或 history,只需要在解构时多写一个就可以了
    let { brothers, brothers: [one, two, three], history, history: [first, second, {name}] } = sun;
    console.log(brothers); // ['猪八戒', '沙僧', '白龙马', '唐僧']
    console.log(history); // [ { name: '白骨精' }, { name: '黑熊精' }, { name: '牛魔王' } ]
    

注意:频繁使用对象方法、数组元素,就可以使用解构赋值形式

4. 模板字符串

模板字符串是增强版的字符串,用反引号``来标识,他可以用来定义单行字符串,也可以定义多行字符串,或者在字符串中嵌入变量。

  1. 字符串中可以出现换行符
  2. 可以使用 ${ xxx } 形式输出变量
// bad
const field = "this is a" + example;

// good
const field = `this is a ${example}`;

注意:当遇到字符串与变量拼接的情况使用模板字符串

5. 函数默认参数

ES6 允许给函数参数赋值初始值

  1. 形参初始值 具有默认值的参数, 一般位置要靠后(潜规则)
  2. 可与解构赋值结合
function add(a, b, c = 10) {
    return a + b + c;
}
let result = add(1, 2);
console.log(result); // 13

6. 简化对象写法

ES6 允许在大括号里面,直接写入变量和函数,作为对象的属性和方法。书写更加简洁。

let name = '孙悟空';
let daYaoGuai = function () {
    console.log('我可以打妖怪!');
}
const sun = {
    name,
    change,
    doing() {
        console.log("我要去西天取经");
    }
}

7. 箭头函数

ES6中引入了箭头函数,用来简化函数的定义(更简洁更优雅):

let fn = (a,b) => {
    return a + b;
}
let result = fn(1, 2);
console.log(result); // 3
  1. 如果形参只有一个,则小括号可以省略

    let add = n => {
        return n + n;
    }
    console.log(add(9)); // 18
    
  2. 函数体如果只有一条语句,则花括号可以省略,函数的返回值为该条语句的执行结果

    let pow = n => n * n;
    console.log(pow(8)); // 64
    
  3. 箭头函数 this 指向声明时所在作用域下 this 的值
    箭头函数不会创建自己的this, 所以没有自己的this,它只会在自己作用域的上一层继承this。所以箭头函数中this的指向在它在创建时已经确定了,之后不会改变。

  4. 不可作为构造函数
    构造函数 new 操作符的执行步骤如下:

    1. 创建一个对象
    2. 将构造函数的作用域赋给新对象(也就是将对象的__proto__属性指向构造函数的prototype属性)
    3. 指向构造函数中的代码,构造函数中的this指向该对象(也就是为这个对象添加属性和方法)
    4. 返回新的对象
      实际上第三步就是将函数中的this指向该对象,又由于箭头函数时没有自己的this的,并且this指向外层的执行环境,不能改变指向,所以不能当做构造函数使用。

8. rest 参数

ES6 引入 rest 参数,用于获取函数的实参,用来代替 arguments

// ES5 获取实参的方式
function fn1() {
    console.log(arguments);
}
fn1(1, 2, 3, 4, 5);

// rest 参数
function fn2(a, b, ...args) {
    console.log(a, b, args);
}
fn2(100, 200, 1, 2, 3, 4, 5, 6);

注意:rest 参数非常适合不定个数参数函数的场景

9. spread 扩展运算符

扩展运算符:(...xxx)就像是rest参数的逆运算,将一个数组转为用逗号分隔的参数序列,对数组进行解包。
扩展运算放到一个数组/对象前面,就变成了展开运算符。

可替代数组concat/对象的 assign 方法,将一个数组/对象展开拆封,合并到一个数组/对象,属于浅拷贝的一种。

var arr1 = [1, 2, 3];
var arr2 = [...arr1, 4, 5, 6];
console.log(arr2);  // [1, 2, 3, 4, 5, 6]

将数组转化为用逗号分隔的参数序列:

function  test(a,b,c){
    console.log(a); // 1
    console.log(b); // 2
    console.log(c); // 3
}

var arr = [1, 2, 3];
test(...arr);

10. Symbol

ES6 引入了一种新的原始数据类型 Symbol,表示独一无二的值。
它是JavaScript 语言的第七种数据类型,是一种类似于字符串的数据类型。

特点:

  1. Symbol 的值是唯一的,用来解决命名冲突的问题
  2. Symbol 值不能与其他数据进行运算
  3. Symbol 定义的对象属性不能使用 for…in 循环遍历,但是可以使用 Reflect.ownKeys 来获取对象的所有键名
let s1 = Symbol();
console.log(typeof s1); // "symbol"

let s2 = Symbol('hello');
let s3 = Symbol('hello');
console.log(s2 === s3); // false

基于以上特性,Symbol 属性类型比较适合用于两类场景中:常量值和对象属性。

  1. 基本用法
    符号需要使用Symbol()函数初始化。

    let sym = Symbol();
    // 因为符号本身是原始类型,所以typeof操作符对符号返回symbol
    console.log(typeof sym); // symbol
    

    调用Symbol()函数时,也可以传入一个字符串参数作为对符号的描述,将来可以通过这个字符串来调试代码。但是,这个字符串参数与符号定义或标识完全无关:

    // Symbol的值是唯一的,不会出现相同值的常量
    let s1 = Symbol();
    let s2 = Symbol();
    console.log(s1 == s2); // false
    
    // 可以传入一个字符串参数作为对符号的描述
    let s3 = Symbol('foo');
    let s4 = Symbol('foo');
    console.log(s3 == s4); // false
    

    符号没有字面量语法。 按照规范,只要创建Symbol()实例并将其用作对象的新属性,就可以保证它不会覆盖已有的对象属性,无论是符号属性还是字符串属性。

    let s1 = Symbol();
    console.log(s1); // Symbol()
    
    let s2 = Symbol('foo');
    console.log(s2); // Symbol(foo)
    

    Symbol()函数不能与new关键字一起作为构造函数使用。
    这样做是为了避免创建符号包装对象,像使用BooleanStringNumber那样,它们都支持构造函数且可用于初始化包含原始值的包装对象。

    let b = new Boolean();
    console.log(typeof b); // "object"
    
    let str = new String();
    console.log(typeof str); // "object"
    
    let n = new Number();
    console.log(typeof n); // "object"
    
    let sym = new Symbol(); // 报错,TypeError
    console.log(sym);
    

    如果想使用符号包装对象,可以借用Object()函数:

    let sym1 = Symbol();
    let sym2 = Object(mySymbol);
    console.log(typeof sym2); // "object"
    
  2. 使用全局符号注册表
    如果运行时的不同部分需要共享和重用符号实例,那么可以用一个字符串作为键,在全局符号注册表中创建并重用符号。
    Symbol.for()方法:

    let fooSymbol = Symbol.for('foo');
    console.log(typeof fooSymbol); // symbol
    

    Symbol.for()对每个字符串键都执行幂等操作。

    第一次使用某个字符串调用时,它会检查全局运行时注册表,发现不存在对应的符号,于是就会生成一个新符号实例并添加到注册表中。
    后续使用相同字符串的调用同样会检查注册表,发现存在与该字符串对应的符号,然后就会返回该符号实例。

    // 创建新符号
    let sym1 = Symbol.for('foo');
    // 重用已有符号
    let sym2 = Symbol.for('foo');
    
    console.log(sym1 === sym2); // true
    

    采用相同符号,在全局注册表中定义的符号跟使用Symbol()定义的符号也不同:

    // 使用Symbol()定义
    let sym1 = Symbol('foo');
    // 使用全局注册表定义
    let sym2 = Symbol.for('foo');
    
    console.log(sym1 === sym2); // false
    

    全局注册表中的符号必须使用字符串键来创建,因此作为参数传给Symbol.for()的任何值都会被转换为字符串。

    注册表中使用的键同时也会被用作符号描述。

    let emptySymbol = Symbol.for();
    console.log(emptySymbol); // Symbol(undefined)
    

    使用 Symbol.keyFor() 来查询全局注册表,这个方法接收符号,返回该全局符号对应的字符串键。如果查询的不是全局符号,则返回undefined。

    // 创建全局符号
    let s1 = Symbol.for('foo');
    console.log(Symbol.keyFor(s1)); // foo
    
    // 创建普通符号
    let s2 = Symbol('bar');
    console.log(Symbol.keyFor(s2)); // undefined
    

    如果传给Symbol.keyFor()的不是符号,则该方法抛出TypeError。

    Symbol.keyFor(123); // TypeError: 123 is not a symbol
    
  3. 使用符号作为属性
    凡是可以使用字符串或数值作为属性的地方,都可以使用符号。
    包括对象字面量属性和 Object.defineProperty(obj, prop, descriptor) / Object.defineProperties() 定义的属性。
    对象字面量只能在计算属性语法中使用符号作为属性。

    let s1 = Symbol('abc'),
        s2 = Symbol('def'),
        s3 = Symbol('ghi'),
        s4 = Symbol('jkl');
    
    let o = {
        // [属性],会对属性进行读取,并且转换成字符串。[s1]是读取了Symbol的字符串键'abc'
        [s1]: 'abc val'
    };
    // 或 o[s1] = 'abc val';
    console.log(o); // { [Symbol(abc)]: 'abc val' }
    
    Object.defineProperty(o, s2, { value: 'def val' });
    console.log(o); // {Symbol(abc): abc val, Symbol(def): def val}
    
    Object.defineProperties(o, {
        [s3]: { value: 'ghi val' },
        [s4]: { value: 'jkl val' }
    });
    console.log(o); // {Symbol(abc): abc val, Symbol(def): def val, Symbol(ghi): ghi val, Symbol(jkl): jkl val}
    
    let s1 = Symbol('abc'),
        s2 = Symbol('def');
    
    let o = {
        [s1]: 'abc val',
        [s2]: 'def val',
        ghi: 'ghi val',
        jkl: 'jkl val'
    };
    
    // Object.getOwnPropertySymbols()返回对象实例的符号属性数组
    console.log(Object.getOwnPropertySymbols(o)); // [ Symbol(abc), Symbol(def) ]
    
    // Object.getOwnPropertyNames()返回对象实例的常规属性数组
    console.log(Object.getOwnPropertyNames(o)); // [ 'ghi', 'jkl' ]
    
    // Object.getOwnPropertyDescriptors()会返回同时包含常规和符号属性描述符的对象
    console.log(Object.getOwnPropertyDescriptors(o));
    
    // Reflect.ownKeys()会返回两种类型的键
    console.log(Reflect.ownKeys(o)); // [ 'ghi', 'jkl', Symbol(abc), Symbol(def) ]
    

    注意:Object.getOwnPropertyNames()Object.getOwnProperty-Symbols()两个方法的返回值彼此互斥。

    因为符号属性是对内存中符号的一个引用,所以直接创建并用作属性的符号不会丢失。

    但是,如果没有显式地保存对这些属性的引用,那么必须遍历对象的所有符号属性才能找到相应的属性键。

    let o = {
        [Symbol('ab')]: 'ab val',
        [Symbol('cd')]: 'cd val'
    };
    console.log(o); // { [Symbol(ab)]: 'ab val', [Symbol(cd)]: 'cd val' }
    
    let sym = Object.getOwnPropertySymbols(o).find((Symbol) => Symbol.toString().match(/ab/));
    console.log(stm); // Symbol(ab)
    
  4. .Symbol 内置值
    除了定义自己使用的 Symbol 值以外,ES6 还提供了 11 个内置的 Symbol 值,指向语言内部使用的方法。可以称这些方法为魔术方法,因为它们会在特定的场景下自动执行。

    属性含义
    Symbol.hasInstance当其他对象使用 instanceof 运算符,判断是否为该对象的实例时,会调用这个方法
    Symbol.isConcatSpreadable对象的 Symbol.isConcatSpreadable 属性等于的是一个布尔值,表示该对象用于Array.prototype.concat() 时,是否可以展开。
    Symbol.species创建衍生对象时,会使用该属性
    Symbol.match当执行 str.match(myObject) 时,如果该属性存在,会调用它,返回该方法的返回值。
    Symbol.replace当该对象被 str.replace(myObject) 方法调用时,会返回该方法的返回值。
    Symbol.search当该对象被 str.search (myObject) 方法调用时,会返回该方法的返回值。
    Symbol.split当该对象被 str.split(myObject) 方法调用时,会返回该方法的返回值。
    Symbol.iterator对象进行 for...of 循环时,会调用 Symbol.iterator 方法,返回该对象的默认遍历器
    Symbol.toPrimitive该对象被转为原始类型的值时,会调用这个方法,返回该对象对应的原始类型值。
    Symbol.toStringTag在该对象上面调用 toString 方法时,返回该方法的返回值
    Symbol.unscopables该对象指定了使用 with 关键字时,哪些属性会被 with 环境排除。

11. 迭代器

遍历器(Iterator)就是一种机制。它是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 Iterator 接口,就可以完成遍历操作。

  1. ES6 创造了一种新的遍历命令 for...of 循环,Iterator 接口主要供 for...of 消费
  2. 原生具备 iterator 接口的数据(可用 for...of 遍历)
    ArrayArgumentsSetMapStringTypedArrayNodeList
  3. 工作原理
    a. 创建一个指针对象,指向当前数据结构的起始位置
    b. 第一次调用对象的 next 方法,指针自动指向数据结构的第一个成员
    c. 接下来不断调用 next 方法,指针一直往后移动,直到指向最后一个成员
    d. 每调用 next 方法返回一个包含 valuedone 属性的对象

注: 需要自定义遍历数据的时候,要想到迭代器。

//声明一个数组
const xiYouJi = ['唐僧', '孙悟空', '猪八戒', '沙僧'];

//使用 for...of 遍历数组
for (let v of xiYouJi) {
    console.log(v); // 唐僧 孙悟空 猪八戒 沙僧
}
let iterator = xiYouJi[Symbol.iterator]();
//调用对象的next方法
console.log(iterator.next()); // {value: '唐僧', done: false}
console.log(iterator.next()); // {value: '孙悟空', done: false}
console.log(iterator.next()); // {value: '猪八戒', done: false}
console.log(iterator.next()); // {value: '沙僧', done: false}
console.log(iterator.next()); // {value: undefined, done: true}

迭代器自定义遍历对象:

//声明一个对象
let shui_hu = {
    name: "水浒",
    staff: [
        '宋江',
        '李逵',
        '时迁',
        '鲁智深'
    ],
    [Symbol.iterator]() {
        //索引变量
        let index = 0;
        // 存储this
        let _this = this;
        return {
            next: function () {
                if (index < _this.staff.length) {
                    const result = { value: _this.staff[index], done: false };
                    //下标自增
                    index++;
                    //返回结果
                    return result;
                } else {
                    return { value: undefined, done: true };
                }
            }
        };
    }
}
//遍历这个对象 
for (let v of shui_hu) {
    console.log(v);
}

12. 生成器

生成器函数是 ES6 提供的一种异步编程解决方案,语法行为与传统函数完全不同。
生成器是一个特殊函数,特殊的迭代器(Iterator

代码说明:

  1. * )的位置没有限制
  2. 生成器函数返回的结果是迭代器对象,调用迭代器对象的 next 方法可以得到yield 语句后的值
  3. yield 相当于函数的暂停标记,也可以认为是函数的分隔符,每调用一次 next 方法,执行一段代码
  4. next 方法可以传递实参,作为 yield 语句的返回值

1. 生成器的迭代
for-of 遍历

function * gen(){
	yield '孙悟空';
	yield '猪八戒';
	yield '沙和尚';
}

//遍历
for(let v of gen()){
  console.log(v);
}
// v= 孙悟空
// v= 猪八戒
// v= 沙和尚

2. 生成器执行next

  • yield 的参数是next方法的返回值
  • yield 把代码分成N段执行
  • next 方法可以分段执行
function* gen() {
    console.log('111')
    yield "孙悟空"; // 第1段
    console.log('222')
    yield "猪八戒" // 第2段
    console.log('333')
    yield "沙和尚" // 第3段
    console.log('444')
}                 // 第4段
let iterator = gen()
console.log('iterator.next()=', iterator.next())
// 111
// iterator.next()= {value: '孙悟空', done: false}
console.log('iterator.next()=', iterator.next())
// 222
// iterator.next()= {value: '猪八戒', done: false}
console.log('iterator.next()=', iterator.next())
// 333
// iterator.next()= {value: '沙和尚', done: false}
console.log('iterator.next()=', iterator.next())
// 444
// iterator.next()= {value: undefined, done: true}

3. 生成器yield的返回值

  • next方法的参数是yield的返回值
  • 第N个next方法的参数是N-1yield的返回值
function* gen(arg) {
    console.log('arg', arg); // arg AAA
    let one = yield 111;
    console.log('one', one) // one BBB
    let two = yield 222;
    console.log('two', two); // two CCC
    let three = yield 333;
    console.log('three', three); // three DDD
}

//执行获取迭代器对象
let iterator = gen('AAA');
console.log('iterator.next()', iterator.next());
// iterator.next() {value: 111, done: false}

//next方法可以传入实参
console.log("iterator.next('BBB')", iterator.next('BBB'));
// iterator.next('BBB') {value: 222, done: false}

console.log("iterator.next('CCC')", iterator.next('CCC'));
// iterator.next('CCC') {value: 333, done: false}

console.log("iterator.next('DDD')", iterator.next('DDD'));
// iterator.next('DDD') {value: undefined, done: true}

4. 异步任务同步化表达

  • 需求: 按顺序获取异步请求的结果
  • 顺序: 用户数据 > 订单数据 > 商品数据
  • 异步: 使用 settimout 模拟
// 使用回调方式执行: 回调地狱
function getData() {
    setTimeout(() => {
        console.log('用户数据')
        setTimeout(() => {
            console.log('订单数据')
            setTimeout(() => {
                console.log('商品数据')
            }, 500)
        }, 500);
    }, 500)
}
getData()
//模拟获取  用户数据  订单数据  商品数据 
function getUsers() {
    setTimeout(() => {
        let data = '用户数据';
        //调用 next 方法, 并且将数据传入
        iterator.next(data);
    }, 1000);
}
function getOrders() {
    setTimeout(() => {
        let data = '订单数据';
        iterator.next(data);
    }, 1000)
}
function getGoods() {
    setTimeout(() => {
        let data = '商品数据';
        iterator.next(data);
    }, 1000)
}
function* gen() {
    let users = yield getUsers();
    let orders = yield getOrders();
    let goods = yield getGoods();
}
//调用生成器函数
let iterator = gen();
iterator.next();

13. Promise

感谢阅读,正在努力撰写博客,敬请稍候。

14. Set

ES6 提供了新的数据结构 Set(集合)。它类似于数组,但成员的值都是唯一的,集合实现了 iterator 接口,所以可以使用『扩展运算符』和『for…of…』进行遍历。

JavaScript 的 Set(集合)是一组唯一值的集合。
每个值只能在 Set 中出现一次。
Set 可以容纳任何数据类型的值。

Set 集合的属性和方法:

属性/方法方法
size返回集合的元素个数
new Set()创建新的 Set
add()增加一个新元素,返回当前集合
delete()删除元素,返回 boolean
has()检测集合中是否包含某个元素,返回 boolean
clear()清空集合,返回 undefined
forEach()为每个元素调用回调函
values()返回包含 Set 中所有值的迭代器
keys()values() 相同
entries()返回迭代器,其中包含 Set 中的 [value,value] 值值对
  1. 如何创建 Set

    • 将数组传递给 new Set()
    • 创建一个新的 Set,然后使用 add() 方法添加值
    • 创建一个新的 Set,然后使用 add() 方法添加变量
  2. 新的 Set() 方法
    将数组传递给新的 Set() 构造函数:

    // 创建 Set
    const letters = new Set(["a","b","c"]);
    

    创建一个 Set 并添加字面量值:

    // 创建 Set
    const letters = new Set();
    
    // 将值添加到 Set
    letters.add("a");
    letters.add("b");
    letters.add("c");
    

    创建一个 Set 并添加变量:

    // 创建变量
    const a = "a";
    const b = "b";
    const c = "c";
    
    // 创建 Set
    const letters = new Set();
    
    // 向 Set 添加变量
    letters.add(a);
    letters.add(b);
    letters.add(c);
    
  3. add() 方法
    基础用法:

    // 创建新的 Set
    const letters = new Set(["a","b","c"]);
    
    // 添加新元素
    letters.add("d");
    letters.add("e");
    

    如果您添加相等的元素,只有第一个会被保存:

    // 创建 Set
    const letters = new Set();
    
    // 向 Set 中添加值
    letters.add("a");
    letters.add("b");
    letters.add("c");
    letters.add("c");
    letters.add("c");
    
  4. forEach() 方法

    // 创建 Set
    const letters = new Set(["a", "b", "c"]);
    // 列出所有元素
    letters.forEach((item) => {
        console.log('item', item)
    })
    
  5. values() 方法

    const letters = new Set(["a", "b", "c"]);
    
    console.log('letters.values()', letters.values())
    // letters.values() => SetIterator {'a', 'b', 'c'}
    
    for (const x of letters.values()) {
        console.log('x', x)
    }
    // x a
    // x b
    // x c
    
  6. keys() 方法
    Set 没有键。
    keys() 返回与 values() 相同的结果。
    这使得 Set 与 Map 相兼容。

    const letters = new Set(["a", "b", "c"]);
    	
    console.log('letters.keys()', letters.keys())
    // letters.keys() => SetIterator {'a', 'b', 'c'}
    	
    for (const x of letters.keys()) {
    	console.log('x', x)
    }
    // x a
    // x b
    // x c
    
  7. entries() 方法
    Set 没有键(key)。
    entries() 方法返回的是 [value,value] 值值对,而不是 [key,value] 键值对。
    这使得 SetMap 兼容:

    const letters = new Set(["a", "b", "c"]);
    const iterator = letters.entries();
    for (const x of iterator) {
        console.log('x', x)
    }
    // x ['a', 'a']
    // x ['b', 'b']
    // x ['c', 'c']
    
  8. 实际应用
    由于集合中元素的唯一性,所以在实际应用中,可以使用Set来实现数组去重:

    let arr = [1,2,3,4,5,4,3,2,1];
    Array.from(new Set(arr))  // [1, 2, 3, 4, 5]
    

    可以通过Set来求两个数组的交集、并集、差集:

    let arr1 = [1, 2, 3, 4, 5, 4, 3, 2, 1];
    let arr2 = [3, 4, 5, 6, 7, 6, 5, 4, 3];
    // 交集
    let result = [...new Set(arr1)].filter(item => new Set(arr2).has(item));
    console.log(result); // [3, 4, 5]
    // 并集
    let union = [...new Set([...arr1, ...arr2])];
    console.log(union); // [1, 2, 3, 4, 5, 6, 7]
    // 差集
    let diff1 = [...new Set(arr1)].filter(item => !(new Set(arr2).has(item)));
    console.log(diff1); // [1, 2]
    let diff2 = [...new Set(arr2)].filter(item => !(new Set(arr1).has(item)));
    console.log(diff2); // [6, 7]
    

    用以下方法可以进行数组与集合的相互转化:

    // Set集合转化为数组
    const arr = [...mySet]
    const arr = Array.from(mySet)
    
    // 数组转化为Set集合
    const mySet = new Set(arr)
    

15. Map

ES6 提供了 Map 数据结构。它类似于对象,也是键值对的集合。但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。Map 也实现了iterator 接口,所以可以使用『扩展运算符』和『for…of…』进行遍历。

Map 保存键值对,其中键可以是任何数据类型。
Map 会记住键的原始插入顺序。
Map 提供表示映射大小的属性。

Map 的属性和方法:

属性/方法方法
size返回 Map 的元素个数
new Map()创建新的 Map 对象
set()增加一个新元素,返回当前 Map
get()返回键名对象的键值
has()检测 Map 中是否包含某个元素,返回 boolean
clear()清空集合,返回 undefined
delete()删除由某个键指定的 Map 元素
forEach()Map 中的每个键/值对调用回调函数
entries()返回迭代器对象,其中包含 Map 中的 [key, value] 键值对
keys()返回迭代器对象,其中包含 Map 中的键
values()返回迭代器对象,其中包含 Map 中的值
  1. 如何创建 Map

    • 将数组传递给 new Map()
    • 创建映射并使用 Map.set()
  2. new Map()
    通过将数组传递给 new Map() 构造函数来创建 Map

    // 创建一个 Map
    const fruits = new Map([
      ["apples", 500],
      ["bananas", 300],
      ["oranges", 200]
    ]);
    
  3. Map.set()
    使用 set() 方法将元素添加到 Map 中:

    // 创建一个 Map
    const fruits = new Map();
    
    // 设置 Map 的值
    fruits.set("apples", 500);
    fruits.set("bananas", 300);
    fruits.set("oranges", 200);
    

    set() 方法还可用于更改现有的 Map 值:

    const fruits = new Map([
      ["apples", 500],
      ["bananas", 300],
      ["oranges", 200]
    ]);
    
    fruits.set("apples", 200);
    
  4. Map.get()
    get() 方法获取 Map 中键的值:

    const fruits = new Map([
      ["apples", 500],
      ["bananas", 300],
      ["oranges", 200]
    ]);
    
    fruits.get("apples"); // 返回 500
    
  5. Map.size
    size 属性返回 Map 中元素的数量:

    const fruits = new Map([
      ["apples", 500],
      ["bananas", 300],
      ["oranges", 200]
    ]);
    
    fruits.size; // 3
    
  6. Map.delete()
    delete() 方法删除 Map 元素:

    const fruits = new Map([
      ["apples", 500],
      ["bananas", 300],
      ["oranges", 200]
    ]);
    
    // Delete an Element
    fruits.delete("apples");
    
  7. Map.clear()
    clear() 方法从 Map 中删除所有元素:

    const fruits = new Map([
      ["apples", 500],
      ["bananas", 300],
      ["oranges", 200]
    ]);
    
    // Clear the Map
    fruits.clear();
    
  8. Map.has()
    如果 Map 中存在键,则 has() 方法返回 true

    const fruits = new Map([
      ["apples", 500],
      ["bananas", 300],
      ["oranges", 200]
    ]);
    
    fruits.has("apples"); // true
    
  9. Map.forEach()
    forEach() 方法为 Map 中的每个键/值对调用回调:

    const fruits = new Map([
      ["apples", 500],
      ["bananas", 300],
      ["oranges", 200],
    ]);
    fruits.forEach(function (value, key) {
      console.log(`${key} - ${value}`);
    });
    
  10. Map.entries()
    entries() 方法返回一个带有 Map 中 [key,values] 的迭代器对象:

    const fruits = new Map([
      ["apples", 500],
      ["bananas", 300],
      ["oranges", 200],
    ]);
    for (const x of fruits.entries()) {
      console.log("x", x);
    }
    
  11. Map.keys()
    keys() 方法返回一个迭代器对象,其中包含 Map 中的键:

    const fruits = new Map([
      ["apples", 500],
      ["bananas", 300],
      ["oranges", 200],
    ]);
    for (const x of fruits.keys()) {
      console.log("x", x);
    }
    
  12. Map.values()
    values() 方法返回一个迭代器对象,其中包含 Map 中的值:

    const fruits = new Map([
      ["apples", 500],
      ["bananas", 300],
      ["oranges", 200],
    ]);
    for (const x of fruits.values()) {
      console.log("x", x);
    }
    
  13. 将对象用作键
    能够将对象用作键是 Map 的一项重要特性。

    // 创建对象
    const apples = {name: 'Apples'};
    const bananas = {name: 'Bananas'};
    const oranges = {name: 'Oranges'};
    
    // 创建 Map
    const fruits = new Map();
    
    // 将对象添加到 Map
    fruits.set(apples, 500);
    fruits.set(bananas, 300);
    fruits.set(oranges, 200);
    
    fruits.get(apples); // 500
    fruits.get("apples"); // undefined
    

16. class 类

ES6 提供了更接近传统语言的写法,引入了 Class(类)这个概念,作为对象的模板。通过 class 关键字,可以定义类。基本上,ES6 的 class 可以看作只是一个语法糖,它的绝大部分功能,ES5 都可以做到,新的 class 写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。

  1. 类的语法
    请使用关键字 class 创建一个类。
    请始终添加一个名为 constructor() 的方法:

    语法

    class ClassName {
      constructor() { ... }
    }
    

    实例
    创建一个名为 “Car” 的类,该类有两个初始属性:“name” 和 “year”:

    class Car {
      constructor(name, year) {
        this.name = name;
        this.year = year;
      }
    }
    
  2. 使用类

    class Car {
      constructor(name, year) {
        this.name = name;
        this.year = year;
      }
    }
    
    let myCar1 = new Car("Ford", 2014);
    let myCar2 = new Car("Audi", 2019);
    

    上面的例子使用 Car 类来创建两个 Car 对象。
    在创建新对象时会自动调用构造方法(constructor method)。

  3. 构造方法
    构造方法是一种特殊的方法:

    • 它必须有确切的名称的 “constructor
    • 创建新对象时自动执行
    • 用于初始化对象属性
    • 如果您没有定义构造方法,JavaScript 会添加一个空的构造方法。
  4. 类方法
    创建类方法的语法与对象方法相同。
    请使用关键字 class 创建一个类。
    请始终添加 constructor() 方法。
    然后添加任意数量的方法。

    语法

    class ClassName {
      constructor() { ... }
      method_1() { ... }
      method_2() { ... }
      method_3() { ... }
    }
    

    实例
    创建一个名为 “age” 的类方法,它返回车年:

    class Car {
      constructor(name, year) {
        this.name = name;
        this.year = year;
      }
      age() {
        let date = new Date();
        return date.getFullYear() - this.year;
      }
    }
    
    let myCar = new Car("Ford", 2014);
    myCar.age()
    

    您可以向类方法发送参数:

    class Car {
      constructor(name, year) {
        this.name = name;
        this.year = year;
      }
      age(x) {
        return x - this.year;
      }
    }
    
    let date = new Date();
    let year = date.getFullYear();
    
    let myCar = new Car("Ford", 2014);
    myCar.age(year)
    
  5. 类继承
    如需创建类继承,请使用 extends 关键字。
    使用类继承创建的类继承了另一个类的所有方法:

    实例
    创建一个名为 “Model” 的类,它将继承 “Car” 类的方法:

    class Car {
      constructor(brand) {
        this.carname = brand;
      }
      present() {
        return 'I have a ' + this.carname;
      }
    }
    
    class Model extends Car {
      constructor(brand, mod) {
        super(brand);
        this.model = mod;
      }
      show() {
        return this.present() + ', it is a ' + this.model;
      }
    }
    
    let myCar = new Model("Ford", "Mustang");
    console.log(myCar.show()); // I have a Ford, it is a Mustang
    

    super() 方法引用父类。
    通过在 constructor 方法中调用 super() 方法,我们调用了父级的 constructor 方法,获得了父级的属性和方法的访问权限。继承对于代码可重用性很有用:在创建新类时重用现有类的属性和方法。

  6. Getter 和 Setter
    类还允许您使用 gettersetter
    为您的属性使用 gettersetter 很聪明,特别是如果您想在返回它们之前或在设置它们之前对值做一些特殊的事情。

    如需在类中添加 gettersetter,请使用 getset 关键字。

    实例
    为 “carname” 属性创建 gettersetter

    class Car {
      constructor(brand) {
        this.carname = brand;
      }
      get cnam() {
        return this.carname;
      }
      set cnam(x) {
        this.carname = x;
      }
    }
    
    let myCar = new Car("Ford");
    console.log(myCar.cnam);
    

    注释:即使 getter 是一个方法,当你想要获取属性值时也不要使用括号。
    getter/setter 方法的名称不能与属性名称相同,在本例中为 carname。
    许多程序员在属性名称前使用下划线字符 _getter/setter 与实际属性分开:

    实例
    您可以使用下划线字符将 getter/setter 与实际属性分开:

    class Car {
      constructor(brand) {
        this._carname = brand;
      }
      get carname() {
        return this._carname;
      }
      set carname(x) {
        this._carname = x;
      }
    }
    
    let myCar = new Car("Ford");
    console.log(myCar.carname);
    

    如需使用 setter,请使用与设置属性值相同的语法,不带括号:

    class Car {
      constructor(brand) {
        this._carname = brand;
      }
      get carname() {
        return this._carname;
      }
      set carname(x) {
        this._carname = x;
      }
    }
    
    let myCar = new Car("Ford");
    myCar.carname = "Volvo";
    console.log(myCar.carname);
    
  7. Static 方法
    static 类方法是在类本身上定义的。
    您不能在对象上调用 static 方法,只能在对象类上调用。

    实例

    class Car {
      constructor(name) {
        this.name = name;
      }
      static hello() {
        return "Hello!!";
      }
    }
    
    let myCar = new Car("Ford");
    
    // 您可以在 Car 类上调用 'hello()' :
    console.log(Car.hello());
    
    // 但不能在 Car 对象上调用:
    // console.log(myCar.hello());
    // 此举将引发错误。
    

    如果要在 static 方法中使用 myCar 对象,可以将其作为参数发送:

    class Car {
      constructor(name) {
        this.name = name;
      }
      static hello(x) {
        return "Hello " + x.name;
      }
    }
    let myCar = new Car("Ford");
    console.log(Car.hello(myCar));
    

17. 数值扩展

  1. 新的数字属性
    ES6 将以下属性添加到 Number 对象:

    • EPSILON 是 JavaScript 表示的最小精度
    • MIN_SAFE_INTEGER 代表在 JavaScript中最小的安全的integer型数字
    • MAX_SAFE_INTEGER 常量表示在 JavaScript 中最大的安全整数
    let x = Number.EPSILON;
    let min = Number.MIN_SAFE_INTEGER;
    let max = Number.MAX_SAFE_INTEGER;
    console.log(x); // 2.220446049250313e-16
    console.log(min); // -9007199254740991
    console.log(max); // 9007199254740991
    
  2. 新的数字方法
    ES6 为 Number 对象添加了 2 个新方法:

    • Number.isInteger()
    • Number.isSafeInteger()

    Number.isInteger() 方法
    如果参数是整数,则 Number.isInteger() 方法返回 true。

    Number.isInteger(10); // true
    Number.isInteger(10.5); // false
    

    Number.isSafeInteger() 方法
    安全整数是可以精确表示为双精度数的整数。
    如果参数是安全整数,则 Number.isSafeInteger() 方法返回 true。

    Number.isSafeInteger(10); // true
    Number.isSafeInteger(12345678901234567890); // false
    

    安全整数指的是从 -(253 - 1) 到 +(253 - 1) 的所有整数。
    这是安全的:9007199254740991。这是不安全的:9007199254740992。

  3. 新的全局方法
    ES6 还增加了 2 个新的全局数字方法:

    • isFinite()
    • isNaN()

    isFinite() 方法
    如果参数为 InfinityNaN,则全局 isFinite() 方法返回 false。否则返回 true

    isFinite(10/0); // false
    isFinite(10/1); // true
    isFinite(10); // true
    isFinite(Infinity); // false
    

    isNaN() 方法
    如果参数是 NaN,则全局 isNaN() 方法返回 true。否则返回 false

    isNaN("Hello"); // true
    isNaN(123); // false
    
  4. 二进制和八进制
    ES6 提供了二进制和八进制数值的新的写法,分别用前缀 0b0o 表示。

    let b = 0b1010;
    let o = 0o777;
    let d = 100;
    let x = 0xff;
    console.log('b', b) // 10
    console.log('o', o) // 511
    console.log('d', d) // 100
    console.log('x', x) // 255
    
  5. Number.parseInt() 与 Number.parseFloat()
    ES6 将全局方法 parseIntparseFloat,移植到 Number 对象上面,使用不变。

  6. Math.trunc
    用于去除一个数的小数部分,返回整数部分。

    Math.trunc(3.5) // 3
    

18. 对象扩展

ES6 新增了一些 Object 对象的方法

  1. Object.is()
    Object.is() 比较两个值是否严格相等,与『===』行为基本一致,不同的地方在于+0/-0NaN

    console.log(+0 === -0); // true
    console.log(NaN === NaN); // false
    
    console.log(Object.is(+0, -0)); // false
    console.log(Object.is(NaN, NaN)); // true
    

    语法

    Object.is(value1, value2);
    

    Object.is()方法返回一个布尔类型的值,若满足以下条件则两个值相等:

    • 都是undefined
    • 都是null
    • 都是true或都是false
    • 都是字符串,且相同长度、相同字符、相同顺序
    • 是相同对象(每个对象有同一个引用)
    • 都是数字,且满足以下任意一个
      • 都是0
      • 都是+0
      • 都是-0
      • 都非零,且非NaN,且是同一个值
      • 都是NaN
  2. Object.assign()
    Object.assign() 对象的合并,将源对象的所有可枚举属性,复制到目标对象

    语法
    target是目标对象,sources是源对象:

    Object.assign(target, sources1, sources2, sources3...)
    

    实例

    let obj1 = { a: 1 };
    let obj2 = { b: 2 };
    
    let res = Object.assign(obj1, obj2);
    console.log(res); // { a: 1, b: 2 }
    

    如果目标对象和源对象中有相同的属性,那么源对象的属性将覆盖目标对象的属性:

    let obj1 = { a: 1, b: 2 };
    let obj2 = { a: 2 };
    
    let res = Object.assign(obj1, obj2);
    console.log(res); // { a: 2, b: 2 }
    

19. 数组方法

  1. reduce()
    reduce() 方法对数组中的每个元素执行一个reducer函数(升序执行),将其结果汇总为单个返回值。其使用语法如下:

    arr.reduce(callback,[initialValue])
    

    reduce 为数组中的每一个元素依次执行回调函数,不包括数组中被删除或从未被赋值的元素,接受四个参数:初始值(或者上一次回调函数的返回值),当前元素值,当前索引,调用 reduce 的数组。
    callback (执行数组中每个值的函数,包含四个参数)

    • previousValue (上一次调用回调返回的值,或者是提供的初始值(initialValue))
    • urrentValue (数组中当前被处理的元素)
    • index (当前元素在数组中的索引)
    • array (调用 reduce 的数组)

    initialValue (作为第一次调用 callback 的第一个参数。)

    let arr = [1, 2, 3, 4]
    let sum = arr.reduce((prev, cur, index, arr) => {
        console.log(prev, cur, index);
        return prev + cur;
    })
    console.log(arr, sum);
    // 1 2 1
    // 3 3 2
    // 6 4 3
    // [1, 2, 3, 4] 10
    

    再来加一个初始值看看:

    let arr = [1, 2, 3, 4]
    let sum = arr.reduce((prev, cur, index, arr) => {
        console.log(prev, cur, index);
        return prev + cur;
    }, 5)
    console.log(arr, sum);
    // 5 1 0
    // 6 2 1
    // 8 3 2
    // 11 4 3
    // [1, 2, 3, 4] 15
    

    通过上面例子,可以得出结论:如果没有提供initialValuereduce 会从索引1的地方开始执行 callback 方法,跳过第一个索引。如果提供initialValue,从索引0开始。
    注意:该方法如果添加初始值,就会改变原数组,将这个初始值放在数组的最后一位。

  2. filter()
    filter()方法用于过滤数组,满足条件的元素会被返回。它的参数是一个回调函数,所有数组元素依次执行该函数,返回结果为true的元素会被返回。该方法会返回一个新的数组,不会改变原数组。

    let arr = [1, 2, 3, 4, 5]
    arr.filter(item => item > 2) // [3, 4, 5]
    

    可以使用filter()方法来移除数组中的undefinednullNAN等值。

    let arr = [1, undefined, 2, null, 3, false, '', 4, 0]
    arr.filter(Boolean) //[1, 2, 3, 4]
    
  3. Array.from
    Array.from 的设计初衷是快速基于其他对象创建新数组,准确来说就是从一个类似数组的可迭代对象中创建一个新的数组实例。其实,只要一个对象有迭代器,Array.from 就能把它变成一个数组(注意:该方法会返回一个的数组,不会改变原对象)。

    语法
    Array.from 有 3 个参数(第一个参数是必选的,后两个参数都是可选的):

    • 类似数组的对象,必选。
    • 加工函数,新生成的数组会经过该函数的加工再返回。
    • this 作用域,表示加工函数执行时 this 的值。
    var obj = {0: 'a', 1: 'b', 2:'c', length: 3};
    
    Array.from(obj, function(value, index){
      console.log(value, index, this, arguments.length);
      return value   //必须指定返回值,否则返回 undefined
    }, obj);
    
    // String
    Array.from('abc');                             // ["a", "b", "c"]
    // Set
    Array.from(new Set(['abc', 'def']));           // ["abc", "def"]
    // Map
    Array.from(new Map([[1, 'ab'], [2, 'de']]));   // [[1, 'ab'], [2, 'de']]
    
  4. fill()
    使用fill()方法可以向一个已有数组中插入全部或部分相同的值,开始索引用于指定开始填充的位置,它是可选的。如果不提供结束索引,则一直填充到数组末尾。如果是负值,则将从负值加上数组的长度而得到的值开始。该方法的语法如下:

    array.fill(value, start, end)
    

    其参数如下:

    • value:必需。填充的值。
    • start:可选。开始填充位置。
    • end:可选。停止填充位置 (默认为 array.length)。
    const arr = [0, 0, 0, 0, 0];
    
    // 用5填充整个数组
    arr.fill(5);
    console.log(arr); // [5, 5, 5, 5, 5]
    arr.fill(0);      // 重置
    
    // 用5填充索引大于等于3的元素
    arr.fill(5, 3);
    console.log(arr); // [0, 0, 0, 5, 5]
    arr.fill(0);      // 重置
    
    // 用5填充索引大于等于1且小于等于3的元素
    arr.fill(5, 3);
    console.log(arr); // [0, 5, 5, 0, 0]
    arr.fill(0);      // 重置
    
    // 用5填充索引大于等于-1的元素
    arr.fill(5, -1);
    console.log(arr); // [0, 0, 0, 0, 5]
    arr.fill(0);      // 重置
    
  5. find()
    find() 方法返回通过测试函数的第一个数组元素的值。
    接受三个参数:

    • 项目值
    • 项目索引
    • 数组本身

    实例
    查找(返回)第一个大于 18 的元素(的值):

    var numbers = [4, 9, 16, 25, 29];
    var first = numbers.find(myFunction);
    
    function myFunction(value, index, array) {
      return value > 18;
    }
    
  6. findIndex()
    findIndex() 方法返回通过测试函数的第一个数组元素的索引。
    接受三个参数:

    • 项目值
    • 项目索引
    • 数组本身

    实例
    查找(返回)第一个大于 18 的元素(的值):

    var numbers = [4, 9, 16, 25, 29];
    var first = numbers.findIndex(myFunction);
    
    function myFunction(value, index, array) {
      return value > 18;
    }
    

20. 模块化

ES6中首次引入模块化开发规范ES Module,让Javascript首次支持原生模块化开发。ES Module把一个文件当作一个模块,每个模块有自己的独立作用域,然后将每个模块组合起来。核心点就是模块的导入与导出。

模块化的优势有以下几点:

  • 防止命名冲突
  • 代码复用
  • 高维护性
  1. export 导出模块
    方式一:分别暴露
    // m1
    export var first = 'test';
    export function func() {
        return true;
    }
    
    方式二:统一暴露
    // m2
    var first = 'test';
    var second = 'test';
    function func() {
        return true;
    }
    export {first, second, func};
    // as关键字
    // export {first, second as two, func};
    // as关键字可以重命名暴露出的变量或方法,经过重命名后同一变量可以多次暴露出去。
    
    方式三:默认暴露
    // m3
    // export default会导出默认输出,即用户不需要知道模块中输出的名字,在导入的时候为其指定任意名字。
    export default {
    	first: 'test',
    	second: 'test',
    	func: function () {
    		return true;
    	}
    }
    // 注意: 导入默认模块时不需要大括号,导出默认的变量或方法可以有名字,但是对外无效。export default只能使用一次。
    
  2. import 导入模块:
    方式一:通用的导入方式
    // 引入 m1.js 模块内容
    import * as m1 from "./m1.js";
    //引入 m2.js 模块内容
    import * as m2 from "./m2.js";
    //引入 m3.js 
    import * as m3 from "./m3.js";
    
    方式二:解构赋值形式
    import {first , func} from "./m1.js";
    import {first, second as two, func} from "./m2.js";
    import {default as m3} from "./m3.js";
    
    方式三:简便形式 针对默认暴露
    import m3 from "./m3.js";
    
  • 9
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值