数据类型
基本数据类型
numberstringbooleanundefinednullSymbol
引用数据类型
- 对象
- 对象的子类型,包括函数,数组,内置对象如
String,Number等
确定类型
typeof
typeof 123 // "number"
typeof '123' // "string"
typeof false // "boolean"
function f() {}
typeof f// "function"
a
typeof a// "undefined" //检查没有声明的变量
typeof window // "object"
typeof {} // "object"
typeof [] // "object"
typeof null // "object"
可以看出使用 typeof 不能区分数组和对象的类型,这时可以使用instanceof。
instanceof
var o = {};
var a = [];
o instanceof Array // false
a instanceof Array // true
Object.prototype.toString
Object.prototype.toString.call(123); //[object Number]
Object.prototype.toString.call('123'); //[object String]
Object.prototype.toString.call(undefined); //[object Undefined]
Object.prototype.toString.call(true); //[object Boolean]
Object.prototype.toString.call({}); //[object Object]
Object.prototype.toString.call([]); //[object Array]
Object.prototype.toString.call(function(){}); //[object Function]
Object.prototype.toString.call(null); //[[object Null]]
注意点
undefined 与 null
- 出现
undefined的情况
function f(x) {
return x;
}
f() // 调用函数时,应该提供的参数没有提供 返回undefined
var o = new Object();
o.p //对象没有赋值的属性 undefined
function f() {}
f() //函数没有返回值时 undefined
undefined与null区别
null是一个表示“空”的对象,转为数值时为0;undefined是一个表示"此处无定义"的原始值,转为数值时为NaN;undefined和null与任何有意义的值比较返回的都是false,但是null与undefined之间不严格比较返回的是true
布尔值为false的情况
这六个值都被转为false,其他值都视为true
- undefined
- null
- false
- +0、-0
- NaN
- " "或' '
NaN
注意NaN是一个特殊数值,而不是一种数据类型。
可认为NaN是一个“警戒值”,用于指出数字类型中的错误情况,即“执行数学运算没有成功,这是失败后返回的结果”
深拷贝与浅拷贝
区别
首先要明确基本数据类型和引用数据类型的存储位置
- 基本数据类型:存放在栈区
- 引用数据类型:数据在堆区中,栈区存放着指向堆内存中该数据的指针,也就是说,需要通过栈区的指针区访问到数据,而不能直接访问到堆中的数据
深浅拷贝都是对于引用数据类型而言的。 浅拷贝就只是复制对象的引用,如果拷贝后的对象发生变化,原对象也会发生变化。
let person1 = {
name:'Bob',
more:{
age:23
}
}
let person2 = person1 //浅拷贝
console.log(person2.more.age) //23
person1.more.age = 12
console.log(person2.more.age) //12
只有深拷贝才是真正地对对象的拷贝。 深拷贝不像浅拷贝那样只是复制了一层引用,就连值也都复制了,两个对象不会互相影响。
let person1 = {
name:'Bob',
more:{
age:23
}
}
let person2 = deepClone(person1) //深拷贝
console.log(person2.more.age) //23
person1.more.age = 12
console.log(person2.more.age) //23
实现
递归
接下来看看如何实现深拷贝吧
明确了引用数据类型有哪些,判断后进行递归便可得到深拷贝后的结果
function deepClone(source){
const target = source.constructor === Array ? [] : {}; // 判断复制的目标是数组还是对象
for(let keys in source){
if(source.hasOwnProperty(keys)){
if(source[keys] && typeof source[keys] === 'object'){
target[keys] = source[keys].constructor === Array ? [] : {};
target[keys] = deepClone(source[keys]);
}else{ // 如果不是,直接赋值
target[keys] = source[keys];
}
}
}
return target;
}
JSON的解析与序列化
json.stringify将 js 对象序列化为 json 字符串
json.parse将 JSON 字符串解析为原生 js 值
let person1 = {
name:'Bob',
more:{
age:23
}
}
let jsonText = JSON.stringify(person1) //'{"name":"Bob","more":{"age":23}}'
let personCopy = JSON.parse(jsonText)
//{name:'Bob',more:{age:23}}
但是这种方法会忽略所有函数及原型成员,值为undefined的任何属性也会被跳过,结果中最终都是值为有效 JSON 数据类型的实例属性。
JS中数组和对象自带的拷贝方法
数组
- concat
来自MDN
将被合并到一个新的数组中。如果省略了所有参数,则concat会返回调用此方法的现存数组的一个浅拷贝 注意:这里说的浅拷贝并不是 ‘=’,而是进行了首层的深拷贝
const sourceArr = [1,2,3,4]
const targetArr = sourceArr.concat()
console.log(sourceArr === targetArr)//false
sourceArr.push(5)
console.log(targetArr)//[1,2,3,4]
const sourceArr = [1,2,3,4,[5,6]]
const targetArr = sourceArr.concat()
console.log(sourceArr === targetArr)//false
sourceArr[4].push(7)
console.log(targetArr)[1,2,3,4,[5,6,7]]
- slice
同concat只是进行了首层拷贝
- 扩展运算符
let sourceArr = [1,2,3,4,[5,6]];
let targetArr = [...sourceArr];
targetArr[0] = 0;
console.log(sourceArr); // [1,2,3,4,[5,6]]
targetArr[4].push(7);
console.log(sourceArr); // [1,2,3,4,[5,6,7]]
对象
- Object.assign()
来自MDN
用于将所有可枚举属性的值从一个或多个源对象分配到目标对象。它将返回目标对象。原对象也会更改。
const sourceObj = {a:1,b:{c:2}};
const targetObj = Object.assign({}, sourceObj);
targetObj.a = 2;
targetObj.b.c = 3;
console.log(sourceObj); // {a:1,b:{c:3}}
同样只是进行了首层拷贝
- 扩展运算符
const sourceObj = {a:1,b:{c:2}};
const targetObj = {...sourceObj};
targetObj.a = 2;
targetObj.b.c = 3;
console.log(sourceObj); // {a:1,b:{c:3}}
同样只是进行了首层拷贝
总结
搞清楚js的数据类型后再进行深拷贝,递归才是真正实现深拷贝的方式。