JavaScript中的this
this是非常的重要的,而理解他又是一件很困惑的事。现在来了解下this的真实面目。
一、this机制
this是在运行中绑定的,并不是在编写时绑定的,他的上下文取决于函数调用时的各种条件。而且,this的绑定和函数声明的位置没有任何关系。
当一个函数被调用时,会创建一个执行上下文。这个执行上下文会记录函数在哪被调用、传入的参数等信息。this就是这个上下文中的一个属性,会在函数执行的过程中用到。
二、调用位置
调用位置是指函数在代码中被调用的位置,而不是声明的位置。通过分析调用栈可以查找出函数真正的调用位置。
三、绑定规则
函数在执行时的调用位置决定了this的绑定对象。
1、默认绑定
常用于独立函数的调用。
var name = 'abc';
var showName = function() {
console.log(name);
console.log(this); // window
};
showName();
首先定义变量name和函数showName。然后当调用showName时,他是在全局作用域下调用的,且没有任何的引用调用,此时使用了this的默认绑定,所以this是指向全局对象的。
当然,当时用严格模式时,this是不会绑定到全局对象上去的。
var name = 'abc';
var showName = function() {
'use strict';
console.log(name); // 'abc'
console.log(this); // undefined
};
showName();
2、隐式绑定
当调用的位置存在上下文时或者被某个对象调用时,我们需要考虑到隐式绑定。
var name = 'abc';
var showName = function() {
console.log(this.name);
};
var obj = {
name: '123',
showName: showName
}
obj.showName(); // '123'
在调用位置会使用obj的上下文来引用函数,隐式绑定规则会把函数嗲用中的this绑定到这个上下文对象。所以this.name和obj.name的效果是一样的。 对象属性引用链中只有最顶层和最后一层会影响调用位置,比如:
var name = 'abc';
var showName = function() {
console.log(this.name);
};
var obj1= {
name: '456',
showName: showName
};
var obj = {
name: '123',
obj1: obj1
};
obj.obj1.showName(); // '456'
影响最后结果的那步只是最接近调用方法的那个对象。
绑定丢失
this隐式绑定时会丢失,从而把this绑定到对象或者undefined(严格模式)上。
var name = 'abc';
var showName = function() {
console.log(this.name);
};
var obj = {
name: '123',
showName: showName
};
var binding = obj.showName;
binding(); // 'abc'
尽管binding是obj.showName的一个引用,但是调用binding函数时用了默认绑定。
同样是一个函数参数传递时会出现的问题,
var name = 'abc';
var showName = function() {
console.log(this.name);
};
var obj = {
name: '123',
showName: showName
};
function fun(fn) {
fn();
};
fun(obj.showName); // 'abc'
因为函数参数传递是隐式赋值,所以这儿的结果和前面是一致的。 在实际编程的过程中,this丢失是很常见的一个现象。
3、显式绑定
对应call和apply方法,ECMAScript中增加了bind方法。 因为此时调用这些方法时,我们可以指定this的绑定对象。
var name = 'abc';
var showName = function() {
console.log(this.name);
};
var obj = {
name: '123',
showName: showName
}
obj.showName(); // '123'
obj.showName.apply(window); // 'abc'
使用apply强制绑定this到window上,并且输出了,最外层window对象上的name属性。
4、new绑定
当我们使用new来调用函数时,会自动执行以下几个操作:
- 创建(构造)一个新的空对象。
- 设置这个对象的原型。一个对象的原型就是他的构造函数的 prototype 属性的值。
- 将这个空对象作为 this 的值传递给构造函数来执行这个构造函数。
- 如果函数没有返回其他对象,就返回new表达式中的这个对象。
new是最后一种可以影响this绑定行为的方法。
注:所有的函数都有一个 prototype 属性,当这个函数被定义的时候,prototype 属性自动的创建和初始化。
prototype 属性的初始化值是一个对象,这个对象只有一个属性,就是 constructor,他指回到和原型相关联的那个构造函数 。
三、优先级
既然有四种影响this的方式,那么就有对应的优先级。
new绑定 > 显式绑定 > 隐式绑定 > 默认绑定。
使用注意:函数内部为了保护this,可以使用其他的一个变量来保存this变量。