JS 中的 this
this机制的背景
- 对象内部的方法中使用对象内部的属性是一个非常普遍的需求,但JS的作用域机制无法做到这一点!
let obj = {
a:4;
foo(){
a = 3;
}
}
obj.foo() // undifined
从上面可以发现,对象内部的方法如果通过作用域链来直接访问对象内部的变量会找不到,因为foo方法的outer是指向全局执行上下文的,所以这个时候 this 应运而生!需要注意的是,this 和 作用域链是两套不同的机制,二者之间并无多大联系
this 是什么
-
在JS的执行上下文中,除了变量环境、词法环境,outer以外,还有一个
this
,this是和执行上下绑定的,即每一个执行上下文中都会存在一个 this,this 随执行上下文分三种- 全局执行上下文 —— 全局 this (浏览器中一般指向 window对象)
- 函数执行上下文 —— 函数中的 this
- eval 的执行上下文 —— eval 中的 this (eval()用于执行一段JS代码)
重点关注函数中的 this
函数中的 this
function foo(){
console.log(this)
}
foo() // window对象
window.foo() // window对象
通常情况下,有三种方式来设置函数执行上下文中的 this 的指向
- 通过函数的 call,bind,apply 方法
let bar = {} function foo(){ console.log(this) } foo.call(bar) // bar对象 foo.bind(bar) // function()... bind() 返回的是一个新函数 foo.bind(bar)() // bar 对象 foo.apply(bar) // bar对象
- 通过对象调用方法设置
let bar = { foo(){ console.log(this) } } bar.foo() // bar对象 // 也可以理解成 bar.foo().call(bar) // bar对象
let baz = bar.foo baz() // window对象 // 在全局环境中调用一个函数,函数内部的this指向的是全局变量window
- 通过构造函数设置
function CreateObj(){ console.log(this) this.name = 'obj' } let obj = new CreateObj(); // obj对象 // 这里可以理解成 let obj = {} CreateObj.call(obj) return obj
不难发现后面两种方法其实都是根据第一种方法改编而来,即本质上都是通过函数内置的方法 call、bind、apply 来改变 this 的
this 的设计缺陷以及应对方案
- 嵌套函数中的 this 不会从外层函数继承
这可以通过设置一个临时变量来保存this,然后在bar函数中使用该变量即可,这个方法的本质是转换变量机制,从this机制换到了作用域链的机制let myObj = { foo(){ function() bar{ console.log(this) } bar() // window对象 } }
- 普通函数中的 this 默认指向全局对象 window
可以通过严格模式解决,在严格模式下,默认一个普通函数的 this 是 undifined