1. 程式人生 > 其它 >javascript中的call和apply

javascript中的call和apply

起因

Array.prototype.push.call( arguments, 3 );當你看到這句程式碼時有沒有同我一樣感到疑惑這句程式碼到底是什麼意思?

PS:call和apply使用的場景有很多這裡目前只是介紹其中的一部分

借用其他物件的方法

第一種場景是“借用建構函式”

利用mdn上的例子

function Product(name, price) {
  this.name = name;
  this.price = price;
}

function Food(name, price) {
  Product.call(this, name, price);
  this.category = 'food';
}

// 上面的函式等價於這個函式
// function Food(name, price) {
//   this.name = name;
//   this.price = price;
//   this.category = 'food';
// }

console.log(new Food('cheese', 5).name);

Product的建構函式是:

function Product(name, price) {
  this.name = name;
  this.price = price;
}

Product.call(this, name, price);是吧Product的建構函式放在了Food函式的中,有了類似“繼承”的效果。
可以在瀏覽器中console中測試一下:

> let a = new Food('cheese', 5)
> a
< Food {name: "cheese", price: 5, category: "food"}
    category: "food"
    name: "cheese"
    price: 5
    __proto__:
      constructor: ƒ Food(name, price)
      __proto__: Object

a的__proto__並沒有變化,但在Food函式中多了name和price兩個屬性,所以才說類似“繼承”。

第二種借用方法

我們再說回上面的例子Array.prototype.push.call( arguments, 3 );其實是借用了Array.prototype.push的方法使arguments物件有了類似array的push功能,為什麼可以這樣使用呢?首先我們來看v8中array的push功能的程式碼:

function ArrayPush() {
  var n = TO_UINT32( this.length );    // 被push的物件的length
  var m = %_ArgumentsLength();     // push的引數個數
  for (var i = 0; i < m; i++) {
    this[ i + n ] = %_Arguments( i );   // 複製元素     (1)
  }
  this.length = n + m;      // 修正length屬性的值    (2)
  return this.length;
};

假如let arguments = {}(PS:IE中必須顯示生命lengthlet arguments = { length: 0 })arguments是這個物件上面的函式也能正常執行,所以不管物件是否是array都可以使用ArrayPush函式但需要滿足(1)和(2)處的條件否則會報錯。

var a = {};
Array.prototype.push.call( a, 'first' );

alert ( a.length );    // 輸出:1
alert ( a[ 0 ] );    // first

總結

原來沒有搞清楚call和apply的點是:

  1. 沒有搞懂“繼承”的關係(現在知道是假繼承)
  2. Array.prototype.push.call( arguments, 3 );為什麼會這樣執行,其實也就是call和apply的真正的含義。

其實call和apply還有一個功能是可以改變this的指向,想必大家在瞭解this的時候就已經知道這一點了,這裡就不展開講了。

華麗謝幕!