ES6 對象的擴展

1.1屬性、方法簡潔表示

容許直接寫入變量和函數做爲對象屬性和方法html

let birth = '2000/01/01';

const Person = {

  name: '張三',

  //等同於birth: birth
  birth,

  // 等同於hello: function ()...
  hello() { console.log('個人名字是', this.name); }

};
複製代碼

1.2表達式做爲屬性名

在ES5中,字面量方式定義對象時,不可使用表達式做爲屬性名。ES6字面量定義對象時容許這種方式,也就是將表達式放在方括號內,也能夠定義方法名。es6

let lastWord = 'last word';

const a = {
  'first word': 'hello',
  [lastWord]: 'world',
  ['h' + 'ello']() {
    return 'hi';
  }
};

console.log(a['first word']); // "hello"
console.log(a[lastWord]); // "world"
console.log(a['last word']); // "world"
console.log(a.hello()); // hi
console.log(a['last word']); // "world"
複製代碼

1.3對象方法有name屬性

方法的name屬性返回函數名web

const person = {
  sayName() {
    console.log('hello!');
  },
};

person.sayName.name   // "sayName"
複製代碼

特殊狀況:數組

  1. 對象的方法使用了取值函數(getter)和存值函數(setter),name屬性不在該方法上,而是在該方法的屬性的描述對象的get和set屬性上。
const obj = {
  get foo() {},
  set foo(x) {}
};

obj.foo.name
// TypeError: Cannot read property 'name' of undefined

const descriptor = Object.getOwnPropertyDescriptor(obj, 'foo');

descriptor.get.name // "get foo"
descriptor.set.name // "set foo"
複製代碼
  1. bind方法創造的函數,name屬性返回bound加上原函數的名字。
  2. Function構造函數創造的函數,name屬性返回anonymous
(new Function()).name // "anonymous"

var doSomething = function() {
  // ...
};
doSomething.bind().name // "bound doSomething"
複製代碼

1.4屬性可枚舉、可遍歷

1.可枚舉性

對象每一個屬性都有描述對象,描述對象有enumerable屬性,稱可枚舉性,若是屬性爲false,表示不可枚舉。bash

四個忽略enumerable爲false屬性的操做 1.for...in循環;Object.keys();JSON.stringify();Object.assign()(ES6新增)dom

2.屬性的遍歷

(1) for...in:遍歷對象自身和繼承的可枚舉屬性(不含Symbol屬性)ide

(2) Object.keys(obj):返回數組,包括對象自身的(不含繼承的)全部可枚舉屬性(不含 Symbol 屬性)的鍵名。函數

(3) Object.getOwnPropertyNames(obj返回一個數組,包含對象自身的全部屬性(不含 Symbol 屬性,可是包括不可枚舉屬性)的鍵名。優化

(4) Object.getOwnPropertySymbols(obj):返回一個數組,包含對象自身的全部 Symbol 屬性的鍵名。ui

(5) Reflect.ownKeys(obj):返回一個數組,包含對象自身的全部鍵名,無論鍵名是 Symbol 或字符串,也無論是否可枚舉。

1.5 super關鍵字

super關鍵字指向當前對象的原型對象,super表示原型對象時,只能用在對象的方法中。

const proto = {
  foo: 'hello'
};

const obj = {
  foo: 'world',
  find() {
    return super.foo;
  }
};

Object.setPrototypeOf(obj, proto);
console.log(obj.find()); // "hello"
複製代碼

1.6對象的擴展運算符

用於取出對象理全部可遍歷的屬性,拷貝到當前對象。

let z = { a: 3, b: 4 };
let n = { ...z };
n // { a: 3, b: 4 }
複製代碼

擴展運算符等同於使用Object.assign(),只拷貝對象實例的屬性

let aClone = { ...a };
// 等同於
let aClone = Object.assign({}, a);
複製代碼

擴展運算符能夠用於合併兩個對象。

let ab = { ...a, ...b };
// 等同於
let ab = Object.assign({}, a, b);
複製代碼

若是用戶自定義的屬性,放在擴展運算符後面,則擴展運算符內部的同名屬性會被覆蓋掉。方便修改對象部分現有屬性。

let aWithOverrides = { ...a, x: 1, y: 2 };
// 等同於
let aWithOverrides = { ...a, ...{ x: 1, y: 2 } };
// 等同於
let x = 1, y = 2, aWithOverrides = { ...a, x, y };
// 等同於
let aWithOverrides = Object.assign({}, a, { x: 1, y: 2 });
複製代碼

若是把自定義屬性放在擴展運算符前面,就變成了設置新對象的默認屬性值。

let aWithDefaults = { x: 1, y: 2, ...a };
// 等同於
let aWithDefaults = Object.assign({}, { x: 1, y: 2 }, a);
// 等同於
let aWithDefaults = Object.assign({ x: 1, y: 2 }, a);
複製代碼

其餘特色可參照數組的擴展運算符。

1.7對象的解構賦值

用於從一個對象取值

let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 };
x // 1
y // 2
z // { a: 3, b: 4 }
複製代碼

參照前面解構賦值

1.8對象的新增方法

1.8.1 Object.is()

比較兩個值是否嚴格相等,與===行爲基本一致,不一樣之處在於:===中,+0等於-0,NaN不等於自身。

+0 === -0 //true
NaN === NaN // false

Object.is(+0, -0) // false
Object.is(NaN, NaN) // true
複製代碼

1.8.2 Object.assign()

用法

用於將全部可枚舉屬性的值從一個或多個源對象複製到目標對象。返回目標對象。

const target = { a: 1 };

const source1 = { b: 2 };
const source2 = { c: 3 };

Object.assign(target, source1, source2);
target // {a:1, b:2, c:3}
複製代碼

注意點

  1. 若是目標對象與源對象有同名屬性,或多個源對象有同名屬性,則後面的屬性會覆蓋前面的屬性;
  2. 若是隻有一個參數會直接返回該參數;
  3. 若是第一個參數不是對象,會轉成對象,undefined和null沒法轉成對象,做爲參數會報錯;
  4. 若是非對象參數不是第一個參數,且沒法轉成對象,就直接跳過。
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" }
複製代碼
  1. 只拷貝源對象自身屬性,不拷貝繼承屬性,也不拷貝不可枚舉屬性
  2. 屬性名爲Symbol值的屬性,也會被拷貝
Object.assign({ a: 'b' }, { [Symbol('c')]: 'd' })
// { a: 'b', Symbol(c): 'd' }
複製代碼
  1. 淺拷貝
  2. 對於嵌套的對象,一旦遇到同名屬性,Object.assign方法會進行替換
const target = { a: { b: 'c', d: 'e' } }
const source = { a: { b: 'hello' } }
Object.assign(target, source)
// { a: { b: 'hello' } }

複製代碼
  1. 能夠處理數組,把數組視爲對象
Object.assign([1, 2, 3], [4, 5])
// [4, 5, 3]
複製代碼

Object.assign把數組視爲屬性名爲 0、一、2 的對象,所以源數組的 0 號屬性4覆蓋了目標數組的 0 號屬性1。 10. Object.assign只能進行值的複製,若是要複製的值是一個取值函數,那麼將求值後再複製。

const source = {
  get foo() { return 1 }
};
const target = {};

Object.assign(target, source)
// { foo: 1 }
複製代碼

source對象的foo屬性是一個取值函數,Object.assign不會複製這個取值函數,只會拿到值之後,將這個值複製過去。

常見用途

  1. 給對象添加屬性
class Point {
  constructor(x, y) {
    Object.assign(this, {x, y});
  }
}
複製代碼
  1. 給對象添加方法
Object.assign(obj, {
  someMethod(arg1, arg2) {
    ···
  },
  anotherMethod() {
    ···
  }
});
複製代碼
  1. 克隆對象(淺拷貝,不能克隆繼承值)
Object.assign({}, origin);
複製代碼
  1. 合併多個對象
Object.assign(target, ...sources);
複製代碼
  1. 給屬性指定默認值
const DEFAULTS = {
  logLevel: 0,
  outputFormat: 'html'
};

function processContent(options) {
  options = Object.assign({}, DEFAULTS, options);
}
複製代碼

1.8.3 Object.getOwnPropertyDescriptors()

用來獲取一個對象的==全部==自身屬性的描述符。參數爲任意對象。

解決了Object.assign()沒法正確拷貝get和set屬性的問題。Object.getOwnPropertyDescriptors()方法配合Object.defineProperties()方法,實現正確拷貝。

const source = {
  set foo(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.create()方法,將對象屬性克隆到新對象(淺拷貝)

const clone = Object.create(Object.getPrototypeOf(obj),
  Object.getOwnPropertyDescriptors(obj));
複製代碼

實現一個對象繼承另外一個對象

const obj = Object.create(
  prot,
  Object.getOwnPropertyDescriptors({
    foo: 123,
  })
);
複製代碼

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

1.Object.keys()

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

參數:obj-要返回其枚舉自身屬性的對象

2.Object.values()

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

參數:obj-要返回其枚舉自身屬性鍵值的對象

Object.entries()

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

參數:obj-能夠返回其可枚舉屬性的鍵值對的對象。

Object.keys,Object.values和Object.entries,做爲遍歷一個對象的補充手段,供for...of循環使用。

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]
}
複製代碼

1.8.6 ES5經常使用對象方法

1.Object.defineProperty()

會直接在一個對象上定義一個新屬性,或者修改一個對象的現有屬性, 並返回這個對象。

參數:

(1)obj-要在其上定義屬性的對象

(2)prop-要定義或修改屬性的名稱

(3)descriptor-將被定義或修改的屬性描述符

descriptor能夠設置的值有

  • [value]:屬性的值。
  • [writable]:該屬性是否可寫,若是設置成 false,則任何對該屬性改寫的操做都無效(但不會報錯)。
  • [configurable]:若是爲false,則任未嘗試刪除目標屬性或修改屬性如下特性(writable, configurable, enumerable)的行爲將被無效化。
  • [enumerable]:可枚舉性。
  • [get]:一旦目標對象訪問該屬性,就會調用這個方法,並返回結果。
  • [set]:一旦目標對象設置該屬性,就會調用這個方法。

實際運用

優化對象獲取和修改屬性的方式

//加入有一個目標節點, 想設置其位移
var targetDom = document.getElementById('target');
var transformText = 'translateX(' + 10 + 'px)';
targetDom.style.webkitTransform = transformText;
targetDom.style.transform = transformText;

// 用defineProperty方法優化
Object.defineProperty(dom, 'translateX', {
set: function(value) {
    var transformText = 'translateX(' + value + 'px)';
    dom.style.webkitTransform = transformText;
    dom.style.transform = transformText;
}
//這樣再後面調用的時候, 十分簡單
dom.translateX = 10;
dom.translateX = -10;
複製代碼

MVVM中數據‘雙向綁定’實現

<!DOCTYPE html>
 <html>
  <head>
    <meta charset="utf-8">
    <title>標題</title>
  </head>
  <body>
    <h3>使用Object.defineProperty實現簡單的雙向數據綁定</h3>
    <input type="text" id="input" />
    <div id="div"></div>
    
	<script>
        var obj = {};
        var inputVal = document.getElementById("input");
        var div = document.getElementById("div");
 
        Object.defineProperty(obj, "name", {
          set: function(newVal) {
            inputVal.value = newVal;
            div.innerHTML = newVal;
          }
        });
        inputVal.addEventListener('input', function(e){
          obj.name = e.target.value;
        });
    </script>
	
  </body>
</html>
複製代碼

2.Object.seal()

封閉一個對象,阻止添加新屬性並將全部現有屬性標記爲不可配置, 當前屬性的值只要可寫就能夠改變。

參數:obj-要被密封的對象

// 若是屬性值可寫
let obj = Object.defineProperty({},'name',{
  value:'hello',
  writable:true
})
Object.seal(obj);
console.log(obj.name); // hello
obj.name = 'world';
delete obj.name;
console.log(obj.name); // world 

// 若是屬性值不可寫
let obj = Object.defineProperty({},'name',{
  value:'hello',
  writable:false,
})
Object.seal(obj);
console.log(obj.name); // hello
obj.name = 'world';
delete obj.name;
console.log(obj.name); // hello
複製代碼

3.Object.freeze()

能夠凍結一個對象。一個被凍結的對象不再能被修改;凍結了一個對象則不能向這個對象添加新的屬性,不能刪除已有屬性,不能修改該對象已有屬性的可枚舉性、可配置性、可寫性,以及不能修改已有屬性的值。此外,凍結一個對象後該對象的原型也不能被修改。

參數:obj-要被凍結的對象

let obj = Object.defineProperty({},'name',{
  value:'hello',
  writable:true
})
Object.freeze(obj);
console.log(obj.name); // hello
obj.name = 'world';
delete obj.name;
console.log(obj.name); // hello

複製代碼

摘自阮一峯

參照MDN

相關文章
相關標籤/搜索