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切换显示的效果实例代码
Feb 27 Javascript
js快速排序的实现代码
Dec 08 Javascript
javascript 处理null及null值示例
Jun 09 Javascript
javascript封装简单实现方法
Aug 11 Javascript
require.js 加载 vue组件 r.js 合并压缩的实例
Oct 14 Javascript
vue的props实现子组件随父组件一起变化
Oct 27 Javascript
angular实现图片懒加载实例代码
Jun 08 Javascript
Angularjs自定义指令实现分页插件(DEMO)
Sep 16 Javascript
详解如何在你的Vue项目配置vux
Jun 04 Javascript
jQuery中each遍历的三种方法实例分析
Sep 07 jQuery
深入理解vue中的slot与slot-scope
Apr 22 Javascript
原生javascript制作贪吃蛇小游戏的方法分析
Feb 26 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与XML、XSLT、Mysql的结合运用实现代码
2009/11/19 PHP
Joomla下利用configuration.php存储简单数据
2010/05/19 PHP
input file获得文件根目录简单实现
2013/04/26 PHP
php实现与erlang的二进制通讯实例解析
2014/07/23 PHP
php处理多图上传压缩代码功能
2018/06/13 PHP
asp.net+js 实现无刷新上传解析csv文件的代码
2010/05/17 Javascript
js中widow.open()方法使用详解
2013/07/30 Javascript
JS字符串拼接在ie中都报错的解决方法
2014/03/27 Javascript
Express作者TJ告别Node.js奔向Go
2014/07/14 Javascript
jQuery中clearQueue()方法用法实例
2014/12/29 Javascript
javascript中hasOwnProperty() 方法使用指南
2015/03/09 Javascript
浅谈jQuery中hide和fadeOut的区别 show和fadeIn的区别
2016/08/18 Javascript
jQuery Easyui datagrid editor为combobox时指定数据源实例
2016/12/19 Javascript
JavaScript实现三级联动菜单实例代码
2017/06/26 Javascript
React-Native中props具体使用详解
2017/09/04 Javascript
layer.open关闭父窗口 以及调用父页面的方法
2018/08/17 Javascript
bootstrap-table实现表头固定以及列固定的方法示例
2019/03/07 Javascript
微信小程序实现pdf、word等格式文件上传的方法
2019/09/10 Javascript
原生js实现随机点名功能
2019/11/05 Javascript
[03:46]DAC趣味视频-中文考试.mp4
2017/04/02 DOTA
按日期打印Python的Tornado框架中的日志的方法
2015/05/02 Python
python创建列表并给列表赋初始值的方法
2015/07/28 Python
Python制作简单的网页爬虫
2015/11/22 Python
Python实现接受任意个数参数的函数方法
2018/04/21 Python
Python3的高阶函数map,reduce,filter的示例详解
2019/07/23 Python
解决Python3下map函数的显示问题
2019/12/04 Python
Python如何操作office实现自动化及win32com.client的运用
2020/04/01 Python
收藏!10个免费高清视频素材网站!【设计、视频剪辑必备】
2021/03/18 杂记
css3绘制天猫logo实现代码
2012/11/06 HTML / CSS
Fabletics官网:美国运动服饰品牌,由好莱坞女演员凯特·哈德森创立
2019/10/19 全球购物
5.12护士节演讲稿
2014/04/30 职场文书
物流管理系毕业生求职信
2014/06/03 职场文书
应急管理工作总结2015
2015/05/04 职场文书
Pytorch distributed 多卡并行载入模型操作
2021/06/05 Python
Mybatis-Plus进阶分页与乐观锁插件及通用枚举和多数据源详解
2022/03/21 Java/Android
Windows Server 2016 配置 IIS 的详细步骤
2022/04/28 Servers