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

在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

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/554046.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

360浏览器设置多标签操作步骤

360浏览器设置多标签操作步骤 360浏览器怎么设置多标签?昨天小编写了一篇如何使用ie7浏览器设置多个主页的文章,就有小伙伴留言给小编360浏览器能否实现这项功能?当然有!下面小编就介绍操作方法: 1、打开360浏览器,在左上角找到“工具”点…

形参和实参是什么?? shim和polyfil是什么意思??

形参和实参是什么?? 实参(argument): 全称为"实际参数"是在调用时传递给函数的参数. 实参可以是常量、变量、表达式、函数等, 无论实参是何种类型的量,在进行函数调用时,它们都必须具有确定的值…

占内存最小的浏览器:360安全浏览器超速版推荐

占内存最小的浏览器:360安全浏览器超速版推荐 占内存最小的浏览器有哪些?今天小编为大家推荐的是360安全浏览器超速版。本站提供360安全浏览器超速版最新正式版及抢票专版下载。那360安全浏览器超速版怎么样?看看小编整理的资料就清楚了。 360安全浏览器超速版资…

PS人像修图,通道磨皮、高低频磨皮、滤镜磨皮、人像精修

1、快速简单美肤-----高斯模糊磨皮: 原理 是高斯模糊蒙版。这种磨皮方法主要是针对面部的斑点及细纹。 操作流程概括:复制背景图层 》【滤镜】|【模糊】|【高斯模糊】命令 》 按‘Alt’键添加图层蒙版 》 在蒙版上使用画笔涂抹出想要模糊的地方》曲线…

java基础学习笔记(一)

java开发环境搭建:JDK配置 java 集成开发环境IDE(Integrated Development Environment) 》Eclipse工具 1、官网下载后安装elipse,注意:Eclipse 安装时JDK位数和Eclpise位数必须保持一致,安装过程中Switch…

谷歌浏览器怎么拦截网页广告 5步解决广告困扰

众所周知,广告是许多门户网站的主要收入来源之一,但广告内容的真实性辨识难度极高,一时大意便会中招。值得庆幸的是,许多浏览器产品都内置了广告拦截功能!下面小编就以谷歌浏览器为例,简单介绍一下广告拦截功能的开启方…

常用矢量图有哪些格式?AI文件存储为psd分层

前言 下载的矢量图资源会发现有ai格式、cdr格式的、eps格式、icon格式,有的能使用Photoshop打开而有的不行。使用Photoshop保存矢量图时候选择什么格式保存都是经常遇到的问题。 常见问题及解决:PS打开ai格式的矢量图文件,你用PS打开发现只…

来自天堂的连枷

来自天堂的连枷主要内容 《来自天堂的连枷》讲述的是:一个农夫将角长得越来越大的两头牛卖给屠户,他们商定的支付方式是,农夫给屠夫一配克油菜籽,然后由屠夫点数,一粒油菜籽付一块金币。途中农夫掉了一粒油菜籽&#…

百度地图API实现地图应用

企业官网上需要用到地图应用,这里对百度地图API的使用做点笔记,好记性不如烂笔头。 实现地图应用的流程如下: 1、获取密钥;教程网址 https://jingyan.baidu.com/article/363872eccda8286e4aa16f4e.html 2、在html的head头部引入…

win10开机密码忘记了的解锁教程

如今Win10系统逐渐成为了主流桌面系统,很多时候我们为了安全起见,往往会给自己的电脑系统设置一个开机密码,这样别人就不会轻易使用自己的电脑。但有时候难免会忘记开机密码。那么遇到Win10开机密码忘了怎么办呢?今天小编就给大家说说win10开…

java基础学习笔记(二)

1、数组排序之选择法排序和冒泡排序&#xff1f; 选择法排序原理&#xff1a;数组第一位和后续位置数值比较&#xff0c;最大或最小的调换位置后放在第一位&#xff1b;依次比较将第二大或小的值调换位置后放在第二位置&#xff1b;代码如下&#xff1a; for (int j 0; j <…

OneNote怎样显示或者隐藏网格线

网格线可添加到图表中以易于查看和计算数据的线条&#xff0c;能够辅助我们来对其图像或者是文本&#xff0c;很多新手用户在使用OneNote的时候不知道要怎样设置&#xff0c;下面小编就分享一下OneNote显示或者隐藏网格线的方法&#xff0c;还不知道的一起来学习一下OneNote显示…

java基础学习笔记(三)

1、装箱和拆箱 所有基本类型都有对应的类类型&#xff0c;比如int对应的类是Integer,这种类就叫做封装类。数字封装类有Byte、Short、Integer、Long、Float、Double这些类都是抽象类Number的子类。封装类和基本类型之间可以相互转换&#xff0c;而基本类型自动转封装类型就叫装…

win7如何设置还原点

win7系统是微软经典的操作系统&#xff0c;有很多比较实用的功能&#xff0c;比如说win7系统还原点的功能。通过win7设置还原点&#xff0c;可以把win7系统还原到还原点状态达到修复系统的目的。那么win7如何设置还原点?下面小编教下大家win7设置还原点的方法。 win7如何设置…

小鱼一键重装系统win7教程

最近有朋友想要自己安装win7操作系统&#xff0c;却不知道如何才能正确安装。于是就叫了电脑师傅来帮忙安装&#xff0c;又花了一点小钱。电脑师傅三下两除二就安装好&#xff0c;朋友询问下&#xff0c;电脑师傅才说出来这个一键安装win7的方法。下面就让我们一起来看看win7怎…

名片设计尺寸及注意事项(详尽),附加:文字转曲线或外框字;网址、文字转二维码方法

附加&#xff1a; 文字如何转曲线或外框字&#xff1f;&#xff1f; "转曲"是在用Corl Draw(简称CD)或者Adobe Illustrator(简称AI)做图时对文字作的一项重要操作,因为如果不转曲的话在把文件拿到其它电脑上编辑时可能会因为缺少字体文件而不能正常显示文字!! 转曲…

从域名注册到网站上线全过程(简单全面)

前言&#xff1a;刚开始是计划个人注册免费域名和免费的虚拟主机&#xff0c;目的是不花钱的完成到上线的全过程&#xff0c;点亮这个网站上线的技能树。后面公司需要做一个简单的展示性网站就名正言顺的进行尝试实现了。 个人总结&#xff1a;上线流程简单可以细分为以下几步…

win7纯净版镜像系统安装教程

虽然win10发布有很多长时间了&#xff0c;但是目前依然无法动摇win7在市面上的地位&#xff0c;有的小伙伴在买了预装win10系统的电脑用过之后还是想装回win7系统&#xff0c;下面就来看看win7纯净版镜像系统安装教程吧。 win7纯净版镜像系统安装教程 1、首先我们打开电脑浏览…

AVI转MP4视频无损格式转码

前言&#xff1a;产品宣传需要无损avi转mp4转换格式 前期准备&#xff1a; 格式工厂 下载链接&#xff1a;https://pan.baidu.com/s/1WLD1ip9EaMdca_k2x7Zhvg 提取码&#xff1a;i4nx DIVX、AVC、HEVC三种压码格式的区别&#xff1a; DivX是早期的MPEG4视频压缩编码&…

谷歌浏览器如何使用访客模式 谷歌浏览器使用访客模式的方法

谷歌浏览器怎么使用访客模式?谷歌浏览器是一款强大的搜索引擎&#xff0c;谷歌浏览器的访客模式可以有效的保护自己电脑浏览器的信息安全&#xff0c;要怎么设置访客模式呢&#xff0c;下面就给大家分享具体步骤。 1、在Chrome浏览器地址栏输入&#xff1a;chrome://flags/#e…