this是javascript中的一个关键字,多数情况下this指向调用它的对象。这句话有两个意思,首先this指向的应该是一个对象(函数执行上下文对象)。其次,这对象指向的是调用它的对象,如果调用它的不是对象或者对象不存在,则会指向全局对象(严格模式下为undefined)。
其实,this的指向是在函数被调用时确定的,它的指向取决于函数调用的地方,而不是它被声明的地方(箭头函数除外)。当函数被调用时,会创建一个执行上下文,它包含函数在哪里被调用(调用栈)、函数的调用方式、传入的参数等信息,this就是这个记录的一个属性,它会在函数执行的过程中被用到。
this在函数的指向绑定形式有四种:默认绑定、隐式绑定、显示绑定、new绑定。
(1)默认绑定(全局环境)
函数在浏览器全局环境中直接使用不掉任何修饰的函数引用进行调用,非严格模式下this指向window;在use strict指明严格模式的情况下就是undefined(严格模式不允许this指向全局对象)。
注意:在浏览器环境下,全局对象是window;在Node.js环境下,全局对象是global。
function fn1(){
console.log(this)
}
function fn2(){
'use strict';
console.log(this)
}
console.log(fn1) //window
console.log(fn2) //undefiend
需要注意一种情况
let count=1
let foo={
count:10,
fn:function(){
console.log(this)
console.log(this.count)
}
}
let fn1=foo.fn
fn1() //这里的输出是window 1
这里的this依然指向window。虽然fn函数在foo对象中作为方法被引用,但是在赋值给fn1之后,fn1的执行仍然是在window全局环境中。因此输出window和1
(2)隐式绑定(上下文对象)
如果函数在某个上下文对象中调用,那么this绑定的是那个上下文对象。
let count=1
let foo={
count:10,
fn:function(){
console.log(this.count)
}
}
foo.fn() //10
在上述代码中,最后会输出10。这里的fn方法是作为对象的属性被调用的,此时fn方法执行时,this会指向foo对象。也就是说,此时this指向的是调用这个方法的对象。
那么如果嵌套了多层对象,this的指向又是怎么样的呢?
const obj1={
text:1,
fn:function(){
return this.text
}
}
const obj2={
text:2,
fn:function(){
return obj1.fn()
}
}
const obj3={
text:3,
fn:function(){
var fn=obj1.fn
return fn()
}
}
console.log(obj1.fn()) //1
console.log(obj2.fn()) //1
console.log(obj3.fn()) //undefiend
对于这段代码,最终的三个输出结果:
- 第一个console输出1,这时this指向了调用fn方法的对象obj1,所以会输出obj1中的属性text的值1
- 第二个console输出1,这里调用了obj2.fn()实际上调用obj1.fn(),因此仍然会输出1
- 第三个console输出undefined,在进行 var fn=obj1.fn赋值之后,是直接调用的,因此这里的this指向window,答案是undefined
根据上面的例子可以看出:如果嵌套了多个对象,那么指向最后一个调用这个方法的对象
那么如何让console.log(obj2.fn())输出2呢?
const obj1={
text:1,
fn:function(){
return this.text
}
}
const obj2={
text:2,
fn:obj1.fn
}
console.log(obj2.fn()) //2
(3)显式绑定(apply、call、bind)
显式绑定是指需要引用一个对象时进行强制绑定调用,显式绑定可以使用apply、call、bind方法来绑定this值,使其指定我们指定的对象。
call、apply、bind丢可以改变函数中this的指向,但是call、apply是直接进行函数调用;bind不会执行函数,而是返回一个新的函数,这个新函数已经自动绑定了this指向,需要我们手动调用。call和apply的区别:call方法接收的是参数列表,而apply方法接收的是一个参数数组。
这三种方法的使用方式如下:
const target={}
fn.call(target,'arg1','arg2')
fn.apply(target,['arg1','arg2'])
fn.bind(target,'arg1','arg2')()
需要注意的是,如果把null或undefined作为this的绑定对象传入call、apply、bind,这些值在调用的时候会被忽略,实际应用的应该是默认绑定规则
var text=1
function fn (){
console.log(this.text)
}
fn.call(null) //1
这里的输出会是1,因为把null作为this传给了call方法,这是this会使用默认绑定规则,指向window
那如果对于一个函数进行多次bind,那么this会指向谁呢?
let a={}
function fn (){
console.log(this)
}
fn.bind().bind(a)() //window
可以发现,不管给函数bind多少次,函数中的this永远由第一次bind决定
(4)new绑定(构造函数)
函数作为构造函数使用new调用时,this绑定的是新创建的构造函数的实例对象。