new call apply bind内部原理之我的理解

126 阅读3分钟

new

new被调用后做了三件事情:

  1. 让实例可以访问到私有属性
  2. 让实例可以访问构造函数原型(constructor.prototype)所在原型链上的属性
  3. 如果构造函数返回的结果不是引用数据类型

实现:

      function New(ctor, ...args) {
        //如果构造函数不是函数类型 那么抛出错误
        if (typeof ctor !== "function") {
          throw "New function the first param must be a function";
        }
        let obj = Object.create(ctor.prototype); //1.创建一个新对象,并且将它的proto指向构造函数的prototype  person {name: "zs", age: 12}  {}
        let res = ctor.apply(obj, args); //2.将构造函数的this指向这个对象并执行,拿到返回结果  undefined  {}
        let isObject = typeof res === "object" && res !== null; //  false true
        let isFunction = typeof res === "function"; //  false false
        return isObject || isFunction ? res : obj;//3.如果这个结果是对象或函数就返回这个结果  否则返回创建的对象
      }

测试:

      function person(name, age) {
        this.name = name;
        this.age = age;
      }
      var c = New(person, "zs", 12);
      var d = New(Object);
      console.log(c); //person {name: "zs", age: 12}
      console.log(d); //{}

image.png

注意的点:执行var d = New(Object);的时候 Object.create(Object.prototype)会返回一个空对象,再把空对象传给Object的构造函数

image.png

call

实现:

        // 实现call 传参是一个一个参数
        // 1.保留this,2.执行函数,3.删除函数
        Function.prototype.newCall=function(context,...args){
          var context=context||window;
          let fn = Symbol("fn");
          context.fn=this;//1.保留this 这个this是指person 这里把person隐式绑定到context上  
          
          let result=eval('context.fn(...args)');//2.执行函数 把元素一个个传进去(ES6 的...args解构) 
          delete context.fn;//3.删除函数
          return result;
        }

测试:

        function person(a,b,c,d){
          return{
            name:this.name,
            a:a,b:b,c:c,d:d
          }
        }
        var egg={name:'老师'};
        
        var bibi=person.newCall(egg,'点赞','收藏','转发','充电')//是person去执行
        console.log(bibi);

image.png

apply

其实和call差不多

实现:

        //实现apply  传入的是数组
        Function.prototype.newApply=function(context,args){
          var context=context||window;
          let fn = Symbol("fn");
          context.fn=this;//1.保留this 这个this是指person 这里把person隐式绑定到context上  
          let result=eval('context.fn(...args)');//2.执行函数 把元素一个个传进去  
         
          delete context.fn;//3.删除函数
          return result;
        }

测试:

        function person(a,b,c,d){
          return{
            name:this.name,
            a:a,b:b,c:c,d:d
          }
        }
        var egg={name:'老师'};
        
        var bibi=person.newApply(egg,['点赞','收藏','转发','充电'])//是person去执行
        console.log(bibi);

image.png

bind

实现:

        //实现bind  难点  bind配合new使用时 this丢失
        Function.prototype.newBind=function(context,...args){
          if(typeof this!=='function'){
            throw new TypeError('错误')
          }
            var self=this//1.保存this
            o=function(){}//通过一个空函数间接
           
            var newf=function(){ // 2.声明函数
                self.apply(this instanceof self ? this:context,//3.处理this丢失 发生了new构造函数就用实例的this 没有就还是用原来的方法执行即可
                args.concat(Array.prototype.slice.call(arguments))//把arguments变成类数组形式
              )
            }
            o.prototype=this.prototype //为了不直接修改person2的原型对象
            newf.prototype=new o()// 4.处理继承,使newf(bibi2)的实例 b 可以访问到self(person2)上的属性
            return newf //5.返回函数
        };

测试:

        function person2(a,b,c){
            console.log(this.name);//undefined  this丢失,访问不到egg2的name
            console.log(a,b,c);//点赞 投币 充电
        }
        person2.prototype.collection='收藏'
        var egg2={name:'老师'};
        
        var bibi2=person2.newBind(egg2,'点赞','投币')
        var b=new bibi2('充电')
        console.log(b.collection);//收藏  因为通过原型链访问到person2上的属性

image.png

注意的点:new调用中的this绑定优先级的最高的,比bind高