手写instanceof、深拷贝、new、apply、bind、softBind、JSON.Stringify

·  阅读 928

实现instanceof

检测数据类型
如果变量是给定引用类型的实例,那么instanceof就返回true。如果检测的是基础类型的值,那么返回false

function myInstanceof(left,right){ 
  // 如果是基础类型直接返回false 
  if(typeof left !== 'object' || left == null) return false;
  // Object.getPrototypeOf() 方法返回指定对象的原型(内部[[Prototype]]属性的值)
  let proto = Object.getPrototypeOf(left);
  while(true){
    if(!proto) return false;
    if(proto == right.prototype) return true;
      proto =  Object.getPrototypeOf(proto);
    }
}
复制代码

实现深拷贝

function deepClone(obj,map = new Map()){
  if(obj instanceof Object){
    if(obj instanceof Function) return obj;
    if(obj instanceof Date) return new Date(obj);
    if(obj instanceof RegExp) return new RegExp(obj);
    // 解决循环引用
    if(map.has(obj)) return map.get(obj);
    // 拷贝原型链
    let allDesc = Object.getOwnPropertyDescriptors(target);
    let cloneObj = Object.create(Object.getPrototypeOf(target), allDesc);
    map.set(obj,cloneObj);
    // Reflect.ownKeys可以拿到不可枚举属性和symbol类型的键名
    for(let key of Reflect.ownKeys(obj)){
      cloneObj[key] = deepClone(obj[key],map);
    }
    return cloneObj
  }else{
    return obj;
  }
}
复制代码

实现new

  1. 创建一个新对象
  2. 将构造函数的作用域赋给新对象(this指向新对象)
  3. 执行构造函数中的代码,为这个新对象添加属性
  4. 返回新对象
function myNew(ctor,...args){
  if(typeof ctor !=='function'){
    throw 'ctor must be function';
  } 
  // 创建一个新对象
  let obj = Object.create(ctor.prototype);
  // 改变this指向,执行构造函数中的代码,
  let res = ctor.apply(obj,[...args]);
  let isObject = typeof res === 'object'&& res !== null;
  let isFunction = typeof res == 'function';
  // 返回新对象
  return isObject || isFunction? res : obj;
}
复制代码

实现apply

改变this的指向
改变了函数的 this 指向之后立马执行
call和apply的区别只是接收参数的不同,apply接收数组,call则是多个参数

Function.prototype.myApply =function(context = window,args){
  // 利用symbol声明唯一变量
  let fn = Symbol('fn')
  // 为context绑定当前的方法,改变this指向
  context[fn] = this;
  // 执行当前的方法
  var result = context[fn](args);
  delete context[fn];
  return result;
}
复制代码

实现bind

与aplly、call的区别在于bind并不会立即执行而是返回一个函数

Function.prototype.myBind =function(context = window,...args){
  var self = this;
  var fbound = function(...args1){
    return self.apply(context,[...args,...args1]);
  }
  fbound.prototype = Object.create(this.prototype);
  // 返回一个函数
  return fbound;
}
复制代码

实现softBind

为什么需要softBind,让我们一同来看一个例子。 我们可以看到bind之后,无法通过call来再次改变this的指向。我们想要输出c的value,但是却依旧输出的是b的value。为了解决以上问题我们要实现一种软绑定。
那如何更改this呢?如何区分直接调用还是通过call这两种情况呢?我们分析可以得到两种情况:

  1. fn直接执行的时候,this指向的window
  2. 调用fn.call的时候,this指向call的第一个参数,
Function.prototype.softBind = function(context = window,...args){
  var self = this;
  var fbound = function(...args1){ 
    //此行逻辑是重点!结合上述分析可分析出原因
    var _context = (!this||this==(window || global))?context:this;
    return self.apply(_context,[...args,...args1]);
  }
  fbound.prototype = Object.create(this.prototype);
  return fbound;
}
复制代码

实现JSON.Stringify

看一下MDN上对规则的描述:

jsonStringify.png

function jsonStringify(data){
  let result = '';
  var type = typeof data;
  if(type !== 'object' || data === null ){
    // 基础类型在此处理
    result = data;
    if(type == 'number' &&(Number.isNaN(data) || !Number.isFinite(data))){
      // 规则8:NaN 和 Infinity格式的数值会被当做 null。
      result = "null";
    }else if(type == 'function' || type == 'undefined' || type == 'symbol'){
      // 规则4:函数、undefined 被单独转换时,会返回 undefined,
      result = "undefined";
    }else if(type == 'string'){
      result = `"${data}"`
    }
    result = String(result);
  }else{
    if(data.toJSON && typeof data.toJSON =='function'){
      //规则1:转换值如果有 toJSON() 方法,该方法定义什么值将被序列化。
      result+=jsonStringify(data.toJSON())
    }else if(data instanceof Array){
      result = [];
      data.forEach((item,index)=>{
        let itemType = typeof item;
        // 规则4:undefined、任意的函数以及 symbol 值,出现在数组中时,被转换成 null
        if(itemType == 'undefined' || itemType =='function' || itemType =='symbol'){
          result[index]="null";
        }else{
          result[index]=jsonStringify(item);
        }
      })
      result = `[${result}]`
    }else{
      result = [];
      Object.keys(data).forEach((item,index)=>{
        // 规则6:所有以 symbol 为属性键的属性都会被完全忽略掉,Object.keys返回包括对象自身的(不含继承的)所有可枚举属性(不含 Symbol 属性)的键名。
        let valueType = typeof data[item];
        if(valueType == 'undefined' || valueType =='function' || valueType =='symbol'){
          // 规则4:undefined、任意的函数以及 symbol 值,在序列化过程中会被忽略(出现在非数组对象的属性值中时)
        }else if(data[item] == data){
          // 规则5:对包含循环引用的对象(对象之间相互引用,形成无限循环)执行此方法,会抛出错误。
          throw "cycling";
        }else{
          result.push(`"${item}":${jsonStringify(data[item])}`);
        }
      })
      result = `{${result}}`
    }
  }
  return result;
}
复制代码
分类:
前端
标签:
分类:
前端
标签:
收藏成功!
已添加到「」, 点击更改