- 调用位置
-
每个函数的 this 是在被调用时绑定的,this的值完全取决于函数在代码中被调用的位置。
-
什么是调用栈和调用位置?
function baz(){ //当前调用栈是:baz //调用位置在全局作用域 console.log("baz") bar(); //<--bar的调用位置 }
function bar(){ //当前调用栈是:baz->bar //调用位置在baz中 console.log("bar") foo();//<--foo的调用位置 } function foo(){ //当前调用栈是:baz->bar-->foo //调用位置在bar中 console.log("foo") } baz(); //开发者工具中查找调用栈和调用位置的方法: 在工具中给 foo() 函数的 第一行代码设置一个断点,或者直接在第一行代码之前插入一条 debugger; 语句。 运行代码时,调试器会在那个位置暂停,同时会展示当前位置的函数 调用列表,这就是你的调用栈。 因此,如果你想要分析 this 的绑定,使用开发者工具得到调用栈,然后找到栈中第二个元素, 这就是真正的调用位置。
- 绑定规则
-
默认绑定
-
function foo(){ console.log(this.a); } var a = 2; foo(); //2 //this的指向取决于函数的调用位置,foo函数在全局中被调用,所以this的作用域指向全局 //在代码中,foo() 是直接使用不带任何修饰的函数引用进行调用的,因此只能使用 默认绑定,无法 //应用其他规则。 //ps:严格模式下与 foo() 的调用位置无关
-
隐式绑定
-
function foo(){ console.log(this.a); } var obj = { a:2, foo:foo }; obj.foo();// 2 //在一个对象内部包含一个指向函数的属性,并通过这个属性间接引用函数,从而把this间接(隐式)绑定到 //这个对象上面。
-
隐式丢失
-
function foo(){ console.log(this.a); } var obj = { a:2, foo:foo }; var bar = obj.foo;//函数别名! var a = 'global'; bar(); //blobal
//虽然 bar 是 obj.foo 的一个引用,但是实际上,它引用的是 foo 函数本身,因此此时的 bar() 其实 //是一个不带任何修饰的函数调用,因此应用了默认绑定. -
function foo(){ console.log(this.a); } function doFoo(fn){ fn(); //调用位置! } var obj = { a:2, foo:foo }; var a = "global"; doFoo(obj.foo); //global
//参数传递是一种隐式赋值, -
显式绑定
-
//在某个对象上强制调用函数 call(...)和apply(...)
function foo(){ console.log(this.a); } var obj = { a:2 }; foo.call(obj); //2 //通过foo.call(...),可以在调用foo时强制把this绑定到obj对象上。 //但是显示绑定依旧无法解决绑定丢失问题 -
硬绑定
-
function foo(){ console.log(this.a); } var obj = { a:2 }; var a = 3; var bar = function(){ foo.call(obj); }; bar(); //2 setTimeout(bar,1000) bar.call(window);//2 //硬绑定式创建一个函数如bar,在该函数内部调用foo.call(obj),无论后面怎么调用函数bar, //总会手动在obj上调用foo
-
New绑定
-
function foo(a){ this.a = a; } var bar = new foo(2); console.log(bar.a); //2
//使用 new 来调用 foo(..) 时,我们会构造一个新对象并把它绑定到 foo(..) 调用中的 this 上