this&手写call/apply/bind&new原理

72 阅读3分钟

作用域

函数提升:函数的调用可以在函数定义之前,因为存在函数提升,函数整体定义提升

变量提升:变量提升的只是定义,赋值还在原地

  console.log(a);
  var a = 11;
  等同于
  var a;
  console.log(a);
  a = 11;
  

this

this是在函数执行时,动态获取上下文决定的,不是创建时决定的。 this代表谁调用这个函数

1.普通函数执行,this指向全局,window

2.隐式绑定,作为对象的方法调用,指向这个对象

   const o1 = {
      text: 'o1',
      fn: function() {
          return this.text
      }
   }
   o1.fn(); // this指向o1
   
   const o2 = {
      text: 'o2',
      fn: function() {
          return o1.fn()
       }
    }
    o2.fn(); //函数中的this依然指向o1,因为函数的最终执行是o1.fn()
    要想让this指向o2, 可以用bind, call, this
    o2.fn.call(o2)

   const o3 = {
      text: 'o3',
      fn: function() {
          let fn = o1.fn //只赋值了函数的引用,没有调用函数
          return fn()
      }
   }
   o3.fn(); //this指向o3
        

3.显示绑定 call, apply, bind

函数想要改变this的指向,可以函数调用call,apply,bind方法 call, apply是直接执行函数,返回结果; bind返回的是新函数,需要再手动执行

call:fn.call(context, arg1, arg2,...)

context为想要指向的this值,arg1,arg2...为函数本身需要的参数

   const fun = function(a, b){
     ...
   };
   fun.call(o1, a, b);
apply:fn.apply(context, [arg1, arg2]); apply和call类似,只是参数传递格式不同
使用

1.获取数组最值

    const num = [1,2,3,4,5];
    Math.max.call(Math, ...num);
    //num没有max方法,借用Math.max
   

2.验证是否是数组

   Object.prototype.toString.call(num) // '[object Array]'
   //toString是object类方法
   

bind, fn.bind(context, arg1, arg2, ...)

手写bind
  Function.protoType.myBind = function(){
     const _this = this; //指向当前调用bind的函数;
     if(typeof _this !== 'function'){
        提示“应该是函数调用bind方法”
     }
     
     const args = Array.prototype.slice.call(arguments);
     //arguments是传入该函数的参数,类数组对象,函数内部语法可以自动获取
     //这里转成数组,第一项是要指向的this,其他数组元素为函数的其他参数
     
     const newThis = args.shift();
     return function(){
        return _this.call(newThis,...args)
     }
  }
  
手写call
  Function.prototype.myCall = function(){
     const _this = this; //同上
        if(typeof _this !== 'function'){
        提示“应该是函数调用bind方法”
     }
     const args = Array.prototype.slice.call(arguments);
     const newThis = args.shift();
     
     //将这个函数,作为属性给要指向的this对象,
     //作为属性方法调用,再删除属性
     const fn = Symbol('call'); //确保属性不重名
     newThis[fn] = _this;
     const result = newThis[fn](...args);
     delete newThis[fn];
     return result;
  }

4.作为构造函数使用

this指向新创建的对象

    function Name(name){
        this.name = name;
    }
    const a = new Name('aaa');

new原理

1.new的作用

执行构造函数,返回一个实例对象;实例对象可以访问构造函数原型链上的属性

new执行总是返回一个对象,要么是实例对象,要么是return语句返回的对象

  function Person(){
     this.name='aaa';
     return '111';
  }
  new Person(); // { name: 'aaa'}
  
  function Per(){
     this.name = 'aaa';
     return {age: 11;}
  }
  new Per(); // {age: 11}

2.new的原理

创建一个新对象; 将构造函数的作用域赋给新对象(this指向新对象); 执行构造函数中的代码(给这个对象添加属性/方法); 返回这个新对象

  function create(fn, ...args){
     let obj = {};
     Object.setPrototypeOf(obj, fn.prototype);
     let result = fn.call(obj,...args);
     return result instanceOf Object ? result : obj;
  }