全局环境下的 this
function f1() {
console.log(this)
}
function f2() {
'use strict'
console.log(this)
}
f1() // window
f2() // undefined
const foo = {
bar: 10,
fn: function () {
console.log(this)
console.log(this.bar)
}
}
var fn1 = foo.fn // 相当于 window.fn1 = foo.fn
fn1() // window undefined
const foo = {
bar: 10,
fn: function () {
console.log(this)
console.log(this.bar)
}
}
foo.fn() // foo 10
结论:在执行函数时,如果函数中的 this 是被上一级的对象所调用,那么 this 指向的就是上一级的对象;否则指向全局环境。
上下文对象调用中的 this
const person = {
name: 'Lucas',
brother: {
name: 'Mike',
fn: function () {
return this.name
}
}
}
console.log(person.brother.fn()) // 'Mike'
// 在这种嵌套的关系中,this 指向最后调用它的对象,因此输出将会是:Mike。
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()
}
}
const o4 = {
text: 'o4',
fn: o1.fn
}
console.log(o1.fn()) // o1
console.log(o2.fn()) // o1
console.log(o3.fn()) // undefined
console.log(o4.fn()) // 04
/*
第一个 console 最简单,o1 没有问题。难点在第二个和第三个上面,关键还是看调用 this 的那个函数。
第二个 console 的 o2.fn(),最终还是调用 o1.fn(),因此答案仍然是 o1。
第三个,在进行 var fn = o1.fn 赋值之后,是「裸奔」调用,因此这里的 this 指向 window,答案当然是 undefined。
第四个,this 指向最后调用它的对象,在 fn 执行时,挂到 o4 对象上即可,我们提前进行了赋值操作
*/
bind/call/apply 改变 this 指向
// 三者使用区别
const printName = (firstName, lastName) => console.log(`${firstName} ${lastName}`)
// bind
printName.bind(null, 'Number', 'One')() // 常见于 React 绑定事件
// call
printName.call(null, 'Number', 'Two')
// apply
printName.apply(null, ['Number', 'Three'])
const foo = {
name: 'lucas',
logName: function () {
return this.name
}
}
const bar = {
name: 'mike'
}
console.log(foo.logName.bind(bar)()) // mike
console.log(foo.logName.call(bar)) // mike
console.log(foo.logName.apply(bar)) // mike
构造函数和 this
function Foo() {
this.bar = 'Lucas'
}
const instance = new Foo()
console.log(instance.bar)
/*
new 操作符调用构造函数,具体做了什么?以下供参考:
1.创建一个新的对象;
2.将构造函数的 this 指向这个新对象;
3.为这个对象添加属性、方法等;
4.最终返回新对象。
以上过程,也可以用代码表述:
var obj = {}
obj.__proto__ = Foo.prototype
Foo.call(obj)
*/
function Foo() {
this.user = 'Lucas'
const o = {}
return o
}
function Bar() {
this.user = 'Lucas'
return 1
}
console.log(new Foo()) // {}
console.log(new Bar()) // Bar {user: "Lucas"}
/*
结论:如果构造函数中显式返回一个值,且返回的是一个对象,那么 this 就指向这个返回的对象;
如果返回的不是一个对象,那么 this 仍然指向实例。
*/
箭头函数中的 this 指向
const foo = {
fn: function () {
setTimeout(function () {
console.log(this)
})
}
}
const bar = {
fn: function () {
setTimeout(() => {
console.log(this)
})
}
}
console.log(foo.fn()) // window
console.log(bar.fn()) // {fn: ƒ}
/*
this 出现在 setTimeout() 中的匿名函数里,因此 this 指向 window 对象。
如果需要 this 指向 foo 这个 object 对象,可以巧用箭头函数解决:
*/
箭头函数不绑定它们自己的
this
,相反,它们从父作用域继承一个,这被称为“词法作用域”。from Understanding "this" in javascript with arrow functions
最后的测试
const bar = {
f1: function () {
console.log(this)
},
f2: () => {
console.log(this)
},
f3: function () {
const fun = () => {
console.log(this)
}
fun()
},
f4: () => {
function fun() {
console.log(this)
}
fun()
}
}
bar.f1() // bar
// `bar.f1()`:在 `bar` 对象上调用 `f1` 方法,此时 `f1` 中的 `this` 指向 `bar` 对象,因此输出 `bar`。
bar.f2() // window
// `f2` 是一个箭头函数,箭头函数的 `this` 始终指向外层作用域的 `this`,而在全局作用域中,`this` 指向全局对象。因此,在这个调用中,输出全局对象 `window`。
bar.f3() // bar
// 在 `bar` 对象上调用 `f3` 方法。在 `f3` 方法中定义了一个箭头函数 `fun`,它的 `this` 也指向 `bar` 对象。因此,在调用 `fun` 方法时,输出 `bar`。
bar.f4() // window
// `f4` 是一个箭头函数,而在 `f4` 中定义的 `fun` 函数是一个普通函数。在全局作用域中调用 `f4` 方法时,`f4` 中的箭头函数的 `this` 指向全局对象,而 `fun` 函数的 `this` 也指向全局对象。因此,在调用 `fun` 方法时,输出全局对象 `window`。
const f1 = bar.f1
f1() // window
// 将 `bar.f1` 赋值给了 `f1` 变量,然后在全局作用域中调用 `f1` 方法。由于此时 `f1` 中的 `this` 没有明确指定,因此默认指向全局对象 `window`,因此输出全局对象 `window`。
const f2 = bar.f2
f2() // window
// 将 `bar.f2` 赋值给了 `f2` 变量,然后在全局作用域中调用 `f2` 方法。由于 `f2` 是一个箭头函数,在全局作用域中定义的,因此它的 `this` 指向全局对象 `window`。因此,在这个调用中,输出全局对象 `window`。
const f3 = bar.f3
f3() // window
// 将 `bar.f3` 赋值给了 `f3` 变量,然后在全局作用域中调用 `f3` 方法。在 `f3` 方法中定义了一个箭头函数 `fun`,它的 `this` 也指向全局对象 `window`。因此,在调用 `fun` 方法时,输出全局对象 `window`。
const f4 = bar.f4
f4() // window
// 将 `bar.f4` 赋值给了 `f4` 变量,然后在全局作用域中调用 `f4` 方法。由于 `f4` 是一个箭头函数,在全局作用域中定义的,因此它的 `this` 指向全局对象 `window`。在 `f4` 中定义的 `fun` 函数的 `this` 也指向全局对象 `window`。因此,在调用 `fun` 方法时,输出全局对象 `window`。