01. 一网打尽this,对上下文说Yes
发布于 2022年 02月 15日 13:11
为什么要掌握this?
- 一直是面试的热点
- 基础,Javascript进阶的重要一环
- 指向多变,很多隐藏bug的源由
- 熟练驾驭,写出更简洁,优雅的代码
this到底指向谁
有一种广为流传的说法是”谁调用它,this就指向谁“,但是这样说法并不全面,也不规范。
这里可以先”死记硬背“一下几条规律:
- 在函数体中,非显式或隐式地简单调用函数时,严格模式下,函数内this绑定到undefined上,非严格模式绑定到全局对象window/global上
- new 方法调用构造函数时,构造函数内部的this会被绑定到新创建的对象上
- call/apply/bind方法显式调用函数,函数内this会被绑定到指定参数对象上
- 通过上下文对象调用函数时,函数体内的this会被绑定到该对象上
- 箭头函数中,this指向由外层(函数或全局)作用域来决定
1. 全局环境中的this
最基础的例子:
function f1(){
console.log(this)
}
function f2(){
'use strict'
console.log(this)
}
f1() //window
f2() //undifined
变种:
const foo1 = {
bar: 10,
fn: function () {
console.log(this)
},
}
let fn1 = foo1.fn
fn1() //window
foo1.fn() //foo1对象
在函数执行时不考虑显式绑定,this如果被上一级对象调用,那么指向上一级对象,否则指向全局环境
2. 上下文中调用this
const o1 = {
text: 'o1',
fn: function() {
return this.text
}
}
const o2 = {
text: 'o2',
fn: function() {
return o1.fn()
}
}
const o3 = {
text: 'o3',
fn: function() {
var fn = o1.fn
return fn()
}
}
console.log(o1.fn()); // o1
console.log(o2.fn()); // o1
console.log(o3.fn()); // undefinde
3. 通过bind,call,apply改变this指向
一段代码说明下面是等价的
const target = {}
fn.call(terget, 'arg1', 'arg1')
fn.applay(terget, ['arg1', 'arg1'])
fn.bind(terget, 'arg1', 'arg1')()
4. 构造函数和this
function Foo(){
this.bar = "Lucas"
}
const instance = new Foo()
console.log(instance.bar);// Lucas
new 操作符做了什么
简略的答案:
- 创建了一个新对象
- 将构造函数的this指向这个新对象
- 为这个对象添加属性,方法等
- 最终返回这个对象
代码表述为
var newObj = {}
newObj.__proto__ = Foo.prototype
Foo.call(this)
如果构造函数中出现return,需要注意:
返回简单数据类型,this指向实例
返回复杂类型,this指向这个对象
5. 箭头函数的this
在《你不知道的JavaScript》书中指出:
箭头函数中的this指向是由其所属函数或者全局作用域决定的
const foo1 = {
fn:function(){
setTimeout(function(){
console.log(this);
})
}
}
const foo2 = {
fn:function(){
setTimeout(()=>{
console.log(this);
})
}
}
foo1.fn();// 出现在匿名函数 指向 window
foo2.fn();// foo对象
箭头函数的this无法更改
var a = 123
function foo(){
return a =>{
console.log(this.a)
}
}
const obj1 = { a:1 }
const obj2 = { a:2 }
var bar = foo.call(obj1) // 2
console.log(bar.call(obj2)) // undefined
两层箭头函数的情况
var a = 123
const foo = () => (a) => {
console.log(this.a)
}
const obj1 = { a:1 }
const obj2 = { a:2 }
var bar = foo.call(obj1) // 123
console.log(bar.call(obj2)) // undefined
如果把全局 var a 改为 let a 或者const a 则为undefined
let 和 const 声明变量不会挂载到全局对象上