起因:
今年週末閒的蛋疼,於是想看看原始碼消磨時間。於是不知從哪翻出 Object-assign的模組,看完覺得挺有意思的,於是寫篇文章記錄一下。
Object.assign() 方法用於將所有可列舉屬性的值從一個或多個源物件複製到目標物件。它將返回目標物件
這個模組是Object.assign的Polyfill
開始:
首先拿Object三個方法
getOwnPropertySymbols 物件的所有符號屬性作為 Symbol 陣列獲取
hasOwnProperty 方法會返回一個布林值,指示物件自身屬性中是否具有指定的屬性
propertyIsEnumerable 判斷屬性是否可以列舉
var getOwnPropertySymbols = Object.getOwnPropertySymbols;
var hasOwnProperty = Object.prototype.hasOwnProperty;
var propIsEnumerable = Object.prototype.propertyIsEnumerable;
複製程式碼
toObject這個函式 第一個引數判斷是否null或者undefined 是的話報錯,否則把值轉換為Object型別返回
function toObject(val) {
if (val === null || val === undefined) {
throw new TypeError('Object.assign cannot be called with null or undefined');
}
return Object(val);
}
複製程式碼
shouldUseNative這個函式比較有意思,不止判斷了Object物件有沒有assign,還判斷了一些舊版V8物件列舉的一些BUG。 在這裡我也用了舊版V8測試了一番。
function shouldUseNative() {
try {
if (!Object.assign) {
return false;
}
// Detect buggy property enumeration order in older V8 versions.
// https://bugs.chromium.org/p/v8/issues/detail?id=4118
var test1 = new String('abc'); // eslint-disable-line no-new-wrappers
test1[5] = 'de';
if (Object.getOwnPropertyNames(test1)[0] === '5') {
return false;
}
// https://bugs.chromium.org/p/v8/issues/detail?id=3056
var test2 = {};
for (var i = 0; i < 10; i++) {
test2['_' + String.fromCharCode(i)] = i;
}
var order2 = Object.getOwnPropertyNames(test2).map(function (n) {
return test2[n];
});
if (order2.join('') !== '0123456789') {
return false;
}
// https://bugs.chromium.org/p/v8/issues/detail?id=3056
var test3 = {};
'abcdefghijklmnopqrst'.split('').forEach(function (letter) {
test3[letter] = letter;
});
if (Object.keys(Object.assign({}, test3)).join('') !==
'abcdefghijklmnopqrst') {
return false;
}
return true;
} catch (err) {
// We don't expect any of the above to throw, but better to be safe.
return false;
}
}
複製程式碼
這個地方 5這個屬性是後面才加上的 列舉順序應該是 0,1,2,5
在舊版V8裡卻是 5,0,1,2。
這裡把0到9轉成字元編碼存到陣列再用map遍歷後轉成字串按道理應該是0123456789 我們卻發現舊版裡的輸出已經亂了。
額 第三個判斷 我想應該個第二個判斷一樣遍歷完後變亂
不過這個判斷用到Object.assgin,我這個舊V8不支援報錯了,如果報錯 會被try catch捕獲 在函式裡返回false。
最後判斷如果 shouldUseNatva 返回true 就說明環境支援Object.assgin和不是舊版的V8,直接呼叫原生Objetc.assgin。
否則返回自己實現assgin函式。
module.exports = shouldUseNative() ? Object.assign : function (target, source) {
var from;
//把第一個引數轉成Object
var to = toObject(target);
var symbols;
//從第二個物件開始遍歷
for (var s = 1; s < arguments.length; s++) {
//轉換為物件
from = Object(arguments[s]);
for (var key in from) {
//判斷物件自身有沒有這個屬性
if (hasOwnProperty.call(from, key)) {
//存在的話 目標物件新增這個屬性
to[key] = from[key];
}
}
// 判斷環境支不支援 getOwnPropertySymbols
if (getOwnPropertySymbols) {
//獲得物件的所有符號屬性作為 Symbol 陣列
symbols = getOwnPropertySymbols(from);
for (var i = 0; i < symbols.length; i++) {
//判斷這個值是否可以遍歷
if (propIsEnumerable.call(from, symbols[i])) {
//可以遍歷的話 目標物件新增這個屬性
to[symbols[i]] = from[symbols[i]];
}
}
}
}
//最後返回這個目標物件
return to;
};
複製程式碼