深入解析JavaScript中this指向及apply、call、bind的区别
本文详细解析ES5中this指向的核心原理:始终指向最后调用它的对象。通过多个代码示例展示this在不同场景下的表现,并深入对比apply、call、bind三个改变this指向的方法。同时介绍ES6箭头函数对this绑定的影响,以及通过变量保存this值的传统技巧,帮助开发者彻底理清函数执行上下文与参数传递机制。
JavaScript this指向与函数绑定详解
这是面试中常遇到的经典问题,也是ES5规范中容易引发错误的陷阱之一。尽管ES6在很大程度上改善了this带来的问题,但为了兼容旧代码,理解this的指向机制以及apply、call、bind三者的区别依然至关重要。
ES5中this指向的核心原理
this 的指向规则
在ES5环境下,this的指向遵循一个核心原则:它永远指向最后调用该函数的对象。请牢记这一点:this永远指向最后调用它的那个对象。掌握这句话,你就已经理解了this的一半逻辑。
接下来通过一个最简单的示例进行说明:
例 1:
var name = "windowsName";
function a() {
var name = "Cherry";
console.log(this.name); // windowsName
console.log("inner:" + this); // inner: Window
}
a();
console.log("outer:" + this) // outer: Window
上述代码输出windowsName的原因在于“this永远指向最后调用它的那个对象”。当执行a()时,由于没有显式的调用对象,因此默认指向全局对象window,相当于执行window.a()。需要注意的是,这里未使用严格模式;若启用严格模式,全局对象为undefined,直接访问this.name会抛出TypeError错误。
观察以下示例:
例 2:
var name = "windowsName";
var a = {
name: "Cherry",
fn : function () {
console.log(this.name); // Cherry
}
}
a.fn();
在此例中,函数fn由对象a调用,因此打印出的this.name即为对象a中的name属性值。
我们对代码稍作调整:
例 3:
var name = "windowsName";
var a = {
name: "Cherry",
fn : function () {
console.log(this.name); // Cherry
}
}
window.a.fn();
输出Cherry的原因依然遵循“this永远指向最后调用它的那个对象”这一原则,因为最终调用fn的对象仍然是a。
再看一个示例:
例 4:
var name = "windowsName";
var a = {
// name: "Cherry",
fn : function () {
console.log(this.name); // undefined
}
}
window.a.fn();
此处输出undefined,是因为调用fn的是对象a,即fn内部的this指向a。由于对象a中并未定义name属性,因此this.name的值为undefined。
该示例再次印证了this的指向规则:因为最后调用fn的是a,即使a中不存在name属性,也不会向上层对象查找,而是直接返回undefined。
接下来看一个较为复杂的案例:
例 5:
var name = "windowsName";
var a = {
name : null,
// name: "Cherry",
fn : function () {
console.log(this.name); // windowsName
}
}
var f = a.fn;
f();
这里输出windowsName而非Cherry。虽然将a对象的fn方法赋值给了变量f,但并未立即调用。根据“this永远指向最后调用它的那个对象”,由于f未被调用,fn()最终仍由window调用,因此this指向window。
通过上述五个例子可以看出,this的指向在函数创建时并不确定,在ES5中,它始终由最后调用该函数的对象决定。
继续观察以下示例:
例 6:
var name = "windowsName";
function fn() {
var name = 'Cherry';
innerFunction();
function innerFunction() {
console.log(this.name); // windowsName
}
}
fn()
经过前面的分析,现在应该能理解其输出结果了。
改变this指向的四种方法
如何改变 this 的指向
总结改变this指向的方法主要有以下几种:
使用ES6的箭头函数
在函数内部使用_this = this
使用apply、call、bind
通过new实例化对象
例 7:
var name = "windowsName";
var a = {
name : "Cherry",
func1: function () {
console.log(this.name)
},
func2: function () {
setTimeout( function () {
this.func1()
},100);
}
};
a.func2() // this.func1 is not a function
若不采用箭头函数,代码会报错。因为最后调用setTimeout的是window,而window中并不存在func1函数。
我们将在“改变this指向”章节中,以此例作为演示进行改造。
ES6箭头函数的this绑定规则
箭头函数
ES6的箭头函数可以有效避免ES5中this指向的问题。箭头函数的this始终绑定为函数定义时的this,而非执行时的this。需记住:箭头函数内部没有this绑定,其值通过作用域链查找决定。若箭头函数被非箭头函数包含,则this绑定最近一层非箭头函数的this;否则,this为undefined。
例 8 :
var name = "windowsName";
var a = {
name : "Cherry",
func1: function () {
console.log(this.name)
},
func2: function () {
setTimeout( () => {
this.func1()
},100);
}
};
a.func2() // Cherry
保存this引用的传统技巧
在函数内部使用 _this = this
如果不使用ES6,保存this引用是最简单的防错方式。先将调用该函数的对象保存在变量_this中,然后在函数内部统一使用_this,这样_this的值就不会随调用上下文改变。
例 9:
var name = "windowsName";
var a = {
name : "Cherry",
func1: function () {
console.log(this.name)
},
func2: function () {
var _this = this;
setTimeout( function() {
_this.func1()
},100);
}
};
a.func2() // Cherry
在func2中,首先定义var _this = this,此时this指向调用func2的对象a。为了防止setTimeout内部this指向window导致错误,我们将指向a的this赋值给变量_this。这样在func2内部使用_this时,始终指向对象a。
apply、call、bind的使用与区别
使用 apply、call、bind
利用apply、call、bind函数也可以改变this的指向,具体原理稍后讲解,先展示实现方式:
使用 apply
例 10:
var a = {
name : "Cherry",
func1: function () {
console.log(this.name)
},
func2: function () {
setTimeout( function () {
this.func1()
}.apply(a),100);
}
};
a.func2() // Cherry
使用 call
例 11:
var a = {
name : "Cherry",
func1: function () {
console.log(this.name)
},
func2: function () {
setTimeout( function () {
this.func1()
}.call(a),100);
}
};
a.func2() // Cherry
使用 bind
例 12:
var a = {
name : "Cherry",
func1: function () {
console.log(this.name)
},
func2: function () {
setTimeout( function () {
this.func1()
}.bind(a)(),100);
}
};
a.func2() // Cherry
apply与call的参数传递差异
apply、call、bind 区别
前文已介绍apply、call、bind均可改变this指向,但三者存在细微差异。
根据MDN文档,apply定义如下:
apply() 方法调用一个函数, 其具有一个指定的this值,以及作为一个数组(或类似数组的对象)提供的参数
语法:
fun.apply(thisArg, [argsArray])
thisArg:在fun函数运行时指定的this值。需注意,指定的this值不一定是函数执行时真正的this值。若处于非严格模式,指定为null或undefined时会自动指向全局对象(浏览器中为window对象);若值为原始值(数字、字符串、布尔值),this将指向该原始值的自动包装对象。
argsArray:一个数组或者类数组对象,其中的数组元素将作为单独的参数传给fun函数。如果该参数的值为null或undefined,则表示不需要传入任何参数。从ECMAScript 5开始可以使用类数组对象。浏览器兼容性请参阅本文底部内容。
apply 和 call 的区别
apply和call基本类似,主要区别在于传入参数的形式不同。
call的语法为:
fun.call(thisArg[, arg1[, arg2[, ...]]])
因此,apply和call的区别在于:call方法接受若干个参数列表,而apply接收一个包含多个参数的数组。
例 13:
var a ={
name : "Cherry",
fn : function (a,b) {
console.log( a + b)
}
}
var b = a.fn;
b.apply(a,[1,2]) // 3
例 14:
var a ={
name : "Cherry",
fn : function (a,b) {
console.log( a + b)
}
}
var b = a.fn;
b.call(a,1,2) // 3
bind方法返回新函数的特性
bind 和 apply、call 区别
我们先尝试使用bind改写之前的例子:
var a ={
name : "Cherry",
fn : function (a,b) {
console.log( a + b)
}
}
var b = a.fn;
b.bind(a,1,2)
此时没有输出,原因在于MDN文档说明:
bind()方法创建一个新的函数, 当被调用时,将其this关键字设置为提供的值,在调用新函数时,在任何提供之前提供一个给定的参数序列。
由此可见,bind返回的是一个新函数,必须手动调用:
var a ={
name : "Cherry",
fn : function (a,b) {
console.log( a + b)
}
}
var b = a.fn;
b.bind(a,1,2)() // 3
总结
以上介绍了JavaScript中this、apply、call、bind的核心概念与用法,希望有所帮助。如有任何疑问,欢迎留言讨论。感谢大家对本站的支持!
5款主流JS在线运行工具对比:CodePen、JSFiddle与StackBlitz怎么选
2026-11-05 17:04:04
JavaScript入门书籍避坑:这些书不适合初学者
2026-11-05 16:31:15
JS使用Math.round实现保留两位小数的原理与代码
2026-11-05 15:35:55
JavaScript 原生 Date 对象怎么实现日期加减
2026-11-05 14:30:00
JavaScript函数定义的三种方式:声明、表达式与箭头函数
2026-11-05 13:50:55
