解释清楚本节讨论这个。在javascript中,这也是神了。相对于java等在编译阶段就确定的语言,在javascript中,这是动态绑定的,即在运行时绑定。这就导致了javascript中this的灵活性,给this指向在不同的调用场景下识别对象带来了一定的麻烦。在全局环境下,this指向window,即this===window。这种灵活性主要体现在功能环境上。也是函数环境中的this点,容易判断错误。这是函数内部的可见属性之一(另一个是参数)。在函数内部,我们可以直接使用this来访问指定的对象属性。那么指定对象是如何确定的呢?下面围绕this的指向对象来梳理一下this是如何确定的。首先,this是函数调用时确定的,即进入函数后,在函数中的表达式和语句执行前,在变量对象创建阶段确定。它指向什么取决于调用函数的位置(在什么对象上)。总的来说,需要注意函数是在哪里调用的,是如何调用的。下面主要分析this在不同调用场景下的指向。当函数作为对象拥有的方法调用时,this指向对象,否则this为undefind,题目描述为:调用函数时,为对象拥有,且函数中的this将绑定到对象。如果独立调用函数,严格模式下函数内部的this是undefind,非严格模式下this会指向window(node.js中是global)。根据以上原则,我们首先要判断的是谁拥有这个功能。这里有几个例子可以更好地理解:例子1:leta=1functionfoo(){console.log(this.a)}foo()//1foo()在全局环境中被独立调用。此时函数foo是全局对象(this指向window)所拥有的,所以this.a得到的是window全局对象下的a。栗子2:vara=1varfoo=function(){console.log(this.a)}vartoo={a:2,b:foo}varbar=too.bfoo()//1too.b()//2bar()//1函数执行时判断this指向的通用逻辑:foo():在全局对象window下调用,所以输出1。too.b():对象too指向的属性b到函数foo,函数foo也是对象内部的方法;too.b()执行时,b也被对象调用,内部this也指向对象;所以this.a也得到了.a,输出2;bar():对象的方法too被赋值给bar,即bar标识符指向与foo标识符相同的栈地址所代表的函数。此时在全局窗口下调用执行bar(),所以输出1。栗子3:vara=1varfoo=function(){console.log(this.a)}vartoo=function(fn){vara=2fn()}too(foo)//1too(foo):这里函数foo也作为参数传递给函数的内部执行。fn()执行时,并没有显式地被其他对象拥有,所以我们隐式判断fn()是在全局对象window下执行的,所以输出1。这个栗子很容易出错(感觉每次看还是会错o(︶︿︶)o),第一印象应该是2,就是因为这个跟作用域链混淆了。永远记住作用域链是在源代码编码阶段确定的,而this是在函数运行阶段确定的,属于执行上下文的概念,在运行阶段根据谁调用函数wherethisis确定位于。我们再把上面的栗子稍微修改一下栗子4:vara=1varfoo=function(){console.log(this.a)//output1console.log(a)//这里不一样,输出1}vartoo=function(fn){vara=2fn()}too(foo)//1,1表达式--值console.log(this.a)基于此表达式所属的上下文。在too函数内部调用了foo函数,当this指向window1console.log(a)时,基于作用域链的全局上下文中的变量a,在foo函数作用域链上。区别栗子5:letc=1letfoo={c:2}lettoo=function(){console.log(this.c)}foo.a={b:too,c:3}foo.a.b()//3this绑定只受最近的成员引用的影响。foo.a.b()函数b作为对象foo.a的一个方法被调用,所以此时this绑定到foo.a。b与对象foo的包含成员关系不大,最近的对象决定了this的绑定。最后console.log(this.c)获取foo.a中c的值3。栗子5:leta=1letfoo={a:2,msg:function(){console.log(`hi,${this.a}`)}}lettoo=Object.create(foo)too.msg()//你好,2上面也使用对象foo作为原型创建了一个新对象,所以这个对象也继承了对象foo的所有属性和方法。在执行too.msg()时,too调用了msg函数,this指向too对象,所以console.log(hi,${this.a})访问的是继承自对象foo的a。因此,对于对象原型链上某处定义的方法,同样的概念适用。如果方法存在于对象的原型链上,那么对象实例的this指向调用该方法的对象,通过this可以访问原型链上的方法。我们的结论通过上面几个栗子验证了:函数作为对象方法调用时,this指向对象,作为函数独立调用时,this指向全局对象窗口(严格模式下this为undefind).大多数时候,根据上面的原则来判断this的方向是没有问题的,但是this有几个特殊的场景需要注意。当函数作为构造函数被调用时,函数内部的this指向函数本身。函数也是一个对象栗子:functionFoo(a){this.a=a//实例化后this指向too}lettoo=newFoo(1)我们知道函数this是在运行时确定的,而在构造函数实例化的时候,内部其实是为我们创建了一个新的对象,经过一系列的操作,this指向了这个新的对象。执行new操作符时的逻辑推导如下:新建一个空对象;将构造函数的this指向这个新对象;将构造函数的原型添加到新对象的原型链中,并将属性和方法挂载到上面的新对象上;返回这个新对象返回的新对象就是我们实例化的对象。即new操作符调用构造函数时,this指向内部创建的新对象,最后将新创建的对象返回给实例变量。所以当一个函数作为构造函数被调用时,函数内部的this被绑定到函数上。当通过构造函数实例化对象时,对象内部的this也指向实例对象。当使用cal和apply方法调用执行函数时,this指向call和apply方法传入的对象。在javascript中,函数也是一个对象。所有函数都继承自Function构造函数,而call和apply是Function.prototype原型的方法,所以函数从原型的原型继承call和apply方法。call和apply用于将this对象和变量注入到函数中(call和apply的区别是传递的参数不同,其他没有区别)。leta=1lettoo={a:2}functionfoo(){console.log(this.a)}foo.call(too)//thisin2functionfoo指向调用传入的对象too,所以this.a输出2。如果传递给它的值不是对象,JavaScript将尝试使用内部ToObject操作将其转换为对象。因此,如果传递的值是原始值,如7或'foo',则会使用相关构造函数将其转换为对象,因此原始值7将转换为对象:newNumber(7)和字符串'foo'转换为newString('foo')。functionbar(){console.log(this,Object.prototype.toString.call(this));}//原始值7隐式转换为对象bar.call(7);//Number{[[PrimitiveValue]]:7}__proto__:Number[[PrimitiveValue]]:7"[objectNumber]"ECMAScript5引入了Function.prototype.bind。当一个函数调用f.bind(someObject)时,它会创建一个与f具有相同函数体和作用域的函数,但在这个新函数中,this将永久绑定到绑定对象someObject。栗子:leta=1lettoo1={a:2}functionfoo(){console.log(this.a)}letbar=foo.bind(too1)letbar2={a:4,b:bar}bar()//2bar.call({a:3})//2bar2.b()//2foo通过bind创建新函数时,将新函数的this绑定到传入对象too1上,以便后续执行即使bar被作为对象方法调用或使用call和apply,它不能用bind替换thisbound。当函数被创建为箭头函数时,this指向箭头函数执行时宿主函数的上下文栗子:functionfoo(){letthat=thislettoo=()=>{console.log(this===that)//true}too()}foo()too是一个箭头函数,内部的this指向创建时的上下文,即foo的this。相反,箭头函数没有自己的上下文,他共享的是封闭的词法上下文。注意,这里所说的“this是动态绑定,运行时绑定”主要是指进入函数后,函数运行前的上下文创建阶段(预处理)。此时,函数中的表达式和语句并没有被执行。.但这里统称为函数运行时,具体请注意变量对象部分(^.^)中的描述。
