this的本质是任何函数创建时都会附带的隐藏参数,但因为不清楚其指向的问题,初学者往往犯迷糊,那么this到底指向谁呢?我们先来看几种情况(不讨论严格模式):
var obj = {
"name": "obj1",
"foo": function(){
console.log(this);
}
}
var fn = obj.foo;
fn(); //window
obj.foo(); //obj
fn.call(obj) //obj
判断this的指向可以用call判断法:
- 有没有调用
call()
或apply()
,若有,则为里面的第一个参数;若无,下一步; - 将函数fn转换为fn.call()的形式,如:
fn() == window.fn() == fn.call(window)
obj.foo() == foo.call(obj);
call()和apply()均是用来指定this指向,传入参数的函数,常常用于高阶函数的计算之中,它们唯一的区别在于apply传入的是数组,而call传入的一个个参数。例:
function sum(x,y){
return x+y;
}
sum.call(null,1,2); //3
sum.apply(null,[1,2]); //3
来看几道面试题:
1.以下代码输出什么?
var john = {
firstName: "John"
}
function func() {
alert(this.firstName + ": hi!")
}
john.sayHi = func;
john.sayHi(); // John:hi!
// 因为john.sayHi() == john.sayHi.call(john),故答案如上;
2.下面代码输出什么,为什么?
func()
function func() {
alert(this)
} //window
//首先函数声明被提前,其次func() == func.call(window);
3.下面代码输出什么?
function fn0(){
function fn(){
console.log(this);
}
fn();
}
fn0(); //window,和上一题本质是一样的;
document.addEventListener('click', function(e){
console.log(this);
setTimeout(function(){
console.log(this);
}, 200);
}, false);
//点击后,先输出document,再输出window;(setTimeout/setInterval中的this均指向window)
4.下面代码输出什么,why?
var john = {
firstName: "John"
}
function func() {
alert( this.firstName )
}
func.call(john)
//John; 前面已解释;
5.下面代码输出什么,why?
var john = {
firstName: "John",
surname: "Smith"
}
function func(a, b) {
alert( this[a] + ' ' + this[b] )
}
func.call(john, 'firstName', 'surname')
// John Smith; 同上
6.以下代码有什么问题,如何修改
var module= {
bind: function(){
$btn.on('click', function(){
this.showMsg();
})
},
showMsg: function(){
console.log('饥人谷');
}
}
module.bind();
由于事件触发中,this代表了$btn这个DOM节点,而不是程序员所期望的module对象,所以输出无效;
正确的应提前绑定this,改正后:
var module= {
bind: function(){
var that = this;
$btn.on('click', function(){
that.showMsg();
})
},
showMsg: function(){
console.log('饥人谷');
}
}
module.bind();
7.下面代码输出什么? why?
obj = {
go: function() { alert(this) }
}
obj.go(); // obj 执行环境为obj;
(obj.go)(); //obj; 被立即执行函数包裹,等价于obj.go();,环境依然为obj;
(a = obj.go)(); // window; 等价于(a = function(){alert(this)})() 此时环境为window;
(0 || obj.go)(); //window;
着重分析第四题,若题目为0 || (obj.go)()
则输出obj
。
实际上,立即执行函数括号中已经生成了一个新的函数,而这个函数的执行环境是window,即已经变成了第三种情况。
网友评论