在JavaScript中call、apply、bind是Function 对象自带的三个方法,这三个方法的主要作用是改变函数中的 this 指向,从而可以达到`接花移木`的效果。本文将对这三个方法进行详细的讲解,并列出几个经典应用场景。

区分:

1、call(object,arg1,arg2) ,call方法的第一个参数是函数中this重新指向的对象,剩下的参数是传入该函数的形参

不传,或者传null,undefined, 函数中的 this 指向 window 对象,传递另一个函数的函数名,函数中的 this 指向这个函数的引用,传递字符串、数值或布尔类型等基础类型,函数中的 this 指向其对应的包装对象,如 String、Number、Boolean,传递一个对象,函数中的 this 指向这个对象。

      function say(){console.log(this);}function eat(){}var obj={name:'Bob',};say.call();             //this指向windowsay.call(null);         //this指向windowsay.call(undefined);    //this指向windowsay.call(eat);          //this指向函数得引用 function eat(){}say.call(1);            //this指向数值基本类型得包装对象Numbersay.call("str");        //this指向数值基本类型得包装对象Stringsay.call(true);         //this指向数值基本类型得包装对象Booleansay.call(obj);          //this指向这个对象obj

call的作用是允许在一个对象上调用该对象没有定义的方法,并且这个方法可以访问该对象中的属性,如下

      var a={name:'Bob',food:'fish',say:function(){console.log('HI,this is a.say!!');}}function b(name){                      //b.call(a,'Tom');使得a对象能调用其他函数方法console.log("post Params:"+name);   //a对象使用了b('Tom')方法, 输出post Params: Tomconsole.log('I am '+this.name);     //a对象获取了自己的属性 ,输出 I am Bobthis.say();                         //a对象使用自己的方法, 输出 HI,this is a.say!!}b.call(a,'Tom');

2、apply(object,[arg1,arg2]),apply方法的第一个参数是函数中this重新指向的对象,第二个参数数组是传入该函数的形参;和call方法唯一区别是第二参数的传递形式是数组。使用如下

      function b(x,y,z){                      console.log(x,y,z);   //会将多个参数拼接输出                      }//apply() 方法接收的参数是数组形式,但是传递给调用的函数时是用参数列别的形式传递的b.apply(null,[1,2,3]);  //输出 1 2 3 ,这里等同于window对象调用b方法并使用参数[1,2,3]

3、bind(object,arg1,arg2) ,bind方法是ES5 新增的一个方法,传参和call方法一致。与call、apply方法的区别是,call和apply方法会对目标函数进行自动执行,会返回一个新的函数。call和apply无法在事件绑定函数中使用。而bind弥补了这个缺陷,在实现改变函数 this 的同时又不会自动执行目标函数,因此可以完美的解决上述问题,

      var obj = {name: 'onepixel'};//给document添加click事件监听函数,并绑定onclick函数//通过bind方法设置onclick的this指向是obj,并传递参数p1,p2document.addEventListener('click',onClick.bind(obj,22,66),false);//可以理解为当网页触发click事件时,obj对象执行onclick函数并传递给该函数参数p1,p2function onClick(p1,p2){console.log(this.name,p1,p2);  //输出 onepixel 22 66}
      var button=document.getElementById("button"),text=document.getElementById("text");button.onclick=function(){  //声明按钮的点击事件触发的函数console.log(this.id);}.bind(text);     //改变this的指向

注意:一旦函数通过bind绑定了有效的this对象,那么在函数执行过程中this会指向该对象,即使使用call、apply也不能改变this的指向

bind 进行深入的理解,我们来看一下 bind 的 polyfill 实现(ie6~ie8不支持该方法):

if (!Function.prototype.bind) {  //浏览器js中不支持bind方法情况下Function.prototype.bind = function(context) {  //在函数原型对象自定义bind方法,context是this重指向的对象,也就是上面function(){}.bind(text)中的text对象var self = this  //因为这个自定义的bind方法返回的是一个匿名函数,匿名函数具有全局性,其this会指向window。所以这里需要将bind方法触发时的调用对象this进行保存, args = Array.prototype.slice.call(arguments);  //这里借用了数组原型对象的slice方法将形参 类数组转化为真正的数组return function() {  //bind方法返回一个匿名函数return self.apply(context, args.slice(1));    //self就是函数触发时的调用对象,this重指向bind方法的第一个参数对象,}};
}

代码解析:在浏览器不支持bind方法时,自定义bind方法中会返回一个匿名函数,该匿名函数保存bind方法调用时的this,这里是self=this; 并通过self.apply(newObj, args); 的原理实现改变this指向不执行函数的效果

使用场景:

1、继承

JavaScript中没有诸如Java、C# 等高级语言中的extend 关键字,因此JS 中没有继承的概念,如果一定要继承的话,call 和 apply 可以实现这个功能:

      function Animal(name,weight){this.name=name;this.weight=weight;}function Cat(){Animal.apply(this,['cat','10kg']);  //理解为在Cat函数对象创建时会使用Animal()方法//Animal.call(this,'cat','10kg');this.say=function(){console.log('I am '+this.name+' , my weight is '+this.weight);}}var cat = new Cat();cat.say();  //输出 I am cat , my weight is 10kg

2、移花接木

在讲下面的内容之前,我们首先来认识一下JavaScript 中的一个非标准专业术语:ArrayLike (类数组/伪数组)

ArrayLike 对象即拥有数组的一部分行为,在DOM 中早已表现出来,而jQuery 的崛起让ArrayLike 在JavaScript 中大放异彩。ArrayLike 对象的精妙在于它和JS 原生的 Array 类似,但是它是自由构建的,它来自开发者对JavaScript 对象的扩展,也就是说:对于它的原型(prototype)我们可以自由定义,而不会污染到JS原生的Array。

ArrayLike 对象在JS中被广泛使用,比如DOM 中的NodeList, 函数中的arguments 都是类数组对象,这些对象像数组一样存储着每一个元素,但它没有操作数组的方法,而我们可以通过call 将数组的某些方法`移接`到ArrayLike 对象,从而达到操作其元素的目的。比如我们可以这样遍历函数中的arguments:

      function test(){console.log(typeof(arguments));     //输出Object ,ArrayLike是类数组对象//检测arguments是否是Array的实例console.log(arguments instanceof Array);  //输出 falseconsole.log(Array.isArray(arguments));    //输出 false//判断arguments是否有forEach的方法console.log(arguments.forEach);          //输出 undefined//将数组中的forEach方法应用到arguments上Array.prototype.forEach.call(arguments,function(item){console.log(item);  //输出 1 2 3 4 5});}test(1,2,3,4,5);

除此之外,对于apply 而言,我们上面提到了它独有的一个特性,即apply 接收的是数组,在传递给调用函数的时候是以参数列表传递的。 这个特性让apply 看起来比call 略胜一筹,比如有这样一个场景:给定一个数组[1,3,4,7],然后求数组中的最大元素,而我们知道,数组中并没有获取最大值的方法,一般情况下,你需要通过编写代码来实现。而我们知道,Math 对象中有一个获取最大值的方法,即Math.max(), max方法需要传递一个参数列表,然后返回这些参数中的最大值。而apply 不仅可以将Math 对象的max 方法应用到其他对象上,还可以将一个数组转化为参数列表传递给max,看代码就能一目了然:

var arr = [-1,2, 3, 1, 5, 4, 223, 11];
var a=Math.max.apply(null, arr); // 223
console.log(a);

学习网址:https://blog.csdn.net/u014267183/article/details/52610600 、软谋前端 (深入理解 call,apply 和 bind)

深入理解call、apply、bind(改变函数中的this指向)相关推荐

  1. 普通函数和箭头函数中的this指向

    普通函数的this指向Window function a(){console.log(this) } a() //Window Dom元素绑定事件时的this,句柄里的 this 值是该元素的引用 d ...

  2. ES6/03/函数的定义方式和调用方式,函数内的this指向,改变函数中this指向的三个方法(call(),apply(),bind())

    函数的定义方式 1,自定义函数(命名函数) function fun(){}; 2,函数表达式(匿名函数) var fun =function(){}; 3,利用new Function( '参数1' ...

  3. ES6箭头函数中的this指向

    1箭头函数中的this (1)箭头函数中没有this : 这意味着 call() apply() bind() 无法修改箭头函数中的this (2)箭头函数中的this指向 :访问上一个作用域的thi ...

  4. 关于setTimeout函数中的this指向问题

    为什么setTimeout函数的延迟执行函数中this指向window,但是延迟执行函数是箭头函数this指向obj? 根据下面的两点原因解释,foo和foo2中的this都指向obj,但是其中set ...

  5. React之函数中的this指向

    我们都知道在React中使用函数时,有两种写法,一是回调函数,二是直接调用,但需要在构造函数中绑定this,只有这样,函数中的this才指向本组件 总结一下没有绑定this的函数中的this指向 不管 ...

  6. JavaScript学习(七十)—函数中this的指向问题

    JavaScript学习(七十)-函数中this的指向问题 一.这些this的指向,是当我们调用函数的时候确定的,调用方式的不同决定了this的指向不同,一般指向我们的调用者

  7. 关于函数中的this指向问题

    关于函数中的this指向问题 前言 一.普通函数中this指向 二.对象里面的this指向 三.函数表达式中的this指向 四.立即执行函数的this指向 五.定时器计时器中this的指向 六.箭头函 ...

  8. 函数的this指向,call,apply bind改变this指向

    函数在定义的时候不会影响它的this指向,只有在调用的时候选择不同的调用方式,才会决定你this指向何处. 在独立函数调用的时候,this指向window,在调用挂载在window的函数时,前面的wi ...

  9. JS高级——深入剖析函数中的this指向问题

    一.this到底指向什么呢? 我们先说一个最简单的,this在全局作用域下指向什么? 这个问题非常容易回答,在浏览器中测试就是指向window 但是,开发中很少直接在全局作用于下去使用this,通常都 ...

最新文章

  1. Python+Dash快速web应用开发——基础概念篇
  2. windows下安装和配置Redis
  3. NavigateUr l动态参数 格式化参数
  4. POJ读书笔记2.1 —— 鸡兔笼带
  5. C语言编译器之四,Turbo C等
  6. ENVI Landsat8影像掩膜裁剪
  7. 支付宝支付接口开通流程
  8. [宋史学习] 取蜀将帅不利
  9. 计算机连接不上蓝牙鼠标,蓝牙鼠标连接不上电脑怎么办_蓝牙鼠标连接不上电脑的解决办法...
  10. 自适应,响应式以及图片的性能优化(响应式图片)
  11. autodesk fbx sdk sample里面的工程无法调试解决方法
  12. SlashData开发者工具榜首等你而定!!!
  13. 数据结构与算法--哈夫曼树及其应用
  14. 智能优化算法之遗传算法python实现细节,GA库函数调用方法
  15. hadoop+Kylin服务器搭建教程
  16. 极米投影仪: U盘安装APP
  17. 队残冒逃茸霞桥镭砸的
  18. 卡梅隆对话刘慈欣:想看《三体》拍成电影
  19. 周志华、贾扬清入选!2022中国高被引学者榜单揭晓,计算机界214人上榜
  20. python 字典处理_python中的字典及其操作

热门文章

  1. java元数据是什么_用存储过程和 JAVA 写报表数据源有什么弊端?
  2. python浅复制与深复制_Python中的浅复制与深复制
  3. python及pycharm
  4. 递归转化成非递归过程_8086微处理器中的递归和重入过程
  5. 登陆 tomcat manager
  6. 配置设备作为DHCP 服务器(基于接口地址池)
  7. 获取页面所有属性并生成html6,JavaScript基础练习题(三)
  8. php 图片后缀,PHP如何实现图片无后缀
  9. linux db2 权限管理,DB2五种管理权限
  10. 前端处理带t的时间_适合家用的跑步机?阿迪达斯轻爵T-19i跑步机测评|adidas|商用机...