跳到主要内容

2 篇博文 含有标签「instanceof」

查看所有标签

· 阅读需 5 分钟

深拷贝

b是a的一份拷贝,且b中没有对a中属性的引用

序列化

const b = JSON.parse(JSON.stringify(a))

缺点:JSON 不支持的类型(函数、Date、undefined、正则等)以及不支持自引用(a.self = a)

基本数据类型

首先根据数据类型区别基本数据类型对象instanceof Object),基本数据类型则直接返回

if(v instanceof Object) {
// ...
} else {
return v
}

对象类型

对象类型使用 for in 递归深拷贝其内部自有属性

if(v instanceof Object) {
let result = undefined
// ...
// 对内部自有属性进行递归拷贝
for(let key in v){
if(v.hasOwnProperty(key)){
result[key] = deepClone(v[key])
}
}
return result
} else {
return v
}

原型

根据 value 的原型,初始化深拷贝结果 result,相当于提前复制原型链上的属性

主要有 ArrayFunctionDateRegExpPlain Object

let result = undefined

// Array
if(v instanceof Array) {
result = new Array()
// Function
} else if(v instanceof Function){
// 普通函数
if(v.prototype) {
result = function(){ return v.apply(this, arguments)}
// 箭头函数
} else {
result = (...args) => { return v.call(undefined, ...args)}
}
// Date
} else if(v instanceof Date) {
// Date的数据减0会转为时间戳,再利用这个时间戳构造新的Date
result = new Date(v - 0)
// RegExp
} else if(v instanceof RegExp) {
result = new RegExp(v.source, v.flags)
// Plain Object
} else {
result = new Object()
}

其中,函数要区别普通函数和箭头函数(v.prototype);时间类型的数据减 0 会隐式转换为时间戳,利用这个时间戳构造新的 Date;尽可能使用 new 关键字来初始化 result

自引用

对于 a.self = a 的自引用情况,可以使用一个 Map 来记录拷贝的属性(key和value均为拷贝的值),如果发现自引用了,就直接返回a

// 利用Map对每一次拷贝做记录
const cache = new Map()
const deepClone = (v) => {
if(v instanceof Object) {
// 如果发现自引用(key 对象存在),直接返回 v
if(cache.get(v)){ return cache.get(v)}
// ...
// 将拷贝的值与结果存入map
cache.set(v, result)
// ...
} else {
return v
}
}

完整代码(附测试用例)

// 利用Map对每一次拷贝做记录
const cache = new Map()
const deepClone = (v) => {
if(v instanceof Object) {// object
// 如果发现自引用(key对象存在),直接返回v
if(cache.get(v)){ return cache.get(v)}
let result = undefined
if(v instanceof Array) {// object-Array
result = new Array()
} else if(v instanceof Function){// object-Function
if(v.prototype) {// 普通函数(都有prototype属性)
result = function(){ return v.apply(this, arguments)}
} else {// 箭头函数
result = (...args) => { return v.call(undefined, ...args)}
}
} else if(v instanceof Date) {// object-Date
// Date的数据减0会转为时间戳,再利用这个时间戳构造新的Date
result = new Date(v - 0)
} else if(v instanceof RegExp) {// object-RegExp
result = new RegExp(v.source, v.flags)
} else { // object-Object
result = new Object()
}
// 将拷贝的值与结果存入map
cache.set(v, result)
// 对内部自有属性进行递归拷贝
for(let key in v){
if(v.hasOwnProperty(key)){
result[key] = deepClone(v[key])
}
}
return result
} else {// 基本数据类型
return v
}
}
// 测试用例
const a = {
number:1, bool:false, str: 'hi', empty1: undefined, empty2: null,
array: [
{name: 'frank', age: 18},
{name: 'jacky', age: 19}
],
date: new Date(2000,0,1,20,30,0),
regex: /\.(j|t)sx/i,
obj: { name:'frank', age: 18},
f1: (a, b) => a + b,
f2: function(a, b) { return a + b }
}
// 自引用
a.self = a

const b = deepClone(a)

b.self === b // true
b.self = 'hi'
a.self !== 'hi' //true

· 阅读需 2 分钟

JS 数据类型

8种:数字(number)、字符串(string)、布尔(boolean)、空(null)、未定义(undefined)、对象(object)、bigintsymbol

除 object 外,都属于原始数据类型

Typeof

优点:能够快速区分基本数据类型 (以及 function)

缺点:不能将 Object、Array、Null、Map 等引用类型区分,统一返回 object

typeof 1                        // number
typeof true // boolean
typeof 'str' // string
typeof Symbol('') // symbol
typeof undefined // undefined
typeof 1n // bigint
typeof function(){} // function

typeof [] // object
typeof {} // object
typeof null // object

Instanceof

优点:能够区分 Array、Object 和 Function 等引用数据类型,适合用于判断自定义类的实例对象

缺点:不能很好判断原始数据类型(number string boolean bigInt 等)

当使用字面量创建原始类型的值时,它们不是对象,也就不是构造函数的实例。原始类型的值在需要的时候会被自动包装成对应的对象类型(String, Number, Boolean),但这种自动包装并不影响 instanceof 运算符的行为

[] instanceof Array                     // true
function(){} instanceof Function // true
{} instanceof Object // true

1 instanceof Number // false
true instanceof Boolean // false
'str' instanceof String // false

Object.prototype.toString.call()

优点:精准判断数据类型

缺点:写法繁琐不容易记,推荐进行封装后使用

var toString = Object.prototype.toString
toString.call(1) //[object Number]
toString.call(true) //[object Boolean]
toString.call('str') //[object String]
toString.call([]) //[object Array]
toString.call({}) //[object Object]
toString.call(function(){}) //[object Function]
toString.call(undefined) //[object Undefined]
toString.call(null) //[object Null]
toString.call(2n) //[object BigInt]