1.数据类型
基础类型存储在栈内存,引用类型存储在堆内存
2.数据类型检查
- typeof
基本数据类型null会判断为Object
引用数据类型 Object,除了 function 会判断为 OK 以外,其余都是 ‘object’. - instanceof
instanceof 可以准确地判断复杂引用数据类型,但是不能正确判断基础数据类型; - Object.prototype.toString
对于 Object 对象,直接调用 toString() 就能返回 [object Object];而对于其他对象,则需要通过 call 来调用,才能返回正确的类型信息。
function myInstanceof(left, right) {
// 这里先用typeof来判断基础数据类型,如果是,直接返回false
if(typeof left !== 'object' || left === null) return false;
// getProtypeOf是Object对象自带的API,能够拿到参数的原型对象
let proto = Object.getPrototypeOf(left);
while(proto) {
//循环往下寻找,直到找到相同的原型对象
if(proto === right.prototype) return true;//找到相同原型对象,返回true
proto = Object.getPrototypeof(proto);
}
return false;
}
// 验证一下自己实现的myInstanceof是否OK
console.log(myInstanceof(new Number(123), Number)); // true
console.log(myInstanceof(123, Number)); // false
全局通用类型判断方法
function getType(obj){
let type = typeof obj;
if (type !== "object") {
// 先进行typeof判断,如果是基础数据类型,直接返回
return type;
}
// 对于typeof返回结果是object的,再进行如下的判断,正则返回结果
return Object.prototype.toString.call(obj).replace(/^\[object (\S+)\]$/, '$1'); // 注意正则中间有个空格
}
3.深浅拷贝
浅拷贝
object.assign、扩展运算符、concat、slice
手写:
const shallowClone = (target) => {
if (typeof target === 'object' && target !== null) {
const cloneTarget = Array.isArray(target) ? []: {
};
for (let prop in target) {
if (target.hasOwnProperty(prop)) {
cloneTarget[prop] = target[prop];
}
}
return cloneTarget;
} else {
return target;
}
}
深拷贝
1.JSON.parse(JSON.stringfy())
缺点:
- 拷贝的对象的值中如果有函数、undefined、symbol 这几种类型,经过 JSON.stringify 序列化之后的字符串中这个键值对会消失;
- 拷贝 Date 引用类型会变成字符串;
- 无法拷贝不可枚举的属性;
- 无法拷贝对象的原型链;
- 拷贝 RegExp 引用类型会变成空对象;
- 对象中含有 NaN、Infinity 以及 -Infinity,JSON 序列化的结果会变成 null;
- 无法拷贝对象的循环引用,即对象成环 (obj[key] = obj)。
2.手写递归
let deepClone = (target) => {
if (typeof target === 'object' && target !== null) {
let cloneTarget = Array.isArray(target) ? [] : {
};
for (let key in target) {
if (target.hasOwnProperty(key)) {
cloneTarget[key] = deepClone(target[key]);
}
}
return cloneTarget;
} else {
return target;
}
}
缺点:
- 不能拷贝不可遍历属性
- 对Date、RegExp、Error、Function 这样的引用类型并不能正确地拷贝;
- 循环引用没有解决
3.递归进阶版
- 针对能够遍历对象的不可枚举属性以及 Symbol 类型,我们可以使用 Reflect.ownKeys 方法;
- 当参数为 Date、RegExp 类型,则直接生成一个新的实例返回;
- 利用 Object 的 getOwnPropertyDescriptors 方法可以获得对象的所有属性,以及对应的特性,顺便结合 Object 的 create 方法创建一个新对象,并继承传入原对象的原型链;
- 利用 WeakMap 类型作为 Hash 表,因为 WeakMap 是弱引用类型,可以有效防止内存泄漏(你可以关注一下 Map 和 weakMap 的关键区别,这里要用 weakMap),作为检测循环引用很有帮助,如果存在循环,则引用直接返回 WeakMap 存储的值。
const isComplexDataType = obj => (typeof obj === 'object' || typeof obj === 'function') && (obj !== null)
let deepClonePlus = (target, hash = new WeakMap()) => {
if (target instanceof Date) return new Date(target);
if (target instanceof RegExp) return new RegExp(target);
if (hash.has(target)) return hash.get(target);
let allDesc = Object.getOwnPropertyDescriptors(target);
let cloneTarget = Object.create(Object.getPrototypeOf(target), allDesc);
hash.set(target, cloneTarget);
for (let key of Reflect.ownKeys(target)) {
cloneTarget[key] = (isComplexDataType(target[key]) && typeof target[key] !== 'function') ? deepClonePlus(target[key], hash) : target[key];
}
return cloneTarget;
}
4. 6种继承方式
5. new、apply、call、bind
function _new(ctor, ...args) {
if(typeof ctor !== 'function'){
throw "ctor must be a function"
}
let obj = {
}<