在 JavaScript 中,关于 this
的指向是一个常见的问题。this
关键字表示当前执行代码的上下文对象,它的值取决于函数的调用方式。以下是一些常见情况和 this
的指向:
默认绑定:如果函数没有使用任何上下文对象进行调用,即独立函数调用,this
默认绑定到全局对象(在浏览器环境下是 window
对象,Node.js 环境下是 global
对象)。在严格模式下,this
将绑定为 undefined
。
js
console.log(this); // 在全局作用域中打印 this,指向全局对象
//案例一
function myFunction() {
console.log(this)
}
myFunction(); // 指向全局对象
//案例二
function foo1() {
console.log(this)
}
function foo2() {
console.log(this)
foo1()
}
function foo3() {
console.log(this)
foo2()
};;;
foo3() //打印3次全局对象
// 案例三
let obj = {
name: 'why',
foo: function () {
console.log(this)
}
}
let bar = obj.foo
bar() // 全局对象
//案例四
function foo() {
console.log(this)
}
let obj = {
name: 'why',
foo: foo
}
let bar = obj.foo
bar() // 全局对象
//案例五
function foo() {
function bar() {
console.log(this)
}
return bar
}
let fn = foo()
fn() //全局对象
一些补充
javascript
//定时器
setTimeout(function () {
console.log(this) //游览器环境下window,
}, 1000)
//node环境下是
//`setTimeout`函数的回调函数中的 `this` 默认是指向一个空对象的,
//而不是指向全局对象`global`,
//这个空对象称为"定时器对象"(Timer object)。
//监听点击
const boxDiv = document.querySelector('.box')
boxDiv.onclick = function () {
console.log(this)
}
boxDiv.addEventListener('click', function () {
console.log(this)
})
//当事件被触发时,`this`都会指向触发事件的元素,也就是`.box`元素。
// 3.数组.forEach/map/filter/find
let names = ['zhaimou', 'zhaione', 'zhaitwo']
names.forEach(function (item) {
console.log(item, this)
}, 'abc')
names.map(function (item) {
console.log(item, this)
}, 'cba')
//zhaimou [String: 'abc']
//zhaione [String: 'abc']
//zhaitwo [String: 'abc']
//zhaimou [String: 'cba']
//zhaione [String: 'cba']
//zhaitwo [String: 'cba']
隐式绑定:当函数作为对象的方法调用时,通过隐式绑定,this
绑定到调用该方法的对象。
javascript
// 案例一
function foo() {
console.log(this)
}
const obj = {
name: 'why',
foo: foo
}
obj.foo() // obj对象
// 案例二:
const obj = {
name: 'why',
eating: function () {
console.log(this.name + '在吃东西')
},
running: function () {
console.log(obj.name + '在跑步')
}
}
obj.eating() //why在吃东西
obj.running() //why在跑步
// 案例三:
const obj1 = {
name: 'obj1',
foo: function () {
console.log(this)
}
}
let obj2 = {
name: 'obj2',
bar: obj1.foo
}
obj2.bar() //{ name: 'obj2', bar: [Function: foo]}
// 案例4
function fn() {
console.log(this)
}
const obj = {
foo: fn
}
const cloneObj = {
foo1: obj
}
cloneObj.foo1.foo() // obj
一些面试题
js
// 立即执行函数
const obj = {
foo: function () {
(function () {
console.log(this)
})()
}
}
obj.foo()
// 另一种情况
const obj = {
foo: function () {
function fn() {
console.log(this)
}
return fn()
}
}
obj.foo()
// 当调用obj.foo()方法时,this指向obj对象。
// 但是当obj.foo()方法执行时,创建一个新函数执行,又用到fn(),所以最后又绑定到全局对象上
显式绑定:通过 call
、apply
或 bind
方法,可以显式地绑定函数的 this
指向一个特定的对象。
js
function foo() {
console.log(this)
}
foo.call('123') //[String: '123']
foo.apply('456') // [String: '456']
let newFoo = foo.bind('aaa')
newFoo() //// [String: 'aaa']
当传入null/undefined时, 自动将this绑定成全局对象
// foo.apply(null) window
// foo.apply(undefined) window
⭐⭐⭐
每次调用bind()
方法都会创建一个新的函数,并将原函数的this
绑定到指定的对象。而后续的bind()
方法调用只会改变新函数的this
绑定目标,而不会影响之前已经绑定过的对象,因此,最终的this
由第一次bind()
决定
js
function fn(){
console.log(this)
}
fn().bind('123').bind('1234')() //123
new绑定:通过使用 new
关键字调用构造函数创建一个新的对象实例时this
绑定到新创建的对象。
javascript
//案例1
function Person(name) {
this.name = name;
}
const person = new Person('Bob');
console.log(person.name); // 使用 new 创建对象实例,this 指向新创建的对象
//案例2
//注意构造函数的返回值 如果是返回的是基础变量就this就指向实例
function Foo() {
this.name = 'Foo'
const obj = {
name: 'obj'
}
return obj
}
const foo = new Foo()
console.log(foo.name) // obj
箭头函数:箭头函数的 this
不会被绑定到任何特定的对象上,而是继承自外部作用域。
javascript
const obj = {
name: 'Alice',
sayHello() {
const greet = () => {
console.log(`Hello, ${this.name}!`);
};
greet();
}
};
obj.sayHello(); // 在箭头函数内部,this 继承自外部作用域,指向 obj,打印 "Hello, Alice!"
//箭头函数不能通过`bind()`、`call()`和`apply()`来改变其上下文,
//因为它们没有自己的上下文。
优先级
new
绑定 > 显示绑定(apply/call/bind
) > 隐式绑定(obj.foo()
) > 默认绑定(独立函数调用
)
一些综合面试题
js
//面试题一
var name = "window";
var person = {
name: "person",
sayName: function () {
console.log(this.name);
}
};
function sayName() {
var sss = person.sayName;
sss(); // window: 独立函数调用
person.sayName(); // person: 隐式调用
(person.sayName)(); // person: 隐式调用
(b = person.sayName)(); // window: 赋值表达式(独立函数调用)
}
sayName();
//面试题二
var name = 'window'
var person1 = {
name: 'person1',
foo1: function () {
console.log(this.name)
},
foo2: () => console.log(this.name),
foo3: function () {
return function () {
console.log(this.name)
}
},
foo4: function () {
return () => {
console.log(this.name)
}
}
}
var person2 = { name: 'person2' }
person1.foo1(); // person1(隐式绑定)
person1.foo1.call(person2); // person2(显示绑定优先级大于隐式绑定)
person1.foo2(); // window(不绑定作用域,上层作用域是全局)
person1.foo2.call(person2); // window
person1.foo3()(); // window(独立函数调用)
person1.foo3.call(person2)(); // window(独立函数调用)
person1.foo3().call(person2); // person2(最终调用返回函数式, 使用的是显示绑定)
person1.foo4()(); // person1(箭头函数不绑定this, 上层作用域this是person1)
person1.foo4.call(person2)(); // person2(上层作用域被显示的绑定了一个person2)
person1.foo4().call(person2); // person1(上层找到person1)
//面试题三
var name = 'window'
function Person (name) {
this.name = name
this.foo1 = function () {
console.log(this.name)
},
this.foo2 = () => console.log(this.name),
this.foo3 = function () {
return function () {
console.log(this.name)
}
},
this.foo4 = function () {
return () => {
console.log(this.name)
}
}
}
var person1 = new Person('person1')
var person2 = new Person('person2')
person1.foo1() // person1
person1.foo1.call(person2) // person2(显示高于隐式绑定)
person1.foo2() // person1 (上层作用域中的this是person1)
person1.foo2.call(person2) // person1 (上层作用域中的this是person1)
person1.foo3()() // window(独立函数调用)
person1.foo3.call(person2)() // window
person1.foo3().call(person2) // person2
person1.foo4()() // person1
person1.foo4.call(person2)() // person2
person1.foo4().call(person2) // person1
//面试题四
var name = 'window'
function Person(name) {
this.name = name
this.obj = {
name: 'obj',
foo1: function () {
return function () {
console.log(this.name)
}
},
foo2: function () {
return () => {
console.log(this.name)
}
}
}
}
var person1 = new Person('person1')
var person2 = new Person('person2')
person1.obj.foo1()() // window
person1.obj.foo1.call(person2)() // window
person1.obj.foo1().call(person2) // person2
person1.obj.foo2()() // obj
person1.obj.foo2.call(person2)() // person2
person1.obj.foo2().call(person2) // obj
总结
this
的值是在函数实际执行时确定的,而不是定义时。它可能会因为函数的调用方式和执行上下文的变化而发生改变。
到这里就结束了,更多作为自己学习,希望对你有所帮助,有错欢迎指出