当 foo() 被调用时,函数确实指向 obj 对象。当函数引用有上下文对象时,隐式绑定规则会把函数调用中的 this 绑定到这个上下文对象。因为调用 foo() 时 this 被绑定到 obj ,因此 this.name 和 obj.name 是一样的。
隐式丢失
一个最常见的 this 绑定问题就是被隐式绑定的函数会丢失绑定对象,也就是说它会应用默认绑定,从而把 this 绑定到全局对象或者 undefined 上,取决于是否是严格模式。
思考下面一段代码:
function foo() {
console.log(this.name);
}
var obj = {
name: 'tom',
foo: foo
};
var bar = obj.foo;
var name = 'cat';
bar(); // cat
虽然 bar 是 obj.foo 的一个引用,但是实际上,它引用的是 foo 函数本身,因此此时的 bar() 其实是一个不带任何修饰的函数调用,因此应用了默认绑定。
在看另一个微妙的例子:
function foo() {
console.log(this.name);
}
function handleFoo(fn){
fn();
}
var obj = {
name: 'tom',
foo: foo
};
var name = 'cat';
handleFoo(obj.foo); // cat
参数传递其实就是一种隐式赋值,因此我们传入函数时也会被隐式赋值,所以结果和上一个例子一样。
还有值得注意的是,系统内置的函数也会丢失 this 绑定,例如:
function foo() {
console.log(this.name);
}
var obj = {
name: 'tom',
foo: foo
};
var name = 'cat';
setTimeout(obj.foo, 100); //cat
回调函数丢失 this 绑定是非常常见的。除了系统内置的函数外, jquery 中的回调就是其中之一,等等其他第三方库也都有此现象。
function foo(){
console.log(this.name);
};
var obj = {
name:'cat'
};
foo.call(obj); //cat
通过 foo.call() ,我们可以在调用 foo 函数时强制把它的 this 绑定到 obj 上。
硬绑定
思考下面一段代码:
function foo(){
console.log(this.name);
}
var obj = {
name:'cat'
}
var bar = function(){
foo.call(obj);
}
bar(); //cat
setTimeout(bar,100); //cat
bar.call(window); //cat
我们创建了一个中间函数 bar ,并在它的内部手动调用了 foo.call(obj) ,因此强制把 foo 的 this 绑定到了 obj 。无论之后如何调用函数 bar ,它总会手动在 obj 上调用 foo 。这种绑定是一种显式的强制绑定,因此我们称之为硬绑定。
硬绑定的一个经典应用:
function foo(){
console.log(this.name);
}
var obj = {
name:'cat'
}
function bind(fn,obj){
return function (){
return fn.apply(obj,arguments);
}
}
var bar = bind(foo,obj);
bar(); //cat
Javascript 也有一个 new 操作符,使用方法看起来也和那些面向类的语言一样,然而, Javascript 中 new 的机制实际上和面向类的语言完全不同。在 Javascript 中,构造函数只是一些使用 new 操作符时被调用的函数。
使用 new 来调用函数,或者说发生构造函数调用时,会自动执行下面的操作。(这里简单说明一下,以后会有专门的章节介绍)
this
new
示例代码:
function foo(name){
this.name = name;
}
var bar = new foo('cat');
console.log(bar.name); //cat
使用 new 来调用 foo(..) 时,我们会构造一个新对象并把它绑定到 foo(..) 调用中的 this 上。 new 是最后一种可以影响函数调用时 this 绑定行为的方法,我们称之为 new 绑定。
this优先级
现在我们已经了解了函数调用中 this 绑定的四条规则,我们需要做的就是找到函数的调用位置并判断应当应用哪条规则。
默认绑定的优先级是四条规则中最低的。
隐式绑定和显式绑定哪个优先级更高?我们来测试一下:
function foo(){
console.log(this.name);
};
var obj1 = {
a: 'tom',
foo: foo
};
var obj2 = {
a: 'cat',
foo: foo
};
obj1.foo(); // tom
obj2.foo(); // cat
obj1.foo().call(obj2); //cat
obj2.foo().call(obj1); //tom
可以看到,显式绑定优先级更高。
隐式绑定和 new 绑定哪个优先级更高?我们来测试一下:
function foo(name){
this.name = name;
};
var obj = {
foo:foo
};
obj.foo('tom');
console.log(obj.name); //tom
var bar = new obj.foo('cat');
console.log(bar.name); //cat
可以看到, new 绑定优先级高。
new 绑定和显式绑定哪个优先级更高?我们来测试一下:
new 和 call/apply 无法一起使用因此无法通过 new foo.call(obj1) 来直接进行测试,但是我们可以间接的测试。
function foo(name){
this.name = name;
};
var obj = {};
var bar = foo.bind(obj);
bar('tom');
console.log(obj.name); //tom
var baz = new bar('cat');
console.log(obj.name); //tom
console.log(baz.name); //cat
bar 函数被硬绑定到 obj 对象上,但 new bar('cat') 并没有像我们预计的那样把 obj.name 修改为 cat 。相反 new 修改了硬绑定 bar 函数调用中的 this 。 new 绑定生成了一个新对象 baz , bar.name 的值为 cat 。
判断this
函数是否在 new 中调用( new 绑定)?如果是的话 this 绑定的是新创建的对象。
var bar = new foo()
函数是否通过 call、apply (显式绑定)或者硬绑定调用?如果是的话, this 绑定的是指定的对象。
Google Play推出全新的应用内评价API,帮助开发者获取更多优质用户反馈。用户每天在Google Play上发表数百万条评论,这有助于开发者了解用户喜好和改进需求。开发者可以选择在适当的时间请求用户撰写评论,以获得全面而有用的反馈。全新应用内评价功能让用户无需返回应用详情页面即可发表评论,提升用户体验。 ...
[详细]