
其中 Symbol和 BigInt是 ES6中新增的数据类型:
这些数据可以分为原始数据类型和引用数据类型:
两种类型的区别在于存储位置的不同:
堆和栈的概念存在于数据结构和操作系统内存中,在数据结构中:
在操作系统中,内存被分为栈区和堆区:
console.log(typeof 2); // number
console.log(typeof true); // boolean
console.log(typeof 'str'); // string
console.log(typeof function(){}); // function
console.log(typeof undefined); // undefined
console.log(typeof {}); // object
console.log(typeof []); // object
console.log(typeof null); // object
//其中数组、对象、null都会被判断为object,其他判断都正确。
instanceof 可以正确判断对象的类型,其内部运行机制是判断在其 原型链中 能否找到 该类型的原型。
console.log(2 instanceof Number); // false
console.log(true instanceof Boolean); // false
console.log('str' instanceof String); // false
console.log([] instanceof Array); // true
console.log(function(){} instanceof Function); // true
console.log({} instanceof Object); // true
可以看到:
console.log((2).constructor === Number); // true
console.log((true).constructor === Boolean); // true
console.log(('str').constructor === String); // true
console.log(([]).constructor === Array); // true
console.log((function() {}).constructor === Function); // true
console.log(({}).constructor === Object); // true
constructor有两个作用:
需要注意,如果创建一个对象来改变它的原型,constructor 就不能用来判断数据类型了:
function Fn(){};
Fn.prototype = new Array();
var f = new Fn();
console.log(f.constructor===Fn); // false
console.log(f.constructor===Array); // true
Object.prototype.toString.call() 使用 Object 对象的原型方法 toString 来判断数据类型:
const a = Object.prototype.toString;
console.log(a.call(2)); // [object Number]
console.log(a.call(true)); // [object Boolean]
console.log(a.call('str')); // [object String]
console.log(a.call([])); // [object Array]
console.log(a.call(function(){})); // [object Function]
console.log(a.call({})); // [object Object]
console.log(a.call(undefined)); // [object Undefined]
console.log(a.call(null)); // [object Null]
obj.toString() 的结果和 Object.prototype.toString.call(obj) 的结果不一样,这是为什么?
这是因为 toString是 Object 的原型方法,而 Array、function 等类型作为 Object 的实例,都重写了 toString 方法。不同的对象类型调用 toString 方法时,根据原型链的知识,调用的是对应的重写之后的 toString() 方法( function类型返回内容为函数体的字符串,Array类型返回元素组成的字符串…),而不会去调用 Object上原型 toString() 方法 (返回对象的具体类型),所以采用 obj.toString() 不能得到其对象类型,只能将 **obj **转换为字符串类型;因此,在想要得到对象的具体类型时,应该调用 Object 原型上的 toString() 方法。
instanceof运算符用于判断构造函数的 prototype属性是否出现在对象的原型链中的任何位置。
function myInstanceof(left, right) {
// 获取对象的原型
let proto = Object.getPrototypeOf(left)
// 获取构造函数的 prototype 对象
let prototype = right.prototype;
// 判断构造函数的 prototype 对象是否在对象的原型链上
while (true) {
if (!proto) return false;
if (proto === prototype) return true; // 如果没有找到,就继续从其原型上找,Object.getPrototypeOf方法用来获取指定对象的原型
proto = Object.getPrototypeOf(proto);
} }
NaN指“不是一个数字”(not a number)用于指出数字类型中的错误情况,即“执行数学运算没有成功,这是失败后返回的结果”
console.log(typeof NaN) // number
console.log(NaN !== NaN) // true
:::info
NaN 是一个特殊值,它和自身不相等,是唯一一个非自反(自反,reflexive,即 **x === x **不成立)的值。而 NaN !== NaN 为 true。
:::
isNaN接收参数后,会尝试将这个参数转换为数值,如果转换后为 number类型,则返回false,任何不能被转换为数值的的值都会返回 true,因此非数字值传入也会返回 true,会影响 **NaN *的判断。
console.log(isNaN(null)); //false
console.log(isNaN(true)); //false
console.log(isNaN(false)); //false
console.log(isNaN(0)); //false
console.log(isNaN(undefined)); //true
console.log(isNaN("AB")); //true
console.log(isNaN({a: 1})); //true
console.log(isNaN(NaN)); //true
Es6新增的,不会进行数据类型的转换,只有 NaN才返回true,其余都是 false
console.log(Number.isNaN(null)); //false
console.log(Number.isNaN(true)); //false
console.log(Number.isNaN(false)); //false
console.log(Number.isNaN(0)); //false
console.log(Number.isNaN(undefined)); //false
console.log(Number.isNaN("AB")); //false
console.log(Number.isNaN({a: 1})); //false
console.log(Number.isNaN(NaN)); //true
Object.prototype.toString.call(arr).slice(8,-1) // Array
arr.__proto__ // Array.prototype
Array.isArray(arr) // true
arr instanceof Array // true
Array.prototype.isPrototypeOf(arr) // true
把数组转成字符串
const fruits = ["Banana", "Orange", "Apple", "Mango"];
console.log(fruits.toString()); // Banana,Orange,Apple,Mango
也可将所有数组元素结合为一个字符串。它的行为类似 toString(),但是您还可以规定分隔符:
const fruits = ["Banana", "Orange","Apple", "Mango"];
console.log(fruits.join(" * ")); // Banana * Orange * Apple * Mango
从数组中删除最后一个元素,返回被删除的值
const fruits = ["Banana", "Orange", "Apple", "Mango"];
console.log(fruits.pop()); // Mango
(在数组结尾处)向数组添加一个新的元素,返回新数组的长度
const fruits = ["Banana", "Orange", "Apple", "Mango"];
const x = fruits.push("Kiwi"); // x 的值是 5
会删除首个数组元素,并把所有其他元素“位移”到更低的索引,返回被“位移出”的字符串
const fruits = ["Banana", "Orange", "Apple", "Mango"];
console.log(fruits.shift()); // Banana
(在开头)向数组添加新元素,并“反向位移”旧元素,返回新数组的长度
const fruits = ["Banana", "Orange", "Apple", "Mango"];
console.log(fruits.unshift('Lemon')); // 4
向数组添加新项,返回一个包含已删除项的数组
const fruits = ["Banana", "Orange", "Apple", "Mango"];
// 使用 splice() 来拼接数组
fruits.splice(2, 0, "Lemon", "Kiwi");
// 第一个参数(2)定义了应添加新元素的位置(拼接)。
// 第二个参数(0)定义应删除多少元素。
// 其余参数(“Lemon”,“Kiwi”)定义要添加的新元素。
console.log(fruits) // ['Banana', 'Orange', 'Lemon', 'Kiwi', 'Apple', 'Mango']
// 使用 splice() 来删除元素
fruits.splice(0, 1); // 删除 fruits 中的第一个元素
// 第一个参数(0)定义新元素应该被添加(接入)的位置。
// 第二个参数(1)定义应该删除多个元素。
console.log(fruits) // ['Orange', 'Lemon', 'Kiwi', 'Apple', 'Mango']
通过合并(连接)现有数组来创建一个新数组,可以使用任意数量的数组参数。不影响原数组,它总是返回一个新数组
const myGirls = ["Cecilie", "Lone"];
const myBoys = ["Emil", "Tobias", "Linus"];
console.log(myGirls.concat(myBoys)) ; // 连接 myGirls 和 myBoys ['Cecilie', 'Lone', 'Emil', 'Tobias', 'Linus']
// 合并三个数组
const myBoysTwo = ["LIli", "JieJie"];
console.log(myGirls.concat(myBoys,myBoysTwo)); // 连接 myGirls 和 myBoys 和 myBoysTwo ['Cecilie', 'Lone', 'Emil', 'Tobias', 'Linus', 'LIli', 'JieJie']
// concat()可以将值作为参数
console.log(myBoysTwo.concat(['hahah'])) // ['LIli', 'JieJie', 'hahah']
用数组的某个片段切出新数组,不影响原数组
const myBoysTwo = ['LIli', 'JieJie', 'hahah']
myBoysTwo.slice(1) // ['JieJie', 'hahah']
myBoysTwo.slice(0,2) // ['LIli', 'JieJie']
为数组中的每一个元素依次执行回调函数,不包括数组中被删除或从未被赋值的元素
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-O7SoiM5o-1660817203338)(https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/a248a43d09cf4be8bdf6efa17701866c~tplv-k3u1fbpfcp-zoom-1.image)]
callback (执行数组中每个值的函数,包含四个参数)
1、previousValue (上一次调用回调返回的值,或者是提供的初始值(initialValue))
2、currentValue (数组中当前被处理的元素)
3、index (当前元素在数组中的索引)
4、array (调用 reduce 的数组)
initialValue (作为第一次调用 callback 的第一个参数。)
const arr = [1, 2, 3, 4];
const sum = arr.reduce(function(prev, cur, index, arr) {
console.log(prev, cur, index);
return prev + cur;
})
console.log(arr, sum);
// 1 2 1
// 3 3 2
// 6 4 3
// [1, 2, 3, 4] 10
这里可以看出index是从1开始的,第一次的prev的值是数组的第一个值。数组长度是4,但是reduce函数循环3次。
const arr = [1, 2, 3, 4];
const sum = arr.reduce(function(prev, cur, index, arr) {
console.log(prev, cur, index);
return prev + cur;
},0) //注意这里设置了初始值,一般来说我们提供初始值通常更安全
console.log(arr, sum);
// 0 1 0
// 1 2 1
// 3 3 2
// 6 4 3
// [1, 2, 3, 4] 10
这里可以看出index是从0开始的,第一次的prev的值是我们设置的初始值0,数组长度是4,reduce函数循环4次
:::info
结论:如果没有提供initialValue,reduce 会从索引1的地方开始执行 callback 方法,跳过第一个索引。如果提供initialValue,从索引0开始。
:::
let arr = [1,2,3,4,4,1]
let newArr = arr.reduce((pre,cur)=>{
if(!pre.includes(cur)){
return pre.concat(cur)
}else{
return pre
}
},[])
console.log(newArr);// [1, 2, 3, 4]
let arr = [
{name: 'mom', id:1},
{name: 'timo', id:2},
{name: 'mom', id:1},
{name: 'timo', id:2},
]
let obj = {}
let newArr = arr.reduce((item, next)=>{
obj[next.id] ? "" : (obj[next.id] = true && item.push(next));
return item;
},[])
console.log(newArr);// [{name: 'mom', id:1}, {name: 'timo', id:2}]
| 方法 | 是否改变原数组 | 特点 |
|---|---|---|
| forEach() | 否 | 数组方法,不改变原数组,没有返回值 |
| map() | 否 | 数组方法,不改变原数组,有返回值,可链式调用 |
| filter() | 否 | 数组方法,过滤数组,返回包含符合条件的元素的数组,可链式调用 |
| for…of | 否 | for…of遍历具有Iterator迭代器的对象的属性,返回的是数组的元素、对象的属性值,不能遍历普通的obj对象,将异步循环变成同步循环 |
| every() 和 some() | 否 | 数组方法,some()只要有一个是true,便返回true;而every()只要有一个是false,便返回false. |
| find() 和 findIndex() | 否 | 数组方法,find()返回的是第一个符合条件的值;findIndex()返回的是第一个返回条件的值的索引值 |
| reduce() 和 reduceRight() | 否 | 数组方法,reduce()对数组正序操作;reduceRight()对数组逆序操作 |
为什么都说遍历后通常是引入当前遍历数组的内存地址和生成一个新的数组,因为按 forEach() 和 map() 创作思想,forEach() 遍历基本引入遍历数组内存地址、**map() **遍历而是生成一个新的数组,但是有些人写的时候不按 map() 和 forEach() 的常规语法来,导致使用 map() 遍历数组时,返回的值是直接把当前遍历数组的每个元素的内存地址给了另外一个数组,本质还是引用遍历数组中每个元素的内存地址,这样生成的数组已经不能称作为一个新的数组同样也把 **map() **最大的一个特性给舍弃了,同理如果 map() 和 forEach() 本质没区别的话,没必要把他们封装成两个函数,封装成一个就好了
let arr = [
{
title:'雪碧',
price: 2.5,
},
{
title:'可乐',
price: 2.5,
}
]
let a = arr.forEach((item,index) =>{
return item
})
let b = arr.map((item,index) =>{
return item
})
console.log(arr) //打印arr数组
console.log(a) //undefined
console.log(b) //打印arr数组
// map()方法是有返回值的,而forEach()方法没有返回值
// 但是如果用map()方法想让b获取arr的数组的值,不建议这样的写法,因为上面我们已经说到了map()方法主要是生成一个新的数组,而不是直接引入arr数组内存地址
let arr = [{title:'雪碧',price: 2.5},{title:'可乐',price: 2.5}]
let list = [{title:'雪碧',price: 2.5},{title:'可乐',price: 2.5}]
let a = [];
let b = [];
arr.forEach((item,index) =>{
a[index] = item;
})
b = list.map((item,index) =>{
return item
})
// 第一次打印
// 到这里我们可以看到,a和b都已经成功的接收了arr和list的数组的数据,
// 强调map()一定不要直接return item,这里这么写主要是为了区分生成新数组和内存地址的区别
console.log(a);
console.log(b);
// 第二次打印
a[0].price = 3;
b[0].price = 3;
console.log(a);
console.log(b);
console.log(arr)
console.log(list)
第一次打印
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Tq4pVlsW-1660817203339)(https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/ae8fdfceaced446cacccb0b304213fd5~tplv-k3u1fbpfcp-zoom-1.image)]
第二次打印
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-funPUIYO-1660817203339)(https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/69bb384523484976a33e27d1062ddf9b~tplv-k3u1fbpfcp-zoom-1.image)]
:::info
从 案例2 这里我们看到只更改了 **a **和 **b **中 price 值,但是 **arr **和 **list **数组 **price 的值也发生了变化,arr 发生变化属于正常现象,因为在上述中已经说过 forEach() 方法遍历通常都是引入遍历数组的内存地址,不管是arr **发生改变还是 **a **发生改变,其实都是直接改变同一个内存地址,所以他们的值会一直同步,但是 **map() **方法生成的是一个新的数组,为什么 **b **的值发生改变,list 值同样发生改变呢,这里就是上述说的内存地址引入问题
:::
let arr = [{title:'雪碧',price: 2.5},{title:'可乐',price: 2.5}]
let list = [{title:'雪碧',price: 2.5},{title:'可乐',price: 2.5}]
let a = [];
let b = [];
arr.forEach((item,index) =>{
a[index] = item;
})
b = list.map((item,index) =>{
return{
title:item.title,
price:item.price
}
})
b[0].price = 3;
console.log(b);
console.log(list)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zFbcELUt-1660817203340)(https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/74a73faa03a84b9da60c5f207b163f9f~tplv-k3u1fbpfcp-zoom-1.image)]
在 案例3这里我们改变了一下写法,就会发现在改变 b 值,list就不会发生变化了。
在 案例2 中改变 b的值,list的值发现变化是因为 map() 遍历后生成的是一个新的数组,但是在遍历的过程还是引入旧数组的内容地址,而在 案例3 中我们通过 map() 遍历的时候自己定义一下想要参数名,只复制一下旧数组的值,遍历完后会生成新的内存空间去存储 b的值,所以我们在改变 b的值时候也只是改变了 b内存中的值,而没有改变 list内存的值,同样的在改变 list的值,b的值也不会发现改变,所以说 map() 方法遍历后会生成一个新的数组
ES6 中也可以通过 …扩展运算符复制一个对象
const obj = { name: 'dengke' }
const obj1 = {
age: 18,
temp: {
a: 10
}
}
const obj2 = { ...obj, ...obj1 }
console.log(obj2) // { name: 'dengke', age: 18, temp: { a: 10 } }
obj2.temp.a = 20
console.log(obj2) // { name: 'dengke', age: 18, temp: { a: 20 } }
console.log(obj1) // { name: 'dengke', age: 18, temp: { a: 20 } }
// 对象的创建并添加属性
const obj = {
name: 'pengyuyan',
address: {
a: '山东省,
b: 266000
},
arr: [1, 2],
sayHelllo: (name) => {
console.log(`hello,${name}`)
}
}
// 用 dot(点 .) 的方式访问
console.log(obj.name) // pengyuyan
console.log(obj.address) // {"a":'山东省',"b":266000}
console.log(obj.arr) // [1,2]
obj.sayHelllo('huge') // hello,huge
// 用 [] 的方式访问
console.log(obj['name']) // pengyuyan
console.log(obj['age']) // {"a":'266000',"b":266000}
console.log(obj['arr']) // [1,2]
obj['sayHelllo']('huge') // hello,huge
const obj = {};
obj.name = 'pengyuyan';
const myName = 'name';
console.log(obj.myName);// undefined,访问不到对应的属性
console.log(obj[myName]);// pengyuyan
const person = {
name:'huge'
};
console.log(person["name"]);//huge
console.log(person.name); //huge
// 可以通过变量来访问属性
const propertyName = 'name';
console.log(person[propertyName]); //huge
var propertyName2 = 'name2';
console.log(person[propertyName2]); //undefined
const obj1 = {};
obj1.1 = 1; // Unexpected number
obj1[2] = 2;
console.log(obj1[2]);//2
console.log(obj1)//{2: 2}
会遍历对象中所有的可枚举属性(包括自有属性和继承属性)
const obj = {
a: '山东省',
b: 266000
}
// 使用Object.create创建一个原型为obj的对象 (模拟继承来的属性)
const newObj = Object.create(obj)
newObj.newA = '山西省'
newObj.newB = '030001'
for(i in newObj){
console.log(i)
}
// newA
// newB
// a
// b
// 将其中的一个属性变为不可枚举属性
Object.defineProperty(newObj, 'newA', {
enumerable: false
})
for(i in newObj){
console.log(i)
}
// newB
// a
// b
返回一个包括所有的可枚举的自有属性的名称组成的数组
// 接 for... in... 的例子
const result = Object.keys(newObj)
console.log(result) // ["newB"]
// 接 for... in... 的例子
const result = Object.getOwnPropertyNames(newObj)
console.log(result) // ['newA','newB']
用于将所有可枚举属性的值从一个或多个源对象分配到目标对象,它将返回目标对象。
常用来合并对象
Object.assign(target, ...sources)
// 参数:target 目标参数,sources源对象 返回值:目标对象
const obj1 = { a: 1, b: 2 }
const obj2 = { b: 3, c: 4 }
const obj3 = Object.assign(obj1, obj2)
const obj4 = Object.assign({}, obj1) // 克隆了obj1对象
console.log(obj1) // { a: 1, b: 3, c: 4 } 对同名属性b进行了替换 obj1发生改变是因为obj2赋给了obj1
console.log(obj2) // { b: 3, c: 4 }
console.log(obj3) // { a: 1, b: 3, c: 4 }
console.log(obj4) // { a: 1, b: 3, c: 4 }
注意:
const obj1 = { a: 1, b: 2 }
const obj5 = {
name: 'pengyuyan',
a: '你好',
fn: {
sum: 10
}
}
const obj6 = Object.assign(obj1, obj5)
console.log(obj6) // { a: '你好', b: 2, fn: {sum: 10},name: 'pengyuyan'}
console.log(obj1) // {a: '你好', b: 2, fn: {sum: 10},name: 'pengyuyan'} 对同名属性a进行了替换
语法:Object.values(obj)
const obj = { name: 'pengyuyan', age: 18 }
console.log(Object.values(obj)) // ['pengyuyan', 18]
const obj1 = { 0: 'a', 1: 'b', 2: 'c' }
console.log(Object.values(obj1)) // ['a', 'b', 'c']
// 如果对象key为number的话,会从升序枚举返回
const obj2 = { 10: 'a', 1: 'b', 2: 'c' }
console.log(Object.values(obj2)) // ['b', 'c', 'a']
语法:obj.hasOwnProperty(prop)
const obj = new Object();
obj.property = 'pengyuyan'
// 只要属性存在,也返回true
obj.property2 = null
obj.property3 = undefined
Object.prototype.property4 = 0
console.log(obj.hasOwnProperty('property')) // true
console.log(obj.hasOwnProperty('property2')) // true
console.log(obj.hasOwnProperty('property3')) // true
console.log(obj.hasOwnProperty('property3')) // true
对于 == 来说,如果对比双方的类型不一样,就会进行类型转换。
console.log(null == undefined) // true
console.log(null === undefined) // false
// *注意: null 或者 undefined 和其他任何的数据类型比较都返回 false
[1,2] == '1,2' //true,[1,2].toString() ==> "1,2"
(function(){console.log('hello')}) == "function(){console.log('hello')}" //true
[1] == true //true,[1].toString() ==> "1",Number("1") ==> 1,Number(true) ==> 1
[12] == 12 //true,[12].toString() ==> "12",Number("12") ==> 12
流程图如下:![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kRP6OS9l-1660817203340)(https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/2c405dd2c48342ab92d3a990afb45a47~tplv-k3u1fbpfcp-zoom-1.image)]](https://1000bd.com/contentImg/2024/03/29/43ccb7290943f797.png)
|| 和 && 首先会对第一个操作数执行条件判断,如果其不是布尔值就先强制转换为布尔类型,然后再执行条件判断。
let outObj = {inObj: {a: 1, b: 2} }
let newObj = {...outObj}
newObj.inObj.a = 2
console.log(outObj) // {inObj: {a: 2, b: 2}}
对象的扩展运算符(…)用于取出参数对象中的所有可遍历属性,拷贝到当前对象之中。
let bar = { a: 1, b: 2 }
let baz = { ...bar }; // { a: 1, b: 2 }
上述方法实际上等价于:
let bar = { a: 1, b: 2 }
let baz = Object.assign({}, bar) // { a: 1, b: 2 }
let bar = {a: 1, b: 2}
let baz = {...bar, ...{a:2, b: 4}} // {a: 2, b: 4}
利用上述特性就可以很方便的修改对象的部分属性。在redux中的reducer函数规定必须是一个纯函数,reducer中的state对象要求不能直接修改,可以通过扩展运算符把修改路径的对象都复制一遍,然后产生一个新的对象返回。
数组的扩展运算符可以将一个数组转为用逗号分隔的参数序列,且每次只能展开一层数组。
console.log(...[1, 2, 3]) // 1 2 3
console.log(...[1, [2, 3, 4], 5]) // 1 [2, 3, 4] 5
下面是数组的扩展运算符的应用:
function add(x, y) {
return x + y
}
const numbers = [1, 2]
add(...numbers) // 3
const arr1 = [1, 2]
const arr2 = [...arr1]
要记住:扩展运算符(…)用于取出参数对象中的所有可遍历属性,拷贝到当前对象之中,这里参数对象是个数组,数组里面的所有对象都是基础数据类型,将所有基础数据类型重新拷贝到新的数组中。
如果想在数组内合并数组,可以这样:
const arr1 = ['two', 'three']
const arr2 = ['one', ...arr1, 'four', 'five'] // ["one", "two", "three", "four", "five"]
const [first, ...rest] = [1, 2, 3, 4, 5];
console.log(first) // 1
console.log(rest) // [2, 3, 4, 5]
需要注意:如果将扩展运算符用于数组赋值,只能放在参数的最后一位,否则会报错。
const [...rest, last] = [1, 2, 3, 4, 5] // 报错
const [first, ...rest, last] = [1, 2, 3, 4, 5] // 报错
[...'hello'] // [ "h", "e", "l", "l", "o" ]
比较常见的应用是可以将某些数据结构转为数组:
// arguments对象
function foo() {
const args = [...arguments]
}
用于替换es5中的**Array.prototype.slice.call(arguments)**写法。
const numbers = [9, 4, 7, 1]
Math.min(...numbers) // 1
Math.max(...numbers); // 9
:::info
用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target)
详细查看
:::
let outObj = { inObj: {a: 1, b: 2} }
let newObj = Object.assign({}, outObj)
newObj.inObj.a = 2
console.log(outObj) // {inObj: {a: 2, b: 2}}
const obj = {
a: 1,
b: 2,
c: 3
}
const copyObj = JSON.parse(JSON.stringify(obj))
copyObj.a = 5;
console.log(obj.a); // 1
console.log(copyObj.a); // 5 修改copyObj的数据,并不会对obj造成任何影响
// 注意:JSON.stringify()以及JSON.parse()它是不可以拷贝 undefined , function, RegExp 等等类型的
// 不能拷贝函数
function deepClone1(obj) {
//判断拷贝的要进行深拷贝的是数组还是对象,是数组的话进行数组拷贝,对象的话进行对象拷贝
const objClone = Array.isArray(obj) ? [] : {};
//进行深拷贝的不能为空,并且是对象或者是
if (obj && typeof obj === "object") {
for (key in obj) {
if (obj.hasOwnProperty(key)) {
if (obj[key] && typeof obj[key] === "object") {
objClone[key] = deepClone1(obj[key]);
} else {
objClone[key] = obj[key];
}
}
}
}
return objClone;
}
// 当数组中都是简单数据类型时
let arr = [1, 2, false, 'a']
let arr2 = [].concat(arr)
arr[0] = 3
console.log(arr) // [3, 2, false, 'a'] arr中的数据更改,并不会影响arr2
console.log(arr2) // [1, 2, false, 'a']
// 如果数组中有复杂数据类型
let arr = [1, 2, false, {a: 3}]
let arr2 = [].concat(arr)
arr[3].a = 4
console.log(arr) // [1, 2, false, {a: 4}] arr中的数据更改,arr2中的数据会跟着变
console.log(arr2) // [1, 2, false, {a: 4}]