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 相关文章推荐
js实现的跟随鼠标移动的时钟效果(中英文日期显示)
Jan 17 Javascript
javascript使用location.search的示例
Nov 05 Javascript
使用jQuery实现的掷色子游戏动画效果
Mar 14 Javascript
JavaScript中函数(Function)的apply与call理解
Jul 08 Javascript
基于javascript实现右下角浮动广告效果
Jan 08 Javascript
JavaScript中利用构造器函数模拟类的方法
Feb 16 Javascript
ES6 javascript中class静态方法、属性与实例属性用法示例
Oct 30 Javascript
vue-devtools的安装步骤
Apr 23 Javascript
jQuery实现合并表格单元格中相同行操作示例
Jan 28 jQuery
jQuery实现的记住帐号密码功能完整示例
Aug 03 jQuery
Vue组件跨层级获取组件操作
Jul 27 Javascript
vue自定义插件封装,实现简易的elementUi的Message和MessageBox的示例
Nov 20 Vue.js
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
留言板翻页的实现详解
2006/10/09 PHP
说说PHP的autoLoad自动加载机制
2012/09/27 PHP
PHP结合JQueryJcrop实现图片裁切实例详解
2014/07/24 PHP
php比较两个字符串长度的方法
2015/07/13 PHP
php中使用websocket详解
2016/09/23 PHP
PHP数据库操作四:mongodb用法分析
2017/08/16 PHP
实现JavaScript中继承的三种方式
2009/10/16 Javascript
javascript中数组的sort()方法的使用介绍
2013/12/18 Javascript
jquery实现页面图片等比例放大缩小功能
2014/02/12 Javascript
在css加载完毕后自动判断页面是否加入css或js文件
2014/09/10 Javascript
chrome不支持form.submit的解决方案
2015/04/28 Javascript
jQuery实现图片与文字描述左右滑动自动切换的方法
2015/07/27 Javascript
使用JQuery实现智能表单验证功能
2016/03/08 Javascript
js基础之事件捕获与冒泡原理
2019/10/09 Javascript
jQuery实现鼠标放置名字上显示详细内容气泡提示框效果的方法分析
2020/04/04 jQuery
vue.js实现简单购物车功能
2020/05/30 Javascript
python查找第k小元素代码分享
2013/12/18 Python
Python version 2.7 required, which was not found in the registry
2014/08/26 Python
Python使用win32com实现的模拟浏览器功能示例
2017/07/13 Python
Python实现的在特定目录下导入模块功能分析
2019/02/11 Python
Python创建或生成列表的操作方法
2019/06/19 Python
Python银行系统实战源码
2019/10/25 Python
Pandas操作CSV文件的读写实现方法
2019/11/13 Python
Python pip配置国内源的方法
2020/02/14 Python
jupyter notebook中美观显示矩阵实例
2020/04/17 Python
python为什么会环境变量设置不成功
2020/06/23 Python
英国在线花园中心:You Garden
2018/06/03 全球购物
阿迪达斯印尼官方网站:adidas印尼
2020/02/10 全球购物
应届实习生的自我评价范文
2014/01/05 职场文书
人力资源管理毕业求职信
2014/08/05 职场文书
2014年乡镇民政工作总结
2014/12/02 职场文书
2015年感恩父亲节演讲稿
2015/03/19 职场文书
中国梦宣传标语口号
2015/12/26 职场文书
电力安全学习心得体会
2016/01/18 职场文书
分析Netty直接内存原理及应用
2021/06/14 Java/Android
Pycharm连接远程服务器并远程调试的全过程
2021/06/24 Python