资讯专栏INFORMATION COLUMN

回味JS基础:call apply 与 bind

plokmju88 / 2476人阅读

摘要:使用方法调用匿名函数在下例中的循环体内,我们创建了一个匿名函数,然后通过调用该函数的方法,将每个数组元素作为指定的值执行了那个匿名函数。这个匿名函数的主要目的是给每个数组元素对象添加一个方法,这个方法可以打印出各元素在数组中的正确索引号。

原文:回味JS基础:call apply 与 bind

在JavaScript中,callapplybindFunction对象自带的三个方法,本文将通过几个场景的应用,来详细理解三个方法。

call()

call() 方法在使用一个指定的this值和若干个指定的参数值的前提下调用某个函数或方法。

当调用一个函数时,可以赋值一个不同的 this 对象。this 引用当前对象,即 call 方法的第一个参数。

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

</>复制代码

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

thisArg

在fun函数运行时指定的this值。需要注意的是下面几种情况

(1)不传,或者传nullundefined, 函数中的this指向window对象
(2)传递另一个函数的函数名,函数中的this指向这个函数的引用,并不一定是该函数执行时真正的this
(3)值为原始值(数字,字符串,布尔值)的this会指向该原始值的自动包装对象,如 StringNumberBoolean
(4)传递一个对象,函数中的this指向这个对象

arg1, arg2, ...

指定的参数列表。

例子

初级应用例子

</>复制代码

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

使用call方法调用匿名函数并且指定上下文的this

在下面的例子中,当调用 greet 方法的时候,该方法的 this 值会绑定到 i对象。

</>复制代码

  1. function greet() {
  2. var reply = [this.person, "是一个轻量的", this.role].join(" ");
  3. console.log(reply);
  4. }
  5. var i = {
  6. person: "JSLite.io", role: "Javascript 库。"
  7. };
  8. greet.call(i);
  9. // JSLite.io 是一个轻量的 Javascript 库。

使用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

使用call方法调用函数传参数

</>复制代码

  1. var a = {
  2. name:"JSLite.io", //定义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");
  13. //Post params: test
  14. //I"m JSLite.io
  15. //I"m function a!
apply()

语法与 call() 方法的语法几乎完全相同,唯一的区别在于,apply的第二个参数必须是一个包含多个参数的数组(或类数组对象)。apply的这个特性很重要,

在调用一个存在的函数时,你可以为其指定一个 this 对象。 this 指当前对象,也就是正在调用这个函数的对象。 使用 apply, 你可以只写一次这个方法然后在另一个对象中继承它,而不用在新对象中重复写该方法。

</>复制代码

  1. 语法:fun.apply(thisArg[, argsArray])

注意: 需要注意:Chrome 14 以及 Internet Explorer 9 仍然不接受类数组对象。如果传入类数组对象,它们会抛出异常。

参数

thisArg

同上callthisArg参数。

argsArray

一个数组或者类数组对象,其中的数组元素将作为多带带的参数传给 fun 函数。如果该参数的值为nullundefined,则表示不需要传入任何参数。从ECMAScript 5 开始可以使用类数组对象。

例子

</>复制代码

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

使用apply来链接构造器的例子

你可以使用apply来给一个对象链接构造器,类似于Java. 在接下来的例子中我们会创建一个叫做construct的全局的Function函数,来使你能够在构造器中使用一个类数组对象而非参数列表。

</>复制代码

  1. Function.prototype.construct = function(aArgs) {
  2. var fConstructor = this,
  3. fNewConstr = function() {
  4. fConstructor.apply(this, aArgs);
  5. };
  6. fNewConstr.prototype = fConstructor.prototype;
  7. return new fNewConstr();
  8. };
  9. function MyConstructor () {
  10. for (var nProp = 0; nProp < arguments.length; nProp++) {
  11. console.log(arguments,this)
  12. this["property" + nProp] = arguments[nProp];
  13. }
  14. }
  15. var myArray = [4, "Hello world!", false];
  16. var myInstance = MyConstructor.construct(myArray);
  17. console.log(myInstance.property1); // logs "Hello world!"
  18. console.log(myInstance instanceof MyConstructor); // logs "true"
  19. console.log(myInstance.constructor); // logs "MyConstructor"

使用apply和内置函数

聪明的apply用法允许你在某些本来需要写成遍历数组变量的任务中使用内建的函数。在接下里的例子中我们会使用Math.max/Math.min来找出一个数组中的最大/最小值。

</>复制代码

  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);
  7. //通常情况我们会这样来找到数字的最大或者最小值
  8. //比对上面的栗子,是不是下面的看起来没有上面的舒服呢?
  9. max = -Infinity, min = +Infinity;
  10. for (var i = 0; i < numbers.length; i++) {
  11. if (numbers[i] > max)
  12. max = numbers[i];
  13. if (numbers[i] < min)
  14. min = numbers[i];
  15. }

参数数组切块后循环传入

</>复制代码

  1. function minOfArray(arr) {
  2. var min = Infinity;
  3. var QUANTUM = 32768;
  4. for (var i = 0, len = arr.length; i < len; i += QUANTUM) {
  5. var submin = Math.min.apply(null, arr.slice(i, Math.min(i + QUANTUM, len)));
  6. console.log(submin, min)
  7. min = Math.min(submin, min);
  8. }
  9. return min;
  10. }
  11. var min = minOfArray([5, 6, 2, 3, 7]);
bind

bind() 函数会创建一个新函数(称为绑定函数)

bind是ES5新增的一个方法

传参和call或apply类似

不会执行对应的函数,call或apply会自动执行对应的函数

返回对函数的引用

</>复制代码

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

下面例子:当点击网页时,EventClick被触发执行,输出JSLite.io p1 p2, 说明EventClick中的thisbind改变成了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. }

兼容例子来源于:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/bind#Compatibility

应用场景:继承

</>复制代码

  1. function Animal(name,weight){
  2. this.name = name;
  3. this.weight = weight;
  4. }
  5. function Cat(){
  6. // 在call中将this作为thisArgs参数传递
  7. // Animal方法中的this就指向了Cat中的this
  8. // 所以Animal中的this指向的就是cat对象
  9. // 在Animal中定义了name和weight属性,就相当于在cat中定义了这些属性
  10. // cat对象便拥有了Animal中定义的属性,从而达到了继承的目的
  11. Animal.call(this,"cat","50");
  12. //Animal.apply(this,["cat","50"]);
  13. this.say = function(){
  14. console.log("I am " + this.name+",my weight is " + this.weight);
  15. }
  16. }
  17. //当通过new运算符产生了cat时,Cat中的this就指向了cat对象
  18. var cat = new Cat();
  19. cat.say();
  20. //输出=> I am cat,my weight is 50
原型扩展

在原型函数上扩展和自定义方法,从而不污染原生函数。例如:我们在 Array 上扩展一个 forEach

</>复制代码

  1. function test(){
  2. // 检测arguments是否为Array的实例
  3. console.log(
  4. arguments instanceof Array, //false
  5. Array.isArray(arguments) //false
  6. );
  7. // 判断arguments是否有forEach方法
  8. console.log(arguments.forEach);
  9. // undefined
  10. // 将数组中的forEach应用到arguments上
  11. Array.prototype.forEach.call(arguments,function(item){
  12. console.log(item); // 1 2 3 4
  13. });
  14. }
  15. test(1,2,3,4);

关注公众号

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/78834.html

相关文章

  • JS函数的一些基础知识

    摘要:为了更好的讲解构造函数原型原型链等,我们需要复习一下函数的一些基础知识。闭包在复习了函数的相关基础知识之后,我们终于可以进入闭包。我们可以这样理解闭包,首先,闭包是一个函数,是一个什么样子的函数呢是一个可以访问另一个函数中变量的函数。 为了更好的讲解构造函数、原型、原型链等,我们需要复习一下函数的一些基础知识。接下来,就让我们一起回味一些JS的基础知识点。 全局变量VS局部变量 简单粗...

    Ashin 评论0 收藏0
  • JS基础篇--callapplybind方法详解

    摘要:首先我们可以通过给目标函数指定作用域来简单实现方法保存,即调用方法的目标函数考虑到函数柯里化的情况,我们可以构建一个更加健壮的这次的方法可以绑定对象,也支持在绑定的时候传参。原因是,在中,多次是无效的。而则会立即执行函数。 bind 是返回对应函数,便于稍后调用;apply 、call 则是立即调用 。 apply、call 在 javascript 中,call 和 apply 都是...

    lastSeries 评论0 收藏0
  • js 面试官想了解你有多理解call,apply,bind?

    摘要:返回值这段在下方应用中有详细的示例解析。回调函数丢失的解决方案绑定回调函数的指向这是典型的应用场景绑定指向,用做回调函数。 showImg(https://segmentfault.com/img/remote/1460000019971331?w=1024&h=680); 函数原型链中的 apply,call 和 bind 方法是 JavaScript 中相当重要的概念,与 this...

    wuaiqiu 评论0 收藏0
  • js基础】之this,call,apply,bind

    摘要:的使用场景作为构造函数执行作为对象属性执行作为普通函数执行。要在执行时才能确认值,定义时无法确认改变上下文指向。 1.this this的使用场景: 1.作为构造函数执行; 2.作为对象属性执行; 3.作为普通函数执行; 4.call apply bind。 this要在执行时才能确认值,定义时无法确认 var a = { name:A, fn:function()...

    khs1994 评论0 收藏0
  • 从一道面试题,到“我可能看了假源码”

    摘要:返回的绑定函数也能使用操作符创建对象这种行为就像把原函数当成构造器。同时,将第一个参数以外的其他参数,作为提供给原函数的预设参数,这也是基本的颗粒化基础。 今天想谈谈一道前端面试题,我做面试官的时候经常喜欢用它来考察面试者的基础是否扎实,以及逻辑、思维能力和临场表现,题目是:模拟实现ES5中原生bind函数。也许这道题目已经不再新鲜,部分读者也会有思路来解答。社区上关于原生bind的研...

    Carson 评论0 收藏0

发表评论

0条评论

plokmju88

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<