JavaScript this指向与call/apply/bind用法详解
本文深入解析JavaScript中this关键字的动态指向规则,涵盖全局、对象方法、构造函数及箭头函数四种场景。详细对比call、apply和bind三个方法的语法差异与参数传递方式,通过实例演示如何改变函数执行上下文及实现属性继承。
JavaScript this关键字基础
一、this
1.什么是this
this关键字在多数编程语言中都是核心概念,JavaScript同样如此。其含义丰富且复杂,深入理解this机制是掌握JavaScript及面向对象编程的关键基础。
2.this 代表什么
this指向函数(或方法)执行时的上下文环境。正如理解文章需要结合上下文以厘清关系,理解this也需要明确其运行的上下文。
在JavaScript中,this并非固定不变,而是随执行环境的改变而动态变化。
1.在方法中,this 表示该方法所属的对象。
2.如果单独使用,this 表示全局对象。
3.在函数中,this 表示全局对象。
4.在函数中,在严格模式下,this 是未定义的(undefined)。
5.在事件中,this 表示接收事件的元素。
6.类似 call() 和 apply() 方法可以将 this 引用到任何对象。
3.绑定 this 的方法
this的动态切换赋予了JavaScript极大的灵活性,但也增加了编程的复杂性与模糊性。为避免意外情况,有时需要将this固定。JavaScript提供了call、apply和bind三个方法,用于切换或固定this的指向。
this指向的四种场景
4.this的指向
1.在一般函数方法中使用 this 指代全局对象
function test(){
this.x = 1; //这里this就是window
console.log(this.x);
}
test(); // 1
JS规定,函数中的this在调用时确定,指向函数当前运行的环境。
2.作为对象方法调用,this 指代上级对象
var x =3;
function test(){
alert(this.x);
}
var o = {
x:1,
m:test
};
o.m(); // 1
当函数作为对象的方法被调用时,方法内部的this指向该对象。
3.作为构造函数调用,this 指代new 出的对象
function test(){
console.log(this);
}
var o = new test();
test();
//可以看出o代表的不是全局对象
new关键词的作用是调用某个函数并拿到其中的返回值,只是调用过程稍特殊。在上面的代码实例中。test函数被new关键词调用时,内部依次执行了以下步骤:
(1)创建一个空对象。
(2)将这个空对象的原型,指向这个构造函数的prototype。
(3)将空对象的值赋给函数内部的this(this就是个空对象了)。
(4)执行函数体代码,为this这个对象绑定键值对。
(5)返回this,将其作为new关键词调用oop函数的返回值。
所以构造函数中的this,依旧是在构造函数被new关键词调用时确定其指向,指向的是当前被实例化的那个对象。
4.箭头函数中的this
箭头函数是ES6的新特性,最重要的特点是它会捕获其所在上下文的this作为自己的this,或者说,箭头函数本身并没有this,它会沿用外部环境的this。也就是说,箭头函数内部与其外部的this是保持一致的。
this.a=20
var test={
a:40,
init:()=>{
console.log(this.a)
function go(){
this.a=60
console.log(this.a)
}
go.prototype.a=50
return go
}
}
var p=test.init()
p()
new (test.init())()
//输出 20 60 60 60
使用call、apply和bind改变this
5.改变指向
this的动态切换,固然为 JavaScript 创造了巨大的灵活性,但也使得编程变得困难和模糊。有时,需要把this固定下来,避免出现意想不到的情况。JavaScript 提供了call、apply、bind这三个方法,来切换/固定this的指向。
bind方法和apply、call稍有不同,bind方法返回一个新函数,以后调用了才会执行,但apply、call会立即执行。
bind方法的详细用法
二、Function.prototype.bind()
bind()方法主要就是将函数绑定到某个对象,bind()会创建一个函数,函数体内的this对象的值会被绑定到传入bind()中的第一个参数的值,例如:f.bind(obj),实际上可以理解为obj.f(),这时f函数体内的this自然指向的是obj;
示例:
function f(y, z){
return this.x + y + z;
}
var m = f.bind({x : 1}, 2);
console.log(m(3));
//6
这里bind方法会把它的第一个实参绑定给f函数体内的this,所以这里的this即指向{x : 1}对象,从第二个参数起,会依次传递给原始函数,这里的第二个参数2,即是f函数的y参数,最后调用m(3)的时候,这里的3便是最后一个参数z了,所以执行结果为1 + 2 + 3 = 6分步处理参数的过程其实是一个典型的函数柯里化的过程(Curry)。
call与apply的语法及区别
三、call/apply
1.定义
每个函数都包含两个非继承而来的方法:call()方法和apply()方法。
call和apply可以用来重新定义函数的执行环境,也就是this的指向;call和apply都是为了改变某个函数运行时的context,即上下文而存在的,换句话说,就是为了改变函数体内部this的指向。
2.语法
call()
调用一个对象的方法,用另一个对象替换当前对象,可以继承另外一个对象的属性,它的语法是:
Function.call(obj[, param1[, param2[, [,...paramN]]]]);
- obj:这个对象将代替Function类里this对象
- params:一串参数列表
说明:call方法可以用来代替另一个对象调用一个方法,call方法可以将一个函数的对象上下文从初始的上下文改变为obj指定的新对象,如果没有提供obj参数,那么Global对象被用于obj。
apply()
和call()方法一样,只是参数列表不同,语法:
Function.apply(obj[, argArray]);
- obj:这个对象将代替Function类里this对象
- argArray:这个是数组,它将作为参数传给Function
说明:如果argArray不是一个有效数组或不是arguments对象,那么将导致一个TypeError,如果没有提供argArray和obj任何一个参数,那么Global对象将用作obj。
3.异同
相同点
call()和apply()方法的相同点就是这两个方法的作用是一样的。都是在特定的作用域中调用函数,等于设置函数体内this对象的值,以扩充函数赖以运行的作用域。
一般来说,this总是指向调用某个方法的对象,但是使用call()和apply()方法时,就会改变this的指向,看个例子:
function add(a, b) {
return a + b;
}
function sub(a, b) {
return a - b;
}
console.log(add.call(sub, 2, 1));//3
为什么add.call(sub, 2, 1)的执行结果是3呢,因为call()方法改变了this的指向,使得sub可以调用add的方法,也就是用sub去执行add中的内容,再来看一个例子:
function People(name, age) {
this.name = name;
this.age = age;
}
function Student(name, age, grade) {
People.call(this, name, age);
this.grade = grade;
}
var student = new Student('小明', 21, '大三');
console.log(student.name + student.age + student.grade);//小明21大三
在这个例子中,我们并没有给Student的name和age赋值,但是存在这两个属性的值,这还是要归功于call()方法,它可以改变this的指向。
在这个例子里,People.call(this, name, age);中的this代表的是Student,这也就是之前说的,使得Student可以调用People中的方法,因为People中有this.name = name;等语句,这样就将name和age属性创建到了Student中。
总结一句话就是call()可以让括号里的对象来继承括号外函数的属性。
至于apply()方法作用也和call()方法一样,可以这么写:
People.apply(this, [name, age]);
或者这么写:
People.apply(this, arguments);
在这里arguments和[name, age]是等价的。
不同点
从定义中也可以看出来,call()和apply()的不同点就是接收参数的方式不同。
1.apply()方法接收两个参数,一个是函数运行的作用域(this),另一个是参数数组。 2.call()方法不一定接受两个参数,第一个参数也是函数运行的作用域(this),但是传递给函数的参数必须列举出来。
在给对象参数的情况下,如果参数的形式是数组的时候,比如之前apply()方法示例里面传递了参数arguments,这个参数是数组类型,并且在调用Person的时候参数的列表是对应一致的(也就是Person和Student的参数列表前两位是一致的)就可以采用apply()方法。
但是如果Person的参数列表是这样的(age,name),而Student的参数列表是(name,age,grade),这样就可以用call()方法来实现了,也就是直接指定参数列表对应值的位置Person.call(this,age,name)。
到此这篇关于JavaScript中的this/call/apply/bind的使用及区别的文章就介绍到这了,更多相关JavaScript this/call/apply/bind内容请搜索本站以前的文章或继续浏览下面的相关文章希望大家以后多多支持本站!
JS动态构建复杂嵌套JSON对象的方法
2026-11-05 15:41:52
JS负数取余为什么是负数?详解余数与模运算区别
2026-11-05 13:34:41
JS 解压出现乱码或报错怎么解决?
2026-11-05 12:32:38
JS 中 startsWith、indexOf 与正则判断字符串前缀的对比
2026-11-04 22:19:43
JS解析Excel后怎么将数据转换为JSON格式
2026-11-04 20:58:28
