zoukankan      html  css  js  c++  java
  • ES6语法~解构赋值、箭头函数、class类继承及属性方法、map、set、symbol、rest、new.target、 Object.entries...

     

    2015年6月17日 ECMAScript 6发布正式版本

     

    前面介绍基本语法,  后面为class用法及属性方法、set、symbol、rest等语法.

     

    一、基本语法: 

     

    1、         定义变量:let

    使用var 定义的变量没有{ }限制,在条件中定义的i,全局中都可以使用,造成变量污染,有变量提升预解析作用,只提升变量名,不提升值!降低js代码的可阅读性

    相同作用域内,let不允许重复声明变量!!否则报错!!但可以更改变量值

     

     使用let定义的变量:不会有变量提升,必须先定义后使用,否则先使用会报错: ReferenceError

       在for循环条件中用let定义i,只能在{ }内使用,不会造成全局污染

     

    2、         定义常量:const

    const d =10; 不允许再为d重新赋值

    const定义的常量必须在定义时给一个初始化的值,否则undefind无意义

    利用const定义的常量,在相同作用域中,无法被重新赋值

    const定义的常量有块级作用域{ } ,for循环中每一次循环都重新定义了一个作用域

     

     

    3、         解构赋值:

    定义:所谓的解构赋值,就是把某个对象中的属性,当作变量,给解放出来,今后就能够当作变量直接使用了;

    语法:

    let user = {  name: “zs”, age : 20, genter: ‘男’ }

    let { name } = user  --à 把name解构出来做变量使用

    let { name: username , age: userage } = user  -à这时name就不存在了,取而代之的是username, 且username/userage无法再被重新赋值!

     

    使用时:直接用username、userage---àconsole.log(username)  //zs

     

    4、         箭头函数--(只针对改造匿名函数)

    (形参体列表)=> { 函数体代码 }

    <1> 特点:

    箭头函数,本质上就是一个匿名函数

    箭头函数的特性: 箭头函数内部的 this, 永远和箭头函数外部的 this 保持一致;

    btn.onclick = function() {

         setTimeout(() => {      //原本定时器内部的this指向window

            console.log(this)  //<button id="btn">点击<button>

            this.style.backgroundColor = "red"

         }, 1000)

    }

     

     

    <2> 箭头函数的三个变体:

    正规:去掉function、函数名:

    var 函数名 = (参数1,…) => {    }

    函数名(参数1,…)----调用

    如:var add = (x, y) => { return x+y }

    add(1, 2)

     

     

    ①   变体1:如果箭头函数,左侧的形参列表中,只有一个参数,则,左侧小括号可以省略;

    var  add  =  x  =>   { return x + 10}

    console.log( add(1) )  //11

    ②    变体2:如果右侧函数体中,只有一行代码,则右侧的 { } 和return可以省略;

    var  add  =  (x , y)  =>  x + y

    console.log(add(1, 2))  //3

     

    ③    变体3:如果箭头函数左侧 只有一个形参,而且右侧只有一行代码,则两边的 () 和 {} 都可以省略

    var  add  =  x  =>  x + 10

    console.log(add(1))  //11

     

     

    二、其他语法

    1、Class 的基本语法

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

    toAdd() {
      return this.x + this.y
    }
    }

    上面代码定义了一个“类”,可以看到里面有一个`constructor`方法,这就是构造方法,而`this`关键字则代表实例对象。也就是说,ES5 的构造函数`Point`,对应 ES6 的`Point`类的构造方法。

    方法之间不需要逗号分隔,加了会报错。

    使用时:

    ```javascript
    let b = new Point(1, 2)
    console.log(b.toAdd()) //3

    typeof Point // "function"
    Point === Point.prototype.constructor // true

    构造函数的prototype属性,在 ES6 的“类”上面继续存在。事实上,类的所有方法都定义在类的prototype属性上面。

    第一段代码相当于:
    Point.prototype = {
     constructor() {},
     toAdd() {},
    };

    在类的实例上面调用方法,其实就是调用原型上的方法。

     

    constructor 方法

    constructor方法是类的默认方法,通过new命令生成对象实例时,自动调用该方法。一个类必须有constructor方法,如果没有显式定义,一个空的constructor`方法会被默认添加。

    class Point {
    }

    // 等同于
    class Point {
     constructor() {}
    }

    与 ES5 一样,类的所有实例共享一个原型对象。

    var p1 = new Point(2,3);
    var p2 = new Point(3,2);

    p1.__proto__ === p2.__proto__
    //true

    Class 表达式

    与函数一样,类也可以使用表达式的形式定义。

    const MyClass = class Me {
     getClassName() {
       return Me.name;
    }
    };

    需要注意的是,这个类的名字是Me,但是Me只在 Class 的内部可用,指代当前类。在 Class 外部,这个类只能用MyClass引用。

    如果类的内部没用到Me的话,可以省略Me,也就是可以写成下面的形式。

    const MyClass = class { /* ... */ };

    采用 Class 表达式,可以写出立即执行的 Class。

    自执行类:
    let person = new class {  //直接new调用自己
     constructor(name) {
       this.name = name;
    }

     sayName() {
       console.log(this.name);
    }
    }('张三');

    person.sayName(); //张三

    上面代码中,person是一个立即执行的类的实例。

     

    注意点

    (1)严格模式

    类和模块的内部,默认就是严格模式,所以不需要使用use strict指定运行模式。只要你的代码写在类或模块之中,就只有严格模式可用。

    (2)不存在提升

    类不存在变量提升(hoist),这一点与 ES5 完全不同。

    new Foo(); // ReferenceError
    class Foo {}

    这种规定的原因与下文要提到的继承有关,必须保证子类在父类之后定义。

    (3)name 属性

    由于本质上,ES6 的类只是 ES5 的构造函数的一层包装,所以函数的许多特性都被Class继承,包括name属性。

    class Point {}
    Point.name // "Point"

    name属性总是返回紧跟在class关键字后面的类名。

     

    (4)this 的指向

    类的方法内部如果含有this,它默认指向类的实例。但是,必须非常小心,一旦单独使用该方法(单独解放出来使用),很可能报错。

    class Logger {

    printName(name = 'there') {

    this.print(Hello ${name});

    }

    print(text) {

    console.log(text);

    }

    }

    const logger = new Logger();

    const { printName } = logger;

    printName(); // 单独使用报错:TypeError: Cannot read property 'print' of undefined

    logger.printName(). //Hello there. 实例调用不会报错

     

    如果需要单独使用它:

    解决办法:

    1、箭头函数:

    class Person {

    printName = (name = 'aaa') => {

    this.print(hello ${name})

    }

    print(text) {

    console.log(text)

    }

    }

    let a = new Person()

    a.printName('zhang') //hello zhang

     

    2、绑定this

    constructor() {

    this.printName = this.printName.bind(this) //this就是Person

    }

     

    静态方法

    类相当于实例的原型,所有在类中定义的方法,都会被实例继承。如果在一个方法前,加上static关键字,就表示该方法不会被实例继承,而是直接通过类直接来调用,这就称为“静态方法”。

    class Foo {
     static classMethod() {
       return 'hello';
    }
    }

    Foo.classMethod() // 'hello'

    var foo = new Foo(); //这是实例属性
    foo.classMethod() // TypeError: foo.classMethod is not a function

    注意,如果静态方法包含this关键字,这个this指的是类,而不是实例。

     

    1、静态方法调用与实例方法调用的区别:

    只要没被static定义的,实例都可以直接访问到!

    class Foo {
     static bar() {
       this.baz();
    }
     static baz() {  //静态方法调用的也必须是static声明的方法
       console.log('hello');
    }
     baz() {
       console.log('world');
    }
    }

    Foo.bar() // hello 静态调用,this指向类Foo
    let a = new Foo()
    a.baz()  //world 实例调用
    a.bar()  // 报错:a.bar is not a function

     

    2、父类的静态方法,可以被子类继承。

    class Foo {
     static classMethod() {
       return 'hello';
    }
    }

    class Bar extends Foo {
    }

    Bar.classMethod() // 'hello'

     

    3、静态方法也是可以从super对象上调用的。

    class Foo {
     static classMethod() {
       return 'hello';
    }
    }

    class Bar extends Foo {
     static classMethod() {
       return super.classMethod() + ', too';
    }
    }

    Bar.classMethod() // "hello, too"

     

    实例属性的新写法

    实例属性除了定义在constructor()方法里面的this上面,也可以定义在类的最顶层。

    所有实例对象自身的属性都定义在类的头部,看上去比较整齐,一眼就能看出这个类有哪些实例属性。

    class IncreasingCounter {
     _count = 0; //直接把属性定义在最顶层,不需在constructor中this._count = 0
     get value() {
       console.log('Getting the current value!');
       return this._count;
    }
     increment() {
       this._count++;
    }
    }

     

    静态属性

    静态属性指的是 Class 本身的属性,写法是在实例属性法的前面,加上static关键字。

    class MyClass {
     static myStaticProp = 42;

     constructor() {
       console.log(MyClass.myStaticProp); // 42
    }
    }

     

    new.target 属性

    ES6 为new命令引入了一个new.target属性,该属性一般用在构造函数之中,返回new命令作用于的那个构造函数。如果构造函数不是通过new命令或Reflect.construct()调用的,new.target会返回undefined,因此这个属性可以用来确定构造函数是怎么调用的。

    function Person(name) {
     if (new.target !== undefined) {
       this.name = name;
    } else {
       throw new Error('必须使用 new 命令生成实例');
    }
    }

    // 另一种写法
    function Person(name) {
     if (new.target === Person) {
       this.name = name;
    } else {
       throw new Error('必须使用 new 命令生成实例');
    }
    }

    var person = new Person('张三'); // 正确
    var notAPerson = Person.call(person, '张三');  // 报错

     

    需要注意的是,子类继承父类时,new.target会返回子类。

    class A {
     constructor() {
       console.log(new.target.name);
    }
    }
    class B extends A {
     constructor() {
       super();
    }
    }
    new A() // A
    new B() // B

    new.target指向当前正在执行的函数。可以看到,在super()执行时,它指向的是子类B的构造函数,而不是父类A的构造函数。也就是说,super()内部的this指向的是B

     

    利用这个特点,可以写出不能独立使用、必须继承后才能使用的类。

     

    2、Class 的继承extends

    Class 可以通过extends关键字实现继承,这比 ES5 的通过修改原型链实现继承,要清晰和方便很多。

    (1)子类通过extends关键字,继承了父类的所有属性和方法(包括静态方法)

    在子类的构造函数中,只有调用super之后,才可以使用this关键字,否则会报错。这是因为子类实例的构建,基于父类实例,只有super方法才能调用父类实例。

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

    class ColorPoint extends Point {
     constructor(x, y, color) {
       this.color = color; // ReferenceError
       super(x, y);
       this.color = color; // 正确
    }
    }

    上面代码中,子类的constructor方法没有调用super之前,就使用this关键字,结果报错,而放在super方法之后就是正确的。

     

    (2)实例对象即是子类实例,也是父类实例

    let cp = new ColorPoint(25, 8, 'green');

    cp instanceof ColorPoint // true
    cp instanceof Point // true

    上面代码中,实例对象cp同时是ColorPointPoint两个类的实例,这与 ES5 的行为完全一致。

    Object.getPrototypeOf()

    Object.getPrototypeOf方法可以用来从子类上获取父类。

    Object.getPrototypeOf(ColorPoint) === Point   // true

    因此,可以使用这个方法判断,一个类是否继承了另一个类。

     

    super 关键字

    super这个关键字,既可以当作函数使用,也可以当作对象使用。在这两种情况下,它的用法完全不同。

    (1)第一种情况,super作为函数调用时,代表调用父类的构造函数。ES6 要求,子类的构造函数必须执行一次super函数,否则报错。

    class A {}

    class B extends A {
     constructor() {
       super();
    }
    }

    注意,super虽然代表了父类A的构造函数,但是返回的是子类B的实例,即super内部的this指的是B的实例,因此super()在这里相当于A.prototype.constructor.call(this)

    (2)super()内部的this指向的是子类实例而不是父类!!。

    而ES6 规定,在子类普通方法中通过super调用父类的方法时,方法内部的this也指向当前的子类实例。

    由于this指向子类实例,所以如果通过super对某个属性赋值,这时super就是this,赋值的属性会变成子类实例的属性。

    class A {
     constructor() {
       this.x = 1;
    }
    }

    class B extends A {
     constructor() {
       super();
       this.x = 2;
       super.x = 3;
       console.log(super.x); // undefined
       console.log(this.x); // 3
    }
    }

    let b = new B();

    上面代码中,super.x赋值为3,这时等同于对this.x赋值为3。而当读取super.x的时候,读的是A.prototype.x,所以返回undefined

     

    (3)super( ) 作为函数时,super()只能用在子类的构造函数之中,用在其他地方就会报错。不能用在方法中!

    class A {}

    class B extends A {
     m() {
       super(); // 报错
    }
     super();   //报错
    }

    上面代码中,super()用在B类的m方法之中,或直接写,就会造成句法错误。

     

    (4) super作为对象时,用在普通方法中,指向父类的原型对象;在静态方法中,指向父类。

    1⃣️ 这里需要注意,由于super指向父类的原型对象,所以定义在父类实例上的方法或属性,是无法通过super调用的。

    super在普通方法之中,指向A.prototype,所以super.p()就相当于A.prototype.p()`。

    class A {
     constructor() {
       this.p = 2;
    }
    }

    class B extends A {
     get m() {
       return super.p;
    }
    }

    let b = new B();
    b.m // undefined

    上面代码中,p是父类A实例的属性,super.p就引用不到它。

    如果属性定义在父类的原型对象上,super就可以取到。

    class A {}
    A.prototype.x = 2;

    class B extends A {
     constructor() {
       super();
       console.log(super.x) // 2
    }
    }

    let b = new B();

     

    2⃣️如果super作为对象,用在静态方法之中,这时super将指向父类,`在普通方法之中指向父类的原型对象。

    class Parent {
     static myMethod(msg) {
       console.log('static', msg);
    }

     myMethod(msg) {
       console.log('instance', msg);
    }
    }

    class Child extends Parent {
     static myMethod(msg) {
       super.myMethod(msg);
    }

     myMethod(msg) {
       super.myMethod(msg);
    }
    }

    Child.myMethod(1); // static 1

    var child = new Child();
    child.myMethod(2); // instance 2

    3⃣️在子类的静态方法中通过super调用父类的方法时,方法内部的this指向当前的子类,而不是子类的实例。

    class A {
     constructor() {
       this.x = 1;
    }
     static print() {
       console.log(this.x);
    }
    }

    class B extends A {
     static x = 4;  //子类静态属性
     constructor() {
       super();
       this.x = 2;
    }
     static m() {
       super.print();
    }
    }

    B.x = 3;  子类静态属性
    B.m() // 3  

    4⃣️使用super的时候,必须显式指定是作为函数、还是作为对象使用,否则会报错。

    class A {}

    class B extends A {
     constructor() {
       super();
       console.log(super.valueOf() instanceof B); // true
    }
    }

    let b = new B();

    上面代码中,super.valueOf()表明super是一个对象,因此就不会报错。同时,由于super使得this指向B的实例,所以super.valueOf()返回的是一个B的实例。

    instanceof的普通的用法,obj instanceof Object 检测Object.prototype是否存在于参数obj的原型链上。

    Person的原型在p的原型链中

    function Person(){};
    var p =new Person();
    console.log(p instanceof Person);//true

     

    类的 prototype 属性和proto属性

    大多数浏览器的 ES5 实现之中,每一个对象都有__proto__属性,指向对应的构造函数的prototype属性。Class 作为构造函数的语法糖,同时有prototype属性和__proto__属性,因此同时存在两条继承链。

    (1)子类的__proto__属性,表示构造函数的继承,总是指向父类。

    (2)子类prototype属性的__proto__属性,表示方法的继承,总是指向父类的prototype属性。

    class A {
    }

    class B extends A {
    }

    B.__proto__ === A // true
    B.prototype.__proto__ === A.prototype // true

    上面代码中,子类B__proto__属性指向父类A,子类Bprototype属性的__proto__属性指向父类Aprototype属性。

     

    3、Symbol—js的第七种数据类型

    ES6 引入了一种新的原始数据类型Symbol,表示独一无二的值。

    它是 JavaScript 语言的第七种数据类型,前六种是:undefinednull、布尔值(Boolean)、字符串(String)、数值(Number)、对象(Object)。

    Symbol 值通过Symbol函数生成。这就是说,对象的属性名现在可以有两种类型,一种是原来就有的字符串,另一种就是新增的 Symbol 类型。凡是属性名属于 Symbol 类型,就都是独一无二的,可以保证不会与其他属性名产生冲突。

    let s = Symbol();

    typeof s
    // "symbol"

     

    注意,Symbol函数前不能使用new命令,否则会报错。这是因为生成的 Symbol 是一个原始类型的值,不是对象。也就是说,由于 Symbol 值不是对象,所以不能添加属性。基本上,它是一种类似于字符串的数据类型。

    let s1 = Symbol('foo');
    let s2 = Symbol('bar');

    s1 // Symbol(foo)
    s2 // Symbol(bar)

    s1.toString() // "Symbol(foo)"
    s2.toString() // "Symbol(bar)"

    上面代码中,s1s2是两个 Symbol 值。如果不加参数,它们在控制台的输出都是Symbol(),不利于区分。有了参数以后,就等于为它们加上了描述,输出的时候就能够分清,到底是哪一个值。

    注意,Symbol函数的参数只是表示对当前 Symbol 值的描述,因此相同参数的Symbol函数的返回值是不相等的。

    所以无论有没有参数,无论参数是否相等,Symbol值都是独一无二的。

    // 没有参数的情况
    let s1 = Symbol();
    let s2 = Symbol();

    s1 === s2 // false

    // 有参数的情况
    let s1 = Symbol('foo');
    let s2 = Symbol('foo');

    s1 === s2 // false

    Symbol 值不能与其他类型的值进行运算,会报错。

    但是,Symbol 值可以显式转为字符串。

    另外,Symbol 值也可以转为布尔值,但是不能转为数值。

    let sym = Symbol();
    Boolean(sym) // true
    !sym  // false

    if (sym) {
     // ...
    }

    Number(sym) // TypeError
    sym + 2 // TypeError

     

     

    作为属性名的 Symbol

    由于每一个 Symbol 值都是不相等的,这意味着 Symbol 值可以作为标识符,用于对象的属性名,就能保证不会出现同名的属性。这对于一个对象由多个模块构成的情况非常有用,能防止某一个键被不小心改写或覆盖。

    let mySymbol = Symbol();

    // 第一种写法
    let a = {};
    a[mySymbol] = 'Hello!';

    // 第二种写法
    let a = {
    [mySymbol]: 'Hello!'
    };

    // 第三种写法
    let a = {};
    Object.defineProperty(a, mySymbol, { value: 'Hello!' });

    // 以上写法都得到同样结果
    a[mySymbol] // "Hello!"

    Symbol 值作为对象属性名时,不能用点运算符。

    在对象的内部,使用 Symbol 值定义属性时,Symbol 值必须放在方括号之中。

     

    4、Set

    ES6 提供了新的数据结构 Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。

    Set本身是一个构造函数,用来生成 Set 数据结构。

    可用于数组去重!

    const s = new Set();

    [2, 3, 5, 4, 5, 2, 2].forEach(x => s.add(x));

    for (let i of s) {
     console.log(i);
    }
    // 2 3 5 4

    上面代码通过add()方法向 Set 结构加入成员,结果表明 Set 结构不会添加重复的值

     

    Set`函数可以接受一个数组(或者具有 iterable 接口的其他数据结构)作为参数,用来初始化。

    // 例一
    const set = new Set([1, 2, 3, 4, 4]);  // set返回一个对象Set{1,2,3,4}、size
    [...set]   // 扩展运算符把对象展开,放进数组。
    // [1, 2, 3, 4]

    // 例二
    const items = new Set([1, 2, 3, 4, 5, 5, 5, 5]);
    items.size // 5   // 返回对象中有5个成员

    // 例三
    const set = new Set(document.querySelectorAll('div'));
    set.size // 56

    // 类似于
    const set = new Set();
    document
    .querySelectorAll('div')
    .forEach(div => set.add(div));
    set.size // 56

    上面代码也展示了一种去除数组重复成员的方法。

    // 去除数组的重复成员
    [...new Set(array)]   // 扩展运算符把对象展开放进数组(利用set去重)

    上面的方法也可以用于,去除字符串里面的重复字符。

    [...new Set('ababbc')].join('')
    // "abc"

     

    向 Set 加入值的时候,不会发生类型转换,所以5"5"是两个不同的值。Set内部判断使用精确判断(===)

    let set = new Set();
    let a = NaN;
    let b = NaN;
    set.add(a);
    set.add(b);
    set // Set {NaN}

    上面代码向 Set 实例添加了两个NaN,但是只能加入一个。这表明,在 Set 内部,两个NaN是相等。

     

    另外,两个对象总是不相等的。两个空对象不相等,所以它们被视为两个值。

    let set = new Set();

    set.add({});
    set.size // 1

    set.add({});
    set.size // 2

     

    属性方法:

    Set 结构的实例有以下属性。

    • Set.prototype.constructor:构造函数,默认就是Set函数。

    • Set.prototype.size:返回Set实例的成员总数。

     

    Set 实例的方法分为两大类:操作方法(用于操作数据)和遍历方法(用于遍历成员)。下面先介绍四个操作方法。

    • add(value):添加某个值,返回 Set 结构本身。

    • delete(value):删除某个值,返回一个布尔值,表示删除是否成功。

    • has(value):返回一个布尔值,表示该值是否为Set的成员。

    • clear():清除所有成员,没有返回值。

     

    遍历操作

    Set 结构的实例有四个遍历方法,可以用于遍历成员。

    • keys():返回键名的遍历器

    • values():返回键值的遍历器

    • entries():返回键值对的遍历器

    • forEach():使用回调函数遍历每个成员

    由于 Set 结构没有键名,只有键值(或者说键名和键值是同一个值),所以keys方法和values方法的行为完全一致。

     

    上面遍历相当于:

    Object.keys(). Object.values()、 Object.entries()

     

    可以省略values方法,直接用for...of循环遍历 Set。

    let set = new Set(['red', 'green', 'blue']);

    for (let x of set) {
     console.log(x);
    }
    // red
    // green
    // blue

    扩展运算符(...)内部使用for...of循环,所以也可以用于 Set 结构。

    let set = new Set(['red', 'green', 'blue']);
    let arr = [...set];
    // ['red', 'green', 'blue']

     

     

    而且,数组的mapfilter方法也可以间接用于 Set 了。

    filter()返回新数组,不改变原数组!!

    因此使用 Set 可以很容易地实现并集(Union)、交集(Intersect)和差集(Difference)。

    let a = new Set([1, 2, 3]);
    let b = new Set([4, 3, 2]);

    // 并集
    let union = new Set([...a, ...b]);
    // Set {1, 2, 3, 4}

    // 交集  
    let intersect = new Set([...a].filter(x => b.has(x)));
    // set {2, 3}   在a里筛选出b里也有的

    // 差集
    let difference = new Set([...a].filter(x => !b.has(x)));
    // Set {1}     在a里筛选出b里没有的

     

     

    如果想在遍历操作中,同步改变原来的 Set 结构,目前没有直接的方法,但有两种变通方法。一种是利用原 Set 结构映射出一个新的结构,然后赋值给原来的 Set 结构;另一种是利用Array.from方法。

    // 方法一
    let set = new Set([1, 2, 3]);
    set = new Set([...set].map(val => val * 2));
    // set的值是2, 4, 6

    // 方法二
    let set = new Set([1, 2, 3]);
    set = new Set(Array.from(set, val => val * 2));
    // set的值是2, 4, 6

    上面代码提供了两种方法,直接在遍历操作中改变原来的 Set 结构。

     

     

    5、map

    JavaScript 的对象(Object),本质上是键值对的集合(Hash 结构),但是传统上只能用字符串当作键。这给它的使用带来了很大的限制。

     

    为了解决这个问题,ES6 提供了 Map 数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。也就是说,Object 结构提供了“字符串—值”的对应,Map 结构提供了“值—值”的对应,是一种更完善的 Hash 结构实现。如果你需要“键值对”的数据结构,Map 比 Object 更合适。

     

    Set的属性和方法,Map也适用!

     

    注意,只有对同一个对象的引用,Map 结构才将其视为同一个键。这一点要非常小心。

    const map = new Map();

    map.set(['a'], 555);
    map.get(['a']) // undefined

    上面代码的setget方法,表面是针对同一个键,但实际上这是两个值,内存地址是不一样的,因此get方法无法读取该键,返回undefined

    同理,同样的值的两个实例,在 Map 结构中被视为两个键。

    const map = new Map();

    const k1 = ['a'];
    const k2 = ['a'];

    map
    .set(k1, 111)
    .set(k2, 222);

    map.get(k1) // 111
    map.get(k2) // 222

    上面代码中,变量k1k2的值是一样的,但是它们在 Map 结构中被视为两个键。

     

    如果 Map 的键是一个简单类型的值(数字、字符串、布尔值),则只要两个值严格相等,Map 将其视为一个键,比如0-0就是一个键,布尔值true和字符串true则是两个不同的键。另外,undefinednull也是两个不同的键。虽然NaN不严格相等于自身,但 Map 将其视为同一个键。

     

    6、Number.isInteger() 用来判断一个值是否为整数。

     

    7、 Object.entries()把对象的 每个key和value放进一个数组

    ES6,引入了跟Object.keys配套的Object.values和Object.entries。

    let {keys, values, entries} = Object;

    let obj = { a: 1, b: 2, c: 3 };

    for (let key of keys(obj)) {
    console.log(key); // 'a', 'b', 'c'
    }


    for (let value of values(obj)) {
    console.log(value); // 1, 2, 3
    }



    for (let [key, value] of entries(obj)) {
    console.log([key, value]); // ['a', 1], ['b', 2], ['c', 3]
    }

     

    8、rest

    ES6 引入 rest 参数(形式为...变量名),用于获取函数的多余参数,

    rest 参数搭配的变量是一个数组,该变量将多余的参数放入数组中。这样就不需要使用arguments对象了。

    function add(...values) {  // 把传入的参数,放进数组中。
     let sum = 0;

     for (var val of values) {
       sum += val;
    }

     return sum;
    }

    add(2, 5, 3) // 10

     

    下面是一个 rest 参数代替arguments变量的例子。

    // arguments变量的写法
    function sortNumbers() {
     return Array.prototype.slice.call(arguments).sort();
    }

    // rest参数的写法
    const sortNumbers = (...numbers) => numbers.sort();

    上面代码的两种写法,比较后可以发现,rest 参数的写法更自然也更简洁。

    arguments对象不是数组,而是一个类似数组的对象。所以为了使用数组的方法,必须使用Array.prototype.slice.call先将其转为数组。rest 参数就不存在这个问题,它就是一个真正的数组,数组特有的方法都可以使用。

    Array.prototype.slice.call ( obj ) ; 可以将伪数组转换为数组!!

     

    下面是一个利用 rest 参数改写数组push方法的例子:

    function push(array, ...items) {
     items.forEach(function(item) {
       array.push(item);
       console.log(item);
    });
    }

    var a = [];
    push(a, 1, 2, 3)

    注意,rest 参数之后不能再有其他参数(即只能是最后一个参数),否则会报错。

     

  • 相关阅读:
    PythonStudy——os 操作系统 模块
    PythonStudy——sys:系统 模块
    PythonStudy——datatime 模块
    PythonStudy——calendar 模块
    PythonStudy——time 模块
    PythonStudy——shelve 模块
    PythonStudy——hashlib 模块
    PythonStudy——hmac 模块
    JDK的版本历史
    Zookeeper分布式协调服务
  • 原文地址:https://www.cnblogs.com/zixuan00/p/10381325.html
Copyright © 2011-2022 走看看