this/call/apply/bind浅析

120 阅读3分钟

什么是this?

在JavaScript中,this的指向是调用时决定的,而不是创建时决定的。

全局上下文

在全局执行上下文中this都指全局对象。

  • this等价于window对象

  • var === this. === winodw.

    console.log(window === this); // true var a = 1; this.b = 2; window.c = 3; console.log(a + b + c); // 6

在浏览器里面this等价于window对象,如果你声明一些全局变量,这些变量都会作为this的属性。

函数上下文

在函数内部,this的值取决于函数被调用的方式。

直接调用

this指向全局变量。

function foo(){
  return this;
}
console.log(foo() === window); // true

call()、apply()

this指向绑定的对象上。

var person = {
  name: "axuebin",
  age: 25
};
function say(job){
  console.log(this.name+":"+this.age+" "+job);
}
say.call(person,"FE"); // axuebin:25
say.apply(person,["FE"]); // axuebin:25

可以看到,定义了一个say函数是用来输出nameagejob,其中本身没有nameage属性,我们将这个函数绑定到person这个对象上,输出了本属于person的属性,说明此时this是指向对象person的。

如果传入一个原始值(字符串、布尔或数字类型)来当做this的绑定对象, 这个原始值会被转换成它的对象形式(new String()),这通常被称为“装箱”。

callapplythis的绑定角度上来说是一样的,唯一不同的是它们的第二个参数。

bind()

this将永久地被绑定到了bind的第一个参数。

bindcallapply有些相似。

var person = {
  name: "axuebin",
  age: 25
};
function say(){
  console.log(this.name+":"+this.age);
}
var f = say.bind(person);
console.log(f());

模拟实现apply方法

// 简例
Function.prototype.applyOne = function(context) {
    // 首先要获取调用call的函数,用this可以获取
    context.fn = this;
    context.fn();
    delete context.fn;
}

//简单写一个不带参数的demo
var aaa = {
    name: "aaa",
    sayHello: function (age) {
         console.log(this.name);
     }
};

var  bbb = {
    name: "bbb",
};

//看看结果:
jawil.sayHello.applyOne(bbb)//bbb

箭头函数

所有的箭头函数都没有自己的this,都指向外层。

function Person(name){
  this.name = name;
  this.say = () => {
    var name = "xb";
    return this.name;
  }
}
var person = new Person("axuebin");
console.log(person.say()); // axuebin

箭头函数常用语回调函数中,例如定时器中:

function foo() {  
  setTimeout(()=>{
    console.log(this.a);
  },100)
}
var obj = {
  a: 2
}
foo.call(obj);

作为对象的一个方法

this指向调用函数的对象。

var person = {
  name: "axuebin",
  getName: function(){
    return this.name;
  }
}
console.log(person.getName()); // axuebin

注意:

var name = "xb";
var person = {
  name: "axuebin",
  getName: function(){
    return this.name;
  }
}
var getName = person.getName;
console.log(getName()); // xb

发现this又指向全局变量了,这是为什么呢?

还是那句话,this的指向得看函数调用时。

作为一个构造函数

this被绑定到正在构造的新对象。

通过构造函数创建一个对象其实执行这样几个步骤:

  1. 创建新对象
  2. 将this指向这个对象
  3. 给对象赋值(属性、方法)
  4. 返回this

所以this就是指向创建的这个对象上。

function Person(name){
  this.name = name;
  this.age = 25;
  this.say = function(){
    console.log(this.name + ":" + this.age);
  }
}
var person = new Person("axuebin");
console.log(person.name); // axuebin
person.say(); // axuebin:25

作为一个DOM事件处理函数

this指向触发事件的元素,也就是始事件处理程序所绑定到的DOM节点。

var ele = document.getElementById("id");
ele.addEventListener("click",function(e){
  console.log(this);
  console.log(this === e.target); // true
})

HTML标签内联事件处理函数

this指向所在的DOM元素

<button onclick="console.log(this);">Click Me</button>

jQuery的this

在许多情况下JQuery的this都指向DOM元素节点。

$(".btn").on("click",function(){
  console.log(this); 
});

总结

如果要判断一个函数的this绑定,就需要找到这个函数的直接调用位置。然后可以顺序按照下面四条规则来判断this的绑定对象:

  1. new调用:绑定到新创建的对象
  2. callapplybind调用:绑定到指定的对象
  3. 由上下文对象调用:绑定到上下文对象
  4. 默认:全局对象

注意:箭头函数不使用上面的绑定规则,根据外层作用域来决定this,继承外层函数调用的this绑定。