# 3. 对象相关
# 3.1.相关API
- newInstance()
- myInstanceOf()
- mergeObject()
- clone1() / clone2()
- deepClone1() / deepClone2() / deepClone3() / deepClone4()
# 3.2.自定义new
# 3.2.1.API 相关
- 语法: newInstance(Fn, ...args)
- 功能: 创建Fn构造函数的实例对象
# 3.2.3.编码实现
src/object/newInstance.js
export function newInstance (Fn, ...args) {
// 创建一个空的object实例对象obj, 作为Fn的实例对象
const obj = {}
// 将Fn的prototype属性值赋值给obj的__proto__属性值
obj.__proto__ = Fn.prototype
// 调用Fn, 指定this为obj, 参数为args列表
const result = Fn.call(obj, ...args)
// 如果Fn返回的是一个对象类型, 那返回的就不再是obj, 而是Fn返回的对象
// 否则返回obj
return result instanceof Object ? result : obj
}
# 3.2.3.测试
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>自定义new</title>
</head>
<body>
<script src="../dist/atguigu-utils.js"></script>
<script>
function Person(name, age) {
this.name = name
this.age = age
// return {}
// return []
// return function (){}
// return 1
// return undefined
}
const p = new Person('tom', 12)
console.log(p)
const p2 = aUtils.newInstance(Person, 'Jack', 13)
console.log(p2, p2.constructor)
</script>
</body>
</html>
# 3.3.自定义instanceof
# 3.3.1. API 相关
- 语法: myInstanceOf(obj, Type)
- 功能: 判断obj是否是Type类型的实例
- 实现: Type的原型对象是否是obj的原型链上的某个对象, 如果是返回tru, 否则返回false
# 3.3.2.编码实现
src/object/myInstanceOf.js
export function myInstanceOf(obj, Type) {
// 得到原型对象
let protoObj = obj.__proto__
// 只要原型对象存在
while(protoObj) {
// 如果原型对象是Type的原型对象, 返回true
if (protoObj === Type.prototype) {
return true
}
// 指定原型对象的原型对象
protoObj = protoObj.__proto__
}
return false
}
# 3.3.3. 测试
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>自定义instanceof</title>
</head>
<body>
<script src="../dist/atguigu-utils.js"></script>
<script>
function Person(name, age) {
this.name = name
this.age = age
}
const p = new Person('tom', 12)
console.log(aUtils.myInstanceOf(p, Object), p instanceof Object)
console.log(aUtils.myInstanceOf(p, Person), p instanceof Person)
console.log(aUtils.myInstanceOf(p, Function), p instanceof Function)
</script>
</body>
</html>
# 3.4.合并多个对象
# 3.4.1.API 相关
- 语法: object mergeObject(...objs)
- 功能: 合并多个对象, 返回一个合并后对象(不改变原对象)
- 例子:
- { a: [{ x: 2 }, { y: 4 }], b: 1}
- { a: { z: 3}, b: [2, 3], c: 'foo'}
- 合并后: { a: [ { x: 2 }, { y: 4 }, { z: 3 } ], b: [ 1, 2, 3 ], c: 'foo' }
# 3.4.2.编码实现
src/object/mergeObject.js
export function mergeObject(...objs) {
const result = {}
// 遍历objs
objs.forEach(obj => {
Object.keys(obj).forEach(key => {
// 如果result还没有key值属性
if (!result.hasOwnProperty(key)) {
result[key] = obj[key]
} else { // 否则 合并属性
result[key] = [].concat(result[key], obj[key])
}
})
})
// 可以使用reduce来代替forEach手动添加
return result
}
# 3.4.3.测试
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>合并多个对象</title>
</head>
<body>
<script src="../dist/atguigu-utils.js"></script>
<script>
const object = {
a: [{ x: 2 }, { y: 4 }],
b: 1
}
const other = {
a: { z: 3},
b: [2, 3],
c: 'foo'
}
console.log(aUtils.merge(object, other))
</script>
</body>
</html>
# 3.5. 对象/数组拷贝
# 3.5.1.区别浅拷贝与深拷贝
- 纯语言表达:
- 浅拷贝: 只是复制了对象属性或数组元素本身(只是引用地址值)
- 深拷贝: 不仅复制了对象属性或数组元素本身, 还复制了指向的对象(使用递归)
- 举例说明: 拷贝persons数组(多个人对象的数组)
- 浅拷贝: 只是拷贝了每个person对象的引用地址值, 每个person对象只有一份
- 深拷贝: 每个person对象也被复制了一份新的
# 3.5.2.实现浅拷贝
src/object/clone.js
/*
实现浅拷贝
方法一: 利用ES6语法
方法二: 利用ES5语法: for...in
*/
/* 方法一: 利用ES6语法*/
export function clone1(target) {
// 如果是对象(不是函数, 也就是可能是object对象或者数组)
if (target!=null && typeof target==='object') {
if (target instanceof Array) {
// return target.slice()
// return target.filter(() => true)
// return target.map(item => item)
return [...target]
} else {
// return Object.assign({}, target)
return {...target}
}
}
// 基本类型或者函数, 直接返回
return target
}
/* 方法二: 利用ES5语法: for...in */
export function clone2(target) {
if (target!=null && typeof target==='object') {
const cloneTarget = Array.isArray(target) ? [] : {}
for (let key in target) {
if (target.hasOwnProperty(key)) {
cloneTarget[key] = target[key]
}
}
return cloneTarget
} else {
return target
}
}
# 3.5.3.实现深拷贝
实现一: 大众乞丐版
- 问题1: 函数属性会丢失
- 问题2: 循环引用会出错
实现二: 面试基础版
- 解决问题1: 函数属性还没丢失
实现三: 面试加强版本
- 解决问题2: 循环引用正常
实现四: 面试加强版本2(优化遍历性能)
- 数组: while | for | forEach() 优于 for-in | keys()&forEach()
- 对象: for-in 与 keys()&forEach() 差不多
编码实现:
src/object/deepClone.js
/*
深度克隆
1). 大众乞丐版
问题1: 函数属性会丢失
问题2: 循环引用会出错
2). 面试基础版本
解决问题1: 函数属性还没丢失
3). 面试加强版本
解决问题2: 循环引用正常
4). 面试加强版本2(优化遍历性能)
数组: while | for | forEach() 优于 for-in | keys()&forEach()
对象: for-in 与 keys()&forEach() 差不多
*/
/*
1). 大众乞丐版
问题1: 函数属性会丢失
问题2: 循环引用会出错
*/
export function deepClone1(target) {
return JSON.parse(JSON.stringify(target))
}
/*
2). 面试基础版本
解决问题1: 函数属性还没丢失
*/
export function deepClone2 (target) {
if (target!==null && typeof target==='object') {
const cloneTarget = target instanceof Array ? [] : {}
for (const key in target) {
if (target.hasOwnProperty(key)) {
cloneTarget[key] = deepClone2(target[key])
}
}
return cloneTarget
}
return target
}
/*
3). 面试加强版本
解决问题2: 循环引用正常
*/
export function deepClone3 (target, map=new Map()) {
if (target!==null && typeof target==='object') {
// 从缓存容器中读取克隆对象
let cloneTarget = map.get(target)
// 如果存在, 返回前面缓存的克隆对象
if (cloneTarget) {
return cloneTarget
}
// 创建克隆对象(可能是{}或者[])
cloneTarget = target instanceof Array ? [] : {}
// 缓存到map中
map.set(target, cloneTarget)
for (const key in target) {
if (target.hasOwnProperty(key)) {
// 递归调用, 深度克隆对象, 且传入缓存容器map
cloneTarget[key] = deepClone3(target[key], map)
}
}
return cloneTarget
}
return target
}
/*
4). 面试加强版本2(优化遍历性能)
数组: while | for | forEach() 优于 for-in | keys()&forEach()
对象: for-in 与 keys()&forEach() 差不多
*/
export function deepClone4 (target, map=new Map()) {
if (target!==null && typeof target==='object') {
// 从缓存容器中读取克隆对象
let cloneTarget = map.get(target)
// 如果存在, 返回前面缓存的克隆对象
if (cloneTarget) {
return cloneTarget
}
// 创建克隆对象(可能是{}或者[])
if (target instanceof Array) {
cloneTarget = []
// 缓存到map中
map.set(target, cloneTarget)
target.forEach((item, index) => {
cloneTarget[index] = deepClone4(item, map)
})
} else {
cloneTarget = {}
// 缓存到map中
map.set(target, cloneTarget)
Object.keys(target).forEach(key => {
cloneTarget[key] = deepClone4(target[key], map)
})
}
return cloneTarget
}
return target
}
# 3.5.4.测试
- 浅拷贝测试
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>浅克隆/浅复制/浅拷贝</title>
</head>
<body>
<!--
实现浅拷贝
方法一: 利用ES6语法
方法二: 利用ES5语法: for...in
-->
<script src="../dist/atguigu-utils.js"></script>
<script>
const obj1 = { x: 'abc', y: {m: 1} }
// const obj2 = aUtils.clone1(obj1)
const obj2 = aUtils.clone2(obj1)
console.log(obj2, obj2===obj1, obj2.x===obj1.x, obj2.y===obj1.y)
const arr1 = ['abc', {m: 1}]
// const arr2 = aUtils.clone1(arr1)
const arr2 = aUtils.clone2(arr1)
console.log(arr2, arr2===arr1, arr2[0]===arr1[0], arr2[1]===arr1[1])
</script>
</body>
</html>
- 深拷贝测试
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>深度克隆/深复制/深拷贝</title>
</head>
<body>
<script src="https://cdn.bootcss.com/lodash.js/4.17.15/lodash.min.js"></script>
<script src="../dist/atguigu-utils.js"></script>
<script>
const obj1 = {
a: 1,
b: [ 'e', 'f', 'g'],
c: { h: { i: 2 } },
d: function (){}
}
obj1.b.push(obj1.c)
obj1.c.j = obj1.b
// const obj2 = _.cloneDeep(obj1)
// const obj2 = aUtils.deepClone1(obj1)
// const obj2 = aUtils.deepClone2(obj1)
// const obj2 = aUtils.deepClone3(obj1)
const obj2 = aUtils.deepClone4(obj1)
console.log(obj2, obj2.c === obj1.c, obj2.d===obj1.d)
</script>
</body>
</html>