美文网首页
js中new,call,apply,bind实现

js中new,call,apply,bind实现

作者: 浅忆_0810 | 来源:发表于2021-09-09 14:09 被阅读0次

    1. new

    1.1 原理介绍

    new 的主要作用就是执行一个构造函数、返回一个实例对象,在 new 的过程中,根据构造函数的情况,来确定是否可以接受参数的传递

    function Person(){
       this.name = 'Jack';
    }
    
    var p = new Person(); 
    console.log(p.name)  // Jack
    

    从输出结果可以看出,p 是一个通过 person 这个构造函数生成的一个实例对象

    1. 创建一个新对象
    2. 将构造函数的作用域赋给新对象(this 指向新对象)
    3. 执行构造函数中的代码(为这个新对象添加属性)
    4. 返回新对象

    如果不用 new 这个关键词,结合上面的代码改造一下

    function Person() {
      this.name = 'Jack';
    }
    
    var p = Person();
    console.log(p) // undefined
    console.log(name) // Jack
    console.log(p.name) // 'name' of undefined
    

    没有使用 new 这个关键词,返回的结果就是 undefined。其中由于 JavaScript 代码在默认情况下 this 的指向是 window,那么 name 的输出结果就为 Jack

    当构造函数中有 return 一个对象的操作

    function Person(){
       this.name = 'Jack'; 
       return {age: 18}
    }
    
    var p = new Person(); 
    console.log(p)  // {age: 18}
    console.log(p.name) // undefined
    console.log(p.age) // 18
    

    当构造函数最后 return 出来的是一个和 this 无关的对象时,new 命令会直接返回这个新对象,而不是通过 new 执行步骤生成的 this 对象

    这里要求构造函数必须是返回一个对象,如果返回的不是对象,那么还是会按照 new 的实现步骤,返回新生成的对象

    function Person() {
       this.name = 'Jack'; 
       return 'tom';
    }
    
    var p = new Person(); 
    console.log(p)  // {name: 'Jack'}
    console.log(p.name) // Jack
    

    当构造函数中 return 的不是一个对象时,还是会根据 new 关键词的执行逻辑,生成一个新的对象(绑定了最新 this),最后返回出来

    总结:new 关键词执行之后总是会返回一个对象,要么是实例对象,要么是 return 语句指定的对象

    1.2 实现

    new 被调用后大致做了哪几件事情。

    1. 让实例可以访问到私有属性
    2. 让实例可以访问构造函数原型(constructor.prototype)所在原型链上的属性
    3. 构造函数返回的最后结果是引用数据类型
    function _new(ctor, ...args) {
        if(typeof ctor !== 'function') {
          throw 'ctor must be a function';
        }
    
        let obj = new Object();
        obj.__proto__ = Object.create(ctor.prototype);
      
        let res = ctor.apply(obj,  [...args]);
        let isObject = typeof res === 'object' && res !== null;
        let isFunction = typeof res === 'function';
    
        return isObject || isFunction 
          ? res 
            : obj;
    };
    
    

    2. apply & call & bind

    2.1 原理介绍

    call、apply 和 bind 是挂在 Function 对象上的三个方法,调用这三个方法的必须是一个函数

    func.call(thisArg, param1, param2, ...)
    func.apply(thisArg, [param1,param2,...])
    func.bind(thisArg, param1, param2, ...)
    

    其中 func 是要调用的函数,thisArg 一般为 this 所指向的对象,后面的 param1、2 为函数 func 的多个参数,如果 func 不需要参数,则后面的 param1、2 可以不写

    call 和 apply 的区别在于,传参的写法不同:apply 的第 2 个参数为数组; call 则是从第 2 个至第 N 个都是给 func 的传参;

    而 bind 和(call、apply)又不同,bind 虽然改变了 func 的 this 指向,但不是马上执行,而(call、apply)是在改变了函数的 this 指向之后立马执行

    2.2 apply 和 call 的实现

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

    实现 call 和 apply 的关键就在 eval 这行代码。其中显示了用 context 这个临时变量来指定上下文,然后还是通过执行 eval 来执行 context.fn 这个函数,最后返回 result

    这两个方法和 bind 的区别就在于,这两个方法是直接返回执行结果,而 bind 方法是返回一个函数

    2.3 bind的实现

    Function.prototype.bind = function (context, ...args) {
        if (typeof this !== "function") {
          throw new Error("this must be a function");
        }
    
        var self = this;
        var fbound = function () {
          self.apply(
            this instanceof self 
                ? this 
                : context, 
            args.concat(Array.prototype.slice.call(arguments))
          );
        }
    
        if (this.prototype) { // 在特殊情况下.prototype会缺失
          fbound.prototype = Object.create(this.prototype);
        }
    
        return fbound;
    }
    

    实现 bind 的核心在于返回的时候需要返回一个函数,故这里的 fbound 需要返回,但是在返回的过程中原型链对象上的属性不能丢失。因此这里需要用Object.create 方法,将 this.prototype 上面的属性挂到 fbound 的原型上面,最后再返回 fbound

    this instanceof self( => this instanceof fBound:判断是否使用new操作符来调用的_new):

    当这个绑定函数被当做普通函数调用的时候,可以直接用context; 而返回的这个之后当做构造函数使用的时候,却是指向这个实例,所以this instanceof self为true时,要用this


    3. 总结

    相关文章

      网友评论

          本文标题:js中new,call,apply,bind实现

          本文链接:https://www.haomeiwen.com/subject/gzuiwltx.html