来自《你不知道的JS》的学习
《=========================================================================》
1.在理解this的绑定过程之前,首先要理解调用位置:调用位置就是函数在代码中被调用的位置,而不是声明的位置。
寻找调用位置就是寻找“函数被调用的位置”。
function baz(){
//当前调用栈是:baz
//因此,当前调用位置是全局作用域
console.log("baz");
bar(); <==bar的调用位置
}
function bar(){
//当前调用栈是baz->bar
//因此,当前调用位置在baz中
foo(); //<--foo的调用位置
}
function foo(){
//当前调用栈是baz->bar->foo
//所以,调用位置在bar中
console.log("foo");
}
baz(); //<---baz的调用位置
2.this的绑定对象规则
- 最常用的函数调用类型:独立函数调用
function foo(){
console.log(this.a);
}
var a=2;
foo();//2
- 隐式绑定:
function foo(){
console.log(this.a);
}
var obj2 = {
a:42,
foo:foo
};
var obj1 = {
a=2,
obj2:obj1
};
//对象属性引用链上只有上一层或者说最后一层在调用位置起作用。
obj1.obj2.foo();//42
(隐式丢失) function foo(){
console.log(this.a);
}
var obj = {
a:2,
foo:foo
};
var bar = obj.foo;//函数别名;
var a = "oops,global";//a是全局变量的属性
bar(); //"oops,global"
虽然bar为obj.food的1个引用,但实际上它引用的依旧是foo函数本身,所以这时的bar()其实是一个不带任何修饰符的函数引用,所以默认绑定。别外,参数传递其实也是一种隐藏赋值,所以传入参数是也会被隐式赋值。
function foo(){
console.log(this.a);
}
function doFoo(fn){
fn();
}
var obj = {
a:2,
foo:foo <------调用位置
};
var bar = obj.foo;//函数别名;
doFoo(obj.foo);//"oops,global"
- 显示绑定(bind、apply,他们的第一个参数是一个对象,给this准备的)
- new绑定
3.优先级
- 函数是否在new中调用(new 绑定)?如果是的话this绑定的是新创建的对象。var bar = new foo();
- 函数是否通过call、apply(显示绑定)或者硬绑定调用?如果是的话,this绑定的是指定对象。var bar = foo.call(obj2);
- 函数是否在某个上下文对象中调用(隐式绑定)?如果是的话,this绑定的那个上下文对象。var bar =obj1.foo();
- 如果都不是的话,使用默认绑定。如果在严格模式下,就绑定到undefined,否则绑定到全局对象。var bar = foo();
4.间接引用:这种情况下,调用这个函数会应用默认绑定规则。
function foo(){
console.log(this.a);
var a=2;
var o = {a:3,foo:foo};
var p = {a:4};
o.foo();//3
(p.foo=o.foo())();//3
}
赋值表达式p.foo = o.foo的返回值是目标函数的引用,因此调用位置是foo()而不是p.foo()或者o.foo()。根据之前我们说过,这里会应用默认绑定。