Skip to content

JS手写实现

通过原生js代码手写实现一些特性

instanceof

js
function myInstanceof(target, contructor) {
  //!!!注意箭头函数不能作为contructor
  // 不断沿着原型链向上判断
  while (true) {
    if (target === null) {
      return false;
    }
    if (target.__proto__ === contructor.prototype) {
      return true;
    }
    target = target.__proto__;
  }
}

// 测试
myInstanceof('a' ,String)  //true

myInstanceof({a:1} ,Object)  //true

myInstanceof(1 ,Object)  //true

myInstanceof(true ,Number)  //false

new

js
function myNew(contructor, ...args) {
    //!!!注意箭头函数不能作为contructor
  //创建以contructor.prototype为原型的对象实例obj  
  const obj = Object.create(contructor.prototype);
  // 修改调用myNew时contructor中的this指向,并传递参数
  const res = contructor.call(obj, ...args);
  // 若contructor调用无返回值,则以obj为返回值
  if (res && (typeof res === "object" || typeof res === "function")) {
    return res;
  }
  return obj;
}

// 测试
function Person(name,age){
    this.name = name;
    this.age = age
}
const obj = myNew(Person,'scw',25)
console.log(obj.name) //'scw'

compose函数

Compose 函数允许你将多个函数组合成一个函数,这个新函数按照从右到左的顺序(即从最后一个函数到第一个函数)来执行这些函数。即compose(f, g, h) 等价于 (...args) => f(g(h(...args)))

js
function compose(...funcs) {  
  if (funcs.length === 0) {  
    return arg => arg;  
  }  
  
  if (funcs.length === 1) {  
    return funcs[0];  
  }  
  
  return funcs.reduce((pre, cur) => (...args) => pre(cur(...args)));  
}  
  
// 使用示例  
function multiply(x) {  
  return x * 2;  
}  
  
function add(x) {  
  return x + 1;  
}  
  
const composed = compose(multiply, add);  
console.log(composed(5)); // 12 (因为 5 先经过 add 变成 6,再经过 multiply 变成 12)

curry函数

Curry 函数是一种将接受多个参数的函数转换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数且返回结果的新函数的技术。这个过程可以递归进行,直到所有参数都被接收。

js
function curry(fn) {  
  return function curried(...args) {  
    if (args.length >= fn.length) {  
      // 如果收集到的参数数量足够,直接调用原函数  
      return fn.apply(this, args);  
    } else {  
      // 否则,返回一个新的函数,该函数将收集剩余的参数  
      return function(...args2) {  
        return curried.apply(this, args.concat(args2));  
      };  
    }  
  };  
}  
  
// 使用示例  
function add(a, b, c) {  
  return a + b + c;  
}  
  
const curriedAdd = curry(add);  
console.log(curriedAdd(1)(2)(3)); // 6  
console.log(curriedAdd(1, 2)(3)); // 6

防抖 & 节流

  • 防抖
js
const debounce = (fun , delay) => {
    let timer: number | null;

    return (...args) => {
        if (timer) {
            clearTimeout(timer);
        }

        timer = window.setTimeout(() => {
            fun.call(EMPTY, ...args);
            timer = null;
        }, delay);
    };
};

// 调用
let elem = document.getElementById('input');
elem.addEventListener('change', debounce(function () {
    console.log('change');
}, 1000))
  • 节流
js
const throttle = (fun, delay) => {
    let timer

    return (...args) => {
        if (timer) {
            return;
        }

        timer = window.setTimeout(() => {
            fun.call(EMPTY, ...args);
            timer = null;
        }, delay);
    };
};



// 调用
let dom = document.getElementById('div');
dom.addEventListener('drag', throttle(function () {
    console.log('drag');
}, 300));

bind & call & apply

js
Function.prototype.myApply = function (context, args) {
    //这里默认不传就是给window,也可以用es6给参数设置默认参数
    context = context || window
    args = args ? args : []
    //给context新增一个独一无二的属性以免覆盖原有属性
    const key = Symbol()
    //this就是调用apply的函数
    context[key] = this
    //通过隐式绑定的方式调用函数
    const result = context[key](...args)
    //删除添加的属性
    delete context[key]
    //返回函数调用的返回值
    return result
}
js
//传递参数从一个数组变成逐个传参了,不用...扩展运算符的也可以用arguments代替
Function.prototype.myCall = function (context, ...args) {
    //这里默认不传就是给window,也可以用es6给参数设置默认参数
    context = context || window
    args = args ? args : []
    //给context新增一个独一无二的属性以免覆盖原有属性
    const key = Symbol()
    context[key] = this
    //通过隐式绑定的方式调用函数
    const result = context[key](...args)
    //删除添加的属性
    delete context[key]
    //返回函数调用的返回值
    return result
}
js
Function.prototype.myBind = function(context) {
    //返回一个绑定this的函数,我们需要在此保存this
    let self = this
        // 可以支持柯里化传参,保存参数
    let arg = [...arguments].slice(1)
        // 返回一个函数
    return function() {
        //同样因为支持柯里化形式传参我们需要再次获取存储参数
        let newArg = [...arguments]
        console.log(newArg)
            // 返回函数绑定this,传入两次保存的参数
            //考虑返回函数有返回值做了return
        return self.apply(context, arg.concat(newArg))
    }
}

深拷贝

js
function deepCopy(obj) {
  if (!obj && typeof obj !== 'object') {
    throw new Error('error arguments');
  }
  // const targetObj = obj.constructor === Array ? [] : {};
  const targetObj = Array.isArray(obj) ? [] : {};
  for (let key in obj) {
    
    //只对对象自有属性进行拷贝
    if (obj.hasOwnProperty(key)) { 
      if (obj[key] && typeof obj[key] === 'object') {
        targetObj[key] = deepCopy(obj[key]);
      } else {
        targetObj[key] = obj[key];
      }
    }
  }
  return targetObj;
}

数组去重

js
function distinct(arr) {
    const finalArr=[]
    arr.forEach(item=>{
        if(finalArr.indexOf(item)===-1){
            finalArr.push(item)
        }
    })
    return finalArr
}
js
function distinct(arr) {
   return arr.filter((item,index)=>{
       return arr.indexOf(item)===index
   })
}
js
function distinct(arr) {
   return arr.reduce((pre,cur,index,array)=>{
       return pre.indexOf(cur)>-1?pre:[...pre,cur]
   },[])
}
console.log(distinct([2,2,3,5,7,9,7])) //[2, 3, 5, 7, 9]

数组扁平化

js
const arr = [1, [2, [3, [4, 5]]], 6];

// 注意:使用Infinity会影响性能,不推荐
console.log(arr.flat(Infinity)) // [1,2,3,4,5,6]
js
function flatReduce(arr) {
    return arr.reduce((pre, cur) => {
          return pre.concat(Array.isArray(cur) ? flatReduce(cur) : cur);
    }, []);
}
const arr = [1, [2, [3, [4, 5]]], 6];

console.log(flatReduce(arr)) // [1,2,3,4,5,6]
js
function flatByArray(arr, res) {
    res = res || [];
    for (let i = 0; i < arr.length; i++) {
        const a = arr[i];
        if (Array.isArray(a)) {
            flatByArray(a, res)
        } else {
            res.push(a)
        }
    }
    return res
}
const arr = [1, [2, [3, [4, 5]]], 6];

console.log(flatByArray(arr)) // [1,2,3,4,5,6]

Promise

js
class MyPrmoise{
    constructor(executor) {
        this.status = 'pending'
        this.value = undefined
        this.reason = undefined
        this.onResCallBackList = []
        this.onRejCallBackList = []

        const resolve=(value)=>{
            if(this.status!=='pending') return
            this.status = 'resolved'
            this.value = value
            this.onResCallBackList.forEach(func=>func())
        }

        const reject=(reason)=>{
            if(this.status!=='pending') return
            this.status = 'rejected'
            this.reason = reason
            this.onRejCallBackList.forEach(func=>func())
        }

        try {
            executor(resolve,reject)
        }catch (e) {
            reject(e)
        }
    }
    then(onRes,onRej){
        if(this.status ==='resolved'){
            return new PromiseA((resolve,reject)=>{
                try{
                    const res = onRes(this.value)
                    resolve(res)
                }catch (e) {
                    reject(e)
                }
                
            })
        }
        if(this.status ==='rejected'){
            return new PromiseA((resolve,reject)=>{
                try{
                    const res = onRej(this.reason)
                    reject(res)
                }catch (e) {
                    reject(e)
                }

            })
        }
        if(this.status ==='pending'){
            this.onResCallBackList.push(()=>{
                onRes(this.value)
            })
            this.onRejCallBackList.push(()=>{
                onRej(this.reason)
            })
        }
    }

}
  • Promise.all
js
Promise.prototype.myAll=function(promiseList) {
    const len = promiseList.length
    const result = [];
    let count = 0;
    return new Promise((resolve, reject) => {
        for (let i = O;i < len;i++){
            promiseList[i]().then((res)=>{
                result [i] = res;
                count++;
                if (count === len) {
                    resolve(result);
                }
                },(err) => {
                return reject(err);
            })
        }
    })
}

MIT Licensed