codes 中的最后一个“深度克隆”需要知道如何封装。
Syntax
...数组; // => 对 数组 展开 ES6
...对象; // => 对 对象 展开 ES7
展开运算符和剩余参数
虽然两者在写法上一模一样,但是含义却大不相同。区分它们也很简单:
- 写在定义一个函数的形参位置上:表示的是剩余参数。
- 否则:表示的是展开运算符。
codes
- 1.js
/*
需求:未知数组求和
*/
/**
* 根据指定的长度来创建一个随机数组
* @param {Number} len 随机数组的长度
*/
function createRandomArr(len) {
const resultArr = [];
for (let i = 0; i < len; i++) {
const item = getRandom(1, 10);
resultArr.push(item);
}
return resultArr;
}
function getRandom(min, max) {
return Math.round(Math.random() * (max - min) + min);
}
function sum(...args) {
let result = 0;
args.forEach(arg => {
result += arg;
});
return result;
}
const arr = createRandomArr(3);
console.log(arr);
// 错误调用方式:
sum(arr); // 相当于仅传入了一个参数
// 正确调用方式:
sum.apply(null, arr); // 将参数合并到一个数组中传入
sum(...arr); // 将数组展开后传入
- 2.js
/*
需求:数组浅度克隆
*/
const arr1 = [1, 2, 3];
const arr2 = [...arr1];
arr1 === arr2; // => false
arr1; // => [1, 2, 3]
arr2; // => [1, 2, 3]
- 3.js
/*
需求:对象浅度克隆
*/
const obj1 = {
name: '成哥',
age: 18,
love: '邓嫂'
}
const obj2 = {
...obj1
}
/* 等价于下面这种写法
const obj2 = {
name: obj1.name,
age: obj1.age,
love: obj1.love
}
*/
obj1; // => { name: '成哥', age: 18, love: '邓嫂' }
obj2; // => { name: '成哥', age: 18, love: '邓嫂' }
obj1 === obj2; // => false
- 4.js
/* 重复定义的对象属性
对象属性不能重复定义,若重复定义的话,那么之后定义的属性值会覆盖之前的。
利用这一特点,可以实现很多需求。
比如:对象混合。
既能确保源对象不变,又能创建一个新的对象。
这种操作在 React 中会很常见。
*/
const obj1 = {
name: '成哥',
age: 18,
love: '邓嫂'
}
const obj2 = {
...obj1,
name: '邓哥'
}
obj1; // => { name: '成哥', age: 18, love: '邓嫂' }
obj2; // => { name: '邓哥', age: 18, love: '邓嫂' }
obj1 === obj2; // => false
- 5.js
/* [示例] 在封装插件的时候,一般都会用到对象混合。
配置对象中的参数,若默认配置对象中的值与用户传入的值有冲突,那么优先使用用户传入的值。
*/
// 用户传入的配置对象
let options = {
width: '100',
height: '100'
}
// 默认的配置对象
const defaultOptions = {
width: '200',
height: '200',
color: '#008c8c'
}
options = {
...defaultOptions,
...options
}
options; // => { width: '100', height: '100', color: '#008c8c' }
defaultOptions; // => { width: '200', height: '200', color: '#008c8c' }
- 6.js
// 用户传入的配置对象
let options = {
width: '100',
height: '100'
}
// 默认的配置对象
const defaultOptions = {
width: '200',
height: '200',
color: '#008c8c'
}
options = Object.assign({}, defaultOptions, options);
options; // => { width: '100', height: '100', color: '#008c8c' }
defaultOptions; // => { width: '200', height: '200', color: '#008c8c' }
/*
个人对 Object.assign() 的理解
Object.assign({}, defaultOptions, options); // 以该语句为例
第一个参数是一个 {} 空对象 内存空间的地址假设为 a
第二个参数是 defaultOptions对象
第三个参数是 options对象
Object.assign() 做的事情就是
1. 先把第二个对象给展开 然后把它的所有键值对 丢到 a 中
2. 再把第三个对象给展开 同样地把它的所有键值对 丢到 a 中
3. ...
一旦发现了了冲突的键 那么 以后面丢进来的为准
最后将 a 返回
*/
- 7.js
/*
浅度克隆
*/
const obj1 = {
name: '成哥',
age: 18,
love: '邓嫂',
address: {
country: '中国',
province: '黑龙江',
city: '哈尔滨'
}
}
const obj2 = {
...obj1
}
obj1;
obj2;
obj1.address === obj2.address;
/* output
{
name: '成哥',
age: 18,
love: '邓嫂',
address: { country: '中国', province: '黑龙江', city: '哈尔滨' }
}
{
name: '成哥',
age: 18,
love: '邓嫂',
address: { country: '中国', province: '黑龙江', city: '哈尔滨' }
}
true
*/
- 8.js
/*
在我们清楚知道被克隆的对象的结构的前提下,我们可以采用下面这种操作来实现深度克隆。
*/
const obj1 = {
name: '成哥',
age: 18,
love: ['邓嫂', '成嫂1', '成嫂2'], // love 是一个引用类型
address: { // address 也是一个引用类型
country: '中国',
province: '黑龙江',
city: '哈尔滨'
}
}
const obj2 = {
...obj1,
address: { // 引用类型 进一步展开
...obj1.address
},
love: [...obj1.love, '成嫂3', '成嫂4'] // 引用类型进一步展开 并且 还可以新增一些成员
}
obj1;
obj2;
obj1.address === obj2.address;
obj1.love === obj2.love;
/* output
{
name: '成哥',
age: 18,
love: [ '邓嫂', '成嫂1', '成嫂2' ],
address: { country: '中国', province: '黑龙江', city: '哈尔滨' }
}
{
name: '成哥',
age: 18,
love: [ '邓嫂', '成嫂1', '成嫂2', '成嫂3', '成嫂4' ],
address: { country: '中国', province: '黑龙江', city: '哈尔滨' }
}
false
false
*/
- 深度克隆.js
/* 深度克隆
在不清楚对象结构的情况下,可以使用 clone 函数来实现深度克隆。
*/
/**
* 克隆
* @param {any} target 被克隆的目标
* @param {Boolean}} isDeep 是否深度克隆
*/
function clone(target, isDeep) {
// 1. 克隆数组
if (Array.isArray(target)) {
if (isDeep) {
let newArr = [];
target.forEach(item => {
newArr.push(clone(item, isDeep));
});
return newArr;
} else { // 浅拷贝一个数组
return [...target];
/* 或者
return target.slice();
target.slice() 等价于 target.slice(0, target.length); 等价于 [ ...target ] */
}
}
// 2. 克隆对象
if (typeof target === 'object') {
let newObj = {};
if (isDeep) {
for (const prop in target) {
newObj[prop] = clone(target[prop], isDeep);
}
} else {
for (const prop in target) {
newObj[prop] = target[prop];
}
/* 或者
newObj = {
...target
}
*/
}
return newObj;
}
// 3. 克隆基本数据类型
return target;
}