JavaScript之call() 、apply() 、bind()函数

来源:互联网 发布:gns3端口8000被拒绝 编辑:程序博客网 时间:2024/06/06 00:46

      在看一段代码的时候,遇到了bind()函数,当时看到的时候有点懵了,不是特别理解这个函数的使用以及作用,于是查找了一些资料之后,终于明白了一些,在查资料的过程中,看到将这个函数与call()和apply()函数对比的文章还不少,于是我想补充并整理一下这三个方法的使用。

      我们可以看到call ()和 apply()的定义说明:call()和apply() 是函数的非继承方法,在某个特定作用范围内调用某个函数,设置函数体内this对象的值,其实质就是调用上下文,改变this的指向。绑定函数bind()最简单的用法是创建一个函数,使这个函数不论怎么调用都有同样的this值。不同于call()和apply()只是单纯地设置this的值后传参,它还会将所有传入bind()方法中的实参(第一个参数之后的参数)与this一起绑定。接下来详细说明一下这三个方法的使用。

1、call()方法

  • 定义:调用一个对象的一个方法,以另一个对象替换当前对象。
  • 方法:call([thisObj[,arg1[, arg2[,  [,.argN]]]]])
  • 参数:thisObj    可选项。将被用作当前对象的对象。arg1, arg2,  , argN    可选项。将被传递方法参数序列。
  • 说明:call ()方法可以用来代替另一个对象调用一个方法。call ()方法可将一个函数的对象上下文从初始的上下文改变为由 thisObj 指定的新对象。如果没有提供 thisObj 参数,那么 Global 对象被用作 thisObj。
看一个例子。
function add(a,b){console.log(a+b);}function sub(a,b){ console.log(a-b);}add.call(sub,3,2); //结果:5
       我们可以看到最后一行代码,可以理解为 sub对象 调用了 add对象 的方法,并把参数也带入方法里,所以执行结果是5。我们再看一个实现继承的情况。
function Animal(name){       this.name = name;       this.showName = function(){       console.log(this.name);     }   }   function Cat(name){     Animal.call(this, name);  }   var cat = new Cat("Black Cat");   cat.showName(); //Black Cat
     我们可以看到在Cat对象中调用了call()方法,并传入一个参数,将this指向的对象改为Animal来实现继承,那么在Cat()对象中就有了Animal的属性以及方法。

2、apply()方法

  • 语法:apply([thisObj[,argArray]]) 
  • 定义:应用某一对象的一个方法,用另一个对象替换当前对象。 
  • 说明: 如果 argArray 不是一个有效的数组或者不是 arguments 对象,那么将导致一个 TypeError。 如果没有提供 argArray 和 thisObj 任何一个参数,那么 Global 对象将被用作 thisObj, 并且无法被传递任何参数。
直接写一个例子。
/*定义一个人类*/ function Person(name,age) { this.name = name; this.age = age; } /*定义一个学生类*/ function Student(name,age,grade) { Person.apply(this,arguments); this.grade = grade; } /*创建一个学生类 */var student = new Student("Wendy",21,"研二"); /*测试 */console.log("name:"+student.name+"\n"+"age:"+student.age+"\n"+"grade:"+student.grade);
      其中,Person.apply(this,arguments); this:在创建对象在这个时候代表的是Student,arguments:是一个数组,也就是["Wendy",21,"研二"]; 通俗一点讲就是:用Student去执行Person这个类里面的内容,在Person这个类里面存在this.name等之类的语句,这样就将属性创建到了Student对象里面,所以最终可以得到Student中三个属性的值。
      apply()的一些巧妙应用方法。
1)Math.max 可以实现得到数组中最大的一项
     因为Math.max 参数里面不支持Math.max([param1,param2]) 也就是数组,但是它支持Math.max(param1,param2,param3…),所以可以根据刚才apply的那个特点来解决 var max=Math.max.apply(null,array),这样轻易的可以得到一个数组中最大的一项(apply会将一个数组装换为一个参数接一个参数的传递给方法)。这块在调用的时候第一个参数给了一个null,这个是因为没有对象去调用这个方法,我只需要用这个方法帮我运算,得到返回的结果就行,所以直接传递了一个null过去。如果把null写成this也是可以的,因为这个this指的是这个方法中的this,同样可以达到这个效果。
    如下代码所示:
var arr = [1,5,3,6,10,5,9];var max=Math.max.apply(null,arr);console.log(max); //输出10
2)Math.min 可以实现得到数组中最小的一项
     同样和 max是一个思想 var min=Math.min.apply(null,array);
如下所示:
var arr = [1,5,3,6,10,5,9];var min=Math.min.apply(null,arr);console.log(min);//输出1
3)Array.prototype.push 可以实现两个数组合并
   同样push方法没有提供push一个数组,但是它提供了push(param1,param,…paramN) 所以同样也可以通过apply来装换一下这个数组,也可以这样理解,arr1调用了push方法,参数是通过apply将数组装换为参数列表的集合。即:
var arr1 = new Array("1","2","3"); var arr2 = new Array("4","5","6"); Array.prototype.push.apply(arr1,arr2);console.log(arr1);//[ '1', '2', '3', '4', '5', '6' ]
    在这个例子中,如果我们按照平时的方法,要把arr2拼接到arr1中的话,我们需要把 arr2展开,然后一个一个追加到arr1中去,最后让arr1=[1,2,3,4,5,6],代码如下所示,直接用arr1.push(arr2)显然是不行的。
var len = arr2.length;for(var i = 0 ; i < len ; i++){    arr1.push(arr2[i]); }console.log(arr1);//[ '1', '2', '3', '4', '5', '6' ]

3、call()和apply()的区别

  • call(),apply()都属于Function.prototype的一个方法,它是JavaScript引擎内在实现的,因为属于Function.prototype,所以每个Function对象实例(就是每个方法)都有call,apply属性。既然作为方法的属性,那它们的使用就当然是针对方法的了,这两个方法是容易混淆的,因为它们的作用一样,只是使用方式不同。我们在继承中也经常会利用这两个方法实现继承。
  • 语法:foo.call(this, arg1,arg2,arg3) == foo.apply(this, arguments) == this.foo(arg1, arg2, arg3);
  • 相同点:两个方法产生的作用是完全一样的。
  • 不同点:方法传递的参数不同。

4、bind()方法

     bind()方法是在ES5中新增的方法,从名字就可以看出,这个方法的主要作用就是将函数绑定至某个对象,最简单的用法是创建一个函数,使这个函数不论怎么调用都有同样的this值。

  • 语法:fun.bind(this,arg1,arg2,...)
  • 定义:bind()方法会创建一个新的函数,称为绑定函数,fun方法在this环境下调用
  • 参数:该方法可传入两个参数,第一个参数作为this,第二个及以后的参数则作为函数的参数调用

    我们先来看看《JavaScript权威指南》中写到的一个例子:

var sum = function(x,y) { return x + y }; //返回两个实参的和值//创建一个类似sum的新函数,但this的值绑定到null,并且第一个参数绑定到1,这个新的函数期望只传入一个实参var succ = sum.bind(null, 1); //让this指向null,其后的实参也会作为实参传入被绑定的函数sumsucc(2); // => 3: 可以看到1绑定到了sum函数中的x,并传入2作为实参y
     ES5中的bind()方法不仅仅是将函数绑定至一个对象,它还附带一些其他应用;除了第一个实参之外,传入bind()的实参也会绑定至this,这个附带的应用是一种常见的函数式编程,有时也称为“科里化”。
     bind()方法所返回的函数的length(形参数量)等于原函数的形参数量减去传入bind()方法中的实参数量(第一个参数以后的所有参数),因为传入bind中的实参都会绑定到原函数的形参,比如:

function func(a,b,c,d){}  //func的length为4var after = func.bind(null,1,2);  //这里输入了两个实参(1,2)绑定到了func函数的a,bconsole.log(after.length);  //after的length为2function f(y,z){ return this.x+y+z;}var g = f.bind({x:1},2);  //绑定this和yconsole.log(g(3));  //结果为6,this.x绑定到1,y绑定到2,z绑定到3console.log(g.length);
         另外,ES5中的bind()方法可以顺带做构造函数,如果bind()返回的函数用作构造函数,将会忽略bind()的this,原始函数就会以构造函数的形式调用,其实参也已经绑定。
      例子1:

var num = 9; var module = {     num: 81,    getNum: function(){        console.log(this.num);    }}; module.getNum(); // 81 ,this->module var getNum = module.getNum;getNum(); // 9, this->window or global var boundGetNum = getNum.bind(module,10); boundGetNum(); // 81,this->module

例子2:

function original(x){  this.a = 1;  this.b = function(){return this.a + x}}var obj={  a = 10}var newObj = new(original.bind(obj, 2)); //传入了一个实参2console.log(newObj.a);  //输出1, 说明返回的函数用作构造函数时obj(this的值)被忽略了console.log(newObj.b()); //输出3 ,说明传入的实参2传入了原函数original


1 0
原创粉丝点击