[TOC]
>[success] # ES6對象新增方法
~~~
'ES6'引入了2個新的方法,'Object.is()' 、'Object.assign()'方法
~~~
<br/>
>[success] ## Object.is()
~~~
在'js'中大家比較'值'的時候通常會使用'相等運算符( == )'、'嚴格相等運算符( === )',但是有時候
'嚴格相等運算符'也不準確,例如:它會認為'+0'和'-0'相等,'NaN === NaN'會返回'false',所以才會使用
'isNaN()'來檢測,'ES6'引入了'Object.is()'方法來彌補'嚴格相等運算'符殘留的怪異點
~~~
~~~
console.log(+0 == -0) // true
console.log(+0 === -0) // true
console.log(Object.is(+0, -0)) // false
console.log(NaN == NaN) // false
console.log(NaN === NaN) // false
console.log(Object.is(NaN, NaN)) // true
console.log(5 == 5) // true
console.log(5 == "5") // true
console.log(5 === 5) // true
console.log(5 === "5") // false
console.log(Object.is(5, 5)) // true
console.log(Object.is(5, "5")) // false
~~~
~~~
在許多情況下,'Object.is()'的結果與'==='運算符是相同的,僅有的例外是:'它會認為 +0 與 -0 不相等',
'而且 NaN 等于 NaN '。不過仍然沒必要停止使用嚴格相等運算符,選 擇'Object.is()',還是選擇'=='或'==='
,取決于代碼的實際情況。
~~~
<br/>
>[success] ## Object.assign()
~~~
語法: Object.assign(target, ...sources)
參數: 其中`target`是目標對象,`sources`是源對象,可以有多個,返回修改后的目標對象`target`,注意:'目標對象會被修改(合并后的最終對象)'
如果'目標對象'中的屬性具有'相同的鍵',則屬性將被源對象中的屬性'覆蓋'。后來的源對象的屬性將類似地'覆蓋'早先
的'屬性'。
'淺拷貝'就是拷貝'第一層'的'基本類型值',以及'第一層的引用類型地址','深拷貝'是拷貝'引用類型'
~~~
<br/>
>[success] ### ES6混入
1. 類的混入
~~~
// 創建構造函數
function EventTarget() { /*...*/ }
// 給構造函數原型鏈添加對象
EventTarget.prototype = {
constructor: EventTarget,
emit: function() { /*...*/ },
on: function() { /*...*/ }
}
// 創建一個對象
var myObject = {}
// 把EventTarget的構造函數中的原型鏈方法合并到myObject對象中
Object.assign(myObject, EventTarget.prototype)
// 調用myObject繼承過來的方法
myObject.emit("somethingChanged")
~~~
2. 對象的混入
~~~
1. 'Object.assign()'方法接受'任意數量'的'供應者',而'接收者'會按照'供應者'在參數中的順序來依次接收它們
的'屬性',如果'2'者有'相似屬性'則被后者的屬性值'覆蓋'
const target = { a: 1, b: 2 };
const source = { b: 4, c: 5 };
const error = { b: 5, c: 6 };
// 接收者:target,供應者:source、error
const returnedTarget = Object.assign(target, source, error);
console.log(target); // { a: 1, b: 5, c: 6 }
console.log(source); // { b: 4, c: 5 }
console.log(error); // { b: 5, c: 6 }
console.log(returnedTarget); // { a: 1, b: 5, c: 6 }
// 這里的target改變,returnedTarget也會改變
target.a = 222;
console.log(returnedTarget); // { a: 222, b: 5, c: 6 }
~~~
3. 淺拷貝
~~~
const obj = { a: 1 };
// 淺拷貝拷貝obj
const copy = Object.assign({}, obj);
console.log(copy); // { a: 1 }
~~~
4. 深拷貝
~~~
僅限于簡單的結構并且是json格式,funcction、new Date()等等都不可以使用該方法
// 源對象
obj1 = { a: 0, b: { c: 0, d: [1, 2, 3, 4] } };
// 克隆對象
let obj3 = JSON.parse(JSON.stringify(obj1));
// 修改源對象
obj1.a = 4;
obj1.b.c = 4;
obj1.b.d[0] = 123456;
// 打印克隆對象
console.log(JSON.stringify(obj3)); // {"a":0,"b":{"c":0,"d":[1,2,3,4]}}
~~~
5. 拷貝的對象有 symbol 類型的屬性
~~~
// 創建私有屬性
let sy = Symbol('foo')
// 創建對象
const o1 = { a: 1 };
const o2 = { [sy]: 2 };
// 拷貝o2到o1對象中
const obj = Object.assign({}, o1, o2);
console.log(obj[sy]) // 2
console.log(obj); // { a : 1, [Symbol("foo")]: 2 }
// Object.getOwnPropertySymbols() 方法返回一個給定對象自身的所有 Symbol 屬性的數組。
Object.getOwnPropertySymbols(obj); // [Symbol(foo)]
~~~
6. 繼承屬性和不可枚舉屬性是不能拷貝的
~~~
const obj = Object.create({ foo: 1 }, { // foo 是個繼承屬性。
bar: {
value: 2 // bar 是個不可枚舉屬性。
},
baz: {
value: 3,
enumerable: true // baz 是個自身可枚舉屬性。
}
});
const copy = Object.assign({}, obj);
console.log(copy); // { baz: 3 }
~~~
7. 原始類型會被包裝為對象
~~~
const v1 = "abc";
const v2 = true;
const v3 = 10;
const v4 = Symbol("foo")
const obj = Object.assign({}, v1, null, v2, undefined, v3, v4);
// 原始類型會被包裝,null 和 undefined 會被忽略。
// 注意,只有字符串的包裝對象才可能有自身可枚舉屬性。
console.log(obj); // { "0": "a", "1": "b", "2": "c" }
~~~
8. 異常會打斷后續拷貝任務
~~~
// Object.defineProperty() 方法會直接在一個對象上定義一個新屬性,或者修改一個對象的現有屬性, 并返回這個對象
const target = Object.defineProperty({}, "foo", {
value: 1,
writable: false
}); // target 的 foo 屬性是個只讀屬性。
Object.assign(target, { bar: 2 }, { foo2: 3, foo: 3, foo3: 3 }, { baz: 4 });
// TypeError: "foo" is read-only
// 注意這個異常是在拷貝第二個源對象的第二個屬性時發生的。
console.log(target.bar); // 2,說明第一個源對象拷貝成功了。
console.log(target.foo2); // 3,說明第二個源對象的第一個屬性也拷貝成功了。
console.log(target.foo); // 1,只讀屬性不能被覆蓋,所以第二個源對象的第二個屬性拷貝失敗了。
console.log(target.foo3); // undefined,異常之后 assign 方法就退出了,第三個屬性是不會被拷貝到的。
console.log(target.baz); // undefined,第三個源對象更是不會被拷貝到的。
~~~
9. 拷貝訪問器(對象的get、set屬性)
~~~
9.1
// 這種方式是只能拷貝到返回值,并不能拷貝到訪問器
const obj = {
foo: 1,
get bar () {
return 2;
}
};
let copy = Object.assign({}, obj);
console.log(copy); // { foo: 1, bar: 2 } copy.bar的值來自obj.bar的getter函數的返回值
9.2
// 下面這個函數會拷貝所有自有屬性的屬性描述符
function completeAssign(target, ...sources) {
sources.forEach(source => {
let descriptors = Object.keys(source).reduce((descriptors, key) => {
descriptors[key] = Object.getOwnPropertyDescriptor(source, key);
return descriptors;
}, {});
// Object.assign 默認也會拷貝可枚舉的Symbols
Object.getOwnPropertySymbols(source).forEach(sym => {
let descriptor = Object.getOwnPropertyDescriptor(source, sym);
if (descriptor.enumerable) {
descriptors[sym] = descriptor;
}
});
Object.defineProperties(target, descriptors);
});
return target;
}
copy = completeAssign({}, obj);
console.log(copy); // { foo:1, get bar() { return 2 } }
~~~
<br/>
>[success] ### ES5混入
~~~
'ES5'用'mixin混入方法'可以進行對'對象'或原型上的方法進行'淺拷貝',如下:
~~~
~~~
// 手動寫一個混入方法
function mixin(receiver, supplier) {
Object.keys(supplier).forEach(function (key) {
receiver[key] = supplier[key]
})
return receiver
}
// 創建構造函數
function EventTarget() { /*...*/ }
// 給構造函數原型鏈添加對象
EventTarget.prototype = {
constructor: EventTarget,
emit: function () { /*...*/ },
on: function () { /*...*/ }
}
// 創建一個對象
let myObject = {}
// 把EventTarget的構造函數中的原型鏈方法合并到myObject對象中
mixin(myObject, EventTarget.prototype)
// 調用myObject繼承過來的方法
myObject.emit("somethingChanged")
~~~
<br/>
>[warning] ### 要注意的點
~~~
1. 需要記住'Object.assign()'并未在'接收者'上'創建訪問器屬性(對象的get屬性、set屬性...)',即使'供應者'
擁有'訪問器屬性'。
由于'Object.assign()'使用'賦值運算符(=)','供應者'的'訪問器屬性'就會轉變成'接收者'的'數據屬性',例如:
// 創建對象
let receiver = {},
supplier = {
get name() {
return "file.js"
}
}
// 把receiver混入到supplier中
Object.assign(receiver, supplier)
// 獲取receiver對象name屬性的屬性描述
let descriptor = Object.getOwnPropertyDescriptor(receiver, "name")
// descriptor的自有屬性的屬性描述:
// {
// value: 'file.js',
// writable: true,
// enumerable: true,
// configurable: true
// }
console.log(descriptor.value) // "file.js"
console.log(descriptor.get) // undefined
console.log(receiver.name) // file.js
~~~
- Javascript基礎篇
- Array數組
- 數組插入值
- filter()
- forEach()
- push()
- pop()
- unshift()
- shift()
- valueOf()
- 面向對象思想
- Javascript 面向對象編程(一):封裝
- Javascript面向對象編程(二):構造函數的繼承
- Javascript面向對象編程(三):非構造函數的繼承
- 解構
- 數組的解構賦值
- 對象的解構賦值
- 函數參數解構
- 字符串的解構賦值
- 數值和布爾值的解構賦值
- 圓括號問題
- 字符串.
- split()
- charAt()
- charCodeAt()
- concat()
- indexOf()
- lastIndexOf()
- match()
- replace()
- includes()
- 初識遞歸
- 渲染ul-li樹形結構
- 異步函數解決方案
- 1. callback回調函數
- 2. ES6 - Promise
- JavaScript高級程序設計(書)
- 在html中使用JavaScript
- script標簽的位置
- 延遲腳本
- 異步腳本
- <noscript>元素
- 基本概念
- 嚴格模式
- 變量詳解
- 數據類型
- typeof操作符
- undefined類型
- Null類型
- Boolean類型
- Number類型
- 深入了解ES6(書)
- var 、let 、 const
- 字符串與正則表達式
- 字符串
- 正則表達式
- 函數
- 函數形參默認值
- 使用不具名參數
- 函數構造器的增強能力
- 擴展運算符
- name屬性
- 明確函數的多重用途
- 塊級函數
- 箭頭函數
- 尾調用優化
- 擴展的對象功能
- 對象類別
- 對象字面量語法的擴展
- ES6對象新增方法
- 重復的對象屬性
- 自有屬性的枚舉順序
- 更強大的原型
- 解構:更方便的數據訪問
- 為什么要用解構?
- 對象解構
- 數組解構
- 混合解構
- 參數解構
- Symbol與Symbol屬性
- 創建Symbol
- Symbol的使用方法
- Symbol全局私有屬性
- Symbol與類型強制轉換
- Symbol屬性檢索
- Symbol的一些構造方法
- Set集合與Map集合
- Set集合
- Weak Set集合(弱引用Set集合)
- Map集合
- JS標準內置對象
- Object 構造函數及屬性
- Object 構造方法
- Symbol 內建對象類的函數及屬性
- Set 構造函數及屬性
- Weak Set 構造函數及屬性
- JS雜項
- 類數組對象
- Class類的理解和使用