javascript学习笔记之Call()和Apply()

来源:互联网 发布:ps素材软件 编辑:程序博客网 时间:2024/04/29 08:05

一、 语法

首先call()和apply()的语法
apply()
fun.apply(thisArg[, argsArray])
apply(this,[1,2,3,4])
call()
fun.call(this Arg[, arg1[, arg2[, …]]])
call(this,1,2,3,4)

二、 call()的用法

(1) 使用call方法调用父构造函数

在一个子构造函数中,你可以通过调用父构造函数的 call 方法来实现继承,类似于Java中的写法。下例中,使用 Food 和 Toy 构造函数创建的对象实例都会拥有在 Product 构造函数中添加的 name 属性和 price 属性,但 category 属性是在各自的构造函数中定义的。

function product(name,price){    this.name=name    this.price=price    //console.log("name:"+name+"price"+price)}
//对象是由function创造的function toy(name,price) {    product.call(this, name, price) //有点类似于c++里的构造函数,toy()在这里重构,并且加入新的参数    this.category = 'toy'}function food(name,price){    product.call(this,name,price)    this.category = 'food'}var cheese = new food('feta', 5);console.log(cheese.category+";"+cheese.name+";"+cheese.price+"\n")var fun = new toy('robot', 40)console.log(fun.category+";"+fun.name+";"+fun.price+"\n")

(2) 使用call方法调用匿名函数

在下例中的for循环体内,我们创建了一个匿名函数,然后通过调用该函数的call方法,将每个数组元素作为指定的this值执行了那个匿名函数。这个匿名函数的主要目的是给每个数组元素对象添加一个print方法,这个print方法可以打印出各元素在数组中的正确索引号。当然,这里不是必须得让数组元素作为this值传入那个匿名函数(普通参数就可以),目的是为了演示call的用法。

var animals =[  {species:'Lion', name:'King'},  {species:'Whale', name:'Fail'}];for(var i =0; i < animals.length; i++){  (function(i){    this.print =function(){      console.log('#'+ i  +' '+this.species +': '+this.name);    }    this.print();  }).call(animals[i], i);}

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

function greet() {    var reply = [this.person, 'Is An Awesome', this.role].join(' ');    console.log(this);    console.log(reply);}var i = {    person: 'Douglas Crockford', role: 'Javascript Developer'};greet()//这样子的thisglobalgreet.call(i);//这样子this指的就是i

三、 apply

(1) 使用apply和内置函数

apply用法允许你在某些本来需要写成遍历数组变量的任务中使用内建的函数

var numbers=[1,5,3,2,4]var max=Math.max.apply(null,numbers) /* This about equal to Math.max(numbers[0], ...) or Math.max(1,5,3,, ..) */var max2=Math.max(numbers)var max3=Math.max(1,5,3,2,4)console.log(max)console.log(max2) //NANconsole.log(max3) //5var min=Math.min.apply(null,numbers)

对于数组大小不确定的情况下很有用。
但是当心:如果用上面的方式调用 apply, 你很可能会遇到方法参数个数越界的问题. 当你对一个方法传入非常多的参数 (比如超过1W多个参数) 时, 就非常有可能会导致越界问题, 这个临界值是根据不同的 JavaScript 引擎而定的 (JavaScript 核心中已经做了硬编码 参数个数限制在65536),因为这个限制(实际上也是任何用到超大栈空间的行为的自然表现)是未指定的. 有些引擎[引擎是指的什么?]会抛出异常. 更糟糕的是其他引擎会直接限制传入到方法的参数个数,导致参数丢失. (举个例子: 如果某个引擎限制了方法参数最多为4个 [实际真正的参数个数限制当然要高得多了, 这里只是打个比方], 上面的代码中, 真正通过 apply 传到目标方法中的参数为1,5,3,2 而不是完整的 numbers 数组.) 如果你的参数数组可能非常大, 那么推荐使用下面这种策略来处理: 将参数数组切块后循环传入目标方法:

functionminOfArray(arr){  var min =Infinity;  var QUANTUM =32768;  for(var i =0, len = arr.length; i < len; i += QUANTUM){    var submin = Math.min.apply(null, arr.slice(i, Math.min(i + QUANTUM, len)));    min = Math.min(submin, min);  }  return min;}var min =minOfArray([5,6,2,3,7]);

(2) 在”monkey-patching”中使用apply

Apply可以作为monkey-patch一个Firefox或JS库内建函数的最好方式。对于someobject.foo 函数,你可以用一种旁门左道的方式来修改这个函数,像这样:

varoriginalfoo =someobject.foo;someobject.foo =function(){  //在调用函数前干些什么  console.log(arguments);  //像正常调用这个函数一样来进行调用:  originalfoo.apply(this,arguments);  //在这里做一些调用之后的事情。}

这种方法在你想要debug事件,或者与一些没有API的东西配合,例如多种 .on([event]… events, 比如那些在Devtools Inspector中可用的).
(3) 使用apply来链接构造器

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

Function.prototype.construct =function(aArgs){  var oNew = Object.create(this.prototype);  this.apply(oNew, aArgs);  return oNew;};

注意: 上面使用的Object.create()方法相对来说比较新。另一种可选的方法是使用闭包,请考虑如下替代方法:

Function.prototype.construct =function(aArgs){  var fConstructor =this,fNewConstr =function(){    fConstructor.apply(this, aArgs);  };  fNewConstr.prototype =fConstructor.prototype;  returnnewfNewConstr();};

使用案例:

functionMyConstructor (){    for(var nProp =0; nProp < arguments.length; nProp++){        this["property"+ nProp]= arguments[nProp];    }}var myArray =[4,"Helloworld!",false];varmyInstance =MyConstructor.construct(myArray);console.log(myInstance.property1);                // logs"Hello world!"console.log(myInstanceinstanceofMyConstructor);// logs"true"console.log(myInstance.constructor);              // logs"MyConstructor"

(4) 说一下Object.create()

Object.create() 顾名思义,就是对象的创建,用法类似于new construct()
Object.create()

为了解决”构造函数法”的缺点,更方便地生成对象,Javascript的国际标准ECMAScript第五版(目前通行的是第三版),提出了一个新的方法Object.create()。
用这个方法,”类”就是一个对象,不是函数。

var Cat = {name: "大毛",makeSound:function(){ alert("喵喵喵"); }};

然后,直接用Object.create()生成实例,不需要用到new。

var cat1 =Object.create(Cat);alert(cat1.name);// 大毛cat1.makeSound();// 喵喵喵目前,各大浏览器的最新版本(包括IE9)都部署了这个方法。如果遇到老式浏览器,可以用下面的代码自行部署。if(!Object.create) {Object.create= function (o) {functionF() {}F.prototype= o;return newF();};}

这种方法比”构造函数法”简单,但是不能实现私有属性和私有方法,实例对象之间也不能共享数据,对”类”的模拟不够全面。
Object=new construct()

这是经典方法,也是教科书必教的方法。它用构造函数模拟”类”,在其内部用this关键字指代实例对象。

functionCat() {this.name ="大毛";}生成实例的时候,使用new关键字。var cat1 =new Cat();alert(cat1.name);// 大毛类的属性和方法,还可以定义在构造函数的prototype对象之上。Cat.prototype.makeSound= function(){alert("喵喵喵");}

参考
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/apply

0 0