起因:
今年周末闲的蛋疼,于是想看看源码消磨时间。于是不知从哪翻出 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;
};
复制代码