this的指向 & call apply bind的内部实现剖析

437 阅读2分钟

this的指向基本在每天的工作开发中经常遇到,本文总结了this几种主要场景的指向,并对call apply bind这三种改变this指向的底层方法进行了实现。

this的几种常见场景及其指向

1.看方法前面是否有点,如果没有严格模式指向undefined 非严格模式指向window; 如果有点,点前面是谁就指向谁。
在立即执行函数中的this也是这种场景的典型代表,如:

//非严格模式下
var num = 1
var obj = {
    num: 2,
    getNum: function() {
	return (function() {
	    return this.num;
	})();
    }
}

console.log(obj.getNum()); // 1

//严格模式下
'use strict'
var num = 1
var obj = {
    num: 2,
    getNum: function() {
	return (function() {
	    return this.num;
	})();
    }
}

console.log(obj.getNum()); //Cannot read property 'num' of undefined

image.png

还有下面的案例:

let o = {
  f1: function () {
    console.log(this);
    var f2 = function () {
      console.log(this);
    }();
  }
} 

当执行o.f1()输出:

image.png

2.元素点击事件等的绑定回调,this指向这个元素;
3.箭头函数: this 和执行无关,由箭头函数的定义上下文决定,可以认为是指向箭头函数外层的this;
4.构造函数内的this,创建新的实例时,this指向新的实例;
5.call apply bind可以修改this的指向。

call的实现

Function.prototype.mycall = function(context, ...params){
 if (typeof this !== 'function') {
   throw new TypeError('this is not a function')
 }

 context == null ? context = window : context;

 const self = this;
 const key = Symbol('key');

 context[key] = self;

 const result = context[key](...params)

 delete context[key];

 return result;

}

// 测试
function aa (a,b){
 console.log('this,', this);
 console.log(a+b);
}


aa.mycall({wwww:11},1,2)

image.png

apply的实现

Function.prototype.myApply = function(context, paramsArr){
  if (typeof this !== 'function') {
    throw new TypeError('this is not a function')
  }

  context == null ? context = window : context;

  const self = this;
  const key = Symbol('key');

  context[key] = self;

  const result = context[key](...paramsArr)

  delete context[key];

  return result;

}

// 测试
function aa (a,b){
  console.log('this,', this);
  console.log(a+b);
}


aa.myApply({wwww:11},[1,2])

image.png

bind的实现

Function.prototype.mybind= function(context, ...params){
  let _this= this;
  return function proxy(args){
    _this.apply(context, params.concat(args))
  }
}

let aa =  function(a,b,e){
  console.log('e',e);
  console.log("this", this);
  console.log(a+b);
  
}

document.addEventListener('click', aa.mybind({qq:11}, 1,2))

image.png

应用call apply

call apply 可以用在长得像鸭子 而自己不是鸭子时,使用鸭子的能力,通过改变this的指向复用一些基础能力,不用自己再去实现一次,如:

function dd (){
  console.log(arguments);

  // 将arguments转化为一个数组有很多方式  Array.from(arguments)  [...arguments]
  // const result = [].slice.call(arguments)


  const result =[];
  [].forEach.call(arguments, (item)=>{
    result.push(item)
  })

  console.log(result);
  
}

image.png