JavaScript中的this是什么?
定义:this是包含它的函数做为方法==被调用时所属的对象。==es6
function fn1(){ this.name = "halo"; } fn1();
咱们将定义拆分一下app
经过上面三点分析,很容易知道fn1函数里的this指向的是window。
那么若是是更复杂的场景咱们如何判断this的指向呢?函数
若是想用一句话总结this的指向,稍微了解一些this
指向的人都能脱口而出this
谁调用它,this就指向谁。
也就是说this
的指向是在调用时肯定的,而不是在定义时肯定的。这么说没有错,可是并不全面。
其实,调用函数会建立新的术语函数自身的==执行上下文==。执行上下文的调用建立阶段会决定this
的指向。因此更加准确的总结应该是:es5
this的指向,是在调用函数时根据执行上下文所动态肯定的。
在es6箭头函数以前,想要判断一个函数内部this指向谁,就根据如下四种方式来决定的。code
先来看一种相对简单的状况,函数在全局环境中被直接调用,严格模式下函数内this指向undefined,非严格模式下函数内this指向window。以下对象
function fn1() { console.log(this) } function fn2() { 'use strict' console.log(this) } fn1() // window fn2() // undefined
再看下面例子:ip
const age = 18; const p = { age:15, say:function(){ console.log(this) console.log(this.age) } } var s1 = p.say s1()
这里say
方法内的this
仍然指向window
,由于p
中的say
函数赋值给s1
后,s1
的执行仍然是在window
的全局环境中。所以上面的代码最后输出window
和undefined
。开发
这里可能有人会有疑问,若是是在全局环境中,那this.age
不是应该输出18么?这是由于使用const
声明的变量不会挂载到window
全局对象上,所以this
指向window
时找不到window
上的age
。换成var声明便可输出18.get
若是想让代码输出p
中的age
,而且让say函数中的this指向对象p
。只须要改变函数的调用方式,以下
const age = 18; const p = { age:15, say:function(){ console.log(this) console.log(this.age) } } p.say()
输出
{age: 15, say: ƒ} 15
由于此刻say
函数内部的this
指向的是最后调用它的对象。再次验证了那句话,==this的指向,是在调用函数时根据执行上下文所动态肯定的。==
const p = { age: 18, fn: function() { return this } } console.log(p.fn() === p)
输出
true
若是第一节的函数式调用理解了。那么这里应该也不会有疑问。
咱们再重复一遍==this的指向,是在调用函数时根据执行上下文所动态肯定的。==
记住这句话后遇到更复杂的场景也能够很容易的肯定this指向。
以下:
const p = { age: 20, child: { age: 18, fn: function() { return this.age } } } console.log(p.child.fn())
不论浅套关系如何变化,this都只想最后调用它的对象,所以输出18。
再升级下代码:
const o1 = { text: 'o1', fn: function() { return this.text } } const o2 = { text: 'o2', fn: function() { return o1.fn() } } const o3 = { text: 'o3', fn: function() { var fn = o1.fn return fn() } } console.log(o1.fn()) console.log(o2.fn()) console.log(o3.fn())
输出结果
o1 o1 undefined
o2.fn()
,其实内部调用的是o1.fn()
,所以仍是输出o1
。fn()
,至关于在全局环境调用函数。this
指向window
。若是如今的需求是想让
console.log(o2.fn())
输出o2,代码该如何修改?
以下:
const o1 = { text: 'o1', fn: function() { return this.text } } const o2 = { text: 'o2', fn: o1.fn } console.log(o2.fn())
function Foo() { this.age = 18 } const instance = new Foo() console.log(instance.age)
输出18。知道输出结果并不难,可是new
操做符调用构造函数时都作了什么呢?
this
指向这个新对象;须要注意的是,若是在构造函数中出现显式的return
,那么就要分为两种场景分析。
function Foo(){ this.age = 18 const o = {} return o } const instance = new Foo() console.log(instance.age)
将会输出undefined
,由于若是在构造函数中出现显式的return
,而且返回一个对象时,那么建立的构造函数实例就是return
返回的对象,这里instance
就是返回的空对象o
。
function Foo(){ this.age = 18 return 1 } const instance = new Foo() console.log(instance.age)
将会输出18
,由于若是构造函数中出现显式return
,可是返回一个非对象的值时,那么this
仍是指向实例。
总结:当构造函数显式返回一个值,而且返回的是一个对象,那么this
就指向这个返回的对象。若是返回的不是一个对象,this
仍然指向实例。
关于基础用法,这里再也不赘述。须要知道的是bind/call/apply
三者都是改变函数this
指向的,call/apply
是改变的同时直接进行函数调用,而bind
只是改变this
指向,而且返回一个新的函数,不会调用函数。call
和apply
的区别就是参数格式不一样。详见以下代码:
const target = {} fn.call(target, 'arg1', 'arg2')
上述代码等同于以下代码
const target = {} fn.apply(target, ['arg1', 'arg2'])
能够看出知识调用的参数形式不一样而已,改写成bind以下所示
const target = {} fn.bind(target, 'arg1', 'arg2')()
不光要调用bind传入参数,仍是在调用bind后再次执行函数。
明白call/apply/bind的使用后,再来看一段代码:
const foo = { age: 18, showAge: function() { console.log(this.age) } } const target = { age: 22 } console.log(foo.showAge.call(target))
结果输出22,只要掌握了call/apply/bind
的基本用法,对于输出结果并不难理解。咱们每每会遇到多种方式同时出现的状况,咱们在说完箭头函数的this
后会再详细说明this
优先级相关内容。
熟悉es6的人应该会知道箭头函数中的this
指向,再也不听从上述的规制,而是根据外层的上下文来决定。
es5代码:
const foo = { fn: function () { setTimeout(function() { console.log(this) }) } } foo.fn() // Window{……}
this
出如今setTimeout()
中的匿名函数里时,this
指向window
对象。这种特性势必会给咱们的开发带来一些坑,es6的箭头函数就很好的解决了这个问题。
es6代码:
const foo = { fn: function () { setTimeout(() => { console.log(this) }) } } foo.fn() // {fn: ƒ}
箭头函数中的this
指向,再也不适用上面的标准,而是找到外层上下文,这段代码中this
在箭头函数中,则找到外层的上下文的调用对象——foo
。所以这里的this
指向的就是foo
。
==注意==:当箭头函数改变了this
指向后,那么该this
指向就再也不受任何影响,也就是说不会再次发生改变,具体在this
优先级章节中会举例说明。
总结:
call、apply、bind、new
等改为this
指向的操做称为显式绑定;this
指向成为隐式绑定。若是一段代码中即出现显式绑定又有隐式绑定,该如何肯定this
指向呢?
往下看
function foo (age) { console.log(this.age) } const o1 = { age: 1, foo: foo } const o2 = { age: 2, foo: foo } o1.foo.call(o2) o2.foo.call(o1)
若是隐式绑定优先级高于显式绑定,那么应该输出1,2
。可是运行代码发现结果输出2,1
。这也就说明了显式绑定中的call、apply
优先级更高。
再看:
function foo (age) { this.age = age } const o1 = {} var fn = foo.bind(o1) fn(18) console.log(o1.age) // 18 var f1 = new fn(22) console.log(f1.age); // 22
分析下上面代码,fn
是foo
函数调用bind
方法返回的函数,也就至关因而返回foo
函数,而且将this
指向o1
对象。执行了fn(18)
后o1
对象的age
值就是18了,因此第一个输出结果是18。
而后经过new
调用fn
函数,这时fn
函数做为构造函数被调用,this
就会指向返回的实例,从而与o1
对象解绑。
所以得出结论:new
的优先级高于bind
。
还记得上一节提到的箭头函数特行么?箭头函数影响的this指向没法被修改。看下面代码:
function foo() { return () => { console.log(this.age) }; } const o1 = { age: 2 } const o2 = { age: 3 } const fn = foo.call(o1) console.log(fn.call(o2))
输出为2,foo
的this
指向了o1
,fn
接收的箭头函数的this
天然也会指向o1
。==而箭头函数的this是不会再次改变的==,因此尽管用显式绑定call
去改变this
指向,也是不起做用的。
结束啦!this涉及知识点繁多,碰到优先级问题也是让人头疼。没有什么捷径,惟有“死记硬背”+“慢慢理解”