Javascript中call,apply,bind方法的详解与总结


Posted in Javascript onDecember 12, 2016

以下内容会分为如下小节:

1.call/apply/bind方法的来源

2.Function.prototype.call()

3.Function.prototype.apply()

3.1:找出数组中的最大数

3.2:将数组的空元素变为undefined

3.3:转换类似数组的对象

4.Function.prototype.bind()

5.绑定回调函数的对象

6.call,apply,bind方法的联系和区别

1.call/apply/bind方法的来源

首先,在使用call,apply,bind方法时,我们有必要知道这三个方法究竟是来自哪里?为什么可以使用的到这三个方法?

call,apply,bind这三个方法其实都是继承自Function.prototype中的,属于实例方法。

console.log(Function.prototype.hasOwnProperty('call')) //true
 console.log(Function.prototype.hasOwnProperty('apply')) //true
 console.log(Function.prototype.hasOwnProperty('bind')) //true

上面代码中,都返回了true,表明三种方法都是继承自Function.prototype的。当然,普通的对象,函数,数组都继承了Function.prototype对象中的三个方法,所以这三个方法都可以在对象,数组,函数中使用。

关于继承的概念,会在以后与大家分享。

2.Function.prototype.call()

 函数实例的call方法,可以指定该函数内部this的指向(即函数执行时所在的作用域),然后在所指定的作用域中,调用该函数。并且会立即执行该函数。

 看个例子来好好理解这段话。

var keith = {
 rascal: 123
 };
 var rascal = 456;
 function a() {
 console.log(this.rascal);
 }
 a(); //456
 a.call(); //456
 a.call(null); //456
 a.call(undefined); //456
 a.call(this); //456
 a.call(keith); //123

上面代码中,a函数中的this关键字,如果指向全局对象,返回结果为456。可以看到,如果call方法没有参数,或者参数为null或undefined或者this,则等同于指向全局对象。如果使用call方法将this关键字指向keith对象,也就是将该函数执行时所在的作用域为keith对象,返回结果为123。

call()方法可以传递两个参数。第一个参数是指定函数内部中this的指向(也就是函数执行时所在的作用域),第二个参数是函数调用时需要传递的参数。

function keith(a, b) {
 console.log(a + b);
 }
keith.call(null, 1, 2); //3

第一个参数是必须的,可以是null,undefined,this,但是不能为空。设置为null,undefined,this表明函数keith此时处于全局作用域。第二个参数中必须一个个添加。而在apply中必须以数组的形式添加。

call方法的一个应用是调用对象的原生方法。也可以用于将类数组对象转换为数组。

var obj = {};
 console.log(obj.hasOwnProperty('toString')); //false
 obj.hasOwnProperty = function() {
 return true;
 }
 console.log(obj.hasOwnProperty('toString')); //true
 console.log(Object.prototype.hasOwnProperty.call(obj, 'toString')); //false

上面代码中,hasOwnProperty是obj对象继承的方法,如果这个方法一旦被覆盖,就不会得到正确结果。call方法可以解决这个方法,它将hasOwnProperty方法的原始定义放到obj对象上执行,这样无论obj上有没有同名方法,都不会影响结果。要注意的是,hasOwnProperty是Object.prototype原生对象的方法,而call是继承自Function.prototype的方法。

3.Function.prototype.apply()

apply方法的作用与call方法类似,也是改变this指向(函数执行时所在的作用域),然后在指定的作用域中,调用该函数。同时也会立即执行该函数。唯一的区别就是,它接收一个数组作为函数执行时的参数。

apply方法的第一个参数也是this所要指向的那个对象,如果设为null或undefined或者this,则等同于指定全局对象。第二个参数则是一个数组,该数组的所有成员依次作为参数,在调用时传入原函数。原函数的参数,在call方法中必须一个个添加,但是在apply方法中,必须以数组形式添加。

看一下call,apply的细微差别。

function keith(a, b) {
 console.log(a + b);
 }
 keith.call(null, 2, 3); //5
 keith.apply(null, [2, 3]); //5

 

上面代码中,第一个参数为null,指向全局作用域;第二个参数传入的形式稍稍不同。

apply方法有以下应用。

3.1:找出数组中的最大数

var a = [2, 4, 5, 7, 8, 10];
console.log(Math.max.apply(null, a)); //10
console.log(Math.max.call(null,2, 4, 5, 7, 8, 10)); //10 

Javascript中是没有提供找出数组中最大值的方法的,结合使用继承自Function.prototype的apply和Math.max方法,就可以返回数组的最大值。

3.2:将数组的空元素变为undefined

通过apply方法,利用Array构造函数将数组的空元素变成undefined。

console.log(Array.apply(null, [1, , 3])); // [1, undefined, 3]

空元素与undefined的差别在于,数组的forEach方法会跳过空元素,但是不会跳过undefined和null。因此,遍历内部元素的时候,会得到不同的结果。

var a = [1, , 3];
 a.forEach(function(index) {
 console.log(index); //1,3 ,跳过了空元素。
 })
 Array.apply(null,a).forEach(function(index){
 console.log(index); ////1,undefined,3 ,将空元素设置为undefined
 })

3.3:转换类似数组的对象

另外,利用数组对象的slice方法,可以将一个类似数组的对象(比如arguments对象)转为真正的数组。当然,slice方法的一个重要应用,就是将类似数组的对象转为真正的数组。call和apply都可以实现该应用。

console.log(Array.prototype.slice.apply({0:1,length:1})); //[1]
console.log(Array.prototype.slice.call({0:1,length:1})); //[1]
console.log(Array.prototype.slice.apply({0:1,length:2})); //[1,undefined]
console.log(Array.prototype.slice.call({0:1,length:2})); //[1,undefined]
function keith(a,b,c){
 return arguments;
 }
console.log(Array.prototype.slice.call(keith(2,3,4))); //[2,3,4]

上面代码的call,apply方法的参数都是对象,但是返回结果都是数组,这就起到了将对象转成数组的目的。从上面代码可以看到,这个方法起作用的前提是,被处理的对象必须有length属性,以及相对应的数字键。

4.Function.prototype.bind()

bind方法用于指定函数内部的this指向(执行时所在的作用域),然后返回一个新函数。bind方法并非立即执行一个函数。

var keith = {
 a: 1,
 count: function() {
 console.log(this.a++);
 }
 };
 keith.count(); //1
 keith.count(); //2
 keith.count(); //3

上面代码中,如果this.a指向keith对象内部的a属性,如果这个方法赋值给另外一个变量,调用时就会出错。

var keith = {
 a: 1,
 count: function() {
 console.log(this.a++);
 }
 };
 var f = keith.count;
 f(); //NaN

上面代码中,如果把count方法赋值给f变量,那么this对象指向不再是keith对象了,而是window对象。而window.a默认为undefined,进行递增运算之后undefined++就等于NaN。

为了解决这个问题,可以使用bind方法,将keith对象里的this绑定到keith对象上,或者是直接调用。

var f = keith.count.bind(keith);
 f(); //1
 f(); //2
 f(); //3
 keith.count.bind(keith)() //1
 keith.count.bind(keith)() //2
 keith.count.bind(keith)() //3

 当然,this也可以绑定到其他对象上。

var obj = {
 a: 100
 };
 var f = keith.count.bind(obj);
 f(); //100
 f(); //101
 f(); //102

同样,我们也可以给bind方法传递参数,第一个参数如果为null或者undefined或者this,会将函数内部的this对象指向全局环境;第二个为调用时需要的参数,并且传递参数的形式与call方法相同。

function keith(a, b) {
 return a + b;
 }
 console.log(keith.apply(null,[1,4])); //5
 console.log(keith.call(null,1,4)); //5
 console.log(keith.bind(null, 1, 4)); //keith()
 console.log(keith.bind(null, 1, 4)()); //5

上面代码中,可以看出call,apply,bind三者的区别:call和apply方法都是在调用之后立即执行的。而bind调用之后是返回原函数,需要再调用一次才行,有点像闭包的味道,如果对闭包概念不熟悉,可以浏览这两篇文章:深入理解javascript函数参数与闭包,浅谈JavaScript的闭包函数。

5.绑定回调函数的对象

在这篇文章javascript中this关键字详解中,有谈及到如果在回掉函数中使用this对象,那么this对象是会指向DOM对象,也就是button对象。如果要解决回调函数中this指向问题,可以用如下方法。

var o = {
 f: function() {
 console.log(this === o);
 }
 }
 $('#button').on('click', function() {
 o.f.apply(o);
 //或者 o.f.call(o);
 //或者 o.f.bind(o)();
 });

点击按钮以后,控制台将会显示true。由于apply方法(或者call方法)不仅绑定函数执行时所在的对象,还会立即执行函数(而bind方法不会立即执行,注意区别),因此不得不把绑定语句写在一个函数体内。

6.call,apply,bind方法的联系和区别

其实用于指定函数内部的this指向的问题,这三个方法都差不多,只是存在形式上的差别。读者可以将以上的例子用三种方法尝试用三种方法实现。

总结一下call,apply,bind方法:

a:第一个参数都是指定函数内部中this的指向(函数执行时所在的作用域),然后根据指定的作用域,调用该函数。

b:都可以在函数调用时传递参数。call,bind方法需要直接传入,而apply方法需要以数组的形式传入。

c:call,apply方法是在调用之后立即执行函数,而bind方法没有立即执行,需要将函数再执行一遍。有点闭包的味道。

d:改变this对象的指向问题不仅有call,apply,bind方法,也可以使用that变量来固定this的指向。如有疑问,请访问 javascript中this关键字详解

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持三水点靠木!

Javascript 相关文章推荐
网页编辑器ckeditor和ckfinder配置步骤分享
May 24 Javascript
Javascript基础教程之JavaScript语法
Jan 18 Javascript
jQuery实现的数值范围range2dslider选取插件特效多款代码分享
Aug 27 Javascript
值得分享的轻量级Bootstrap Table表格插件
May 30 Javascript
详解javascript表单的Ajax提交插件的使用
Dec 29 Javascript
js实现鼠标左右移动,图片也跟着移动效果
Jan 25 Javascript
vue.js中created方法作用
Mar 30 Javascript
js使用ajax传值给后台,后台返回字符串处理方法
Aug 08 Javascript
vue数据初始化initState的实例详解
Apr 11 Javascript
详解vue 动态加载并注册组件且通过 render动态创建该组件
May 30 Javascript
Vue组件实现触底判断
Jun 26 Javascript
JavaScript展开运算符和剩余运算符的区别详解
Feb 18 Javascript
微信小程序开发之圆形菜单 仿建行圆形菜单实例
Dec 12 #Javascript
深入理解javascript中concat方法
Dec 12 #Javascript
js仿微信语音播放实现思路
Dec 12 #Javascript
解析JavaScript数组方法reduce
Dec 12 #Javascript
实例分析浏览器中“JavaScript解析器”的工作原理
Dec 12 #Javascript
JS双击变input框批量修改内容
Dec 12 #Javascript
jQuery中animate的几种用法与注意事项
Dec 12 #Javascript
You might like
PHP表单提交表单名称含有点号(.)则会被转化为下划线(_)
2011/12/14 PHP
通达OA公共代码 php常用检测函数
2011/12/14 PHP
一个PHP针对数字的加密解密类
2014/03/20 PHP
php中注册器模式类用法实例分析
2015/11/03 PHP
PHP随机数 C扩展随机数
2016/05/04 PHP
PHP的PDO预定义常量讲解
2019/01/24 PHP
JavaScript中的View-Model使用介绍
2011/08/11 Javascript
Js 冒泡事件阻止实现代码
2013/01/27 Javascript
JavaScript简单修改窗口大小的方法
2015/08/03 Javascript
AngularJS模块学习之Anchor Scroll
2016/01/19 Javascript
nodejs修复ipa处理过的png图片
2016/02/17 NodeJs
基于JSON格式数据的简单jQuery幻灯片插件(jquery-slider)
2016/08/10 Javascript
JavaScript-html标题滚动效果的简单实现
2016/09/08 Javascript
JS 循环li添加点击事件 (闭包的应用)
2016/12/10 Javascript
JS遍历对象属性的方法示例
2017/01/10 Javascript
Agularjs妙用双向数据绑定实现手风琴效果
2017/05/26 Javascript
页面间固定参数,通过cookie传值的实现方法
2017/05/31 Javascript
XMLHttpRequest对象_Ajax异步请求重点(推荐)
2017/09/28 Javascript
Vue-CLI项目中路由传参的方式详解
2019/09/01 Javascript
原生js实现无缝轮播图
2020/01/11 Javascript
使用Vue-cli 中为单独页面设置背景图片铺满全屏
2020/07/17 Javascript
Python重新引入被覆盖的自带function
2014/07/16 Python
Windows8下安装Python的BeautifulSoup
2015/01/22 Python
python安装oracle扩展及数据库连接方法
2017/02/21 Python
python基础_文件操作实现全文或单行替换的方法
2017/09/04 Python
Python中print和return的作用及区别解析
2019/05/05 Python
Django框架视图层URL映射与反向解析实例分析
2019/07/29 Python
Python openpyxl 插入折线图实例
2020/04/17 Python
基于python实现查询ip地址来源
2020/06/02 Python
python实现批量转换图片为黑白
2020/06/16 Python
高一物理教学反思
2014/01/24 职场文书
2014旅游局党组书记党建工作汇报材料
2014/11/02 职场文书
2015年全国“爱牙日”宣传活动总结
2015/03/23 职场文书
毕业生登记表班级意见
2015/06/05 职场文书
MySQL中distinct与group by之间的性能进行比较
2021/05/26 MySQL
Python实现排序方法常见的四种
2021/07/15 Python