1.1屬性、方法簡潔表示
允許直接寫入變數和函式作為物件屬性和方法
let birth = '2000/01/01';
const Person = {
name: '張三',
//等同於birth: birth
birth,
// 等同於hello: function ()...
hello() { console.log('我的名字是', this.name); }
};
複製程式碼
1.2表示式作為屬性名
在ES5中,字面量方式定義物件時,不可以使用表示式作為屬性名。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屬性返回函式名
const person = {
sayName() {
console.log('hello!');
},
};
person.sayName.name // "sayName"
複製程式碼
特殊情況:
- 物件的方法使用了取值函式(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"
複製程式碼
- bind方法創造的函式,name屬性返回bound加上原函式的名字。
- Function建構函式創造的函式,name屬性返回anonymous
(new Function()).name // "anonymous"
var doSomething = function() {
// ...
};
doSomething.bind().name // "bound doSomething"
複製程式碼
1.4屬性可列舉、可遍歷
1.可列舉性
物件每個屬性都有描述物件,描述物件有enumerable屬性,稱可列舉性,如果屬性為false,表示不可列舉。
四個忽略enumerable為false屬性的操作 1.for...in迴圈;Object.keys();JSON.stringify();Object.assign()(ES6新增)
2.屬性的遍歷
(1) for...in:遍歷物件自身和繼承的可列舉屬性(不含Symbol屬性)
(2) Object.keys(obj):返回陣列,包括物件自身的(不含繼承的)所有可列舉屬性(不含 Symbol 屬性)的鍵名。
(3) Object.getOwnPropertyNames(obj返回一個陣列,包含物件自身的所有屬性(不含 Symbol 屬性,但是包括不可列舉屬性)的鍵名。
(4) Object.getOwnPropertySymbols(obj):返回一個陣列,包含物件自身的所有 Symbol 屬性的鍵名。
(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}
複製程式碼
注意點
- 如果目標物件與源物件有同名屬性,或多個源物件有同名屬性,則後面的屬性會覆蓋前面的屬性;
- 如果只有一個引數會直接返回該引數;
- 如果第一個引數不是物件,會轉成物件,undefined和null無法轉成物件,作為引數會報錯;
- 如果非物件引數不是第一個引數,且無法轉成物件,就直接跳過。
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" }
複製程式碼
- 只拷貝源物件自身屬性,不拷貝繼承屬性,也不拷貝不可列舉屬性
- 屬性名為Symbol值的屬性,也會被拷貝
Object.assign({ a: 'b' }, { [Symbol('c')]: 'd' })
// { a: 'b', Symbol(c): 'd' }
複製程式碼
- 淺拷貝
- 對於巢狀的物件,一旦遇到同名屬性,Object.assign方法會進行替換
const target = { a: { b: 'c', d: 'e' } }
const source = { a: { b: 'hello' } }
Object.assign(target, source)
// { a: { b: 'hello' } }
複製程式碼
- 可以處理陣列,把陣列視為物件
Object.assign([1, 2, 3], [4, 5])
// [4, 5, 3]
複製程式碼
Object.assign把陣列視為屬性名為 0、1、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不會複製這個取值函式,只會拿到值以後,將這個值複製過去。
常見用途
- 給物件新增屬性
class Point {
constructor(x, y) {
Object.assign(this, {x, y});
}
}
複製程式碼
- 給物件新增方法
Object.assign(obj, {
someMethod(arg1, arg2) {
···
},
anotherMethod() {
···
}
});
複製程式碼
- 克隆物件(淺拷貝,不能克隆繼承值)
Object.assign({}, origin);
複製程式碼
- 合併多個物件
Object.assign(target, ...sources);
複製程式碼
- 給屬性指定預設值
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