JS当中的call 、apply、和bind

这三个方法都是js function当中自带的方法,用来改变this的指向。

call()方法

语法格式:

  1. fun.call(thisArg[,arg1[, arg2[, ...]]])

该方法可以传递一个thisArgs参数和一个参数列表,thisArgs指定了函数在运行期的调用者,也就是函数中的this对象,而参数列表会被传入调用函数中。

通过 call 方法,你可以在一个对象上借用另一个对象上的方法,比如Object.prototype.toString.call([]),就是一个Array对象借用了Object对象上的方法。

thisArgs的取值有以下4种情况:

(1) 不传,或者传null,undefined, 函数中的this指向window对象

(2) 传递另一个函数的函数名,函数中的this指向这个函数的引用

(3) 传递字符串、数值或布尔类型等基础类型,函数中的this指向其对应的包装对象,如 String、Number、Boolean

(4) 传递一个对象,函数中的this指向这个对象

  1. function a(){
  2. console.log(this); //输出函数a中的this对象
  3. }
  4. function b(){} //定义函数b
  5. var obj = {name:'onepixel'}; //定义对象obj
  6. a.call(); //window
  7. a.call(null); //window
  8. a.call(undefined);//window
  9. a.call(1); //Number
  10. a.call(''); //String
  11. a.call(true); //Boolean
  12. a.call(b);// function b(){}
  13. a.call(obj); //Object

这是call的核心功能,它允许你在一个对象上调用该对象没有定义的方法,并且这个方法可以访问该对象中的属性。

例如:

  1. var a = {
  2. name:'onepixel', //定义a的属性
  3. say:function(){ //定义a的方法
  4. console.log("Hi,I'm function a!");
  5. }
  6. };
  7. function b(name){
  8. console.log("Post params: "+ name);
  9. console.log("I'm "+ this.name);
  10. this.say();
  11. }
  12. b.call(a,'test'); //将b的this指向a,参数是b的

还可以通过call方法来调用匿名函数

在下例中的for循环体内,我们创建了一个匿名函数,然后通过调用该函数的call方法,将每个数组元素作为指定的this值执行了那个匿名函数。这个匿名函数的主要目的是给每个数组元素对象添加一个print方法,这个print方法可以打印出各元素在数组中的正确索引号。当然,这里不是必须得让数组元素作为this值传入那个匿名函数(普通参数就可以),目的是为了演示call的用法。

  1. var animals = [
  2. {species: 'Lion', name: 'King'},
  3. {species: 'Whale', name: 'Fail'}
  4. ];
  5. for (var i = 0; i < animals.length; i++) {
  6. (function (i) {
  7. this.print = function () {
  8. console.log('#' + i + ' ' + this.species + ': ' + this.name);
  9. }
  10. this.print();
  11. }).call(animals[i], i);
  12. }
  13. //#0 Lion: King
  14. //#1 Whale: Fail

apply()方法

语法格式:

  1. fun.apply(thisArg[, argsArray])

apply和call的唯一区别是第二个参数的传递方式不同,apply的第二个参数必须是一个数组,而call允许传递一个参数列表。值得你注意的是,虽然apply接收的是一个参数数组,但在传递给调用函数时,却是以参数列表的形式传递。

注意:这里的argsArray可以是一个数组或者类数组对象,如果该参数的值为null 或 undefined,则表示不需要传入任何参数。

  1. function b(x,y,z){
  2. console.log(x,y,z);
  3. }
  4. b.apply(null,[1,2,3]); // 1 2 3

apply方法的实用写法

  1. //里面有最大最小数字值的一个数组对象
  2. var numbers = [5, 6, 2, 3, 7];
  3. /* 使用 Math.min/Math.max 在 apply 中应用 */
  4. var max = Math.max.apply(null, numbers);
  5. // 一般情况是用 Math.max(5, 6, ..) 或者 Math.max(numbers[0], ...) 来找最大值
  6. var min = Math.min.apply(null, numbers);

当参数是明确知道数量时用call;不确定的时候用apply,然后把参数push进数组传递进去,也可以通过arguments这个数组来遍历所有的参数。

bind()方法

语法格式:

  1. fun.bind(thisArg[, arg1[, arg2[, ...]]])

bind是ES5新增的一个方法,它的传参和call类似,但又和call/apply有着显著的不同,即调用call或apply都会自动执行对应的函数,而bind不会执行对应的函数,只是返回了对函数的引用。

ES5中新增加的bind方法可以弥补call()和apply()方法的不足,由于call/apply会对目标函数自动执行,从而导致它无法在事件绑定函数中使用,因为事件绑定函数不需要我们手动执行,它是在事件被触发时由JS内部自动执行的。而bind在实现改变函数this的同时又不会自动执行目标函数,因此可以完美的解决上述问题,看一个例子就能明白:

当点击网页时,EventClick被触发执行,输出JSLite.io p1 p2, 说明EventClick中的this被bind改变成了obj对象。如果你将EventClick.bind(obj,'p1','p2') 变成 EventClick.call(obj,'p1','p2') 的话,页面会直接输出 JSLite.io p1 p2

  1. var obj = {name:'JSLite.io'};
  2. /**
  3. * 给document添加click事件监听,并绑定EventClick函数
  4. * 通过bind方法设置EventClick的this为obj,并传递参数p1,p2
  5. */
  6. document.addEventListener('click',EventClick.bind(obj,'p1','p2'),false);
  7. //当点击网页时触发并执行
  8. function EventClick(a,b){
  9. console.log(
  10. this.name, //JSLite.io
  11. a, //p1
  12. b //p2
  13. )
  14. }
  15. // JSLite.io p1 p2

兼容写法

  1. if (!Function.prototype.bind) {
  2. Function.prototype.bind = function (oThis) {
  3. if (typeof this !== "function") {
  4. // closest thing possible to the ECMAScript 5
  5. // internal IsCallable function
  6. throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
  7. }
  8. var aArgs = Array.prototype.slice.call(arguments, 1),
  9. fToBind = this, // this在这里指向的是目标函数
  10. fNOP = function () {},
  11. fBound = function () {
  12. return fToBind.apply(this instanceof fNOP
  13. ? this //此时的this就是new出的obj
  14. : oThis || this,//如果传递的oThis无效,就将fBound的调用者作为this
  15. //将通过bind传递的参数和调用时传递的参数进行合并,并作为最终的参数传递
  16. aArgs.concat(Array.prototype.slice.call(arguments)));
  17. };
  18. fNOP.prototype = this.prototype;
  19. //将目标函数的原型对象拷贝到新函数中,因为目标函数有可能被当作构造函数使用
  20. fBound.prototype = new fNOP();
  21. //返回fBond的引用,由外部按需调用
  22. return fBound;
  23. };
  24. }

应用

通过call和apply实现继承:

  1. function Animal(name,weight){
  2. this.name = name;
  3. this.weight = weight;
  4. }
  5. function Cat(){
  6. Animal.call(this,'cat','50');
  7. //Animal.apply(this,['cat','50']);
  8. this.say = function(){
  9. console.log("I am " + this.name+",my weight is " + this.weight);
  10. }
  11. }
  12. var cat = new Cat();
  13. cat.say();//I am cat,my weight is 50

JS apply 、call和bind的更多相关文章

  1. js call apply caller callee bind

    call apply bind作用类似.即调用一个对象的一个方法,以另一个对象替换当前对象. call 语法:call([thisObj[,arg1[, arg2[,   [,.argN]]]]]) ...

  2. Js apply方法与call方法详解 附ES6新写法

    我在一开始看到javascript的函数apply和call时,非常的模糊,看也看不懂,最近在网上看到一些文章对apply方法和call的一些示例,总算是看的有点眉目了,在这里我做如下笔记,希望和大家 ...

  3. 由js apply与call方法想到的js数据类型(原始类型和引用类型)

    原文地址:由js apply与call方法想到的js数据类型(原始类型和引用类型) js的call方法与apply方法的区别在于第二个参数的不同,他们都有2个参数,第一个为对象(即需要用对象a继承b, ...

  4. 动态作用域与this +apply和call +bind

    词法作用域是一套关于引擎如何寻找变量以及会在何处找到变量的规则. (函数作用域和块作用域) JavaScript 中的作用域就是词法作用域,也就是静态作用域,由定义代码决定 动态作用域似乎暗示有很好的 ...

  5. [荐]Js apply()和call()方法详解 - http://www.w3cfuns.com/article-5596443-1-1.html

    本帖最后由 默默DE人生 于 2013-3-19 13:22 编辑 Js apply方法详解我在一开始看到javascript的函数apply和call时,非常的模糊,看也看不懂,最近在网上看到一些文 ...

  6. Js apply 方法 详解

    Js apply方法详解 我在一开始看到JavaScript的函数apply和call时,非常的模糊,看也看不懂,最近在网上看到一些文章对apply方法和call的一些示例,总算是看的有点眉目了,在这 ...

  7. Js apply() call()使用详解

    Js apply方法详解我在一开始看到javascript的函数apply和call时,非常的模糊,看也看不懂,最近在网上看到一些文章对apply方法和call的一些示例,总算是看的有点眉目了,在这里 ...

  8. Js apply 方法 具体解释

    Js apply方法具体解释 我在一開始看到javascript的函数apply和call时,很的模糊,看也看不懂,近期在网上看到一些文章对apply方法和call的一些演示样例,总算是看的有点眉目了 ...

  9. Js apply call方法详解

    Js apply方法详解 我在一开始看到javascript的函数apply和call时,非常的模糊,看也看不懂,最近在网上看到一些文章对apply方法和call的一些示例,总算是看的有点眉目了,在这 ...

随机推荐

  1. Java-DatabaseConnectionPool工具类

    package org.zxjava.test; import java.sql.Connection; import java.sql.DatabaseMetaData; import java.s ...

  2. C/C++字符串转换函数;

    字符串转数字: char 转 int 函数: atoi   wchar  转 int 函数: _wtoi 数字转字符串: std::to_string(): std::to_wsting();

  3. 计算机HDMI端口与电视机相连

     造冰箱的大熊猫@cnblogs 2019/2/27 打算通过HDMI接口将计算机桌面投影到电视机上,结果遇到问题,折腾了好一阵才搞定.现将这些问题记录下来 1.设备环境 计算机:使用Ubuntu 1 ...

  4. 2019牛客暑期多校训练营(第二场)D bitset

    题意 给一个n个结点的带点权的图,找到第k小的团的权值 分析 用bitset表示团的状态,一个结点必须和团里的每个结点都连边才能加进去,所以可以直接用\(\&\)运算来判断一个结点是否能加进去 ...

  5. 删除git的远程分支

    命令:$ git push origin [空格][冒号][需要删除的分支名字] git push origin :dev 删除git的凭证的方法: 控制面板\用户帐户\凭据管理器  --->普 ...

  6. 关于vue.js element ui 表单验证 this.$refs[formName].validate()的问题

        方法使用前需了解: 来自”和“小编的小提示: 首先打印一下this.$refs[formName],检查是否拿到了正确的需要验证的form. 其次在拿到了正确的form后,检查该form上添加 ...

  7. Taro 遇到的坑

    1.createSelectorQuery无法获取节点宽高 业务场景: 列表需要在最后一页底部显示 ‘我是有底线的~’ 提示,但是如果数据只有一页且不占满屏幕的话,就不显示.需要判断 ‘我是有底线的~ ...

  8. soa soap http rpc

    soa 是一种计算机软件的设计模式,主要应用于不通应用组件中通过某种协议来互操作 它的基本设计原理是:服务提供了一个简单的接口,抽象了底层的复杂性,然后用户可以访问独立的服务,而不需要去了解服务底层平 ...

  9. easyui datagrid的API

    本文可以当做api来使用 动态编辑用到DOM解析的话参考 http://www.w3school.com.cn/xmldom/dom_htmlcollection.asp 使用方法(Usage Exa ...

  10. flink杂记

    flink的特点:能同时满足高性能.高吞吐.低延时,相比较于storm和spark streaming,storm的吞吐量不如flink,而spark streaming的延时比较高,因为spark ...