ECMA Script 6_對象的擴展

對象html

1. ES6 容許直接寫入變量和函數,做爲對象的屬性和方法算法

  • const foo = 'bar';
    
    /*****************屬性的優化********************/
    const baz = {foo: foo};
    
    // 優化爲 const baz = {foo}; /*****************屬性的優化********************/
    
    function f(x, y) {
      return {x: x, y: y};
    };
    
    // 優化爲
    function f(x, y) {
        return {x, y};
    };
    
    f(1, 2);    // Object {x: 1, y: 2}
    
    /*****************方法的簡化*******************/
    const o = {
        method: function() {
            return "Hello!";
        },
    };
    
    // 優化爲 const o = { method() { return "Hello!"; }, };

2. 對象動態添加屬性數組

obj.name = 'RyenToretto';瀏覽器

obj['age'] = 22;數據結構

bind方法創造的函數,name 屬性返回 'bound 原函數的名字'函數

Function 構造函數創造的函數,name 屬性返回 'anonymous'優化

對象的方法是一個 Symbol 值,那麼name屬性返回的是這個 Symbol 值的描述this

  • (new Function()).name // "anonymous"
    
    var doSomething = function() {
        // ...
    };
    doSomething.bind().name;     // "bound doSomething"
    
    /*****************************************************/
    const key1 = Symbol('description');
    const key2 = Symbol();
    let obj = {
      [key1]() {},
      [key2]() {},
    };
    obj[key1].name // "[description]"
    obj[key2].name // ""

3. 屬性的可枚舉性url

對象的每一個屬性,都有一個屬性描述對象,該對象有個 enumerable 控制屬性是否可遍歷spa

  • 有四個操做會忽略 enumerable 爲 false 的屬性,即不會遍歷
  • for...in        只遍歷對象自身的和繼承的可枚舉的屬性。
  • Object.keys()        返回對象自身的全部可枚舉的屬性的鍵名。
  • JSON.stringify()        只串行化對象自身的可枚舉的屬性。
  • Object.assign()        忽略enumerable爲false的屬性,只拷貝對象自身的可枚舉的屬性。

 

  • ES6 規定,全部 Class 的原型的方法都是不可枚舉的

Object.getOwnPropertyDescriptor(class {foo() {}}.prototype, 'foo').enumerable    // false

4. ES6 一共有 5 種方法能夠遍歷對象的屬性名

遍歷次序:

首先遍歷全部數值鍵,按照數值升序排列。
其次遍歷全部字符串鍵,按照加入時間升序排列。
最後遍歷全部 Symbol 鍵,按照加入時間升序排列。

for...in

循環遍歷對象自身的繼承的可枚舉屬性(不含 Symbol 屬性)

Object.keys(obj)

返回一個數組,包括對象自身的(不含繼承的)全部可枚舉屬性(不含 Symbol 屬性)的鍵名

Object.getOwnPropertyNames(obj)

返回一個數組,包含對象自身的全部屬性(不含 Symbol 屬性,可是包括不可枚舉屬性)的鍵名

Object.getOwnPropertySymbols(obj)

返回一個數組,包含對象自身的全部 Symbol 屬性的鍵名

Reflect.ownKeys(obj)

包含對象自身的全部鍵名,無論鍵名是 Symbol 或字符串,也不論是否可枚舉

5. super 關鍵字

ES6 又新增了另外一個相似 this 的關鍵字 super,指向當前對象的原型對象

表示原型對象時只能用在對象的方法之中,用在其餘地方都會報錯

JavaScript 引擎內部

super.foo 等同於

  • Object.getPrototypeOf(this).foo(屬性)
  • Object.getPrototypeOf(this).foo.call(this)(方法)
  • 目前,只有對象方法的簡寫法可讓 JavaScript 引擎確認定義的是對象的方法
  • const proto = {
        foo: 'hello',
    };
    
    const obj = {
        foo: 'world',
        find() { return super.foo; },
    };
    
    Object.setPrototypeOf(obj, proto);
    obj.find();    // "hello"

6. 對象的 擴展運算符 ...

回憶數組的 擴展運算符

  • const [a, ...b] = [1, 2, 3];    // 定義屬性的新方式
    console.log(a);    // 1
    console.log(b);    // [2, 3]

現在對象的解構賦值

就是利用了 ...指定對象

解構賦值必須是最後一個參數,不然會報錯。用於從一個對象取值,至關於將目標對

象自身的全部可遍歷的(enumerable)但還沒有被讀取的屬性和屬性值,分配到 ...指定對象 上面。

全部的鍵和它們的值,都會拷貝到新對象上面

  • let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 };
    console.log(x);    // 1
    console.log(y);    // 2
    console.log(z);    // { a: 3, b: 4 }

是淺拷貝,即 若是對象的某屬性,是複合對象,則拷貝的只是一個引用地址而已

擴展運算符的解構賦值,不能複製繼承自原型對象的屬性

7. 對象的新增方法

Object.is() 

背景需求:

ES5 比較兩個值是否相等,只有兩個運算符:相等運算符(==嚴格相等運算符(===

它們都有缺點,== 會自動轉換數據類型,=== NaN不等於自身,以及 +0 === -0

JavaScript 缺少一種運算,在全部環境中,只要兩個值是同樣的,它們就應該相等。

ES6 提出「Same-value equality」(同值相等)算法,用來解決這個問題。

Object.is() 就是部署這個算法的新方法。

它用來比較兩個值是否嚴格相等,與嚴格比較運算符(===)的行爲基本一致

不一樣之處只有兩個:    +0 不等於 -0    NaN 等於 NaN

  • +0 === -0    //true
    NaN === NaN    // false
     Object.is(+0, -0); // false Object.is(NaN, NaN); // true
    
    Object.is('foo', 'foo');    // true
    Object.is({}, {});    // false

Object.assign()

用於對象屬性的合併,將 多個源對象(source)的全部可枚舉屬性,複製到目標對象(target)

  • const target = { a: 1 };
    
    const source1 = { b: 2 };
    const source2 = { c: 3 };
    
    Object.assign(target, source1, source2);
    console.log(target);     // {a:1, b:2, c:3}

只能進行值的複製,若是要複製的值是一個取值函數,那麼會執行函數,再將返回值複製過去

若是是賦值的是非取值函數,則正常複製

  • const a = {
        foo(x, y){
            console.log('哈哈');
            return "呵呵";
        },
    };
    const b = {};
    Object.assign(b, a);
    
    console.dir(b);
    console.log(b.foo());
  • 若是目標對象與源對象有同名屬性,或多個源對象有同名屬性,則後面的屬性會覆蓋前面的屬性(處理方法是替換,而不是添加。)
  • const target = { a: 1, b: 1 };
    
    const source1 = { b: 2, c: 2 };
    const source2 = { c: 3 };
    
    Object.assign(target, source1, source2);
    console.log(target);    // {a:1, b:2, c:3}
  • 若是非對象參數出如今源對象的位置(即非首參數),那麼這些參數都會轉成對象,若是沒法轉成對象,就會跳過
  • let obj = {a: 1};
    Object.assign(obj, undefined) === obj     // true
    Object.assign(obj, null) === obj     // true
  • 字符串會以數組形式,拷貝入目標對象,其餘值都不會產生效果(undefined,null,number,boolean)
  • const v1 = 'abc'; 
    const v2 = true;    // 被忽略
    const v3 = 10;    // 被忽略
    
    const obj = Object.assign({}, v1, v2, v3);
    console.log(obj);    // { "0": "a", "1": "b", "2": "c" }
  • 布爾值、數值、字符串分別轉成對應的包裝對象時

能夠看到它們的原始值都在包裝對象的內部屬性[[PrimitiveValue]]上面,

這個 內部屬性[[PrimitiveValue]] 屬性是不會被 Object.assign() 拷貝的

  • Object(true);    // {[[PrimitiveValue]]: true}
    Object(10);     //  {[[PrimitiveValue]]: 10}
    Object('abc');    // {0: "a", 1: "b", 2: "c", length: 3, [[PrimitiveValue]]: "abc"}
  • 不拷貝 不可枚舉的屬性(enumerable: false)
  • 屬性名爲 Symbol 值的屬性,也會被 Object.assign() 拷貝

注意:

Object.assign() 實行的是淺拷貝,而不是深拷貝。

也就是說,若是源對象某個屬性的值是對象,那麼目標對象拷貝獲得的是這個對象的引用

  • Object.assign() 能夠用來處理數組,可是會把數組視爲對象
  • Object.assign([1, 2, 3], [4, 5]);    // [4, 5, 3]

常見用途

  • 爲對象添加屬性
  • class Point { constructor(x, y) { Object.assign(this, {x, y}); // 將 x 屬性 和 y 屬性 添加到 Point類 的對象實例 }; };
  • 爲對象 添加方法
  • Object.assign(Point.prototype, { someMethod(arg1, arg2) { ··· }, anotherMethod() { ··· }, });
  • ③ 克隆對象
  • 只克隆 原始對象 自己的值
  • function clone(origin) {
        return Object.assign({}, origin);
    };
  • 克隆,並克隆其原型鏈
  • function clone(origin) {
        let originProto = Object.getPrototypeOf(origin);
        return Object.assign(Object.create(originProto), origin);
    };
  • 合併多個對象
  • const merge = (target, ...sources) => Object.assign(target, ...sources);

    const newObj = merge({}, obj1, obj2, obj3);
  • 爲屬性指定默認值
  • const DEFAULTS = {
        logLevel: 0,
        outputFormat: 'html'
    };
    
    function processContent(options) {
        options = Object.assign({}, DEFAULTS, options);
        console.log(options);
        // ...
    };
  • 注意,因爲存在淺拷貝的問題
  • const DEFAULTS = {
        url: {
            host: 'example.com',
            port: 7070
        },
    };
    
    processContent({ url: {port: 8000} })
    // {
    //   url: {port: 8000}    // 因爲拷貝了整個對象,因此覆蓋了原來的 url 對象,致使對象裏的內容不同
    // }

Object.getOwnPropertyDescriptors()

ES6 引入了 Object.getOwnPropertyDescriptors() 返回指定對象全部自身屬性(非繼承屬性)的描述對象

是爲了解決 Object.assign() 沒法正確拷貝 get 屬性 和 set 屬性 的問題

Object.assign() 老是拷貝一個屬性的值,而不會拷貝它背後的賦值方法取值方法

  • const obj = {
        foo: 123,
        get bar() { 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 } }

讀寫當前對象的 原型對象

ES5 指定原型對象,是經過 隱式原型屬性 __proto__

__proto__先後的雙下劃線,說明它本質上是一個內部屬性,而

不是一個正式的對外的 API,只是因爲瀏覽器普遍支持,才被加入了 ES6。

ES6 標準明確規定,只有瀏覽器必須部署這個屬性,其餘運行環境不必定須要部署,並且新的代碼最好認爲這個屬性是不存在的

所以,不管從語義的角度,仍是從兼容性的角度,都應該使用下面的 方法 代替

Object.setPrototypeOf()(寫操做)

Object.getPrototypeOf()(讀操做)

Object.create()(生成操做)

Object.keys()

ES5 引入了 Object.keys(),返回一個數組

成員是參數對象自身的(不含繼承的)全部可遍歷(enumerable)屬性的鍵名

Object.values

返回一個數組,成員是參數對象自身的(不含繼承的)全部可遍歷(enumerable)屬性的鍵值

Object.entries

返回一個數組,成員是參數對象自身的(不含繼承的)全部可遍歷(enumerable)屬性的鍵值對數組

  • 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.fromEntries()

 

返回一個對象,是Object.entries()的逆操做,用於將一個 鍵值對數組 轉爲 對象

  • Object.fromEntries([
        ['foo', 'bar'],
        ['baz', 42]
    ]);
    // { foo: "bar", baz: 42 }
  • 配合URLSearchParams對象,將查詢字符串轉爲對象
  • Object.fromEntries(new URLSearchParams('foo=bar&baz=qux'))
    // { foo: "bar", baz: "qux" }
  • 主要目的,是將 鍵值對的數據結構 還原爲對象,所以特別適合將 Map 結構轉爲對象。
  • // 例一
    const entries = new Map([
        ['foo', 'bar'],
        ['baz', 42]
    ]);
    
    Object.fromEntries(entries);    // { foo: "bar", baz: 42 }
    
    // 例二
    const map = new Map().set('foo', true).set('bar', false);
    Object.fromEntries(map);    // { foo: true, bar: false }
相關文章
相關標籤/搜索