1.手写 Object.create
/**
* 手写 Object.create
* @param {object} obj 传入对象
* @author sunzhibin
*/
function create(obj){
function F(){} // 定义空的构造函数
F.prototype = obj // 将传入的obj加到构造函数的原型中
return new F()
}
2.手写 instanceof 方法
/**
* 手写 instanceof 方法
* 通过原型链的方式判断类型
* 1.首先获取类型的原型
* 2.然后获得对象的原型
* 然后一直循环判断对象的原型是否等于类型的原型,直到对象原型为 null,因为原型链最终为 null
* @author sunzhibin
*/
function myInstanceof(left,right){
let leftPro = left.__proto__,
rightPro = right.prototype
while(true){
if(!leftPro) return false // 通过循环查到原型链的最底层,当返回的是null,停止循环,返回false
if(leftPro === rightPro) return true // 查到,返回true
leftPro = leftPro.__proto__ // 将leftPro赋值为下一层的原型,一级一级的匹配,直到最底层原型为null时,停止循环
}
}
3. 手写 new 操作符
/**
* 手写 new 操作符
* 在调用 new 的过程中会发生以上四件事情:
* 1.首先创建了一个新的空对象
* 2.设置原型,将对象的原型设置为函数的 prototype 对象
* 3.让函数的 this 指向这个对象,执行构造函数的代码(为这个新对象添加属性)
* 4.判断函数的返回值类型,如果是值类型,返回创建的对象。如果是引用类型,就返回这个引用类型的对象
* @param {function} fn
* @author sunzhibin
*/
function _new(fn,...args){
// 判断参数是否是一个函数
if (typeof fn !== "function") {
console.error("type error not function");
return;
}
const o = {} // 1. 在内存中创建一个新空对象
o.__proto__ = fn.prototype // 2. 这个新对象内部的 [[Prototype]] 指针被赋值为构造函数(fn)的 prototype 属性
const r = fn.apply(o,args) // 3.构造函数内部的 this 被赋值为这个新对象(即 this 指向新对象)
const isTrue = r && typeof r === 'function' || typeof r === 'object' // 4.判断r是否为function或者object
return isTrue ? r : o // 4. 如果构造函数返回非空对象,则返回该对象;否则,返回刚才创建的新对象
}
4.手写防抖函数
/**
* 手写防抖函数
* 函数防抖是指在事件被触发 n 秒后再执行回调,如果在这 n 秒内事件又被触发,则重新计时。这可以使用在一些点击请求的事件上,避免因为用户的多次点击向后端发送多次请求
* 例:搜索框
* @param {function} fn
* @param {number} wait 延迟时间-默认800毫秒
* @author sunzhibin
* @example
*/
function debounce(fn,wait=800){
let timer = null
return function(...args){
const that = this
if(timer) clearTimeout(timer) // 清除定时器
timer = setTimeout(()=>{
fn.apply(that,args) // 修改函数this指向,并传递参数,执行传进来的方法
timer = null // 将定时器置空,放置内存泄漏
},wait)
}
}
5.手写节流函数
/**
* 手写节流函数
* 函数节流是指规定一个单位时间,在这个单位时间内,只能有一次触发事件的回调函数执行,如果在同一个单位时间内某事件被触发多次,只有一次能生效。
* 例:节流可以使用在 scroll 函数的事件监听上,通过事件节流来降低事件调用的频率。
* @param {function} fn
* @param {number} delay 延迟时间-默认800毫秒
* @author sunzhibin
*/
function throttle(fn,delay=800){
let cueTime = Date.now();
return function(...args){
const that = this
let nowTime = Date.now();
if(nowTime - curTime >= delay){
fn.apply(that,args)
cueTime = nowTime
}
}
}
6.手写类型判断函数
/**
* 手写类型判断函数
* 判断类型的4种方式:
* 1.typeof:基本数据类型中:null 。引用数据类型中的:Array,Object,Date,RegExp。不可以用typeof检测。都会返回小写的object 例:typeof 100 //"number"
* 2.instanceof:基本数据类型中:Number,String,Boolean。字面量值不可以用instanceof检测 例:[1,2,3] instanceof Array //true
* 3.constructor:除了undefined和null报错之外,其他类型都可以通过constructor属性来判断类型 例:num.constructor==Number // true
* 4.Object.prototype.toString.call():例:Object.prototype.toString.call(123) //"[object Number]"
* 当前函数是封装类型判断
* @param {any} value
* @author sunzhibin
*/
function getType1(value){
// 判断数据是 null 的情况
if(value == null) return value+""
// 判断数据是引用类型的情况
if(typeof value === 'object'){
return Object.prototype.toString.call(value).replace(/^\[object (\S+)\]$/, '$1')
}else{
// 判断数据是基本数据类型的情况和函数的情况
return typeof value;
}
}
function getType2(value){
let r = Object.prototype.toString.call(value)
return r.replice(/^\[object (\S+)\]$/,'$1')
}
7.手写 call 函数
/**
* 手写 call 函数
* 1.判断传入上下文对象是否存在,如果不存在,[则设置为 window] 。globalThis(因为环境不一样,可能是window,可能是node),所以不用window用globalThis
* 2.生成唯一key,防止添加对象时,存在相同名称导致覆盖
* 4.将this放入,这里的this指向调用者。例:obj.myCall,this指向的是obj
* 5.使用上下文对象来调用这个方法,并保存返回结果。
* 6.删除刚才新增的属性。
* 7.返回结果。
* @param {context} context -this指向
* @param {any} args -剩余参数
*/
Function.prototype.myCall = function(ctx,...args){
ctx = Object(ctx) || globalThis // context = Object(context) || window (这里不用window是因为可能环境不同)
const key = Symbol() // 生成唯一key
ctx[key] = this
const result = ctx[key](...args)
delete ctx[key]
return result
}
8.手写 apply 函数
/**
* 手写 apply 函数
*/
Function.prototype.myApply = function(ctx,args){
ctx = Object(ctx) || globalThis
const key = Symbol()
ctx[key] = this
const result = ctx[key](...args)
delete ctx[key]
return result
}
9.手写 bind 函数
/**
* 手写 bind 函数
*/
Function.prototype.bind = function(ctx,...args1) {
var fn = this;
return function(...args2) {
fn.apply(ctx, ...args1, ...args2);
};
};
10.函数柯里化的实现
11.手写实现深拷贝函数
/**
* 手写实现深拷贝函数
* @param {array | object} obj -针对数组或对象的深拷贝
* @author sunzhibin
*/
function deepClone(obj){
// null, Array,Object,Date,RegExp 都会返回小写的object
if(!obj || typeof obj !=='object') return
let newObj = Array.isArray(obj) ? [] : {} // 判断类型是数组还是对象
for(key in obj){
if(obj.hasOwnProperty(key)){ // 判断是否存在key值
// 这里的判断,如果说obj[key]里面还是对象或数组,继续调用自身,一层层展开
newObj[key] = typeof obj[key] === 'object' ? deepClone(obj[key]) : obj[key]
}
}
return newObj
}