第八天2019-01-08

碎碎念

今天入职以来第一次迟到,作啊,8点43分有车,然后悠哉一会儿,52有车,悠哉穿个衣服,然后就9.20的车了。可能还有可能到公司,但是,出了隧道之后,公交每个红绿灯都停了,没有例外的巧合啊!期间还火车过道,这运气没谁了!最后10点10分才到公司。辛亏一个月可以迟到两次。

笔记-Object.assign()-下

继承属性和不可枚举属性是不能拷贝的

原生情况下挂载在 Object 上的属性是不可枚举的,但是直接在 Object 上挂载属性 a 之后是可枚举的,所以这里必须使用 Object.defineProperty,并设置 enumerable: false 以及 writable: true, configurable: true。3

var obj = Object.create({foo: 1}, { // foo 是个继承属性。
bar: {
value: 2 // bar 是个不可枚举属性。
},
baz: {
value: 3,
enumerable: true // baz 是个自身可枚举属性。
}
});

var copy = Object.assign({}, obj);
console.log(copy); // { baz: 3 }
检查是否可以枚举

查看Object.assign 是否可枚举,使用 Object.getOwnPropertyDescriptor 或者 Object.propertyIsEnumerable(会检查给定的属性名是否直接存在于对象中,而不是在原型链上,并且满足 enumerable: true)。

// 方法一:查看Object.assign()是否可以枚举
Object.getOwnPropertyDescriptor(Object, "assign");
// 方法二
Object.propertyIsEnumerable("assign"); // false
// 实现 Object.assign时要用
Object.defineProperty(Object, 'assign',{
...
writable: true,
enumerable: false,
configurable: true
})

原始类型会被包装为对象

有时候可以利用这一点包装为对象

var v1 = "abc";
var v2 = true;
var v3 = 10;
var v4 = Symbol("foo")

var obj = Object.assign({}, v1, null, v2, undefined, v3, v4);
// 原始类型会被包装,null 和 undefined 会被忽略。
// 注意,只有字符串的包装对象才可能有自身可枚举属性。
console.log(obj); // { "0": "a", "1": "b", "2": "c" }

异常会打断后续拷贝任务

在出现错误的情况下,例如,如果属性不可写,会引发TypeError,如果在引发错误之前添加了任何属性,则可以更改target对象2

// 定义不能写
var 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,第三个源对象更是不会被拷贝到的。

拷贝访问器

var obj = {
foo: 1,
// get 访问器
get bar() {
return 2;
}
};
var copy = Object.assign({}, obj);
console.log(copy); // { foo: 1, bar: 2 }
// copy.bar的值来自obj.bar的getter函数的返回值
拷贝所有自有属性的属性描述符(难点)
var obj = {
foo: 1,
get bar() {
return 2;
}
};
function completeAssign(target, ...sources) {
sources.forEach(source => {
// reduce 可能是不太好理解,下期可能需要出一个笔记
let descriptors = Object.keys(source).reduce((descriptors, key) => {
// 获取属性描述符(可以分为数据描述符和存取描述符均,属性的信息可以查看:Object.defineProperty)
descriptors[key] = Object.getOwnPropertyDescriptor(source, key);
return descriptors;
}, {});
// Object.assign 默认也会拷贝可枚举的Symbols
// Object.getOwnPropertySymbols() 方法返回一个给定对象自身的所有 Symbol 属性的数组。
Object.getOwnPropertySymbols(source).forEach(sym => {
let descriptor = Object.getOwnPropertyDescriptor(source, sym);
if (descriptor.enumerable) {
descriptors[sym] = descriptor;
}
});
Object.defineProperties(target, descriptors);
});
return target;
}

var copy = completeAssign({}, obj);
console.log(copy);
// { foo:1, get bar() { return 2 } }

MDN实现Obeject.assign()

if (typeof Object.assign != 'function') {
// Must be writable: true, enumerable: false, configurable: true
Object.defineProperty(Object, "assign", {
value: function assign(target, varArgs) { // .length of function is 2
'use strict';
if (target == null) { // TypeError if undefined or null
throw new TypeError('Cannot convert undefined or null to object');
}

var to = Object(target);

for (var index = 1; index < arguments.length; index++) {
var nextSource = arguments[index];

if (nextSource != null) { // Skip over if undefined or null
for (var nextKey in nextSource) {
// Avoid bugs when hasOwnProperty is shadowed
// 直接使用 myObject.hasOwnProperty(..) 是有问题的,因为有的对象可能没有连接到 Object.prototype 上所以这里用上call()
if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
to[nextKey] = nextSource[nextKey];
}
}
}
}
return to;
},
writable: true,
configurable: true
});
}

今天就先很粗糙地写一下,明天再详细的谈一些细节部分吧。
– end

参考

[1]. Object.assign()–MDN
[2]. JavaScript 中的对象拷贝
[3]. 【进阶4-2期】Object.assign 原理及其实现

文章作者: lmislm
文章链接: http://lmislm.com/2019/01/08/2019-01-08/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 LMISLMのBlog