this相关

JavaScript基础知识系列

作者 Trevor Cui 日期 2018-02-24
this相关

参考自:深入理解javascript原型和闭包(10)——this(王福朋 - 博客园)。如有侵权,请联系删除。

在具体了解 this 之前,先强调一个非常重要的知识点:在函数中 this 到底取何值,是在函数真正被调用执行的时候确定的,函数定义的时候确定不了。因为 this 的取值是执行上下文环境的一部分,每次调用函数,都会产生一个新的执行上下文环境。

this的取值,分四种情况。我们来挨个看一下。

情况1:构造函数

所谓构造函数就是用来 new 对象的函数。其实严格来说,所有的函数都可以 new 一个对象,但是有些函数的定义是为了 new 一个对象,而有些函数则不是。另外注意,构造函数的函数名第一个字母大写(规则约定)。例如:Object、Array、Function 等。

function Foo() {
this.name = "aa";
this.year = 2018;
console.log(this); // Foo {name: "aa", yaer: 2018}
}
var f1 = new Foo();
console.log(f1.name); // aa
console.log(f1.year); // 2018

以上代码中,如果函数作为构造函数用,那么其中的 this 就代表它即将 new 出来的对象。

以下有两个需要注意的地方

1、情况1 仅限 Foo 函数作为构造函数的情况。如果直接调用 Foo 函数,而不是 new Foo(),情况就大不一样了(this 指向 Window)。

function Foo() {
this.name = "aa";
this.year = 2018;
console.log(this); // Window {postMessage: ƒ, blur: ƒ, ...
}
Foo();

2、在构造函数的 prototype 中,this 指向的是构造的对象:

function Foo() {
this.name = "aa";
this.year = 2018;
}
Foo.prototype.getName = function() {
console.log(this.name);
}
var f1 = new Foo();
f1.getName(); // aa

其实,不仅仅是构造函数的 prototype,即便是在整个原型链中,this 代表的也都是当前对象的值

情况2:函数作为对象的一个属性

如果函数作为对象的一个属性时,并且作为对象的一个属性被调用时,函数中的 this 指向该对象。

var obj = {
x: 10,
fn: function() {
console.log(this); // {x: 10, fn: f}
console.log(this.x); // 10
}
};

obj.fn();

以上代码中,fn 不仅作为一个对象的一个属性,而且的确是作为对象的一个属性被调用。结果 this 就是 obj 对象。

注意,如果fn函数不作为obj的一个属性被调用,会是什么结果呢?

var obj = {
x: 10,
fn: function() {
console.log(this); // Window {postMessage: ƒ, blur: ƒ, ...
console.log(this.x); // undefined
}
};

var fn1 = obj.fn;
fn1();

如上代码,如果 fn 函数被赋值到了另一个变量中,并没有作为 obj 的一个属性被调用,那么 this 的值就是 Window,this.x 为 undefined。

情况3:函数用 call 或者 apply 调用

当一个函数被 call 和 apply 调用时,this 的值就取传入的对象的值。

var obj = {
x: 10
};

var fn = function() {
console.log(this); // {x: 10}
console.log(this.x); // 10
}
fn.call(obj);

情况4:全局 & 调用普通函数

这一部分代码都很好理解:

console.log(this === window);  // true

var x = 10;
var fn = function() {
console.log(this); // Window {postMessage: ƒ, blur: ƒ, ...
console.log(this.x); // 10
}
fn();

需要注意的是,如果在对象内的函数属性中调用一个函数,这个被调用的函数属于调用普通函数,而不属于情况2 :

var obj = {
x: 10,
fn: function() {
function f() {
console.log(this); // Window {postMessage: ƒ, blur: ƒ, ...
console.log(this.x); // undefined
}
f();
}
};
obj.fn();

一个经典的 jQuery 源码案例

jQuery.extend = jQuery.fn.extend = function() {
// …… 此处省略若干行 ……

// extend jQuery itself if only one argument is passed
if (i === length) {
target = this;
i--;
}

// …… 此处省略若干行 ……
};

以上代码是从 jQuery 中摘除来的部分代码。jQuery.extend 和 jQuery.fn.extend 都指向了同一个函数,但是当执行时,函数中的 this 是不一样的。

执行 jQuery.extend(…) 时,this 指向 jQuery;执行 jQuery.fn.extend(…) 时,this 指向 jQuery.fn。

这样就巧妙的将一段代码同时共享给两个功能使用,更加符合设计原则。