手写JS原生方法

530 阅读1分钟

浅拷贝

let obj = { a: 1, b: 2 }

Object.assign({},obj) // 它包含了原始对象种Symbol属性的处理,for in就没有

Object.keys(obj) 拿不到symbol

如果想要完整的 需要搭配 Object.getOwnPropertySymbols(obj) // 专门拿Symbols属性

Object.getOwnpropertySymbols(obj) 专门拿symbol

function shallowClone(obj){
    let type = Object.prototype.toString.call(obj)
    let contor = obj.constructor;
    
    if(type === '[object Symbol]' || type === '[object Bigint]') return Object(obj);
    
    if(type === '[object RegExp]' || type === '[object Date]') return new contor(obj)
    
    if(type === '[object Error]') return new contor(obj.message)
    
    if(type === '[object Function]') {
        return function(){
          return obj.call(this,...arguments)
        }
    }
    
    if(type === '[object Array]' || type === '[object Object]'){
       // 这样的话包含Symbol属性,
      return type === '[object Array]' ? [...obj] : {...obj}
    }
    
    return obj
}

深拷贝

function deepClone(obj,cache = new Set()){
  let type = Object.prototype.toString.call(obj);
  let contor = obj.constructor;
   // 如果不是数组和对象的话直接copy过去
  if(type !== '[object Array]' || type !== '[object Object]') return shallowClone(obj);
  
  // 为了防止无限套娃
  if(cache.has(obj)) return obj;
  cache.add(obj)
  
  let keys = [
      ...Object.keys(obj),
      ...Object.getOwnPropertySymbols(obj)
  ]
  
  let result = new contor();
  
  keys.forEach(itm => {
      result[key] = deepClone(obj[key], cache)
  })
  
  return result
}
  

类数组转化为数组

  • Array.prototype.slice.call(arguments)
  • Array.from(document.querySelectorAll('div'))
  • [...document.querySelectorAll('div')]
  • Array.prototype.concat.apply([],arguments)
  • [].forEach.call(arguments,(itm) => { console.log(itm) })

数组方法

forEach

MDN: developer.mozilla.org/zh-CN/docs/…

Array.prototype.forEach = function(callback,context){
    let self = this;
    i = 0;
    len = self.length;
    context = context === null ? window : context;
    for(;i<len;i++){
     typeof callback === 'function' ? callback.call(context,self[i]) : null
    }
  }
    

for in

用for in的时候,最好配合 obj.hasOwnProper辨别是否是obj的私有属性

性能比较差,因为他会把所有的可枚举属性全部迭代一遍(包括原型链)

for of

for of原理是按照迭代器规范遍历Symbol.iterator来遍历的,迭代器就是返回一个对象,这个对象包含next方法,而next方法也返回一个对象,有两个属性,一个是 value:值,一个是 done:是否遍历完成

例如

let arr = [1,2,3,4,5]
for(let key of arr){

}

等价于

obj[Symbol.iterator] = function(){
    let self = this;
    idx = 0;
    return {
        // 包含next方法,执行它
        // done:false or true 完成 or 未完成
        // value: 每次值
           
        next(){
             // 最终大于最大索引的话
            if(idx > self.length - 1){
                return  {
                   done: true,
                   value: undefined
                }
            }
            return {
                   done: false,
                   value: self[idx++]
            }    
        }
    }
}
// let itor = arr[Symbol.iterator]()
// itor.next()


如何将对象可以通过for of遍历呢

    let ab = {
        name: '刷',
        age: '18'
    } 
    
    function createSymbolIterator(obj){
       let arr = Object.entries(obj);
       let idx = 0;
       return {
           next(){
             if(idx > self.length - 1){
                return  {
                   done: true,
                   value: undefined
                }
            }
            return {
                   done: false,
                   value: arr[idx++]
            }
           }
       }
    }
    
    ab[Symbol.iterator] = function(){
        return createSymbolIterator(ab)
    }
    for(let key of ab){
        console.log(key)
    }

实现call

eval方法:developer.mozilla.org/zh-CN/docs/…

Function.prototype.call_ = function (obj) {
    //判断是否为null或者undefined,同时考虑传递参数不是对象情况
    // 目的是为了防止报错
    obj = obj ? Object(obj) : window;
    let args = [];
    // 注意i从1开始
    let len = arguments.length;
    for (var i = 1, i < len; i++) {
        args.push("arguments[" + i + "]");
    };
    obj.fn = this; // 此时this就是函数fn
    eval("obj.fn(" + args + ")"); // 执行fn
    delete obj.fn; //删除fn
};

实现bind

Function.prototype.bind=function (context,...params){
    let self = this;
    
    return function proxy(args){
        self.apply(context,params.concat(args))
    }
}

用setTimeout实现setInterval

难点:涉及到setTimeout多次更新后的值怎么保存

const utils = (
    function () {
        let interVal = 0;
        let interObj = {};

        var _setInterval = function (fn,t){
            var newInterVal = ++interVal;
            function next(){
                interObj[newInterVal] = setTimeout(() => {
                    fn();
                    next();
                },t)
            }
            next();
            return newInterVal;
        }

        var _clearInterval = function (id){
            clearTimeout(interObj[id])
        }

        return {setInterval: _setInterval,clearInterval: _clearInterval}
    }
)()