Tips:本文已参与「新人创作礼」活动,一起开启掘金创作之路。
this
前言
本文是在非严格模式下讨论this,读者在自己测试的时候要注意浏览器是有Window的,但是如果使用的是node,是没有window的,会报undefined,其次要注意,let 声明的变量在全局(window)下,但不会作为属性挂在全局下,而var声明的会,所以某些变量定义的时候用let,打印结果会是undefined,用var则正常打印。
什么是this?
当一个函数被调用的时候,会生成一个执行上下文,其中会记录函数在哪里被调用(调用栈),函数的调用方式,传入的参数信息等,this就是这个执行上下文中的一个属性,会在函数执行的过程中用到
this的绑定规则
1. 默认绑定
函数在全局下调用,this是默认指向Window对象的
单独的打印this,结果也是Window对象
2. 隐式绑定
当函数引用时有上下文对象,那么就会触发隐式绑定,把函数的this绑定到该上下文对象
function foo(){
console.log(this.a)
}
var a = 2
var obj = {
a: 3,
foo: foo
}
foo()//打印2
obj.foo()//打印3
上述例子中定义了一个obj对象,其中foo:foo,这行代码是函数的引用而不是调用,因为冒号后面的foo后面没有接(),所以foo:foo这行代码其实可以等价于
foo: function () {
console.log(this.a)
}
foo函数定义在obj外部,所以它其实并不属于obj,但是执行代码时,调用位置会使用obj的执行上下文,因此this就指向obj这个上下文对象了,所以obj.foo()打印出来的是3,是obj的a属性的值,而foo()是在全局下调用的,所以是全局下的a的值,foo()的调用放在这里只是为了作比较,让代码的作用和不同之处更直观的给读者看到。
Tips:对象属性链中只有最后一层会影响到调用位置。
比如
function foo(){
console.log(this.a)
}
var a = 2
var obj = {
a: 3,
foo: foo
}
var obj2 = {
a: 4,
another: obj,
foo: foo
}
obj2.foo()//打印的是4
obj2.another.foo()//打印的还是3
3. 隐式丢失
当隐式绑定的函数丢失绑定对象,就会引用默认绑定
function foo(){
console.log(this.a);
}
var obj = {
a:2,
foo:foo
}
var bar = obj.foo
var a = 'glob'
bar()//打印的是glob
(其实这里我个人认为,隐式丢失其实说白了就是this又一不小心绑回了全局,因为很多隐式丢失的例子,都是把obj.foo以引用的方式放在了另一个函数里,就像此处,将obj.foo赋值给了bar,而bar是在全局下调用的,所以this指向全局,这是我个人的理解,单纯有助于理解例子,如有大神觉得不对请指出。)
4. 显示绑定
显示绑定其实就是人为的强行改变this指向,常见的方法是call,apply,bind call和apply返回的是一个值,而bind方法返回的是一个函数
function foo(){
console.log(this.a);
}
var obj = {
a:2
}
foo.call(obj)//打印的是2
foo.apply(obj)//打印的是2
let bar = foo.bind(obj)
bar()//打印的是2
this的指向
1. 普通函数
函数在谁的词法环境中生效,该函数内部的this就指向谁
其实也可以说,this指向包含它的函数作为方法被调用时所属的对象。
(以上两句话其实意思都一样)
我们给个例子来理解一下这个定义:
全局下定义一个foo函数,里面打印this(至此foo就是包含this的函数,即this是在foo的内部),然后把foo函数拿到Window下调用(代码第四行),此时foo被作为方法在被调用(即foo在Window的词法环境中生效),所以this指向被调用时所属的对象即Window
function foo(){
console.log(this)
}
foo()//打印出来是Window对象
2. 构造函数
构造函数(用new方法调用)的this指向实例化对象
3. 箭头函数
箭头函数本身没有this的概念,写在其内部的this,取决于箭头函数定义在了谁的词法环境里面,this就指向谁
上述例子中fn是箭头函数,且定义在全局,所以它的this是指向全局Window的,所以即使是obj.fn()调用,this.a打印的也是1
其它
改变this的指向,可以使用箭头函数,new方法,和显示绑定(call,apply,bind)和在函数里面定义一个变量,使其等于this,比如:let _this = this,这样就可以使用_this来代替this的使用了