es6新特性

一. es6對象的擴(kuò)展

1. 屬性的簡潔表示法

ES6 允許直接寫入變量和函數(shù),作為對象的屬性和方法。這樣的書寫更加簡潔。

    let birth ='foo';

    const Person ={

      name:'張三';

     //等同于birth: birth
     birth,   

     // 等同于hello: function ()...
     hello(){ console.log('我的名字是',this.name);}  

    };

2. 屬性名表達(dá)式

JavaScript 定義對象的屬性,有兩種方法。

    // 方法一

    obj.foo =true;

    // 方法二

    obj['a'+'bc']=123;

ES6 允許字面量定義對象時(shí),用表達(dá)式作為對象的屬性名。

    let propKey ='foo';

    let obj ={

     [propKey]:true,

     ['a'+'bc']:123

    };

表達(dá)式還可以用于定義方法名。

    let obj ={

      ['h'+'ello'](){

        return 'hi' ;

      }

    };

    obj.hello()

3.屬性的遍歷

遍歷對象屬性的方法:

(1)for...in 循環(huán)遍歷對象自身的和繼承的可枚舉屬性(不含 Symbol 屬性)。

(2)Object.keys(obj) 返回一個(gè)數(shù)組,包括對象自身的(不含繼承的)所有可枚舉屬性(不含 Symbol 屬性)的鍵名。

(3)Object.getOwnPropertyNames(obj) 返回一個(gè)數(shù)組,包含對象自身的所有屬性(不含 Symbol 屬性,但是包括不可枚舉屬性)的鍵名。

(4)Object.getOwnPropertySymbols(obj) 返回一個(gè)數(shù)組,包含對象自身的所有 Symbol 屬性的鍵名。

(5)Reflect.ownKeys(obj) 返回一個(gè)數(shù)組,包含對象自身的所有鍵名,不管鍵名是 Symbol 或字符串,也不管是否可枚舉。

以上的 5 種方法遍歷對象的鍵名,都遵守同樣的屬性遍歷的次序規(guī)則。

  • 首先遍歷所有數(shù)值鍵,按照數(shù)值升序排列。
  • 其次遍歷所有字符串鍵,按照加入時(shí)間升序排列。
  • 最后遍歷所有 Symbol 鍵,按照加入時(shí)間升序排列。
       Reflect.ownKeys({[Symbol()]:0, b:0,10:0,2:0, a:0})

       // [2, 10, b, a, Symbol()]

4.super 關(guān)鍵字

this關(guān)鍵字總是指向函數(shù)所在的當(dāng)前對象,ES6 又新增了另一個(gè)類似的關(guān)鍵字super,指向當(dāng)前對象的原型對象。

    const proto ={

        foo:'hello'

    };

    const obj ={

        foo:'world',

        find(){

            return super.foo;

        }

    };

    Object.setPrototypeOf(obj, proto);

    obj.find()

JavaScript 引擎內(nèi)部,super.foo等同于Object.getPrototypeOf(this).foo(屬性)或Object.getPrototypeOf(this).foo.call(this)(方法)。

    const proto ={

        x:'hello',

        foo(){

            console.log(this.x);

        },

    };

    const obj ={

        x:'world',

        foo(){

            super.foo();

        }

    }

    Object.setPrototypeOf(obj, proto);

    obj.foo()

5.解構(gòu)賦值

對象的解構(gòu)賦值用于將目標(biāo)對象自身的所有可遍歷的(enumerable)、但尚未被讀取的屬性,分配到指定的對象上面。所有的鍵和它們的值,都會拷貝到新對象上面。

    let{ x, y,...aa }={ x:1, y:2, a:3, b:4};

    x // 1

    y // 2

    z // { a: 3, b: 4 }

解構(gòu)賦值的拷貝是淺拷貝,即如果一個(gè)鍵的值是復(fù)合類型的值(數(shù)組、對象、函數(shù))、那么解構(gòu)賦值拷貝的是這個(gè)值的引用。

    let obj ={ a:{ b:1}};

    let{...x }= obj;

    obj.a.b =2;

    x.a.b // 2

另外,擴(kuò)展運(yùn)算符的解構(gòu)賦值,不能復(fù)制繼承自原型對象的屬性。

    let o1 ={ a:1};

    let o2 ={ b:2};

    o2.__proto__ = o1;

    let{...o3 }= o2;

    o3 // { b: 2 }

    o3.a// undefined

下面是另一個(gè)例子。

    const o = Object.create({ x:1, y:2});

    o.z =3;

    let{ x,...newObj }= o;

    let{ y, z }= newObj;

    x // 1

    y // undefined

    z // 3

6.擴(kuò)展運(yùn)算符

對象的擴(kuò)展運(yùn)算符(...)用于取出參數(shù)對象的所有可遍歷屬性,拷貝到當(dāng)前對象之中。

    let z ={ a:3, b:4};

    let n ={...z };

    n // { a: 3, b: 4 }

數(shù)組是特殊的對象,所以對象的擴(kuò)展運(yùn)算符也可以用于數(shù)組。

    let foo ={...[a,b,c]};

    foo

    // {0: a, 1: b, 2: c}

如果擴(kuò)展運(yùn)算符后面不是對象,則會自動將其轉(zhuǎn)為對象。

    // 等同于 {...Object(1)}

    {...1} // {}

上面代碼中,擴(kuò)展運(yùn)算符后面是整數(shù)1,會自動轉(zhuǎn)為數(shù)值的包裝對象Number{1}。由于該對象沒有自身屬性,所以返回一個(gè)空對象。

如果擴(kuò)展運(yùn)算符后面是字符串,它會自動轉(zhuǎn)成一個(gè)類似數(shù)組的對象,因此返回的不是空對象。

    {...'hello'}

    // {0: 'h', 1: 'e', 2: 'l', 3: 'l', 4: 'o'}

擴(kuò)展運(yùn)算符的參數(shù)對象之中,如果有取值函數(shù)get,這個(gè)函數(shù)是會執(zhí)行的。

// 并不會拋出錯(cuò)誤,因?yàn)?x 屬性只是被定義,但沒執(zhí)行
 
    let aWithXGetter ={

    ...a,

    getx(){

        throw new Error('not throw yet');

    }

    };

// 會拋出錯(cuò)誤,因?yàn)?x 屬性被執(zhí)行了

    let runtimeError ={

    ...a,

    ...{

        getaa(){

            throw new Error('throw now');

        }

    }

    };

二.Es6對象的新增方法

1.Object.is()

ES5 比較兩個(gè)值是否相等:相等運(yùn)算符(==)和嚴(yán)格相等運(yùn)算符(===)。

Object.is它用來比較兩個(gè)值是否嚴(yán)格相等,與嚴(yán)格比較運(yùn)算符(===)的行為基本一致。

    Object.is('foo','foo')

    // true

    Object.is({},{})

    // false

不同之處只有兩個(gè):一是+0不等于-0,二是NaN等于自身。

    +0===-0 //true

    NaN===NaN // false

    Object.is(+0,-0) // false

    Object.is(NaN,NaN) // true

2.Object.assign()

用于對象的合并,將源對象(source)的所有可枚舉屬性,復(fù)制到目標(biāo)對象(target)。

    const target ={ a:1};

    const source1 ={ b:2};

    const source2 ={ c:3};

    Object.assign(target, source1, source2);

    target // {a:1, b:2, c:3}

如果目標(biāo)對象與源對象有同名屬性,或多個(gè)源對象有同名屬性,則后面的屬性會覆蓋前面的屬性。

    const target ={ a:1, b:1};

    const source1 ={ b:2, c:2};

    const source2 ={ c:3};

    Object.assign(target, source1, source2);

    target // {a:1, b:2, c:3}

如果該參數(shù)不是對象,則會先轉(zhuǎn)成對象,然后返回。

    typeof Object.assign(2) // [object]

由于undefined和null無法轉(zhuǎn)成對象,所以如果它們作為參數(shù),就會報(bào)錯(cuò)。

    Object.assign(undefined) // 報(bào)錯(cuò)

    Object.assign(null) // 報(bào)錯(cuò)

Object.assign只拷貝源對象的自身屬性(不拷貝繼承屬性),也不拷貝不可枚舉的屬性(enumerable: false)

    Object.assign({b:c},

    Object.defineProperty({},'invisible',{

        enumerable:false,

        value:'hello';

    })

    )

    // { b: c }

上面代碼中,Object.assign要拷貝的對象只有一個(gè)不可枚舉屬性invisible,這個(gè)屬性并沒有被拷貝進(jìn)去。

注意點(diǎn)

(1)淺拷貝

Object.assign方法實(shí)行的是淺拷貝,而不是深拷貝。也就是說,如果源對象某個(gè)屬性的值是對象,那么目標(biāo)對象拷貝得到的是這個(gè)對象的引用。

(2)同名屬性的替換

對于這種嵌套的對象,一旦遇到同名屬性,Object.assign的處理方法是替換,而不是添加。

    const target ={ a:{ b:c, d:e;}}

    const source ={ a:{ b:'hello';}}

    Object.assign(target, source)

    // { a: { b: 'hello' } }

(3)數(shù)組的處理

Object.assign可以用來處理數(shù)組,但是會把數(shù)組視為對象。

    Object.assign([1,2,3],[4,5])

(4)取值函數(shù)的處理

Object.assign只能進(jìn)行值的復(fù)制,如果要復(fù)制的值是一個(gè)取值函數(shù),那么將求值后再復(fù)制。

    const source ={

        getfoo(){return1}

    };

    const target ={};

    Object.assign(target, source)

    // { foo: 1 }

常見用途

(1)為對象添加屬性

    class Point{

        constructor(x, y){

            Object.assign(this,{x, y});

        }

    }

上面方法通過Object.assign方法,將x屬性和y屬性添加到Point類的對象實(shí)例。

(2)為對象添加方法

    Object.assign(SomeClass.prototype,{

        someMethod(arg1, arg2){

            ···

        }

    });

    // 等同于下面的寫法

    SomeClass.prototype.someMethod =function(arg1, arg2){

    ···

    };

(3)克隆對象

    functionclone(origin){

        return Object.assign({}, origin);

    }

不過,采用這種方法克隆,只能克隆原始對象自身的值,不能克隆它繼承的值。如果想要保持繼承鏈,可以采用下面的代碼。

    functionclone(origin){

        let originProto = Object.getPrototypeOf(origin);

        return Object.assign(Object.create(originProto), origin);

    }

3.Object.getOwnPropertyDescriptors()

ES5 的Object.getOwnPropertyDescriptor()方法會返回某個(gè)對象屬性的描述對象(descriptor)。

ES2017 引入了Object.getOwnPropertyDescriptors()方法,返回指定對象所有自身屬性(非繼承屬性)的描述對象。

    const obj ={

        foo:123,

        getbar(){return'abc'}

    };

    Object.getOwnPropertyDescriptors(obj)

    // { foo:

    //    { value: 123,

    //      writable: true,

    //      enumerable: true,

    //      configurable: true },

    //   bar:

    //    { get: [Function: get bar],

    //      set: undefined,

    //      enumerable: true,

    //      configurable: true } }

該方法的引入目的,主要是為了解決Object.assign()無法正確拷貝get屬性和set屬性的問題。

    const source ={

        setfoo(value){

            console.log(value);

        }

    };

    const target1 ={};

    Object.assign(target1, source);

    Object.getOwnPropertyDescriptor(target1,'foo')

    // { value: undefined,

    //   writable: true,

    //   enumerable: true,

    //   configurable: true }

上面代碼中,source對象的foo屬性的值是一個(gè)賦值函數(shù),Object.assign方法將這個(gè)屬性拷貝給target1對象,結(jié)果該屬性的值變成了undefined。這是因?yàn)镺bject.assign方法總是拷貝一個(gè)屬性的值,而不會拷貝它背后的賦值方法或取值方法。

這時(shí),Object.getOwnPropertyDescriptors()方法配合Object.defineProperties()方法,就可以實(shí)現(xiàn)正確拷貝。

    const source ={

        setfoo(value){

            console.log(value);

        }

    };

    const target2 ={};

    Object.defineProperties(target2, Object.getOwnPropertyDescriptors(source));

    Object.getOwnPropertyDescriptor(target2, 'foo')

    // { get: undefined,

    //   set: [Function: set foo],

    //   enumerable: true,

    //   configurable: true }

Object.getOwnPropertyDescriptors()方法的另一個(gè)用處,是配合Object.create()方法,將對象屬性克隆到一個(gè)新對象。這屬于淺拷貝。

    const clone = Object.create(Object.getPrototypeOf(obj),

    Object.getOwnPropertyDescriptors(obj));

另外,Object.getOwnPropertyDescriptors()方法可以實(shí)現(xiàn)一個(gè)對象繼承另一個(gè)對象。

    const obj = Object.create(

        prot,

        Object.getOwnPropertyDescriptors({

            foo:123,

        })

    )

4.__proto__屬性,Object.setPrototypeOf(),Object.getPrototypeOf()

JavaScript 語言的對象繼承是通過原型鏈實(shí)現(xiàn)的。ES6 提供了更多原型對象的操作方法。

(1)Object.setPrototypeOf()

Object.setPrototypeOf方法的作用與__proto__相同,用來設(shè)置一個(gè)對象的prototype對象,返回參數(shù)對象本身。它是 ES6 正式推薦的設(shè)置原型對象的方法。

    // 格式

    Object.setPrototypeOf(object, prototype)

    // 用法

    const o = Object.setPrototypeOf({},null);

該方法等同于下面的函數(shù)。

    functionsetPrototypeOf(obj, proto){

    obj.__proto__ = proto;

    return obj;

    }

下面是一個(gè)例子。

    let proto ={};

    let obj ={ x:10};

    Object.setPrototypeOf(obj, proto);

    proto.y =20;

    proto.z =40;

    obj.x // 10

    obj.y // 20

    obj.z // 40

上面代碼將proto對象設(shè)為obj對象的原型,所以從obj對象可以讀取proto對象的屬性。

如果第一個(gè)參數(shù)不是對象,會自動轉(zhuǎn)為對象。但是由于返回的還是第一個(gè)參數(shù),所以這個(gè)操作不會產(chǎn)生任何效果。

(2)Object.getPrototypeOf()

該方法與Object.setPrototypeOf方法配套,用于讀取一個(gè)對象的原型對象。

    functionRectangle(){

    // ...

    }

    const rec =newRectangle();

    Object.getPrototypeOf(rec)=== Rectangle.prototype

    // true

如果參數(shù)是undefined或null,它們無法轉(zhuǎn)為對象,所以會報(bào)錯(cuò)。

    Object.getPrototypeOf(null)

    // TypeError: Cannot convert undefined or null to object

5.Object.keys(),Object.values(),Object.entries()

(1)Object.keys()

ES5 的Object.keys方法,返回一個(gè)數(shù)組,成員是參數(shù)對象自身的(不含繼承的)所有可遍歷(enumerable)屬性的鍵名。

ES2017 引入了跟Object.keys配套的Object.values和Object.entries,作為遍歷一個(gè)對象的補(bǔ)充手段,供for...of循環(huán)使用。

    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]

    }

(2)Object.values()

Object.values方法返回一個(gè)數(shù)組,成員是參數(shù)對象自身的(不含繼承的)所有可遍歷(enumerable)屬性的鍵值。

    const obj ={ foo:'bar', baz:42};

    Object.values(obj)

    // ['bar', 42]

返回?cái)?shù)組的成員順序,與本章的《屬性的遍歷》部分介紹的排列規(guī)則一致。

    const obj ={100:'a',2:'b',7:'c'};

    Object.values(obj)

    // ['b', 'c', 'a']

上面代碼中,屬性名為數(shù)值的屬性,是按照數(shù)值大小,從小到大遍歷的,因此返回的順序是b、c、a。

Object.values只返回對象自身的可遍歷屬性。

    const obj = Object.create({},{p:{value:42}});

    Object.values(obj) // []

上面代碼中,Object.create方法的第二個(gè)參數(shù)添加的對象屬性(屬性p),如果不顯式聲明,默認(rèn)是不可遍歷的,因?yàn)閜的屬性描述對象的enumerable默認(rèn)是false,Object.values不會返回這個(gè)屬性。只要把enumerable改成true,Object.values就會返回屬性p的值。

    const obj = Object.create({},{p:

        {

            value:42,

            enumerable:true

        }

    });

    Object.values(obj) // [42]

Object.values會過濾屬性名為 Symbol 值的屬性

    Object.values({[Symbol()]:123, foo:'abc'});

    // ['abc']

如果Object.values方法的參數(shù)是一個(gè)字符串,會返回各個(gè)字符組成的一個(gè)數(shù)組。

    Object.values('foo')

    // ['f', 'o','o']

(3)Object.entries()

Object.entries()方法返回一個(gè)數(shù)組,成員是參數(shù)對象自身的(不含繼承的)所有可遍歷(enumerable)屬性的鍵值對數(shù)組。

    const obj ={ foo:'bar', baz:42};

    Object.entries(obj)

    // [['foo', 'bar'], ['baz', 42] ]

除了返回值不一樣,該方法的行為與Object.values基本一致。

如果原對象的屬性名是一個(gè) Symbol 值,該屬性會被忽略。

    Object.entries({[Symbol()]:123, foo:'abc'});

    // [[ 'foo', 'abc'] ]

上面代碼中,原對象有兩個(gè)屬性,Object.entries只輸出屬性名非 Symbol 值的屬性。將來可能會有Reflect.ownEntries()方法,返回對象自身的所有屬性。

Object.entries的基本用途是遍歷對象的屬性。

    let obj ={ one:1, two:2};

        for(let[k, v] of Object.entries(obj)){

        console.log(

            `${JSON.stringify(k)}: ${JSON.stringify(v)}`

        );

    }

    // one: 1

    // two: 2

Object.entries方法的另一個(gè)用處是,將對象轉(zhuǎn)為真正的Map結(jié)構(gòu)。

    const obj ={ foo:'bar', baz:42};

    const map =newMap(Object.entries(obj));

    map // Map { foo: 'bar', baz: 42 }

(4)Object.fromEntries()

Object.fromEntries()方法是Object.entries()的逆操作,用于將一個(gè)鍵值對數(shù)組轉(zhuǎn)為對象。因此特別適合將 Map 結(jié)構(gòu)轉(zhuǎn)為對象。

    // 例一

    const entries =newMap([

        ['foo','bar'],

        ['baz',42]

    ]);

    Object.fromEntries(entries)

    // { foo: 'bar', baz: 42 }

    // 例二

    const map =newMap().set('foo',true).set('bar',false);

    Object.fromEntries(map)

    // { foo: true, bar: false }

原文:https://yolkpie.net/2020/02/04/es6%E6%96%B0%E7%89%B9%E6%80%A7/

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容