JavaScript中的this/call/apply/bind的使用及区别


Posted in Javascript onMarch 06, 2020

一、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)。

到此这篇关于JavaScript中的this/call/apply/bind的使用及区别的文章就介绍到这了,更多相关JavaScript this/call/apply/bind内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Javascript 相关文章推荐
jQuery编写widget的一些技巧分享
Oct 28 Javascript
jquery性能优化高级技巧
Aug 24 Javascript
jquery实现瀑布流效果 jquery下拉加载新数据
Dec 12 Javascript
js模态对话框使用方法详解
Feb 16 Javascript
Jquery中.bind()、.live()、.delegate()和.on()之间的区别详解
Aug 01 jQuery
JS实现图片上传多次上传同一张不生效的处理方法
Aug 06 Javascript
VUE在for循环里面根据内容值动态的加入class值的方法
Aug 12 Javascript
基于vue-cli npm run build之后vendor.js文件过大的解决方法
Sep 27 Javascript
用Fundebug插件记录网络请求异常的方法
Feb 21 Javascript
JS实现获取当前所在周的周六、周日示例分析
May 11 Javascript
JavaScript对象字面量和构造函数原理与用法详解
Apr 18 Javascript
详解Typescript里的This的使用方法
Jan 08 Javascript
JS实现网页端猜数字小游戏
Mar 06 #Javascript
extjs4图表绘制之折线图实现方法分析
Mar 06 #Javascript
JavaScript canvas基于数组生成柱状图代码实例
Mar 06 #Javascript
vue 使用 canvas 实现手写电子签名
Mar 06 #Javascript
Vue Router 实现动态路由和常见问题及解决方法
Mar 06 #Javascript
Vue基于iview实现登录密码的显示与隐藏功能
Mar 06 #Javascript
Vue状态模式实现窗口停靠功能(灵动、自由, 管理后台Admin界面)
Mar 06 #Javascript
You might like
PHP不用递归遍历目录下所有文件的代码
2014/07/04 PHP
PHP/HTML混写的四种方式总结
2017/02/27 PHP
php base64 编码与解码实例代码
2017/03/21 PHP
PHP使用ActiveMQ实现消息队列的方法详解
2019/05/31 PHP
JS 容错处理代码, 屏蔽错误信息
2021/03/09 Javascript
js读取本地excel文档数据的代码
2010/11/11 Javascript
jQuery实用技巧必备(上)
2015/11/02 Javascript
D3.js实现文本的换行详解
2016/10/14 Javascript
微信小程序 picker-view 组件详解及简单实例
2017/01/10 Javascript
JavaScript严格模式详解
2017/01/16 Javascript
vue router动态路由下让每个子路由都是独立组件的解决方案
2018/04/24 Javascript
JavaScript实现一个简易的计算器实例代码
2018/05/10 Javascript
Layui Table js 模拟选中checkbox的例子
2019/09/03 Javascript
vue动态绘制四分之三圆环图效果
2019/09/03 Javascript
ligerUI的ligerDialog关闭刷新的方法
2019/09/27 Javascript
Angular封装表单控件及思想总结
2019/12/11 Javascript
JS中准确判断变量类型的方法
2020/06/01 Javascript
electron踩坑之dialog中的callback解决
2020/10/06 Javascript
javascript实现点击小图显示大图
2020/11/29 Javascript
使用python检测手机QQ在线状态的脚本代码
2013/02/10 Python
简单介绍Python2.x版本中的cmp()方法的使用
2015/05/20 Python
python中使用iterrows()对dataframe进行遍历的实例
2018/06/09 Python
python 字典修改键(key)的几种方法
2018/08/10 Python
人工神经网络算法知识点总结
2019/06/11 Python
python实现二级登陆菜单及安装过程
2019/06/21 Python
python GUI库图形界面开发之PyQt5 Qt Designer工具(Qt设计师)详细使用方法及Designer ui文件转py文件方法
2020/02/26 Python
Python爬虫爬取博客实现可视化过程解析
2020/06/29 Python
阿提哈德航空官方网站:Etihad Airways
2017/01/06 全球购物
公积金转移接收函
2014/01/11 职场文书
食品工程专业求职信
2014/06/15 职场文书
中职毕业生自我鉴定范文(3篇)
2014/09/28 职场文书
幼儿园感恩节活动方案2014
2014/10/11 职场文书
中国世界遗产导游词
2015/02/13 职场文书
同事离别感言
2015/08/04 职场文书
JavaScript小技巧带你提升你的代码技能
2021/09/15 Javascript
MySQL笔记 —SQL运算符
2022/01/18 MySQL