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 连接数据库如何操作数据库中的数据
Nov 23 Javascript
将HTML的左右尖括号等转义成实体形式的两种实现方式
May 04 Javascript
chrome不支持form.submit的解决方案
Apr 28 Javascript
js去除浏览器默认底图的方法
Jun 08 Javascript
js实现仿MSN带关闭功能的右下角弹窗代码
Sep 04 Javascript
JS获取本周周一,周末及获取任意时间的周一周末功能示例
Feb 09 Javascript
使用express搭建一个简单的查询服务器的方法
Feb 09 Javascript
关于js的三种使用方式(行内js、内部js、外部js)的程序代码
May 05 Javascript
layui原生表单验证的实例
Sep 09 Javascript
深入理解基于vue-cli的webpack打包优化实践及探索
Oct 14 Javascript
js实现旋转的星空效果
Nov 01 Javascript
JS highcharts实现动态曲线代码示例
Oct 16 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正则的Unknown Modifier错误解决方法
2010/03/02 PHP
PHP中几种常见的超时处理全面总结
2012/09/11 PHP
PHP ? EasyUI DataGrid 资料存的方式介绍
2012/11/07 PHP
PHP发送短信代码分享
2015/08/11 PHP
简单了解将WordPress中的工具栏移到底部的小技巧
2015/12/31 PHP
thinkPHP多表查询及分页功能实现方法示例
2017/07/03 PHP
PHP pthreads v3下的Volatile简介与使用方法示例
2020/02/21 PHP
dess中一个简单的多路委托的实现
2010/07/20 Javascript
三级下拉菜单的js实现代码
2011/05/23 Javascript
jQuery中filter()和find()的区别深入了解
2013/09/25 Javascript
基于jquery实现的文字淡入淡出效果
2013/11/14 Javascript
Jquery实现textarea根据文本内容自适应高度
2015/04/03 Javascript
javascript类型系统 Window对象学习笔记
2016/01/07 Javascript
Bootstrap实现input控件失去焦点时验证
2016/08/04 Javascript
JS控制页面跳转时未请求要跳转的地址怎么回事
2016/10/14 Javascript
NodeJS学习笔记之Module的简介
2017/03/24 NodeJs
Node.js pipe实现源码解析
2017/08/12 Javascript
深入浅析vue组件间事件传递
2017/12/29 Javascript
Angular实现点击按钮控制隐藏和显示功能示例
2017/12/29 Javascript
使用nvm和nrm优化node.js工作流的方法
2019/01/17 Javascript
实例分析Array.from(arr)与[...arr]到底有何不同
2019/04/09 Javascript
微信小程序实现星级评价
2019/11/20 Javascript
python 转换 Javascript %u 字符串为python unicode的代码
2016/09/06 Python
关于python pyqt5安装失败问题的解决方法
2017/08/08 Python
python中pip的使用和修改下载源的方法
2019/07/08 Python
Python线程threading模块用法详解
2020/02/26 Python
降低python版本的操作方法
2020/09/11 Python
css3的transform中scale缩放详解
2014/12/08 HTML / CSS
澳大利亚吉他在线:Artist Guitars
2017/03/30 全球购物
乌克兰数字设备、配件和智能技术的连锁商店:KTC
2020/08/18 全球购物
女娲补天教学反思
2014/02/05 职场文书
天网工程实施方案
2014/03/26 职场文书
经典搞笑版检讨书
2015/02/19 职场文书
亲戚关系证明
2015/06/24 职场文书
安全教育主题班会教案
2015/08/12 职场文书
2016年九九重阳节活动总结
2016/04/01 职场文书