對象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
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 等同於
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
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"}
注意:
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" }
// 例一 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 }