数组浅拷贝练习题
let utils = (function () {
/*
* toArray:转换为数组的方法
* @params
* 不固定数量,不固定类型
* @return
* [Array] 返回的处理后的新数组
* by haha on 2020
*/
// 方法1
function toArray(...args) {
// ES6的剩余运算符获取的参数集合本身就是数组
return args;
}
// 方法2
function toArray() {
// arguments是实参集合,获取的结果是一个类数组(箭头函数中没有arguments),不能直接调取数组的办法(因为它不是ARRAY的实例)
// return [...arguments];
return Array.from(arguments);
}
// 方法3
function toArray() {
// 把类数组转换为数组(克隆)
// 原理:只要把slice执行,让方法中的this变为arguments,这样就可以实现把类数组arguments转换为数组
// 1.让slice执行: Array.prototype.slice() 或者 [].slice()
// 2.把this改变成为arguments [].slice.call(arguments)
// 前提:操作数组的代码(内置方法中的代码)也需要适配改变的THIS(类数组)
[].forEach.call(arguments, item => {
console.log(item);
});
return [].slice.call(arguments);
/* let arr = [];
for (let i = 0; i < arguments.length; i++) {
arr.push(arguments[i]);
}
return arr; */
}
return {
toArray
};
})();
let ary = utils.toArray(10, 20, 30); // => [10,20,30]
console.log(ary);
ary = utils.toArray('A', 10, 20, 30); // => ['A',10,20,30]
console.log(ary);
// 简单模拟slice的实现
Array.prototype.slice = function slice() {
// this -> ary
let arr = [];
for (let i = 0; i < this.length; i++) {
arr.push(this[i]);
}
return arr;
};
// let ary = [10, 20, 30, 40];
// ary.slice() 把数组克隆一份
// console.log(ary.slice());
浅拷贝
- 克隆:内存地址是不一样的
【for循环也可以实现浅拷贝。】
实现对象的克隆:
Object.assign:浅比较/浅克隆
{...obj}:展开运算符,也只能展开第一级,也是浅克隆
newObj = obj :这不是克隆,只是赋值
let obj1 = { a: 1, b: 2, c: [5, 6] } let obj2 = Object.assign({}, obj1) console.log(obj2, obj1 == obj2) // {a: 1, b: 2, c: Array(2)} false console.log(obj1.c === obj2.c) // true let obj3 = { ...obj1 } console.log(obj3) // {a: 1, b: 2, c: Array(2)} console.log(obj1.c === obj3.c) // true
实现数组的克隆
slice、concat... 这些都是浅克隆
forEach、map、reduce... 遍历也只是遍历第一级
let newArr = arr.concat([]);
console.log(newArr === arr); // => false
console.log(newArr[2] === arr[2]); // => true
// 补充:使用for循环实现对象、数组的浅拷贝
// 使用for循环实现对象的浅拷贝
let obj1 = { a: 1, b: 2, c: [5, 6] }
let newObj = {}
for (let i in obj1) {
newObj[i] = obj1[i]
}
console.log(newObj) // {a: 1, b: 2, c: Array(2)}
console.log(obj1.c === newObj.c) // true
// ---------------------------
// 使用for循环实现数组的浅拷贝
let arr = [11, 22, {name: '哈哈'}]
let newArr = []
for(let i=0;i<arr.length;i++) {
newArr[i] = arr[i]
}
console.log(newArr) // [11, 22, {name: '哈哈'}]
console.log(newArr[2] === arr[2]) // true
深拷贝
JSON.parse(JSON.stringify())
// 暴力法:把原始数据直接变为字符串,再把字符串变为对象(此时浏览器要重新开辟所有的内存空间),实现出深度克隆、深拷贝
let arr = [11, 22, { name: '哈哈' }]
let newArr = JSON.parse(JSON.stringify(arr));
console.log(newArr);
console.log(newArr === arr); // => false
console.log(newArr[2] === arr[2]); // => false
// 出现的问题:正则会变为空对象,日期变为字符串,函数、undefined、Symbol直接消失,BigInt直接报错
let newObj = JSON.parse(JSON.stringify(obj));
console.log(newObj);
console.log(newObj === obj); // => false
console.log(newObj.c === obj.c); // => false
自己实现深拷贝 【有缺陷,不用】
/*
补充:获取实例的构造函数
console.log((11).constructor) // Number函数
console.log(('11').constructor) // String函数
console.log((true).constructor) // Boolean函数
console.log((false).constructor) // Boolean函数
console.log((BigInt(10)).constructor) // BigInt函数
console.log((BigInt('33')).constructor) // BigInt函数
// console.log((undefined).constructor) // 报错
// console.log((null).constructor) // 报错
console.log((function fn() {}).constructor) // Function函数
console.log(({}).constructor) // Object函数
*/
/* 数组或者对象的深克隆/浅克隆 */
let obj = {
a: 100,
b: [10, 20, 30],
c: function () { },
d: /^d+$/,
e: new Date(),
f: Symbol('f'),
g: BigInt('10')
};
let arr = [10, [100, 200], {
x: 10,
y: 20
}];
// 深克隆、深拷贝
function cloneDeep(obj) {
// 获取实例的构造函数 【当obj是部分基本数据类型,或者是空对象{}的时候,这样写有问题,应该先用Object(obj)转换】
const constructor = obj.constructor;
if (obj === null) return null;
if (typeof obj !== "object") return obj;
// 正则实例、日期实例的constructor都是返回RegExp、Date构造函数 --> console.log(new Date().constructor):Date、 console.log(/d/.constructor):RegExp.
// constructor.name:返回构造函数的名字:(11).constructor.name --> Number,是constructor.name,不是constructor
if (/^(RegExp|Date)$/i.test(constructor.name)) return new constructor(obj);
// 重新创建一个constructor类的实例,用于保存克隆的数据
// 不能写成 let clone = {},因为这样写就是往对象里克隆,而有时候克隆的是数组,so应该往obj同样的数据类型的实例中克隆
let clone = new constructor();
for (let key in obj) {
if (!obj.hasOwnProperty(key)) break;
// 如果不递归,只会实现浅拷贝。如果obj[key]是引用类型,需要重新cloneDeep。不管是基本、引用类型,都cloneDeep。
clone[key] = cloneDeep(obj[key]);
}
return clone;
}
let newArr = cloneDeep(arr);
console.log(newArr);
console.log(newArr === arr); // => false
console.log(newArr[2] === arr[2]); // => false
let newObj = cloneDeep(obj);
console.log(newObj);
console.log(newObj === obj); // => false
console.log(newObj.d === obj.d); // => false