讲解Object.assign API的使用和原理
Object.assign(target, …sources)
函数参数为一个目标对象(该对象作为最终的返回值),源对象(此处可以为任意多个)。通过调用该函数可以拷贝所有可被枚举的自有属性值到目标对象中。
浅拷贝,利用Object.assign可以对只有一层的对象实现深拷贝
- 这里我们需要强调的三点copy条件:
- 可被枚举的属性
- 自有属性
- string或者Symbol类型是可以被直接分配的
- 拷贝过程中将调用源对象的getter方法,并在target对象上使用setter方法实现目标对象的拷贝。
拷贝过程中将调用源对象的getter方法,并在target对象上使用setter方法实现目标对象的拷贝。
// 我们参考上面的原型函数说明即可知道其最开始的o1因为设置为target,则调用其setter方法设置了其他对象的属性到自身。
var o1 = { a: 1 };
var o2 = { b: 2 };
var o3 = { c: 3 };
var obj = Object.assign(o1, o2, o3);
console.log(obj); // { a: 1, b: 2, c: 3 }
console.log(o1); // { a: 1, b: 2, c: 3 }, target object itself is changed.
我们自定义了一些对象,这些对象有一些包含了不可枚举的属性,另外注意使用 Object.defineProperty 初始化的对象默认是不可枚举的属性。对于可枚举的对象我们可以直接使用Object.keys()获得,或者使用for-in循环遍历出来.
// 对于不可枚举的属性,使用Object.assign的时候将被自动忽略。
var obj = Object.create({ foo: 1 }, { // foo is an inherit property.
bar: {
value: 2 // bar is a non-enumerable property.
},
baz: {
value: 3,
enumerable: true // baz is an own enumerable property.
}
});
var copy = Object.assign({}, obj);
console.log(copy); // { baz: 3 }
// 对于只读的属性,当分配新的对象覆盖他的时候,将抛出异常:
var target = Object.defineProperty({}, 'foo', {
value: 1,
writable: false
});
Object.assign(target, { bar: 2 })
//{bar: 2, foo: 1}
Object.assign(target, { foo: 2 })
//Uncaught TypeError: Cannot assign to read only property 'foo' of object '#<Object>'(…)
实现es5版本的Object.assign:
-
实现步骤:
- 判断是否原生支持该函数,如果不存在的话创建一个立即执行函数,该函数将创建一个assign函数绑定到Object上。
- 判断参数是否正确(目的对象不能为空,我们可以直接设置{}传递进去,但必须设置该值)
- 使用Object在原有的对象基础上返回该对象,并保存为out
- 使用for…in循环遍历出所有的可枚举的自有对象。并复制给新的目标对象(hasOwnProperty返回非原型链上的属性)
if (typeof Object.assign != 'function') {
(function () {
Object.assign = function (target) {
'use strict';
if (target === undefined || target === null) {
throw new TypeError('Cannot convert undefined or null to object');
}
var output = Object(target);
for (var index = 1; index < arguments.length; index++) {
var source = arguments[index];
if (source !== undefined && source !== null) {
for (var nextKey in source) {
if (source.hasOwnProperty(nextKey)) {
output[nextKey] = source[nextKey];
}
}
}
}
return output;
};
})();
}
如何实现深拷贝,使用原始的递归函数
- 浅拷贝:浅拷贝只复制了指向对象的指针,新旧对象共用同一块内存,修改某一个对象的同时也会把另一个都一并修改了。
如果是数组,我们可以利用数组的一些方法,比如slice,concat方法返回一个新数组的特性来实现拷贝,但假如数组嵌套了对象或者数组的话,使用concat方法克隆并不完整,如果数组元素是基本类型,就会拷贝一份,互不影响,而如果是对象或数组,就会只拷贝对象和数组的引用,这样我们无论在新旧数组进行了修改,两者都会发生变化。
我们可以思考下如何实现一个对象或数组的浅拷贝,遍历对象,然后把属性和属性值都放在一个新的对象里即可
// 浅拷贝对象
var shallowCopyObject = function(obj) {
// 只拷贝对象
if (typeof obj !== 'object') return;
// 根据obj的类型判断是新建一个数组还是对象
var newObj = obj instanceof Array ? [] : {};
// 遍历obj,并且判断是obj的属性才拷贝
for (var key in obj) {
// hasOwnProperty判断自身存在的可遍历且非继承的属性
if (obj.hasOwnProperty(key)) {
newObj[key] = obj[key];
}
}
return newObj;
}
// 深拷贝数组或者对象最简单的方法:JOSN对象中的stringify可以把一个js对象序列化为一个JSON字符串,parse可以把JSON字符串反序列化为一个js对象,通过这两个方法,也可以实现对象的深复制。---超经典
var arr = ['old', 1, true, ['old1', 'old2'], {old: 1,new:{new:12}}]
var new_arr = JSON.parse(JSON.stringify(arr) );
console.log(new_arr);
- 深拷贝:跟浅拷贝最简单明了的区别就是修改拷贝的对象,不会改变源对象,最简单直接的方法就是函数递归。
function deepClone (sourceObj, targetObj) {
let cloneObj = targetObj || {}
// 判断非空,非数组的数据类型
if(!sourceObj || typeof sourceObj !== "object" || sourceObj.length === undefined){
return sourceObj
}
// 数组
if(sourceObj instanceof Array){
cloneObj = sourceObj.concat()
// 对象
} else {
// for...in循环得到所有胡可遍历的属性
for(let i in sourceObj){
// if (typeof sourceObj[i] === 'object') {
// cloneObj[i] = deepClone(sourceObj[i], {})
// } else {
// cloneObj[i] = sourceObj[i]
// }
cloneObj[i] = typeof sourceObj[i] === 'object' ? deepClone(sourceObj[i], {}):sourceObj[i]
}
}
return cloneObj
}
-
js中对象的copy,深拷贝
var deepCopy = function(obj) { if (typeof obj !== 'object') return; var newObj = obj instanceof Array ? [] : {}; for (var key in obj) { if (obj.hasOwnProperty(key)) { newObj[key] = typeof obj[key] === 'object' ? deepCopy(obj[key]) : obj[key]; } } return newObj; }
-
介绍两个用来做深拷贝的库
**jquery**
//使用方法:
let targetObj = $.extent(true,{},sourceObj)
//**lodash函数库**
//使用方法:
//npm install lodash
//**es5写法**
let lodash = require('lodash')
//**es6写法**
import lodash from 'lodash'
let targetOj = lodash.cloneDeep(sourceObj)