js this机制详解

this是什么

当一个函数被调用时,会创建一个执行上下文,它会记录函数的调用位置,调用方法,传入参数等信息。 this就是这些信息中的一个属性,会在函数执行时用到。

this既不指向函数的词法作用城,也不指向函数自身。this是在函数被调用时才被绑定,并非函数编写或声明时绑定。它指向什么完全取决于函数在哪里被调用

2.绑定规则

2.1默认绑定

当函数调用时,如果是使用不带任何修饰和前缀的,独立的函数引用进行调用时,此时的this为默认绑定,指向全局对象(浏览器中是window)

 function foo(){
 	console.log(this.a)  //2
 var a = 2
 foo()  //不带修饰和前缀的独立调用
复制代码

在strict严格模式下,全局对象无法使用默认绑定,此时this为undefined

2.2 隐式绑定

当函数调用时,存在上下文对象或者说被某个对象拥有和包含,隐式绑定规则会把this绑定到这个上下文对象中,

function foo()(
	console.log(this.a)
}
const obj ={
    a:2,
    foo:foo
}
obj.foo() //2
复制代码

此外,对象属性引用链中只有最后一层影响调用的this:

 function foo(){
 	console.log(this.a)
}
 const obj2 ={
	 a:2foo:foo
}
 conat obj1 = {
	 a:1obj2:obj2
 }

 obj1.obj2.foo() //2
复制代码

隐式绑定的this丢失现象:

 function foo(){
  console.log (this.a)
 }


 const obj = {
	  a:2foo:foo
}

 var bar = 【obj.foo //赋值foo的引用
 var a ="window的a"
 bar() //"window的a"
复制代码

虽然bar是obj.foo的一个引用,但是实际上,它引用的是foo函数本身,此时我们可以把obj.foo想象成一个新封装的独立函数,所以bar在调用时应用了默认绑定规则,此时this为全局对象,而不再是原来的上下文对象。

除此之外,当我们给一个函数A传递一个函数类型的参数B(有可能B是某个对象的属性引用)时,无论A是我们自己声明的函数还是js内置的函数(如setTimeout),当传入的函数B被调用时,都会存在这件的this被重置为默认绑定的规象。

function foo(){
    console.log(this.a)
}
const obi ={
	a:2foo:foo

}

var a ="window的a"
setTimeout (obj.foo, 1000) //window的a
复制代码

2.3 显式绑定

显式绑定顺名思义,我们可以主动设置并明确知道某个函数调用时的this指间。

(1)apply/call

我们可以通过apply/call来修改函数调用时的this指向。

function foo{
	console.log(this.a)
}
const obj ={
 	a:2
}
foo.call (obj) //2
复制代码

针对这个this指向参数,如果你传入了一个基本类型(如number或string)的值,它会自动转为封装对象(如new String())

(2) bind

但是如果是上面那种函数的引用在其他地方传递时,this丢失被重置为默认绑定的情况怎么办?这个时候没法在它调用时去修改this。这时候需要用到bind

bind会强制修改某个函数的this并返回一个修改this后的新函数,即使该函数在调用时又使用了call/apply/bind修改hthis,此时会修改无效:

function foo(){
 	console.log(this.a)
}

 const obj = {
 	 a:2,
 }


 var a = "window的a"
 let newFoo = foo.bind(obj) 
 newFoo () //2
 newFoo.cal1 (window) //2
复制代码

(3)API调用时的“bind”

一些内置的JS API(如forEach,map)等可以提供一个可选参数来指定this,其作用与bind一致

function foo(){
 	console.log(this.a)
}

 const obj = {
 	 a:2,
 }


 var a = "window的a"
 
[1,2,3].forEach(foo,obj) //2 2 2
复制代码

2.4 new绑定

使用new来调用函数或者说发生构造函数调用时,会发生以下行为:

1:创建一个全新的对象
2:这个对象会被执行原型链接
3:函数调用的this会绑定到这个新对象
4:如果函数没有返回其他对象,那么new表达式中的函数调用会默认返回这个新对象

function foo(a){
	 this.a=a
}
const bar = new foo(2)
console.log(bar.a) //2

复制代码

2.5 箭头函数

箭头函数的this指向永远与箭头函数定义时的上下文所指向的this一致,其他任何绑定方式都不会修改箭头函数的this指向、

2.6 其他场景

非箭头函数情况下:

  • js的DOM事件监听相关API中,其国调函数的默认this指向为当前监听的DOM元素

  • 一些JS高阶函效(如未指定this参数的map,forEach等)和内置函数(如setTimeout),其默认this指向为全局对象window

3. this绑定优先级

当一个函数同时存在多种this绑定规则时,其优先级如下:

new绑定 > 显式绑定 > 隐式绑定 > 默认绑定

4.被忽略的this

有时停,我们会通过call/apply/bind来传递函数的参数,如果此时我们并不需要关心this绑定(但是this需要一个占位符),你也许会将null/undefined作为this绑定参数传入进去,但是此时函数在调用时会忽略这样的this绑定值,从而应用的是默认绑定规则(window)。即使你不关心this指向,但是如果函数内部用到了(比如一些第三方库的函数)。那么这种情况下调用有可能会导致例如修改全局对象的一些副作用。

对此,在忽略this绑定的情况下,推荐的做法是将Object.create(null)返回的对象作为this绑定值传递进去。

Object.create(null)会创建一个空对象,与{}的区别在于它不会创建Object.prototype这个委托。所以它更“空”。此时,在关于函数的调用中,对于this的使用都会被限制在这个空对象中,不会对全局对象产生任何影响

function foo(a,b,c){
	console.log(this.obj)
}
var obj = {}
const empty  = Object.create(null) 
const bar =  foo.bind(empty ,1,2, 3)
bar() //undefined(没有访问到全局的obj)
复制代码
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享