原生js 之 new apply bind call 实现

125 阅读3分钟

原生js 之 new apply bind call 实现

三上

  • 缘起:

因为自己报了一个金三银四冲刺班, 2.21 的题目是手动实现一个bind 函数,自己不会实现,而且连基本的用法都不清楚,另外这样一道题是大厂的高频面试题,需要熟练手写实现,所以在这里记录一下自己的实现过程;

  • new 函数的实现:

使用工厂函数实现new 新建对象的时候,会返回一个含有this 指向的对象或者是return 出来的对象,总之new 是返回一个对象;

那么在实现new 函数的时候需要return obj;

那么 new 的实现代码是:

function _new (ctor , ...args) {
    if(typeof ctor !== 'function') {
      throw new TypeError('ctor must be a function');
    }
    let obj = new Object();
    obj.__proto__ = Object.create(ctor.prototype);
    
    let res = ctor.apply(obj , [...args]);
    let isObj = typeof res === 'object' && typeof res !== null;
    let isFunction =  typeof res === 'function';
    return isFunction || isObj ? res : obj;
}

看一下 _new 函数的用法:

function person (name) {
   this.name = name;
}
let myperson = _new(person , 'lili');
console.log(myperson);

上面输出的是:

person {name : 'lili'}

看上面的例子可以看出,_new 函数的参数就好理解了; ctor 接收的是对象参数,...args是 函数接收的参数对象; 那么 _new 函数关键的逻辑是如何实现生成一个对象并把接受的...args 添加到新建的这个对象上了;

我们继续: 首先需要边界值判断,ctor 不是函数的情况需要throw 一个TypeError; 之后new 一个obj 作为对象副本(深拷贝), __proto__ 需要继承ctor 的prototype , 之后新建一个res 来apply 获取args 数据,看写法:

let res = ctor.apply(obj , [...args]);
这里借用的是ctor 对象,...args 参数传递给obj;

最后的情况,添加一个判断,判断是对象类型还是函数类型,按照判断的结果进行输出;

前面说到new 可以返回一个对象,那么测试的用例可以这样写:

function anotherPerson (name) {
    var obj = {};
    obj[name] = name;
    return obj;
}

let myanotherperson = _new(anotherPerson , 'huanghuang');

console.log(myanotherperson);

输出的结果{name:'huanghuang'};

上面说完了new 函数的实现,下面分一个组直接说call 和 apply 的实现;

注意上面两个函数的使用是直接用在Function 上面的,那么写法应该是:

Function.prototype.myapply = function (context , ..args){
      let context = context || window;
      context.fn = this;
      let result = eval('context.fn(...args)');
      delete context.fn;
      return result;
}

eval 函数是计算一个字符串的值;

call 函数的实现方式和bind函数的实现方式一致;

是需要立即执行的,bind 函数和他们不一致的地方在于bind 函数不需要立即执行,所以实现的时候是不一样的;

实现的代码如下:

Function.prototype.myBind = function(context , ...args) {
    if(typeof this !== 'function'){
       throw new TypeError('is not a function');
    }
    // 注意 bind 不是立即执行,所以需要返回一个函数;
    let self = this;
    let fbound = function() {
        self.apply(this instanceof self ? this : context , context.concat(Array.prototype.slice.call(arguments)));
    }
    // 需要判断fbound 的prototype 是否继承函数原型链的prototype;
    if(this.prototype) {
        fbound.prototype = Object.create(this.prototype);
    }
    return fbound;
}

上述就是apply , call, bind new 函数的手动实现,很神奇,大概是自己并不了解这四个函数的原理吧; 不熟悉的原因还在于对js 的原型链自己并不熟悉, 是自己的知识盲区,通过实现者四个函数自己对于原型链的把握会好的多;

about me : 毕业一年半,目前正在搭建完整的web 体系 ,金三银四也在看机会,欢迎沟通; wechat:Yingbin192 , 微信公众号:

qrcode_for_gh_7c727e9e9e7c_258 1.jpg

, 欢迎关注;