this
this
关键字,虽然它们之间关联不大,但是它们一起使用却容易让人产生疑惑。下面列出了使用 this
this
是 JavaScript 的关键字,指函数执行时的上下文,跟函数定义时的上下文无关。随着函数使用场合的不同,this
的值会发生变化。但是有一个总的原则,那就是 this
全局上下文
this
// 在浏览器中,this 指代全局对象 window
console.log(this === window); // true
函数上下文
this
this
function f1(){
return this;
}
console.log(f1() === window); // true
f1()
,相当于为 window
对象定义了一个属性。直接执行函数 f1()
,相当于执行 window.f1()
。所以函数 f1()
中的 this
指代调用函数的那个对象,也就是 window
function f2(){
"use strict"; // 这里是严格模式
return this;
}
console.log(f2() === undefined); // true
this
关键字指向全局对象(在浏览器环境中也就是 window
对象),this
的值将维持 undefined
this
var o = {
name: "stone",
f: function() {
return this.name;
}
};
console.log(o.f()); // "stone"
o
中包含一个属性 name
和一个方法 f()
。当我们执行 o.f()
时,方法 f()
中的 this
指代调用函数的那个对象,也就是对象 o
,所以 this.name
也就是 o.name
。this
的行为,我们也可以首先定义函数,然后再将其附属到 o.f
。这样做 this
var fun = function() {
return this.name;
};
var o = { name: "stone" };
o.f = fun;
console.log(o.f()); // "stone"
this
的绑定只受最靠近的成员引用的影响。在下面的这个例子中,我们把一个方法 g()
当作对象o.b
的函数调用。在这次执行期间,函数中的 this
将指向 o.b
。事实上,这与对象本身的成员没有多大关系,最靠近的引用才是最重要的。
o.b = {
name: "sophie"
g: fun,
};
console.log(o.b.g()); // "sophie"
eval() 方法中的 this
eval()
方法可以将字符串转换为 JavaScript 代码,使用 eval()
方法时,this
指向哪里呢?答案很简单,看谁在调用 eval()
方法,调用者的执行环境中的 this
就被 eval()
// 全局上下文
function f1(){
return eval("this");
}
console.log(f1() === window); // true
// 函数上下文
var o = {
name: "stone",
f: function() {
return eval("this.name");
}
};
console.log(o.f()); // "stone"
call() 和 apply() 方法中的 this
call()
和 apply()
是函数对象的方法,它的作用是改变函数的调用对象,它的第一个参数就表示改变后的调用这个函数的对象。因此,this
var x = 0;
function f() {
console.log(this.x);
}
var o = {};
o.x = 1;
o.m = f;
o.m.apply(); // 0
call()
和 apply()
的参数为空时,默认调用全局对象。因此,这时的运行结果为 0
,证明 this
o.m.apply(o); // 1
1
,证明了这时 this
指代的是对象 o
。
bind() 方法中的 this
Function.prototype.bind
。调用 f.bind(someObject)
会创建一个与 f
具有相同函数体和作用域的函数,但是在这个新函数中,this
将永久地被绑定到了 bind
function f() {
return this.a;
}
var g = f.bind({
a: "stone"
});
console.log(g()); // stone
var o = {
a: 28,
f: f,
g: g
};
console.log(o.f(), o.g()); // 28, stone
this
addEventListener
,被用作事件处理函数时,它的 this
<!DOCTYPE HTML>
<html>
<head>
<meta charset="UTF-8">
<title>test</title>
</head>
<body>
<button id="btn" type="button">click</button>
<script>
var btn = document.getElementById("btn");
btn.addEventListener("click", function(){
this.style.backgroundColor = "#A5D9F3";
}, false);
</script>
</body>
</html>
attachEvent
,被用作事件处理函数时,它的 this
却指向 window
。如下代码所示:
<!DOCTYPE HTML>
<html>
<head>
<meta charset="UTF-8">
<title>test</title>
</head>
<body>
<button id="btn" type="button">click</button>
<script>
var btn = document.getElementById("btn");
btn.attachEvent("onclick", function(){
console.log(this === window); // true
});
</script>
</body>
</html>
this
this
<button onclick="alert(this.tagName.toLowerCase());">
Show this
</button>
alert
会显示 button
,注意只有外层代码中的 this
是这样设置的。如果 this
<button onclick="alert((function(){return this})());">
Show inner this
</button>
this
被包含在匿名函数中,相当于处于全局上下文中,所以它指向 window
关卡
仔细想想,下面代码块会输出什么结果呢?
// 挑战一
function func1() {
function func2() {
console.log(this)
}
return func2;
}
func1()(); // ???
// 挑战二
scope = "stone";
function Func() {
var scope = "sophie";
function inner() {
console.log(scope);
}
return inner;
}
var ret = Func();
ret(); // ???
// 挑战三
scope = "stone";
function Func() {
var scope = "sophie";
function inner() {
console.log(scope);
}
scope = "tommy";
return inner;
}
var ret = Func();
ret(); // ???
// 挑战四
scope = "stone";
function Bar() {
console.log(scope);
}
function Func() {
var scope = "sophie";
return Bar;
}
var ret = Func();
ret(); // ???
// 挑战五
var name = "The Window";
var object = {
name: "My Object",
getNameFunc: function() {
return function() {
return this.name;
};
}
};
console.log(object.getNameFunc()()); // ???
// 挑战六
var name = "The Window";
var object = {
name: "My Object",
getNameFunc: function() {
var that = this;
return function() {
return that.name;
};
}
};
console.log(object.getNameFunc()());