JavaScript中的this/call/apply/bind的使用及区别
作者:刘亦枫 发布时间:2023-09-15 06:23:19
一、this
1.什么是this
this 关键字在大部分语言中都是一个重要的存在,JS中自然不例外,其表达的意义丰富多样甚至有些复杂,深刻理解this是学习JS、面向对象编程非常重要的一环。
2.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的指向。
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
5.改变指向
this的动态切换,固然为 JavaScript 创造了巨大的灵活性,但也使得编程变得困难和模糊。有时,需要把this固定下来,避免出现意想不到的情况。JavaScript 提供了call、apply、bind这三个方法,来切换/固定this的指向。
bind方法和apply、call稍有不同,bind方法返回一个新函数,以后调用了才会执行,但apply、call会立即执行。
二、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
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)。
来源:https://blog.csdn.net/liuyifeng0000/article/details/104563241


猜你喜欢
- 1.算法:设有一组关键字{ K 1 , K 2 ,…, K n };排序开始就认为 K 1 是一个有序序列;让 K 2 插入上述表长为 1
- 内容摘要:为什么要什么XML文件:其优势就是处理该XML数据的文档可以是静态文档,比如HTML文件通过Javascript、XMLDOM来解
- 什么是计算属性???1、在computed中,可以定义一些属性,这些属性叫做【计算属性】2、计算属性的本质是一个方法,不过一般是将他们的名称
- 以下所有例子中结构定义如下:type User struct { Id_ bson.ObjectId `bson:
- 目录1. 画布(canvas) 1.1 设置画布大小2. 画笔 2.1 画笔的状态 2.2 画笔的属性 2.3 绘图命令3. 命令详
- 方法一、线程池执行的循环代码为自己写的情况定义一个全局变量,默认为T,当QT界面关闭后,将该变量值改为F。线程执行的循环代码内增加一个判断方
- 1、Mycat 应用场景Mycat 发展到现在,适用的场景已经很丰富,而且不断有新用户给出新的创新性的方案,以下是几个典型的应用场景:1.
- 问题你想使用一个简单的REST接口通过网络远程控制或访问你的应用程序,但是你又不想自己去安装一个完整的web框架。解决方案构建一个REST风
- 记得在网上Down的很多源码用的最多的一种方式就是:var ie = document.all();这是因为IE浏览器的document下有
- 一.GUI(Graphical User Interface(图形用户接口))1.导入需要用到的包import tkinter as tki
- python中判断文件结束符的方法:可以使用try except语句块来进行判断。具体使用方法:【try:while True:s = in
- 1. 将下载好的字体放到本地目录分别是两种字体放到项目的 assets 目录中2. 引入字体文件首先创建一个 styles 文件
- alt的准确含义是,当照片不存在或者load错误时的提示。但同时img也同时支持alt和title,再有某些浏览器的错误解析,因此经常被误导
- <html> <head> <title>JavaScript Data Access Test<
- 以前我在mysql中分页都是用的 limit 100000,20这样的方式,我相信你也是吧,但是要提高效率,让分页的代码效率更高一些,更快一
- 本文实例讲述了Python微信企业号开发之回调模式接收微信端客户端发送消息及被动返回消息。分享给大家供大家参考,具体如下:说明:此代码用于接
- SQL是用于访问ORACLE数据库的语言,PL/SQL扩展和加强了SQL的功能,它 同时引入了更强的程序逻辑。 PL/SQL支持DML命令和
- windows系统中文件名不能包含 \ / : * ? " < > |想要创建必须过滤掉这些字符def setFile
- INSTR的第三个参数为1时,实现的是indexOf功能。 INSTR的第三个参数为-1时,实现的是lastIndexOf功能。 例如: I
- 在我上一篇文章,我搭了一个框架,模拟了Flask网站上“@app.route(‘/')”第一条例子的行为。如果你错过了那篇“这不是魔