基本类型和引用类型
除了基本数据类型 String,Number,Boolean,null,undefined,Symbol,所有类型都是引用数据类型; 引用数据类型存储在堆内存中,在栈中存储了指针;引用类型复制时只是的一个地址指针而已,实际指向的是同一个对象。
判断类型方式
| 类型判断 | typeof | instanceof | constructor | toSting.call |
|---|---|---|---|---|
| num | number | false | true | [object Number] |
| str | stgring | false | true | [object String] |
| bool | boolean | false | true | [object Boolean] |
| arr | object | true | true | [object Array] |
| json | object | true | true | [object Object] |
| func | function | true | true | [object Function] |
| und | undefined | false | - | [object Undefined] |
| null | object | false | - | [object Null] |
| date | object | true | true | [object Date] |
| reg | object | true | true | [object RegExp] |
| err | object | true | true | [object Error] |
| 优点 | 使用简单,能直接输出结果 | 能检测出复杂的类型 | 基本能检测出所有的类型 | 检测出所有的类型 |
| 缺点 | 检测出的类型太少 | 基本类型检测不出,不能跨iframe | 不能跨iframe,constructor易被修改 | IE6下的undefined,null均为Object |
1、typeof
typeof是一个操作符而不是函数,其右侧跟一个一元表达式,并返回这个表达式的数据类型。返回的结果用该类型的字符串(全小写字母)形式表示 包括以下 8 种:string、number、boolean、undefined、function 、symbol、bigInt、object。
2、instanceof
instanceof 是用来判断 A 是否为 B 的实例,表达式为:A instanceof B,如果 A 是 B 的实例,则返回 true,否则返回 false。 需特别注意:instanceof 检测的是原型
即instanceof 用来比较一个对象是否为某一个构造函数的实例。instanceof可以准确的判断复杂数据类型,但是不能正确判断基本数据类型。
3、constructor
JavaScript中,每个对象都有一个constructor属性,可以得知某个实例对象,到底是哪一个构造函数产生的, constructor属性表示原型对象与构造函数之间的关联关系。
-
当一个函数F被定义时,JS引擎会为F添加prototype原型,然后在prototype上添加一个constructor属性,并让其指向F的引用,F利用原型对象的constructor属性引用了自身,当F作为构造函数创建对象时,原型上的constructor属性被遗传到了新创建的对象上,从原型链角度讲,构造函数F就是新对象的类型。这样做的意义是,让对象诞生以后,就具有可追溯的数据类型。
-
通过typeof运算符来判断它是原始的值还是对象。如果是对象,就可以使用constructor属性来判断其类型。
-
如判断数组的函数:
function isArray(data){
return typeof data == "object" && data.constructor == Array;
}
isArray([]) // true
注意:null 和 undefined 是没有 constructor 存在的,这两种类型的数据需要通过其他方式来判断。
4、Object.prototype.toString.call()
Object.prototype.toString(o)是 Object 的原型方法。
- 获取对象o的class属性。这是一个内部属性,
- 连接字符串:[object + 结果(1)],格式为 [object Xxx] ,其中 Xxx 就是对象的类型。
深拷贝例子
let obj1 = {
// 基本数据类型可以不做处理, typeof !== 'object'
num: 12,
str: "xxx",
bol: false,
fn: function () {
console.log(1111);
}, // 函数的处理一般只是功能的复用
// 也不需要处理
nulVal: null,
// 无元素,可以直接通过构造函数new 出来的新数据
reg: /7/,
date: new Date(),
// 新的数据结构
map: new Map([
["d", 4],
["5", 5],
]),
set: new Set(["s", "g"]),
array: [3, 3],
arrayOfObj: [{ a: 3 }, { a: 7 }],
obj: { a: 4 },
};
方式一 1.耗性能 2. js关键字 function ... 等不能复制
let objCl = JSON.parse(JSON.stringify(obj1));
console.log(objCl, obj1);
方式二
function deepClone2(source) {
const targetObj = source.constructor === Array ? [] : {};
for (const keys in source) {
if (Object.hasOwnProperty.call(source, keys)) {
const element = source[keys];
if (element && typeof element === "object") {
targetObj[keys] = deepClone2(element);
} else {
targetObj[keys] = element;
}
}
}
return targetObj;
}
let clone2 = deepClone2(obj1);
console.log(clone2, obj1);
方式三 Map 会增加引用计数,引用计数不为0 会导致垃圾回收机制无法回收变量的空间 WeakMap 不会额外引用计数
let cacheMap = new WeakMap();
function deepClone(obj) {
// [1. 不处理]
if (typeof obj !== "object" || !obj) return obj;
// 判断是否已经处理过
if (cacheMap.has(obj)) return cacheMap.get(obj);
let constructor = obj.constructor;
let tmp, params;
// [2 无元素]
if (obj instanceof RegExp || obj instanceof Date) {
params = obj;
}
tmp = new constructor(params);
cacheMap.set(obj, tmp);
// 新结构
if (obj instanceof Map) {
for (let [key, value] of obj) {
tmp.set(deepClone(key), deepClone(value));
}
} else if (obj instanceof Set) {
for (let value of obj) {
tmp.add(deepClone(value));
}
} else {
for (let key in obj) {
if (Object.hasOwnProperty.call(obj, key)) {
tmp[key] = deepClone(obj[key]);
}
}
}
return tmp;
}
// 业务需求:环形对象
let obj2 = {
to: obj1,
};
obj1.to = obj2;
let cloned = deepClone(obj1);
console.log(cloned, obj1);