准确把握 JavaScript 中的 this
this 是 JavaScript 中的一个关键字,当一个函数被挪用时,除了传入函数的显式参数之外,名为 this 的隐式参数也被传入了函数。this 参数指向了一个自动天生的内部对象,这个内部对象被称为函数上下文。与其他面向对象的言语差别的是, JavaScript 中的 this 依赖于函数的挪用体式格局。所以,想要邃晓 this 的指向题目,还必须先研讨函数在 JavaScript 中是怎样被挪用的。
挪用体式格局 1、作为函数举行挪用
这说法有点新鲜,函数当然是被作为函数举行挪用的,但我们说一个函数“作为函数”被挪用,只是为了区分于其他的挪用体式格局。先看一个简朴的例子:
function func1() {
console.log(this === window); // true
}
func1();
var func2 = function () {
console.log(this === window); // true
}
func2();
function func3() {
"use strict";
console.log(this); // undefined
}
func3();
上面例子中的第一第二个 this 在非严厉形式下指向全局上下文,即 window 对象,第三个在严厉形式下则为 undefined。
所以,当函数被作为函数举行挪用时,在非严厉形式下,函数的上下文是 window 对象,而在严厉形式下,函数上下文为 undefined。
挪用体式格局 2、作为要领举行挪用
当一个函数被赋值给一个对象的一个属性,并运用援用该函数的这个属性举行挪用函数时,那末函数就是作为该对象的一个要领举行挪用的。看下面的例子:
var obj = {
func: function() {
console.log(this === obj); // true
}
};
obj.func();
上面的例子中,函数 func 的挪用对象为 obj,所以函数上下文就是 obj。因而可知,将函数作为对象的一个要领举行挪用时,该对象就是函数上下文,而且在函数内部能够用 this 来访问这个对象。
此时,我们再看一下第一种挪用体式格局,即“作为函数”挪用。“作为函数”举行挪用的函数是定义在 window 上的,挪用时也不需要 window 的援用,实在体式格局 1 中例子中的 func1()
就是 window.func1()
,所以例子中的函数上下文就是 this。
挪用体式格局 3、作为组织器举行挪用
将函数作为组织器时,函数的声明与其他挪用体式格局的函数声明一致。将函数作为组织器挪用的例子以下:
function Student() {
this.getContext = function() {
return this;
}
}
var stu = new Student();
console.log(stu.getContext() === stu); // true
将函数作为组织器挪用时,便会经由过程这个函数天生一个新对象,这时候,this 指向这个新建立的对象。
从上面几种挪用体式格局来看,函数挪用体式格局之间的重要差别是:作为 this 参数传递给实行函数的上下文对象之间的区分。作为要领举行挪用,该上下文是要领的具有者;作为全局函数挪用,其上下文永远是 window (也就是说,该函数是 window 的一个要领);作为组织器举行挪用时,其上下文对象则是新建立的对象实例。
下面的一种挪用体式格局能够显式地指定上下文。
挪用体式格局 4、运用 apply() 和 call() 要领举行挪用
JavaScript 的每一个函数都有 apply() 和 call() 函数,能够应用任何一个函数都能够显式指定任何一个对象作为其函数上下文。
经由过程 apply() 要领来挪用函数,我们要给 apply() 传入两个参数:一个作为函数上下文对象,另一个作为函数参数所构成的数组。call() 要领的运用体式格局相似,唯一差别的是给函数传入的参数是一个参数列表,而不是单个数组。
function func() {
var result = 0;
for(var i = 0; i < arguments.length; i++) {
result += arguments[i];
}
this.result = result;
}
var obj1 = {};
var obj2 = {};
func.apply(obj1, [1, 2, 3]);
func.call(obj2, 4, 5, 6);
console.log(obj1.result === 6); // true
console.log(obj2.result === 15); // true
在上面的代码中,func.apply(obj1, [1, 2, 3]);
将函数的上下文定义为 obj1,而且传入 1、2、3 三个参数,func.call(obj2, 4, 5, 6);
将函数的上下文定义为 obj2,而且传入 4、5、6 三个参数。
那 apply 和 call 基础雷同,那末我们该用哪个呢?实在 apply 和 call 的区分仅仅在于挪用时传入的参数差别,其他完整一样。所以,在挑选时,重要看传入的参数。假如已知参数已在数组里了则用 apply 即可,或许参数是动态天生的,能够把参数 push 进一个数组,然后再用 apply 挪用。当参数数目已知,或许在参数里有许多无关的值则用 call 要领挪用。
其他补充
应用 bind() 转变函数上下文
先看下面例子:
var obj1 = {
a: 1
};
var obj2 = {
a: 2,
func: function() {
console.log(this.a);
}.bind(obj1)
};
obj2.func(); // 1
ECMAScript 5 引入了 Function.prototype.bind
,其会建立一个绑定函数,当挪用这个绑定函数时,函数上下文将会是 bind() 要领的第一个参数。上面的例子中,将 obj1 设置为函数上下文,所以应用 func 来挪用函数时,函数的上下文为 obj1,而不是它的挪用者 obj2。
应用 Array 的 5 个要领转变函数上下文
5 个要领分别是:
Array.prototype.every(callbackfn [, thisArg ])
Array.prototype.some(callbackfn [, thisArg ])
Array.prototype.forEach(callbackfn [, thisArg ])
Array.prototype.map(callbackfn [, thisArg ])
Array.prototype.filter(callbackfn [, thisArg ])
当挪用以上 5 个要领时,传入的参数除了回调函数之外,还能够传入别的一个可选地参数,即函数上下文,代表回调函数中的函数上下文。假如省略该参数,则 callback 被挪用时的 this 值,在非严厉形式下为全局对象,在严厉形式下传入 undefined。看下面的例子:
var arr = ["segmentfault"];
var obj = {};
arr.forEach(function(ele, ind) {
console.log(this === window); // true
});
arr.forEach(function(ele, ind) {
console.log(this === obj); // true
}, obj);
测试
在网上找了几个代码小例子,来测试对上面内容的明白,答案在最下面。
1、
if (true) {
// this
}
2、
var obj = {
someData: "a string"
};
function myFun() {
// this
}
obj.staticFunction = myFun;
obj.staticFunction();
3、
var obj = {
myMethod : function () {
// this
}
};
var myFun = obj.myMethod;
myFun();
4、
function myFun() {
// this
}
var obj = {
someData: "a string"
};
myFun.call(obj);
答案
1、window
2、obj
3、window
4、obj
注:以上代码均在浏览器环境中测试。