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:2,
foo:foo
}
conat obj1 = {
a:1,
obj2:obj2
}
obj1.obj2.foo() //2
复制代码
隐式绑定的this丢失现象:
function foo(){
console.log (this.a)
}
const obj = {
a:2,
foo: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:2,
foo: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)
复制代码