call,apply,bind的用法与区别

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

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

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

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

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

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

 

2.Function.prototype.call()

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

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

复制代码
 1     var keith = {2         rascal: 1233     };4 5     var rascal = 456;6 7     function a() {8         console.log(this.rascal);9     }
10 
11     a(); //456
12     a.call(); //456
13     a.call(null); //456
14     a.call(undefined); //456
15     a.call(this); //456
16     a.call(keith); //123
复制代码

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

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

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

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

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

复制代码
 1     var obj = {};2     console.log(obj.hasOwnProperty('toString')); //false3 4     obj.hasOwnProperty = function() {5         return true;6     }7 8     console.log(obj.hasOwnProperty('toString')); //true9 
10     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的细微差别。

复制代码
1     function keith(a, b) {
2         console.log(a + b);
3     }
4 
5     keith.call(null, 2, 3); //5
6     keith.apply(null, [2, 3]); //5
复制代码

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

  apply方法有以下应用。

 

  3.1:找出数组中的最大数

1     var a = [2, 4, 5, 7, 8, 10];
2 
3     console.log(Math.max.apply(null, a)); //10
4     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。

1 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都可以实现该应用。

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

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

 

  

4.Function.prototype.bind()

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

复制代码
 1     var keith = {2         a: 1,3         count: function() {4             console.log(this.a++);5         }6     };7 8     keith.count(); //19     keith.count(); //2
10     keith.count(); //3
复制代码

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

复制代码
1     var keith = {
2         a: 1,
3         count: function() {
4             console.log(this.a++);
5         }
6     };
7 
8     var f = keith.count;
9     f(); //NaN
复制代码

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

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

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

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

复制代码
1     var obj = {
2         a: 100
3     };
4     var f = keith.count.bind(obj);
5     f(); //100
6     f(); //101
7     f(); //102
复制代码

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

复制代码
1     function keith(a, b) {
2         return a + b;
3     }
4     console.log(keith.apply(null,[1,4])); //5
5     console.log(keith.call(null,1,4)); //5
6     console.log(keith.bind(null, 1, 4)); //keith()
7     console.log(keith.bind(null, 1, 4)()); //5
复制代码

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

 

5.绑定回调函数的对象

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

复制代码
 1     var o = {2         f: function() {3             console.log(this === o);4         }5     }6 7     $('#button').on('click', function() {8         o.f.apply(o);9         //或者 o.f.call(o);
10         //或者 o.f.bind(o)();
11     });
复制代码

  点击按钮以后,控制台将会显示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的指向。

转载于:https://www.cnblogs.com/Jade-Liu18831/p/9580410.html

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

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

相关文章

幅值与峰峰值的计算_电厂振动测量、计算基础及汽轮机组振动标准!

法律顾问:张友全律师正文:电厂振动测量、计算基础及汽轮机组振动标准!1. 常用的振动测量参数常用的振动测量参数有振幅、振动速度(振速)、振动加速度。对应单位表示为:mm、mm/s、mm/s。 振幅是表象,定义为在波动或振动…

实例15:python

#利用条件运算符的嵌套来完成此题:学习成绩>90分的同学用A表示,60-89分之间的用B表示,60分以下的用C表示。 #!/usr/bin/python -- coding: UTF-8 -- score int(input(‘输入分数:\n’)) if score > 90: grade ‘A’ elif score &g…

对比分析

【课程1.3】 对比分析对比分析 → 两个互相联系的指标进行比较绝对数比较(相减) / 相对数比较(相除) 结构分析、比例分析、空间比较分析、动态对比分析 import numpy as np import pandas as pd import matplotlib.pyplot as plt…

平面设计中的网格系统pdf_深入浅出,带你认识网格系统与版式设计

设计分享 研习设 设计Hello大家好,这里是研习设计分享。时不时分享/新鲜/有趣/有料/设计资讯和案例,欢迎大家订阅关注!~预估阅读时间:10分钟ME研习设小研研:今天,和大家深入浅出,带你认识网格系…

实例16:python

#输出指定格式的日期。 import time print(time.time()) print(time.localtime()) print(time.asctime()) print(time.strftime(’%Y-%m-%d %H:%M:%S’,time.localtime())) import datetime print(datetime.date.today()) print(datetime.date.today().strftime(’%d/%m/%Y’…

cad直线和圆弧倒角不相切_曲线操作-直线,圆弧,圆,倒斜角

直线该命令用于创建直线段。执行直线命令,主要有以下两种方式。菜单:选择“菜单”“捕入”一“曲线” 一“直线” 命令。功能区:单击“曲线”选项卡“曲线” 组中的“直线”按钮。执行上述操作后,系统打开如图4.13 所示的“直线”对话框。“直线”对话框…

WPF vs2015,vs2012 添加ArcObjects SDK

一:如果是vs2015,找到注册表中HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\VisualStudio\14.0\下面的这两个,并且将名称和数据记下来。如图: (如果是vs2012,只添加InstallDir及其数据值就行了。) 二…

实例17:python

#输入一行字符,分别统计出其中英文字母、空格、数字和其它字符的个数。 #!/usr/bin/python3 a input(‘请输入一串字符:’) 英文 0 空格 0 数字 0 其他 0 for i in a: if i.isalpha(): 英文 1 elif i.isspace(): 空格 1 elif i.isnumeric(): 数字 1 else: 其他…

bow 折叠键盘 linux,bow便携折叠蓝牙键盘体验

bow便携折叠蓝牙键盘体验2019-10-07 02:29:3913点赞8收藏0评论买了个键盘 bow的hb188似乎对蓝牙键盘一直以来都有一种执念,自从想要写点什么东西的想法在脑子里面生根发芽之后,一直都在找一种相对来讲比较便携的键盘。一开始的时候想买苹果的原装键盘&am…

admin——django自带数据库管理工具

admin是Django自带的数据库管理工具,是一个app 在admin.py中可以自己定制类来使admin数据库管理页面展现自己想要查看的信息 models.py from django.db import models# Create your models here.class Book(models.Model):title models.CharField(max_length32)pri…

remote addr 取到内网ip_内网穿透工具frp

前言渗透渗透中面对的大部分目标除了暴露在互联网的部分,还有隐藏在内网的大量资产,这部分资产往往无法联通外网,很少暴露在攻击者面前,与此同时安全一般也没有外部资产做的全面 。如果我们从某一外部互联网入口成功进入了内网&am…

实例18:python

#题目:求saaaaaaaaaaaa…a的值,其中a是一个数字。例如222222222222222(此时共有5个数相加),几个数相加由键盘控制。 #!/usr/bin/env python3 -- coding: utf-8 -- n int(input(n )) a int(input(a )) sum 0 total 0 for i in range(…

AQS是什么?

AQS介绍AQS,即AbstractQueuedSynchronizer, 队列同步器,它是Java并发用来构建锁和其他同步组件的基础框架。来看下同步组件对AQS的使用:AQS是一个抽象类,主是是以继承的方式使用。AQS本身是没有实现任何同步接口的,它仅…

实例19:python

#题目:一个数如果恰好等于它的因子之和,这个数就称为"完数"。 #例如61+2+3.编程找出1000以内的所有完数。 #!/usr/bin/python3 list2 [] for x in range(1, 1001): list1 [] for i in range(1, int(x / 2) 1): if x…

实例20:python

#题目:一球从100米高度自由落下,每次落地后反跳回原高度的一半;再落下 #,求它在第10次落地时,共经过多少米?第10次反弹多高? #!/usr/bin/python -- coding: UTF-8 -- tour [] height [] h…

圆弧齿轮啮合原理_图解八种齿轮的加工原理

齿形有多种形式,其中以渐开线齿形最为常见。渐开线齿形常用的加工方法有两大类,即成形法和展成法。1铣齿采用盘形模数铣刀或指状铣刀铣齿属于成形法加工,铣刀刀齿截面形状与齿轮齿间形状相对应。2成形磨齿也属于成形法加工,成形砂…

单片机c语言1602怎么接线,lcd1602中文资料分享:lcd1602接线图_lcd1602与单片机连接图 - 全文...

lcd1602液晶屏在很懂工业产品上都有应用,LCD1602能够能够同时显示32个字符,价格便宜,编程简单而且稳定可靠。lcd1602液晶屏是一种图形点阵显示器,显示原理简单易懂,都是液晶屏内部的液晶材料变化而显示不同的字符&…

[SimplePlayer] 1. 从视频文件中提取图像

在开始之前,我们需要了解视频文件的格式。视频文件的格式众多,无法三言两语就能详细分析其结构,尽管如此,ffmpeg却很好地提取了各类视频文件的共同特性,并对其进行了抽象描述。 视频文件格式,统称为contain…

实例21:python

#猴子吃桃问题:猴子第一天摘下若干个桃子, #当即吃了一半,还不瘾,又多吃了一个第二天早上又将剩下的桃子吃掉一半, #又多吃了一个。以后每天早上都吃了前一天剩下的一半零一个。 #到第10天早上想再吃时,见只…

android5.1 显示方向,Android5.1 Settings.apk定制显示选项

在Android5.0后,系统应用的目录结构发生了一些变化,以往/system/app/下直接是APK文件,目前是/system/app/应用名目录/应用apk类似这种目录结构。同时在Android5.1上反编译Settings.apk需要使用最新apktool_2.0.3来反编译,否则无法…