apply,call,bind的用法

Lessong發表於2019-02-16

apply()

Function.prototype.apply()將會呼叫一個以this和陣列形式的arguments為引數的方法。
m

fun.apply(thisArg,[argsArray])

每當要為一個新的物件新增一個方法時,有時不得不為其重寫一個方法。而如果利用apply的話,只需要寫一次方法,然後在這個新的物件中繼承它即可,十分方便。
apply和call方法十分相似,僅僅只是引數不同而已,但正是因為這一點,我們在用apply時不必知道被呼叫的物件的具體引數,可以只穿arguments,如此一來,被呼叫的這個物件將負責handle觸底的arguments。
1.我們可以像Java那樣,利用apply為一個物件建立構造鏈。在下面的例子中,我們將建立一個名為construct的全域性方法,這個方法可以不必讓你傳遞一個一個的引數,取而代之的則是傳遞一個引數陣列。當地一個引數為null時,指向的時window物件。

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

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

Function.prototype.construct = function (aArgs) {
  var fNewConstr = new Function("");
  fNewConstr.prototype = this.prototype;
  var oNew = new fNewConstr();
  this.apply(oNew, aArgs);
  return oNew;
};
function MyConstructor() {
  for (var nProp = 0; nProp < arguments.length; nProp++) {
    this[`property` + nProp] = arguments[nProp];
  }
}
var myArray = [4, `Hello world!`, false];
var myInstance = MyConstructor.construct(myArray);
console.log(myInstance.property1);                // logs `Hello world!`
console.log(myInstance instanceof MyConstructor); // logs `true`
console.log(myInstance.constructor);  
function minOfArray(arr){
    var min = Infinity;
    var QUANTUM = 32768;
    var len=arr.length;
    for(var i=0;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]);
console.log(min)//2
    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("zhangsan",21,"一年級");  
    //測試  
    alert("name:"+student.name+"
"+"age:"+student.age+"
"+"grade:"+student.grade);  

call()

call()方法呼叫一個函式,其具體有一個指定的this值和分別地提供的引數。
注意:

該方法的作用和apply()方法類似,只有一個區別,就是call()方法接受的是若干個引數的列表,而apply()方法接受的是一個包含多個引數的陣列。

fun.call(thisArg,arg1,arg2,…)
引數:

在fun函式執行時指定的是this值。需要注意的是,指定的this值並不一定是該函式執行時真正的this值,如果這個函式處於非嚴格模式下,則指定為null和undefinde的this值會自動指向全域性物件,同時值為原始值(數字,字串,布林值)的this會指向該原始值的自動包裝物件。
arg1,arg2指定的引數列表。

返回值:

返回值是你呼叫的方法的返回值,若該方法沒有返回值,則返回undefined。

call()允許為不同的物件分配和呼叫屬於一個物件的函式/方法。
可以讓call()中的物件呼叫當前物件所擁有的function。你剋以使用call()來實現繼承:寫一個方法,然後讓另一個新的物件來繼承它。

function Product(name,price){
    this.name = name;
    this.price = price;
}
function Food(name,price){
    Product.call(this,name,price);
    this.category = `food`
}
console.log(new Food(`cheese`,5).name)//cheese
//在一個子勾走函式中,你可以通過呼叫父建構函式的call方法來實現繼承,類似於Java中的寫法。下例中,使用Food和Toy建構函式建立的物件示例都會擁有Product建構函式中新增的name屬性和price屬性,但category屬性是在各自的建構函式中定義的。
function Food(name, price) {
  this.name = name;
  this.price = price;
  if (price < 0) {
    throw RangeError(
      `Cannot create product ` + this.name + ` with a negative price`
    );
  }

  this.category = `food`;
}

//function Toy 同上
function Toy(name, price) {
  Product.call(this, name, price);
  this.category = `toy`;
}

var cheese = new Food(`feta`, 5);
var fun = new Toy(`robot`, 40);

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

使用call方法呼叫函式並且指定上下文的this
在下面的例子中,當呼叫greet方法的時候,該方法的this值會繫結到i物件。

function greet(){
    var reply = [this.person,`Is An Awesome`,this.role].join(``);
    console.log(reply);
}
var i = {
    person:`Douglas Crockford`,
    role:`JavaScript DeveLoper`
}
greet.call(i);// Douglas Crockford Is An Awesome Javascript Developer

bind()

bind()方法建立一個新的函式,當這個新函式被呼叫時this鍵值為其提供的值,其引數列表前幾項值為建立時指定的引數序列。
語法:

fun.bind(thisArg[,arg1[,arg2[,...]]])

引數:

thisArg呼叫繫結函式時作為this引數傳遞給目標函式的值。如果使用new運算子構造繫結函式,則忽略該值。當使用bind在setTimeout中建立一個函式(作為回撥提供)時,作為thisArg傳遞的任何原始值都將轉換為object。如果沒有提供繫結的引數,則執行作用域的this被視為新函式的thisArg
arg1,arg2,...當繫結函式被呼叫時,這些引數將置於實參之前傳遞給被繫結的方法。

返回值:

返回由指定的this值和初始化引數改造的原函式拷貝。
    this.x = 9; 
    var module = {
        x: 81,
        getX: function() { return this.x; }
    };

    console.log(module.getX()); // 返回 81

    var retrieveX = module.getX.bind(module);
    console.log(retrieveX()); // 返回 9, 在這種情況下,"this"指向全域性作用域

    // 建立一個新函式,將"this"繫結到module物件
    // 新手可能會被全域性的x變數和module裡的屬性x所迷惑
    var boundGetX = retrieveX.bind(module);
    console.log(boundGetX()); // 返回 81
var o={
    f:function(){
        var self = this;
        var fff = function(){
            console.log(self.value)//此時this指向的是全域性作用域,因此需要使用self指向物件o
        };
        fff();
    },
    value:`Hello World`
};
o.f()//Hello World!

上例子是我們常用了保持this上下文的方法,把this賦值給了中間變數self,這樣在內部巢狀的函式中能夠使用self訪問到物件o,否則仍使用this.value,內部巢狀函式的this此時指向的是全域性作用域,最後的輸出將會是undefined。
但是,如果我們使用bind()函式,將fff函式的繫結在物件o中,即將fff()函式內部的this物件繫結為物件o,那麼可以遇見此時this.value是存在的。程式碼如下:

var o={
    f:function(){
        var self = this;
        var fff = function(){
            console.log(this.value);
        }.bind(this);
        fff();
    }
    valeu:"Hello World!"
}
o.f()//Hello World!

更普通的使用情形:

function f(y,z){
    return this.x+y+z;
}
var m = f.bind({X;1},2);
console.log(m(3))

最後將輸出6
這是因為bind()方法會把傳入它的第一個實參繫結給f函式體的this,從第二個實參起,將依次傳遞給原始函式,因此{x:1}傳遞給this,2傳遞給形參y,m(3)呼叫時的3傳遞給形參z.

相關文章