注:本篇博客续上一篇“异步编程函数式编程底层代码手写”,因此编号从13开始
13. instanceof操作符的实现
function imitateInstanceof(left, right) {
// 开启尾调用优化条件
"use strict";
// 检查右值是否有[Symbol.hasInstance]属性
if (right && !right[Symbol.hasInstance])
throw new TypeError("类型检查函数的第二个参数必须是类")
// 如果左值不是引用类型,那么不存在原型链,直接返回false
if(!left || left && typeof left !== "object" && typeof left !== "function")
return false
// 初始值设置
// 如果getPrototypeOf传入一个原始类型,那么会经历装箱,变成引用类型来寻找原型链,所以之前必须检查left是否是原始类型
let leftProto = Object.getPrototypeOf(left)
let rightProto = right.prototype;
// 使用尾调用优化
return (function check() {
if(!leftPrototype)
return false
else if(leftPrototype === rightPrototype)
return true
else {
leftPrototype = Object.getPrototypeOf(leftPrototype)
return check()
}
})();
}
14.new操作符的实现
function imitateNew(constructor, ...args) {
if(typeof constructor !== "function")
throw new TypeError("构造函数必须是函数")
// 如果是ES6之前,默认使用组合模式创建对象,构造函数的prototype中只允许有方法,不允许有其它非方法的引用类型
// 第一步:创建一个新对象
let target = Object.create(constructor.prototype)
// 第二步:绑定this
let result = constructor.call(target, ...args)
// 第三步:绑定prototype
Object.setPrototypeOf(target, constructor.prototype)
// 第四步:判断返回值
return result instanceof Object ? result : target
}
15.对象浅拷贝实现
function copy(obj) {
// 如果obj不是引用类型,那么进行装箱操作
obj = obj instanceof Object ? obj : Object(obj)
// 初始化浅拷贝目标对象和枚举键
let target = null, key
// 实现最基础的常见对象类型:”数组,函数和非JavaScript特殊内置对象“的浅拷贝
if(obj instanceof Array)
target = []
else if(obj instanceof Function)
// 单个函数对象的浅复制没有明确实际意义,这里返回一个相同的引用
return obj
else
target = new Object
// 将自有可枚举属性复制到浅拷贝目标对象,不包括Symbol
for([key, target[key]] of Object.entries(obj));
// 将自有可枚举的Symbol属性复制到浅拷贝目标对象
for(let p of Object.getOwnPropertySymbols(obj))
if(Object.getOwnPropertyDescriptor(obj, p).enumerable)
target[p] = obj[p]
return target
}
16.Object.assign()实现
Object.$assign = function(target, ...sources) {
// 处理参数,如果参数不是引用类型,那么进行装箱
target = target instanceof Object ? target : Object(target)
sources = sources.map(el => el instanceof Object ? el : Object(el))
// 下面操作类似于单个对象的浅拷贝
let key
sources.forEach(obj => {
// 自有可枚举属性不包括符号属性复制到目标对象target
for([key, target[key]] of Object.entries(obj));
// 自有可枚举符号属性复制到目标对象target
for(let p of Object.getOwnPropertySymbols(obj))
if(Object.getOwnPropertyDescriptor(obj, p).enumerable)
target[p] = obj[p]
})
return target
}
17.扩展操作符合并对象实现
Object.$expand = function(target, ...sources) {
// 处理参数,如果参数不是引用类型,那么进行装箱
target = target instanceof Object ? target : Object(target)
sources = sources.map(el => el instanceof Object ? el : Object(el))
// 扩展操作符会返回一个新对象
let res = {}
// 复制操作
function copy(obj) {
for(let [key, value] of Object.entries(obj))
// 扩展操作符复制后不存在访问器属性,在此设置属性特性,使之一定不存在
Object.defineProperty(res, key, {
value: value,
enumerable: true,
configurable: true,
writable: true
})
// 自有可枚举符号属性复制到目标对象target
for(let p of Object.getOwnPropertySymbols(target))
if(Object.getOwnPropertyDescriptor(target, p).enumerable)
Object.defineProperty(obj, p, {
value: target[p],
enumerable: true,
configurable: true,
writable: true
})
}
// 复制源对象
sources.forEach(copy)
// 复制目标对象
copy(target)
return res
}
18.函数的深复制(实际应用中没有任何意义,实现纯属娱乐)
// 函数的属性都是只读的,在这里重新复制一个函数没什么意义
// 如果考虑构造函数上定义静态方法,还有的考虑,但下面不会考虑这一点
function copyFunction(target) {
let func = target.toString(), args, body
// 获取非箭头函数的参数,注意ES6增强写法的函数也没有prototype
if(target.prototype || func.startsWith(`${target.name}`)) {
args = func.slice(func.indexOf("(") + 1, func.indexOf(")")).split(",")
// 获取函数体
body = func.slice(func.indexOf("{") + 1, func.lastIndexOf("}"))
// 递归调用时函数名无法使用,将其改为arguments.callee调用递归
body = body.replaceAll("this." + target.name, "arguments.callee")
} else {
if(func.startsWith("("))
// 箭头函数参数非简写形式
args = func.slice(func.indexOf("(") + 1, func.indexOf(")")).split(",")
else
// 箭头函数单参数简写形式
args = func.slice(0, func.indexOf("=>")).trim().split(",")
if(func.endsWith("}"))
// 箭头函数函数体非简写形式
// 对象中的箭头函数方法中无法使用函数名调用自己来进行递归,这里不用做递归处理
body = func.slice(func.indexOf("{") + 1, func.lastIndexOf("}"))
else
// 箭头函数函数体简写形式
// 对象中的箭头函数方法中无法使用函数名调用自己来进行递归,这里不用做递归处理
body = "return " + func.slice(func.indexOf("=>") + 2).trim()
}
// Function只能创建匿名函数
return new Function(...args, body)
}
19.对象的深复制
// 由于18中提到函数深复制没有意义,所以当类型是函数时设置为递归边界,直接返回就行
function deepClone(obj) {
let wm = new WeakMap()
// 深复制非函数属性
function copy(key, value, target, obj) {
// 深复制要复制属性的特性
if (value instanceof Object) {
let descriptor = Object.getOwnPropertyDescriptor(obj, key)
// 如果是引用类型,那么就递归继续复制
descriptor.value = clone(value)
Object.defineProperty(target, key, descriptor)
} else
Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(obj, key))
}
function clone(obj) {
// 深复制函数参数必须是对象
obj = obj instanceof Object ? obj : Object(obj)
// JavaScript特殊内置对象处理
if (obj instanceof Date) return new Date(obj)
if (obj instanceof RegExp) return new RegExp(obj)
if (wm.has(obj))
return wm.get(obj)
let target
// JavaScript非特殊对象处理
if (typeof obj === "object")
// 保持继承链
target = new obj.constructor()
else if (typeof obj === "function")
return obj
else if (Array.isArray(obj))
target = new Array
// 解决循环引用
wm.set(obj, target)
// 复制所有非符号自有可枚举属性及其特性
Object.entries(obj).forEach(([key, value]) => copy(key, value, target, obj))
// 复制所有符号自有可枚举属性及其特性
Object.getOwnPropertySymbols(obj).forEach(key => {
if (Object.getOwnPropertyDescriptor(obj, key).enumerable)
copy(key, obj[key], target, obj)
})
return target
}
return clone(obj)
}