总结:
函数内部的this指向取决于函数的调用方式,而不是它被定义的位置。当函数被独立调用时,在非严格模式下,this指向全局对象。在严格模式下,独立调用的函数中的this是undefined。
因此,当你看到函数内部的this指向全局对象时,通常是因为这个函数被独立调用了。要改变这种行为,可以使用箭头函数、显式绑定或闭包(保存this)等方法
核心原因:调用方式决定 this
关键点 :this 的指向不取决于函数在哪里定义,而取决于函数如何被调用。
1. 独立函数调用(最常见的陷阱)
javascript
javascript
var name = 'Global Name';
function showThis() {
console.log(this); // window
console.log(this.name); // 'Global Name'
}
// 独立调用 - this 指向 window
showThis();
// 等同于:
window.showThis(); // 浏览器环境下实际上是这样
为什么?
- 当函数被独立调用时(前面没有
对象.),JavaScript 使用默认绑定规则 - 在非严格模式下,默认绑定会将
this指向全局对象
2. 嵌套函数中的陷阱
javascript
javascript
var name = 'Global';
var obj = {
name: 'Object',
outer: function() {
console.log('outer this:', this.name); // 'Object'
function inner() {
console.log('inner this:', this.name); // 'Global' - 为什么??
}
inner(); // 独立调用!
}
};
obj.outer(); // outer 作为方法调用,inner 作为独立函数调用
解析:
obj.outer()→outer的this指向objinner()→ 独立调用,inner的this指向window
3. 回调函数中的陷阱
javascript
javascript
var name = 'Global';
var obj = {
name: 'Object',
handleClick: function() {
console.log('handleClick this:', this.name); // 'Object'
},
setupEvent: function() {
console.log('setupEvent this:', this.name); // 'Object'
// 回调函数 - 独立调用!
setTimeout(function() {
console.log('setTimeout this:', this.name); // 'Global'
}, 100);
}
};
obj.setupEvent();
解析:
obj.setupEvent()→this指向objsetTimeout的回调函数被独立调用 ,this指向window
为什么会这样设计?
历史原因和设计哲学
- 早期设计决策:JavaScript 最初设计时,函数被认为是"独立"的实体
- 动态语言特性:JavaScript 是动态语言,函数可以很容易地在不同上下文中重用
- 灵活性:这种设计让函数可以在不同对象间共享
javascript
javascript
// 同一个函数可以在不同对象上使用
function introduce() {
return `Hello, I'm ${this.name}`;
}
var person = { name: 'Alice' };
var company = { name: 'Tech Corp' };
// 显式绑定
console.log(introduce.call(person)); // "Hello, I'm Alice"
console.log(introduce.call(company)); // "Hello, I'm Tech Corp"
如何解决这个问题?
方法1:使用箭头函数(推荐)
箭头函数没有自己的 this,会继承外层作用域的 this
javascript
javascript
var obj = {
name: 'Object',
outer: function() {
console.log('outer this:', this.name); // 'Object'
const inner = () => {
console.log('inner this:', this.name); // 'Object' - 继承 outer 的 this
};
inner();
}
};
obj.outer();
方法2:保存 this 引用(传统方式)
javascript
javascript
var obj = {
name: 'Object',
outer: function() {
var self = this; // 保存 this 的引用
console.log('outer this:', this.name); // 'Object'
function inner() {
console.log('inner self:', self.name); // 'Object' - 使用保存的引用
}
inner();
}
};
obj.outer();
方法3:使用 bind()
javascript
javascript
var obj = {
name: 'Object',
outer: function() {
console.log('outer this:', this.name); // 'Object'
const inner = function() {
console.log('inner this:', this.name); // 'Object'
}.bind(this); // 绑定 outer 的 this
inner();
}
};
obj.outer();
严格模式的影响
在严格模式下,独立调用的函数中 this 是 undefined,而不是 window
javascript
javascript
'use strict';
var name = 'Global';
function showThis() {
console.log(this); // undefined
console.log(this?.name); // TypeError: Cannot read property 'name' of undefined
}
showThis(); // 严格模式下,独立调用的 this 是 undefined
总结
| 场景 | this 指向 |
原因 |
|---|---|---|
func() |
window |
独立调用,默认绑定 |
obj.method() |
obj |
方法调用,隐式绑定 |
嵌套函数 inner() |
window |
独立调用,不是方法调用 |
| 回调函数 | window |
被事件循环独立调用 |
| 箭头函数 | 外层 this |
词法作用域,继承父级 |
核心要点:
- 调用方式决定
this,不是定义位置 - 独立调用的函数 ,
this指向全局对象 - 使用箭头函数 或保存
this引用来避免这个问题 - 严格模式 下独立调用的
this是undefined
理解这个机制后,你就能预测和控制 this 的行为了!
JavaScript this 指向练习题
以下是一系列关于 this 指向的练习题,涵盖了各种常见场景。请先尝试自己解答,然后再查看解析。
基础题目
题目 1
javascript
ini
var name = 'Global';
function sayName() {
console.log(this.name);
}
sayName();
题目 2
javascript
javascript
var obj = {
name: 'Object',
sayName: function() {
console.log(this.name);
}
};
obj.sayName();
题目 3
javascript
ini
var name = 'Global';
var obj = {
name: 'Object',
sayName: function() {
console.log(this.name);
}
};
var fn = obj.sayName;
fn();
题目 4
javascript
ini
var obj1 = {
name: 'Object1',
sayName: function() {
console.log(this.name);
}
};
var obj2 = {
name: 'Object2'
};
obj2.sayName = obj1.sayName;
obj2.sayName();
嵌套函数题目
题目 5
javascript
javascript
var name = 'Global';
var obj = {
name: 'Object',
outer: function() {
console.log('outer:', this.name);
function inner() {
console.log('inner:', this.name);
}
inner();
}
};
obj.outer();
题目 6
javascript
ini
var name = 'Global';
var obj = {
name: 'Object',
outer: function() {
console.log('outer:', this.name);
const inner = () => {
console.log('inner:', this.name);
};
inner();
}
};
obj.outer();
回调函数题目
题目 7
javascript
javascript
var name = 'Global';
var obj = {
name: 'Object',
handleClick: function() {
setTimeout(function() {
console.log(this.name);
}, 100);
}
};
obj.handleClick();
题目 8
javascript
ini
var name = 'Global';
var obj = {
name: 'Object',
handleClick: function() {
setTimeout(() => {
console.log(this.name);
}, 100);
}
};
obj.handleClick();
构造函数题目
题目 9
javascript
ini
function Person(name) {
this.name = name;
console.log(this.name);
}
var person = new Person('Alice');
题目 10
javascript
javascript
function Person(name) {
this.name = name;
setTimeout(function() {
console.log('Timeout:', this.name);
}, 100);
}
var person = new Person('Alice');
显式绑定题目
题目 11
javascript
ini
var obj1 = {
name: 'Object1'
};
var obj2 = {
name: 'Object2'
};
function sayName() {
console.log(this.name);
}
sayName.call(obj1);
sayName.call(obj2);
题目 12
javascript
javascript
var obj = {
name: 'Object',
sayName: function(greeting) {
console.log(greeting + ', ' + this.name);
}
};
var boundFn = obj.sayName.bind({ name: 'Bound' });
boundFn('Hello');
综合题目
题目 13
javascript
javascript
var name = 'Global';
var obj = {
name: 'Object',
method: function() {
console.log('1:', this.name);
const arrow = () => {
console.log('2:', this.name);
};
function regular() {
console.log('3:', this.name);
}
arrow();
regular();
setTimeout(() => {
console.log('4:', this.name);
}, 10);
setTimeout(function() {
console.log('5:', this.name);
}, 10);
}
};
obj.method();
题目 14
javascript
ini
var length = 10;
function fn() {
console.log(this.length);
}
var obj = {
length: 5,
method: function(fn) {
fn();
arguments[0]();
}
};
obj.method(fn, 1);
题目 15
javascript
javascript
var name = 'Global';
function Person(name) {
this.name = name;
this.sayName = function() {
console.log('1:', this.name);
};
this.sayNameArrow = () => {
console.log('2:', this.name);
};
}
Person.prototype.delayedSayName = function() {
setTimeout(function() {
console.log('3:', this.name);
}, 10);
};
Person.prototype.delayedSayNameArrow = function() {
setTimeout(() => {
console.log('4:', this.name);
}, 10);
};
var person = new Person('Alice');
person.sayName();
person.sayNameArrow();
person.delayedSayName();
person.delayedSayNameArrow();
做完再去问ai吧加油!!!!!