一些方法的模拟实现——Polyfill

2,169 阅读3分钟

人,总会有智力、运气的差别

Object.create()

定义:
创建一个新对象,使用现有的对象来提供新创建的对象的__proto__

思路:
利用new操作符可以将实例对象的__proto__指向构造函数prototype的原理去实现

实现:
暂不考虑第二参数,也不使用ES6Object.getPrototypeOf()方法
Object.myCreate = function(proto){
    if (typeof proto !== 'object' && typeof proto !== 'function') {
        throw new TypeError('Object prototype may only be an Object or null');
    }
    function F() {}
    F.prototype = proto;
    return new F();
}

instanceof

定义:
检测构造函数的 prototype 属性是否出现在某个实例对象的原型链

思路:
遍历实例对象的原型链,判断是否出现过构造函数的 prototype属性

实现:
var myInstanceof = function(left, right){
    if(left ===null || right === null)    return false;
    left = Object.getPrototypeOf(left);
    right = right.prototype;
    while(true){
        if(left === right)   return true;
        left = Object.getPrototypeOf(left);
    }
}

call()

定义:
使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数

思路:
利用this的隐式绑定,即谁调用该函数this指向谁
  1. 将函数设为对象的属性
  2. 接收参数,执行该函数
  3. 删除该函数
注意:
  1. 指定的this值为 null时,视为指向 window
  2. 指定的this值为原始值(数字,字符串,布尔值)时,this会指向该原始值的自动包装对象
  3. 函数有返回值时,返回函数的返回值
实现:
Function.prototype.myCall = function(_this, ...args){
    _this = Object(_this) || window;
    const fn = Symbol("fn");    // 避免属性覆盖
    _this[fn] = this;	// 用 this 拿到要执行的函数并将函数设为属性
    let result = _this[fn](...args);
    delete _this[fn];
    return result;
}

apply()

定义:
调用一个具有给定this值的函数,以及作为一个数组(或类似数组对象)提供的参数

思路:
传递给函数的参数处理不同,其他部分跟call()相同

实现:
Function.prototype.myApply = function(_this){
    let arrs = arguments[1];    // 获取参数数组
    try{
        arrs = arrs ? Array.from(arrs) : [];
    }catch(e){
    	throw new TypeError('第二个参数既不为数组,也不为类数组对象');
    }
    _this = Object(_this) || window;
    const fn = Symbol("fn");    // 避免属性覆盖
    _this[fn] = this;	// 用 this 拿到要执行的函数并将函数设为属性
    let result = _this[fn](...arrs);
    delete _this[fn];
    return result;
}

bind()

定义:
创建一个新的函数,在 bind() 被调用时,这个新函数的 this 被指定为 bind() 的第一个参数,而其余参数将作为新函数的参数,供调用时使用

思路:
  1. 需返回一个函数
  2. 因为一个绑定函数也能使用new操作符创建对象,此时提供的 this 值将被忽略,所以需要判定后重绑this,然后修改返回的函数的原型
  3. 返回的函数内部再借用call()来改变this指向和处理参数
实现:
Function.prototype.myBind = function(objThis, ...params) {
  const thisFn = this;  //存储调用函数
  let funcForBind = function(...secondParams) {
    const isNew = this instanceof funcForBind;  // 检查是否通过new调用
    const thisArg = isNew ? this : Object(objThis);
    return thisFn.call(thisArg, ...params, ...secondParams);
  };
  funcForBind.prototype = Object.create(thisFn.prototype);
  return funcForBind;
}

new

定义:
创建一个用户定义的对象类型的实例或具有构造函数的内置对象的实例

思路:
  1. 创建一个空的简单JavaScript对象(即{});
  2. 链接该对象(即设置该对象的构造函数)到另一个对象 ;
  3. 将步骤1新创建的对象作为this的上下文 ;
  4. 如果该函数没有返回对象,则返回新建的对象。
实现:
function myNew() {  // 接收参数:第一个为构造函数,后面为需要传入构造函数的参数
    let obj = {},
        construct = [].shift.call(arguments);
    Object.getPrototypeOf(obj) = construct.prototype;
    let ret = construct.apply(obj, arguments);  // 注意:函数返回值为null,null被忽略
    return (ret && typeof ret === 'object') ? ret : obj;
};

参考:
《深入理解 call、apply、bind》
《JavaScript深入之bind的模拟实现》