this
is a binding made for each function invocation, based entirely on its call-site (how the function is called).
this是为函数被引用而创建的绑定!完全地基于函数如何被调用/函数的call-site!
Call-site
:the location in code where a function is called
call-stack: 调用函数的堆栈。(函数的调用链条,函数的call-site的移动形成了stack。)
The call-site is in the invocation before the currently executing function.
function baz() { ???// call-stack is: `baz` ???// so, our call-site is in the global scope ???console.log( "baz" ); ???bar(); // <-- 下一个调用位置是‘bar‘}function bar() { ???// call-stack is: `baz` -> `bar` ???// so, 我们的call-site是在‘baz‘内。 ???console.log( "bar" ); ???foo(); // <-- call-site for `foo`}function foo() { ???// call-stack is: `baz` -> `bar` -> `foo` ???// so, our call-site is in `bar` ???console.log( "foo" );}baz(); // <-- call-site for `baz`
在浏览器inspector中,可以使用debugger工具观察call-site。
function baz() { ???console.log(this); ???console.log( "baz" ); ???bar(); // <-- call-site for `bar`}function bar() { ???console.log(this) ???console.log( "bar" ); ???foo(); // <-- call-site for `foo`}function foo() { ???console.log(this); ???console.log( "foo" );}baz();
最后的结果是3个this都指向window对象。
Nothing But Rules
在函数执行期间,call-site如何决定this指向哪里?
有:4条法则!及这4条法则的优先级。
default Binding
第一条法则来自最常用的函数调用: 独立的函数引用。(没有其他法则影响的函数)
function foo() { ???console.log( this.a );}var a = 2;foo(); // 2
this指向全局对象,变量a是全局对象的属性。
如果是严格模式,this指向undefined!
第一条:this绑定完全基于call-site,非严格模式下,全局对象是默认的绑定对象。
注意:不要混用严格模式和非严格模式!
Implicit Binding
第2条:如果call-site有一个context对象,也涉及一个拥有或包含的对象。
function foo() { ???console.log( this.a );}var obj = { ???a: 2, ???foo: foo};obj.foo(); // 2 ??因为obj就foo()调用的this, 所以this.a等同于obj.a
当有一个content object 拥有一个函数引用时,
Implicit Binding Rule 就是那个对象应当拥有这个函数调用的this绑定
??:Only the top/last level of an object property reference chain matters to the call-site.
也就是说假如有: obj1.obj2.obj3.foo(), 调用foo()函数的this绑定在obj3上。
Implicitly Lost
当一个Implicit bound 函数丢弃了context对象的binding, 就会使用默认binding。或用全局对象,或undefined(use strict)。
function foo() { ???console.log( this.a );}var obj = { ???a: 2, ???foo: foo};var bar = obj.foo; // function reference/alias!var a = "oops, global"; // `a` also property on global objectbar(); // "oops, global" obj.foo
结果是:? foo() { ???console.log( this.a );}
bar //变量bar的值就是函数foo, 因为bar被分配了obj.foo
结果是? foo() { ???console.log( this.a );}
所以: this指向全局对象window, this.a就是 window.a
另一个例子:参数传递
其实就是把obj.foo的值,分配给fn参数。等同于在doFoo函数内,声明了变量:var fn = obj.foo;
function foo() { ???console.log( this.a );}function doFoo(fn) { ???// `fn` is just another reference to `foo` ???fn(); // <-- call-site!}var obj = { ???a: 2, ???foo: foo};var a = "oops, global"; // `a` also property on global objectdoFoo( obj.foo ); // "oops, global"
看到了吧: 结果this绑定的是全局对象。this.a就是window.a
JavaScript内建函数也同样:
function foo() { ???console.log( this.a );}var obj = { ???a: 2, ???foo: foo};var a = "oops, global"; // `a` also property on global objectsetTimeout( obj.foo, 100 ); // "oops, global"setTimeout()函数相当于:function MySetTimeout(fn) { ???//0.1秒后; ???fn();}MySetTimeout( obj.foo )
函数回调丢失它们的this绑定是很常见的。
还有一类方式,是主动改变this:
Event handlers就会强制你的回调有一个this指向DOM中被激活的元素。
如果有一种方法可以固定住this,就好了!当然有了,见??,明确的绑定!
Explicit Binding
call()方法,apply()方法。第一个参数是一个对象的话,这个对象就绑定了this。
function foo() { ???console.log( this.a );}var obj = { ???a: 2};foo.call( obj ); // 2
??,call(..),apply(..)可接受多个参数,它们的用途这里不谈!
不幸的是,明确绑定,不能解决一个函数丢失它想要的this绑定的问题。
Hard Binding(explict and strong)
var bar = function() {foo.call( obj );};
API Call "Contexts"
new
Binding
Everything In Order
Determining this
Binding Exceptions
Ignored this
Safer this
Indirection
Softening Binding
Lexical this
Review (TL;DR)
4条rule
小心:
箭头函数
You Don't Know JS: this & Object Prototypes( 第2章 this)
原文地址:https://www.cnblogs.com/chentianwei/p/9744504.html