認識Function.prototype.call

1、前言                                                             html

  你們先預計一下如下四個函數調用的結果吧!git

複製代碼
var test = function(){
  console.log('hello world')
return 'fsjohnhuang' }
test.call() // ① Function.prototype.call(test) // ② Function.prototype.call.call(test) // ③ Function.prototype.call.call(Function.prototype.call, test) // ④
複製代碼

  揭曉:①、③和④. 控制檯顯示hello world,並返回fsjohnhuang。②. 返回undefined且不會調用test函數;github

  那究竟是啥回事呢?下面將一一道來。數組

 

2、從經常使用的call函數提及                                                        
瀏覽器

  仍是經過代碼說事吧函數

var test2 = function(){
  console.log(this)
  return 'fsjohnhuang'
}
test2() // 控制檯顯示window對象信息,返回值爲fsjohnhuang
test2.call({msg: 'hello world'}) // 控制檯顯示{msg:'hello world'}對象信息,返回值爲fsjohnhuang

  test2.call其實是調用 Function.prototype.call (thisArg [ , arg1 [ , arg2, … ] ] ) ,而其做用我想你們都瞭解的,但其內部的工做原理是怎樣的呢? 這時咱們能夠參考ECMAScript5.1語言規範。如下是參照規範的僞代碼(各瀏覽器的具體實現均不盡相同)post

複製代碼
Function.prototype.call = function(thisArg, arg1, arg2, ...) {
  /*** 注意:this指向調用call的那個對象或函數 ***/
// 1. 調用內部的IsCallable(this)檢查是否可調用,返回false則拋TypeError if (![[IsCallable]](this)) throw new TypeError() // 2. 建立一個空列表 // 3. 將arg1及後面的入參保存到argList中 var argList = [].slice.call(arguments, 1) // 4. 調用內部的[[Call]]函數 return [[Call]](this, thisArg, argList) }
複製代碼

  那如今咱們能夠分析一下 ①test.call() ,並以其爲基礎去理解後續的內容。它內部實現的僞代碼以下:性能

test.call = function(thisArg, arg1, arg2, ...){
  if (![[IsCallable]](test)) throw new TypeError()

  var argList = [].slice.call(arguments, 1)
  return [[Call]](test, thisArg, argList)
}

  下面咱們再來分析② Function.prototype.call(test) ,僞代碼以下:學習

複製代碼
Function.prototype.call = function(test, arg1, arg2, ...){
  /***  Function.prototype是一個function Empty(){}函數  ***/

  if (![[IsCallable]](Function.prototype)) throw new TypeError()

  var argList = [].slice.call(arguments, 1)
  // 實際上就是調用Empty函數而已,那返回undefined是理所固然的
  return [[Call]](Function.prototype, test, argList)
}
複製代碼

 

3、Function.prototype.call.call內部究竟又幹嗎了?                                       this

  有了上面的基礎那麼Function.prototype.call.call就不難理解了。就是以最後一個call函數的thisArg做爲Function.prototype.call的this值啦!僞代碼以下:

複製代碼
// test做爲thisArg傳入
Function.prototype.call.call = function(test, arg1, arg2,...){
  if ([[IsCallable]](Function.prototype.call)) throw new TypeError()
  
  var argList = [].slice.call(arguments, 1)
  return [[Call]](Function.prototype.call, test, argList)
}

// test做爲函數的this值
// 注意:入參thisArg的值爲Function.prototype.call.call的入參arg1
Function.prototype.call = function(thisArg, arg1, arg2,...){
  if ([[IsCallable]](test)) throw new TypeError()

  var argList = [].slice.call(arguments, 1)
  return [[Call]](test, thisArg, argList)
}
複製代碼

 

4、見鬼的合體技——Function.prototype.call.call(Function.prototype.call, test) 

  看僞代碼理解吧!

複製代碼
// test做爲arg1傳入
Function.prototype.call.call = function(Function.prototype.call, test){
  if ([[IsCallable]](Function.prototype.call)) throw new TypeError()
  
  var argList = [].slice.call(arguments, 1)
  return [[Call]](Function.prototype.call, Function.prototype.call, argList)
}

Function.prototype.call = function(test){
  if ([[IsCallable]](Function.prototype.call)) throw new TypeError()

  var argList = [].slice.call(arguments, 1)
  return [[Call]](Function.prototype.call, test, argList)
}

Function.prototype.call = function(thisArg){
  if ([[IsCallable]](test)) throw new TypeError()

  var argList = [].slice.call(arguments, 1)
  return [[Call]](test, thisArg, argList)
}
複製代碼

  這種合體技不就是比第三節的多了一個步嗎?有必有嗎?  

 

5、新玩法——遍歷執行函數數組                            

複製代碼
Array.prototype.resolve = function(){
  this.forEach(Function.prototype.call, Function.prototype.call)
}
var cbs = [function(){console.log(1)}, function(){console.log(2)}]
cbs.resolve() 
// 控制檯輸出
// 1
// 2
複製代碼

   這是爲何呢?那先要看看 Array.prototype.forEach(fn, thisArg) 的內部實現了,僞代碼以下:

複製代碼
Array.prototype.forEach = function(fn, thisArg){
  var item
  for (var i = 0, len = this.length; i < len; ++i){
    item = this[i]
    fn.call(thisArg, item, i, this)
  }
}
複製代碼

   你們再自行將編寫 Function.prototype.call.call(Function.prototype.call, item, i,this) 的僞代碼就明白了

 

6、總結                                          

  在項目中關於Function.prototype.call.call的用法確實少見,並且性能不高,本篇僅僅出於學習的目的,只但願再深刻了解一下Function.prototype.call的內部原理而已。

  尊重原創,轉載請註明在:http://www.cnblogs.com/fsjohnhuang/p/4160942.html ^_^肥仔John

 

7、參考                                           

  在JavaScript的Array數組中調用一組Function方法

  https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach

  Annotated ECMAScript 5.1

 

出處:http://www.cnblogs.com/fsjohnhuang/p/4160942.html

相關文章
相關標籤/搜索