JS-ES6新特性详解

ES6:2015年推出的ECMAScript的语法规范

let/const声明变量

  • ES6中新增了声明变量的关键字let/const
  • let/constvar的区别
    1. let/const声明的变量必须在声明后使用
      • var声明的变量会被预解析,可以在声明前使用
      • let/const声明的变量因为有暂时性死区的存在,不能在声明前使用
    console.log(x); // undefined
    var x = 1;
    
    console.log(y); // 报错:'Cannot access 'y' before initialization'
    let y = 2;
    
    console.log(z); // 报错:'Cannot access 'z' before initialization'
    const z = 3;
    
    1. let/const不能重复声明同名变量
      • var可以重复声明同名变量
    var x = 0;
    var x = 1;
    console.log(x); // 1
    
    let y = 2;
    let y = 3;
    console.log(y); // 报错:'Identifier 'y' has already been declared'
    
    const z = 4;
    const z = 5;
    console.log(z); // 报错:'Identifier 'z' has already been declared'
    
    1. 任何一个{}都会限制let/const声明的变量的使用范围
      • 对于let/const声明的变量来说,任何一个{}都是一个作用域
      • var声明的变量只有函数的{}才会限制使用范围
    if (true) {
        var a = 1;
        let b = 2;
        const c = 3;
        console.log(a, b, c); // 1 2 3
    }
    console.log(a); // 1
    console.log(b); // 报错:'b is not defined'
    console.log(c); // 报错:'c is not defined'
    
    for (var i = 0; i <= 3; i++) {
        console.log(i); // 0  1  2  3
        setTimeout(function() {
            // 此代码执行时,for循环已经结束,全局变量i的值为4
            console.log(i); // 4
        }, 500);
    }
    console.log(i); // 4
    
    for (let i = 0; i <= 3; i++) {
        // 对于变量i来说,for循环的{}就是一个块级作用域
        console.log(i); // 0  1  2  3
        setTimeout(function() {
            console.log(i); // 0  1  2  3
        }, 500);
    }
    console.log(i); // 报错:'i is not defined'
    
    1. let/const声明的变量不会挂载到window
      • 在全局中通过var声明的变量,本质上相当于是给window添加的属性
        • 也就是说,在全局中通过var声明的变量都会自动挂载到window
      • 然而在全局中通过let/const声明的变量,不会挂载到window
    var a = 100;
    let b = 200;
    const c = 300;
    console.log(window.a, window.b, window.c); // 100 undefined undefined
    
  • letconst的区别
    1. let声明变量的时候可以不赋值
      • const声明变量的时候必须赋值
    let x;
    console.log(x); // undefined
    const y; // 报错:'Missing initializer in const declaration'
    
    1. let声明变量后可以修改变量的值
      • const声明变量后不可修改变量的值
      • 因此const声明的变量也可叫做常量
    let n = 100;
    n = 200;
    console.log(n); // 200
    
    // 情况1
    const m = 100;
    m = 200; // 报错:'Assignment to constant variable'
    
    // 情况2
    // 变量obj中的值是对象的地址
    const obj = { a: 1 }
    // 根据obj中的地址,找到存储对象并修改其中的数据
    obj.a = 666;
    // 没有修改obj中的值,值还是那个地址
    console.log(obj); // { a: 666 }
    // 此操作会将obj中的地址替换为别的地址
    obj = { a: 666 } // 报错:'Assignment to constant variable'
    

模板字符串

  • 反引号包裹的字符串
  • 特点:
    1. 可以换行书写
    2. 可以识别解析变量${变量}
let n = 10;
let m = 20;
let str = `n的值为${n},m的值为${m}`;
console.log(str); // n的值为10,m的值为20
  • 扩展:使用模板字符串调用函数
function fn(arg, n1, n2) {
    // 第一个参数是一个数组,是由模板字符串通过${}分隔后组成的数组
    console.log(arg, n1, n2);
}

let x = 666;
let y = 999;

// 使用模板字符串调用
fn`I${x}LOVE${y}U`; // ['I', 'LOVE', 'U', raw: Array(3)]  666  999
fn`I LOVE U`; // ['I LOVE U', raw: Array(1)]  undefined  undefined

函数形参默认值

  • 在定义函数的时候()中直接给形参赋的值,就是形参的默认值
  • 形参默认值的作用
    • 函数调用的时候,如果没有给形参赋值实参,则函数内该形参使用默认值
    • 如果给形参传递了实参,则函数内该形参使用传递的实参值
function fn(n = 10, m = 20, x) {
    console.log(n, m, x);
}
fn(); // 10  20  undefined
fn(666, 777, 888); // 666  777  888

对象简写

  1. 当对象的属性名和属性值变量同名的时候,可以简写
    • 比如:
      • let n = 100; let obj = { n: n }
        • 属性名为n,属性值是变量n的值
      • 简写:let n = 100; let obj = { n }
  2. 当对象的属性值是匿名函数的时候,可以简写
    • 比如:
      • let obj = { fn: function() {} }
        • 属性值为匿名函数
      • 简写:let obj = { fn() {} }
        • 属性名就是fn,值为匿名函数

箭头函数

  • 是函数表达式(赋值式函数)中匿名函数的简写
    • 语法:let 变量 = () => {}
      • () 书写函数形参的位置
      • => 箭头函数的标志(不能省略)
      • {} 书写函数代码的位置
let ff1 = function(n, m) { console.log(n + m); }
let ff2 = (n, m) => { console.log(n + m); }

ff1(10, 20); // 30
ff2(10, 20); // 30
  • 箭头函数的特点
    1. 如果函数只有一个形参则可以省略()
      • 注意:如果形参有默认值则不能省略()
    let fn1 = n => {} // 只有一个形参可以省略()
    let fn2 = (n = 1) => {} // 形参有默认值不能省略()
    
    1. 如果函数只有一行代码则可以省略{}
      • 并且会自动将这行代码的结果作为函数返回值返回,所以不用写return
    let fn = (n, m) => n + m;
    console.log(fn(10, 20)); // 30
    
    // 在数组方法中经常使用
    let r1 = [1, 2, 3, 4].map(v => v * 10);
    console.log(r1); // [10, 20, 30, 40]
    
    let r2 = [1, 2, 3].every(v => v > 0);
    console.log(r2); // true
    
    let r3 = [1, 2, 3].reduce((prev, v) => prev + v, 0);
    console.log(r3); // 6
    
    1. 箭头函数内没有arguments
    function fn1() {
      console.log(arguments); // [10, 20, 30, callee: ƒ...]
    }
    fn1(10, 20, 30);
    
    let fn2 = () => {
      console.log(arguments); // 报错:'arguments is not defined'
    }
    fn2(10, 20, 30);
    
    1. 箭头函数内的this指向问题(没有自己的this
      • 箭头函数内的this与函数的定义(书写)位置有关,与函数的调用方式无关
      • 箭头函数内的this与其所在的作用域中的this指向一样
      • 注意:call()/apply()/bind()等方法不能改变箭头函数内的this指向
    let fn = () => {
        console.log(this);
    }
    
    fn(); // window
    
    let obj = { a: 1 };
    obj.f = fn;
    obj.f(); // window
    
    document.onclick = fn; // window
    
    fn.call({ a: 666 }); // window
    
    document.onclick = function() {
        console.log(this); // document
        let f = () => {
            console.log(this); // document
        }
        f();
    }
    
    1. 箭头函数不能作为构造函数使用
    2. 箭头函数内没有prototype原型

扩展运算符

  • 扩展运算符具有两个作用
    1. 作为展开运算符使用
      • 作用:展开大部分数据
        • 比如:对象、字符串、数组、伪数组等
      • 语法:...数据
      • 注意:展开后的数据必须使用在合适的地方
    // 1. 展开对象(只能用于对象{}中)
    let obj1 = { a: 1, b: 2 }
    let o1 = {
        c: 3,
        ...obj1
    }
    console.log(o1); // { c: 3, a: 1, b: 2 }
    
    let obj2 = { a: 1, b: 2 }
    let obj3 = { c: 3, d: 4 }
    let o2 = {
        ...obj2,
        ...obj3
    }
    console.log(o2); // { a: 1, b: 2, c: 3, d: 4 }
    
    // 2. 展开字符串
    let str = 'qwertyu';
    
    let arr = [...str];
    console.log(arr); // ['q', 'w', 'r', 't', 'y', 'u']
    
    function fn() {
        console.log(arguments); // Arguments(6)
    }
    // 将str展开,作为fn函数调用的实参使用
    fn(...str); // 等价于 fn('q','w','e','r','t','y')
    
    // 3. 展开数组
    let arr1 = ['q', 'w', 'r', 't', 'y'];
    let newArr = [1, 2, 3, ...arr1];
    console.log(newArr); // [1, 2, 3, 'q', 'w', 'r', 't', 'y']
    
    let arr2 = ['q', 'w', 'r'];
    let arr3 = [1, 2, 3, 4];
    // 使用concat
    console.log(arr2.concat(arr3)); // ['q', 'w', 'r', 1, 2, 3, 4]
    // 使用展开运算符
    console.log([...arr2, ...arr3]); // ['q', 'w', 'r', 1, 2, 3, 4]
    
    let arr4 = [1, 2, 3, 4, 5];
    function fn() {
        console.log(arguments); // Arguments(5)
    }
    // 将arr4展开,作为fn函数调用的实参使用
    fn(...arr4); // 等价于:fn(1, 2, 3, 4, 5)
    
    console.log(Math.max(...arr4)); // 5
    
    // 4. 展开伪数组
    let btns = document.querySelectorAll('button');
    
    let arr5 = Array.from(btns);
    console.log(arr5); // [button, button, button]
    
    let arr6 = [...btns];
    console.log(arr6); // [button, button, button]
    
    // 扩展
    let obj = {
      0: 'a',
      1: 'b',
      length: 2
    }
    
    console.log(Array.from(obj)); // ['a', 'b']
    console.log([...obj]); // 报错:'obj is not iterable'
    
    1. 作为合并运算符使用
      • 只有当函数定义的时候在函数形参位置使用才是合并运算符
      • 作用:将接收到的实参合并为一个数组
        • 比如:function fn(...arg) {}
          • 在本次函数执行中,函数内的arg就是接收的实参组成的数组
      • 注意:合并运算符只能作用于最后一个形参
    function ff(...arg) {
      console.log(arg);
    }
    ff(1, 2, 3); // [1, 2, 3]
    ff(); // []
    
    function ff(a, b, ...arg) {
        console.log(a, b); // 1 2
        console.log(arg); // [3, 4, 5]
    }
    ff(1, 2, 3, 4, 5);
    
    function ff(a, b, ...arg, c, d) {} // 报错:'Rest parameter must be last formal parameter'
    
    // ...合并运算符可以缓解箭头函数内没有arguments的问题
    let ff = (...arg) => {
        console.log(arg); // [10, 20, 30, 40, 50]
    }
    ff(10, 20, 30, 40, 50);
    

解构赋值

  • 解析数据结构并赋值
  • 作用:快速获取数据
  • 数组解构
    • 语法:let [变量名, ..., 变量名] = 数组
      • 等号=左边的[]是解构的含义
    • 作用:将数组中的数据依次赋值给左边的变量
    • 注意:等号=右边的只要是可迭代数据即可
let arr1 = ['a', 'b', 'c', 'd'];

let [a1, a2] = arr1; // 等价于 let a1 = arr1[0] let a2 = arr1[1]
console.log(a1, a2); // 'a' 'b'

let [b1, b2, b3, b4, b5] = arr1;
console.log(b1, b2, b3, b4, b5); // 'a' 'b' 'c' 'd' undefined

let [n1, n2, ...ar] = arr1; // 将剩余的数据合并到ar数组中
console.log(n1, n2, ar); // 'a' 'b' ['c', 'd']

// 使用数组解构交换两个变量的值
let c1 = 100;
let c2 = 200;
[c2, c1] = [c1, c2]; // 等号右边是数组,左边是解构赋值
console.log(c1, c2); // 200 100

let [d] = 1234; // 报错:'1234 is not iterable'

// 解构多维数组
let arr2 = [10, [20, [30, [40, 50]]]];

let [e1, e2] = arr2;
console.log(e1); // 10
console.log(e2); // [20, [30, [40, 50]]]

let [f1, [f2, [f3, [f4, f5]]]] = arr2;
console.log(f1, f2, f3, f4, f5) // 10 20 30 40 50
  • 对象解构
    • 语法1:let { 属性名, ..., 属性名 } = 对象
      • 等号=左边的{}是解构的含义
    • 作用:将对象中的属性名对应的属性值,赋值给左边的属性名同名变量
      • 比如:let { age } = 对象等价于let age = 对象.age
    • 语法2:let { 属性名: 变量, ..., 属性名: 变量 } = 对象
      • 等号=左边的{}是解构的含义
    • 作用:将对象中的属性名对应的属性值,赋值给左边的属性名同名变量
      • 比如: let { age: userAge } = 对象等价于let userAge = 对象.age
    • 注意:如果等号=左边没有声明变量的关键字则需要将整个表达式用()包裹
      • 比如:let age; ({ age } = 对象);
let obj = { name: 'zs', age: 17 }
let { name, age } = obj; // 等价于 let name = obj.name let age = obj.age
console.log(name, age); // 'zs' 17

let { gender } = obj; // 等价于 let gender = obj.gender
console.log(gender); // undefined

let { age: userAge } = obj; // 等价于 let userAge = obj.age
console.log(userAge); // 17
console.log(age); // 报错:'age is not defined'

// 解构多级对象
let user = {
    userName: 'zs',
    info: {
        age: 18,
        gender: '男'
    }
}
let { userName, info } = user;
console.log(userName, info); // 'zs' { age: 18, gender: '男' }

let { userName, info: { age, gender } } = user;
console.log(userName, age, gender); // 'zs' 18 '男'
console.log(info); // 报错:'info is not defined'

// 如果解构对象的时候,没有使用声明变量的关键字,则需要将整个解构赋值用()包裹
let user = { age: 17, userName: 'zs' }
let age, userName;
({ age, userName } = user);
console.log(age, userName); // 17 'zs'

// 利用解构赋值获取事件对象身上的属性
document.onclick = function(e) {
    let { target, type, offsetX } = e;
    console.log(target, type, offsetX);
}

document.onclick = function({ target }) {
    console.log(target);
}
  • 扩展:在js中{}的含义
    1. 表示对象,存储键值对
    2. 表示解构赋值
    3. 块级代码作用域

严格模式

  • 是一种js代码开始模式
  • 早期js语法有较多的不合理、不严谨之处
    • 为了更好的版本迭代(为了给后续的新语法做铺垫),于是就推出了严格模式
  • 如何开启严格模式:
    • 在代码开始位置书写:'use strict';
  • 在严格模式下:
    1. 函数普通调用时(直接函数名()
      • 函数内的this不再指向window,而是指向undefined
    function fn() {
        console.log(this); // undefined
    }
    fn();
    
    1. 函数形参名不能重复
    // 严格模式下报错
    function fn(a, a) {}
    
    1. 函数中的arguments.callee不能使用
    // 严格模式下报错
    function fn() {
        console.log(arguments.callee);
    }
    fn();
    
    1. 不能使用隐式全局变量(变量必须先通过关键字声明才可以使用)
    // 严格模式下报错
    n = 100;
    
    1. 不能使用0开头的八进制数
    // 严格模式下报错
    let n1 = 0o12;
    let n2 = 012;
    console.log(n1, n2);
    
    1. 不能设置只读属性
    // 严格模式下报错
    let str = 'qwerty';
    str.length = 666;
    

Symbol数据类型

  • 是ES6中新增的一种基本数据类型
  • 特点:Symbol数据是唯一的
  1. 创建Symbol数据
    • 语法1:Symbol()
    let s1 = Symbol();
    let s2 = Symbol();
    
    console.log(s1, s2); // Symbol() Symbol()
    console.log(s1 == s2); // false
    
    • 语法2:Symbol(描述)
    let s1 = Symbol('index');
    let s2 = Symbol('index');
    
    console.log(s1, s2); // Symbol(index) Symbol(index)
    console.log(s1 == s2); // false
    
    • 语法3:Symbol.for(描述)
      • 在全局Symbol注册表中查找拥有相同描述的Symbol数据,如果有则返回
        • 如果没有则创建并返回
    let s1 = Symbol.for('index');
    console.log(s1); // Symbol(index)
    
    let s2 = Symbol.for('index');
    console.log(s2); // Symbol(index)
    
    console.log(s1 == s2); // true
    
  2. 检测Symbol数据类型
    • 直接使用typeof检测
    let s = Symbol('index');
    console.log(typeof s); // 'symbol'
    
  3. Symbol数据无法隐式转换,必须使用方法强制转换
    let s = Symbol('index');
    console.log('abc' + String(s)); // 'abcSymbol(index)'
    
    console.log('abc' + s); // 报错:'Cannot convert a Symbol value to a string'
    
  4. 获取Symbol数据的描述
    • 语法:Symbol数据.description
    • 返回值:Symbol数据的描述
    let s = Symbol('index');
    console.log(s.description); // 'index'
    
  5. 使用Symbol数据作为对象的属性名
    • 注意:
      1. 使用Symbol数据作为对象的属性名,则需要使用[]包裹
      2. Symbol作为对象的属性名不能被for-in遍历
    let va = Symbol('index');
    let obj = {
        num: 17,
        [va]: 'aaa',
        [Symbol('index')]: 'bbb'
    }
    
    console.log(obj); // { num: 17, Symbol(index): 'aaa', Symbol(index): 'bbb' }
    
    for (const key in obj) {
        console.log(key); // 'num'
    }
    

set数据类型

  • 是ES6中新增的一种复杂数据类型
  • set是数据集合,与数组相似
    • set集合中的数据是无序的
    • set集合中的数据是唯一的、不重复的
  • 注意:set集合中NaNNaN是一样的数据
  1. 创建set集合
    • 语法:new Set(可迭代数据)
    let s1 = new Set();
    console.log(s1); // 空set集合
    
    let s2 = new Set('abcde');
    console.log(s2); // { 'a', 'b', 'c', 'd', 'e' }
    
    let s3 = new Set([1, 2, 3, 4]);
    console.log(s3); // { 1, 2, 3, 4 }
    
    let s4 = new Set([1, 2, 3, 3, 3, 3, 4, NaN, NaN]);
    console.log(s4); // { 1, 2, 3, 4, NaN }
    
  2. set集合的数据操作API方法
    1. 语法:set集合.size
      • 作用:返回set集合中的数据个数
    let s = new Set([10, 20, 30]);
    console.log(s.size); // 3
    
    1. 语法:set集合.add(数据)
      • 作用:向set集合中添加数据
      • 返回值:添加数据后的set集合
    let s = new Set([10, 20, 30]);
    
    console.log(s.add(30)); // { 10, 20, 30 }
    console.log(s.add('30')); // { 10, 20, 30, '30' }
    console.log(s.add(NaN)); // { 10, 20, 30, '30', NaN }
    
    console.log(s); // { 10, 20, 30, '30', NaN }
    
    1. 语法:set集合.has(数据)
      • 作用:判断set集合中是否存在传入的数据
      • 返回值:布尔值,存在返回true,不存在返回false
    let s = new Set([10, 20, 30, { a: 1 }]);
    
    console.log(s.has(10)); // true
    console.log(s.has(30)); // true
    console.log(s.has('30')); // false
    console.log(s.has({ a: 1 })); // false
    
    1. 语法:set集合.delete(数据)
      • 作用:删除传入数据对应的set集合中的数据
      • 返回值:布尔值,成功删除返回true,没有删除返回false
    let s = new Set([10, 20, 30, { a: 1 }]);
    
    console.log(s.delete(10)); // true
    console.log(s.delete(20)); // true
    console.log(s.delete({ a: 1 })); // false
    
    console.log(s); // { 30, {…} }
    
    1. 语法:set集合.clear()
      • 作用:清空set集合中的所有数据
    let s = new Set([10, 20, 30, { a: 1 }]);
    console.log(s); // { 10, 20, 30, {…} }
    
    s.clear();
    console.log(s); // 空set集合
    
    1. 语法:set集合.forEach(函数)
      • 作用:遍历set集合中的所有数据
        • set集合中有多少个数据则执行多少次传入的函数
    let s = new Set([10, 20, 30, { a: 1 }]);
    
    s.forEach(function(v, i, s) {
        // v, i 表示的都是set集合中的数据(值相同)
        // s 表示调用forEach方法的set集合
        console.log(v, i, s);
    });
    
  3. set集合和数组的转换
    • 数组转换为set集合
    let arr = [10, 20, 30, 40, 20, 30];
    let s = new Set(arr);
    
    console.log(s); // {10, 20, 30, 40}
    
    • set集合转换为数组
    console.log(Array.from(s)); // [10, 20, 30, 40]
    console.log([...s]); // [10, 20, 30, 40]
    
  4. set集合的使用
    • 需求:三个按钮都点击后才可以进行后续操作
    <button>1</button>
    <button>2</button>
    <button>3</button>
    
    let s = new Set();
    let btns = document.querySelectorAll('button');
    
    btns.forEach(ele => ele.onclick = () => {
        s.add(ele);
        if (s.size == btns.length) {
            console.log('三个按钮都点击了,可以继续后续操作');
        }
    });
    

map数据类型

  • 是ES6中新增的一种复杂数据类型
  • map是数据集合,与对象相似
    • map集合中数据也是键值对存储
    • map集合与对象的区别
      1. 对象的键名(属性名)只能是字符串或Symbol类型数据
        • map集合中的键名(属性名)可以是任意类型数据
      2. 对象通过点语法或数组关联法进行数据操作
        • map集合通过自带的API方法进行数据操作
  • 注意:map集合中NaNNaN是一样的键名
  1. 创建map集合
    • 语法:new Map(可迭代数据)
      • 一般创建map集合传入的参数是一个二维数组
      • 在每个小数组中:
        • 第一个数据作为map集合中的键名(属性名)
        • 第二个数据作为map集合中对应的键值(属性值)
    let m1 = new Map();
    console.log(m1); // 空map集合
    
    let m2 = new Map([[10, 20], ['10', 20], [{ a: 10 }, 20]]);
    console.log(m2); // { 10 => 20, '10' => 20, {…} => 20 }
    
  2. map集合的数据操作API方法
    1. 语法:map集合.size
      • 作用:返回set集合中的数据个数
    let m = new Map([[10, 20], ['10', 20], [{ a: 10 }, 20]]);
    console.log(m.size); // 3
    
    1. 语法:map集合.set(键名, 键值)
      • 作用:设置map集合中的键值对
      • 返回值:操作后的map集合
    let m = new Map([[10, 20], [{ a: 10 }, 20]]);
    
    m.set(10, '666');
    m.set({ a: 10 }, 999);
    m.set(NaN, 123);
    m.set(NaN, 456);
    
    console.log(m); // { 10 => '666', {…} => 20, {…} => 999, NaN => 456 }
    console.log(m.set(true, '789')); // { 10 => '666', {…} => 20, {…} => 999, NaN => 456, true => '789' }
    
    1. 语法:map集合.get(键名)
      • 作用:获取键名在map集合中的键值
      • 返回值:键名对应的键值,如果不存在则返回undefined
    let m = new Map([[10, 20], ['10', 80], [{ a: 10 }, 20]]);
    
    console.log(m.get(10)); // 20
    console.log(m.get('10')); // 80
    console.log(m.get({ a: 10 })); // undefined
    console.log(m.get(100)); // undefined
    
    1. 语法:map集合.has(键名)
      • 作用:判断键名在map集合中是否存在
      • 返回值:布尔值,存在返回true,不存在返回false
    let m = new Map([[10, 20], ['10', 80], [{ a: 10 }, 20]]);
    
    console.log(m.has(10)); // true
    console.log(m.has('10')); // true
    console.log(m.has({ a: 10 })); // false
    console.log(m.has(100)); // false
    
    1. 语法:map集合.delete(键名)
      • 作用:根据键名删除map集合中的数据
      • 返回值:布尔值,成功删除返回true,没有删除返回false
    let m = new Map([[10, 20], ['10', 80], [{ a: 10 }, 20]]);
    
    console.log(m.delete(10)); // true
    console.log(m.delete(100)); // false
    console.log(m.delete({a:10})); // false
    console.log(m); // { '10' => 80, {…} => 20 }
    
    1. 语法:map集合.clear(键名)
      • 作用:清空map集合中的所有数据
    let m = new Map([[10, 20], ['10', 80], [{ a: 10 }, 20]]);
    console.log(m); // { 10 => 20, '10' => 80, {…} => 20 }
    
    m.clear();
    console.log(m); // 空map集合
    
    1. 语法:map集合.forEach(函数)
      • 作用:遍历map集合中的所有数据
        • map集合中有多少个键值对则执行多少次传入的函数
    let m = new Map([[10, 20], ['10', 80], [{ a: 10 }, 100]]);
    
    s.forEach(function(v, k, m) {
        // v 表示map集合中的数据(键值)
        // k 表示map集合中该数据对应的键名
        // m 表示调用forEach方法的map集合
        console.log(v, k, m);
    });
    

WeakSet与WeakMap

  • WeakSetWeakMap对应setmap,数据类型、作用以及使用都是一模一样
  • 区别:
    1. WeakSetWeakMap只能存储复杂类型数据
      • WeakMap的键名只能存储复杂类型数据,键值不受限制
    let ws = new WeakSet();
    ws.add(10); // 报错:'Invalid value used in weak set'
    
    let wm = new WeakMap();
    wm.set(10); // 报错:'Invalid value used as weak map key'
    
    1. WeakSetWeakMap中的数据是弱引用,而setmap中的数据是强引用
      • 数组、对象、setmap等集合中存储的复杂类型数据是强引用
    let o1 = { a: 1 };
    o1 = null;
    // 此时{ a: 1 }没有任何内容对其引用,会被js垃圾回收机制销毁(回收)
    
    let arr = [];
    let o2 = { a: 2 };
    arr[0] = o2;
    o2 = null;
    // 此时虽然o2不再引用{ a: 1 },但是arr[0]依旧引用了{ a: 1 }
    // 所以{ a: 1 }不会被js垃圾回收机制销毁(回收)
    
    let arr = [];
    let ws = new WeakSet();
    
    let o1 = { a: 1 };
    let o2 = { a: 2 };
    
    arr[0] = o1;
    ws.add(o2);
    
    o1 = null;
    o2 = null;
    // 此时a[0]引用了{ a: 1 },是强引用,所以不会被js垃圾回收机制销毁(回收)
    // 此时ws引用了{ a: 2 },但是是弱引用,所以会被js垃圾回收机制销毁(回收)
    

for-of语句

  • 是ES6中新增的遍历可迭代数据的语句
  • 语法:for (变量 of 可迭代数据) { 循环获取可迭代数据中的数据 }
  • 常见的可迭代数据:
    • 数组、伪数组、字符串、set集合、map集合
  • 判断数据结构是否可迭代的小技巧
    • 如果数据结构中具有Symbol.iterator属性,则一般都是可迭代数据
// 遍历数组
let arr = [10, 20, 30];
for (const v of arr) {
    console.log(v); // 10  20  30
}

// 遍历字符串
let str = 'qwert';
for (const v of str) {
    console.log(v); // 'q'  'w'  'e'  'r'  't'
}

// 遍历set集合
let s = new Set([10, 20, 30, 40, 40, 40]);
for (const v of s) {
    console.log(v); // 10  20  30  40
}

// 遍历map集合
let m = new Map([[10, 20], [30, 40], [40, 40]]);
m.set(true, 666);
m.set({ a: 1 }, 999);
for (const v of m) {
    console.log(v);
    // [10, 20]  [30, 40]  [40, 40]  [true, 666]  [{…}, 999]
}
  • for-offor-in的区别
    • for-of适用于可迭代数据
    • for-in适用于可枚举属性
let arr = [10, 20, 30];
arr.abc = '666';

for (const v of arr) {
    console.log(v); // 10  20  30
}

for (const k in arr) {
    console.log(k); // '0'  '1'  '2'  'abc'
    console.log(arr[k]); // 10  20  30  '666'
}

console.log(arr); // [10, 20, 30, abc: '666']

模块化语法

  • 将逻辑复杂的代码,分为多个模块单独书写
    • 使用的时候,导入对应的模块实现功能
  • 模块化的优点
    1. 方便多人协同开发
    2. 便于代码复用以及后续维护
    3. 对于代码封装更友好,使得代码的变量不会被污染
  • 使用模块化的前提条件
    1. 使用模块化语法的script标签必须要有type属性,属性值为module
    2. 使用模块化语法的页面必须通过服务器访问
  • 模块化语法
    • 在js模块化中,一个js文件就是一个模块,每个模块之间都是相互独立的
      • 每个独立的模块都是一个模块作用域,各自模块中的数据只能在当前模块中使用
      • 如果想在当前模块中使用其他模块中的数据,则需要用到模块的导入导出语法
    • 模块导出语法
      • 作用:向外部暴露当前模块中的数据
        • 使其他模块在导入当前模块的时候可以使用暴露的数据
      • 语法1:默认导出
        • export default 数据
          • ”默认导出“一个模块中只能导出一次
      • 语法2:常规导出
        • export let 变量 = 数据
        • export { 变量 }
          • 变量必须是当前模块中的变量,可以导出多次
    let num = 100;
    let fn = () => { console.log(num) };
    
    // 默认导出
    export default { n: num };
    
    // 常规导出
    export let age = num;
    export { num, fn };
    
    • 模块导入语法
      • 作用:接收导入模块中导出的数据
        • 导入模块中的代码会立即执行
      • 语法1:import 变量 from 模块
        • 变量接收的是导入模块中”默认导出“的数据
      • 语法2:import { 变量, ..., 变量 } from 模块
        • 变量名必须是导入模块中”常规导出“的变量名
      • 语法3:import * as 变量 from 模块
        • 变量接收导入模块中所有导出的数据
          • 变量是一个对象,对象中的default属性的值就是”默认导出“的数据
      • 语法4:import 模块
        • 仅仅为了执行导入模块中的代码,不接收导入模块中导出的数据
      • <script src="模块" type="module"></script>
        • 可以使用html中的script标签导入模块
    // 语法1
    import moduleA1 from './modules/a.js';
    console.log(moduleA1); // { n: 100 }
    
    // 语法2
    import { age, num, fn } from './modules/a.js';
    console.log(age, num); // 100 100
    fn(); // 100
    
    // 语法1 + 语法2
    import moduleA2, { age, num, fn } from './modules/a.js';
    console.log(moduleA2, age, num); // { n: 100 } 100 100
    fn(); // 100
    
    // 语法3
    import * as moduleA3 from './modules/a.js';
    console.log(moduleA3.default); // { n: 100 }
    console.log(moduleA3.age); // 100
    console.log(moduleA3.num); // 100
    console.log(moduleA3.fn()); // 100
    
    // 语法4
    import './modules/a.js';
    
    <!-- 使用script标签导入模块 -->
    <script src="./modules/a.js" type="module"></script>
    
  • 注意:
    1. 模块化语法中默认开启严格模式
    2. ES6的模块化是静态导入的

类语法(class)

  • class的作用与构造函数一样
    • 都是用于实例化对象(创建新对象)的
  • class的类型本质上也是函数
    • 但是class只能和new一起使用来创建对象,不能单独调用
  • class创建语法
class 类名{
    // 构造器方法,在实例化类的时候会执行,并且该方法函数中的this指向实例对象
    constructor(参数) {
        // 参数接收实例化对象时传递的实参
        // 一般都会在此方法中给实例对象添加属性
    }
    // 此处书写的方法是给prototype原型添加的方法
    方法名() {}
    // 静态属性方法 => 给类对象添加的属性(注意不是实例对象)
    static 属性名 =;
}

class Person {
    constructor(name, age) {
        this.name = name;
        this.age = age;
        this.ff = () => this;
    }
    say() {
        console.log('hi');
    }
    static eat = function() { console.log('food'); }
}
// 给Person.prototype原型添加属性方法
Person.prototype.like = function() { console.log('ctrl'); }

let p = new Person('zs', 17);
console.log(p); // Person { name: 'zs', age: 17, ff: ƒ }
console.log(p.ff() === p); // true
console.log(p.say()); // 'hi'
console.log(p.like()); // 'ctrl'
console.log(Person.eat()); // 'food'
console.log(Person.prototype); // { like: ƒ, constructor: ƒ, say: ƒ }
// say方法和constructor一样不可枚举
for (const key in p) {
    console.log(key); // name age ff like
}
  • 注意:class内部代码默认开启严格模式
  • 31
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Turbosaa

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值