原生JS:Function對象(apply、call、bind)詳解

Function對象(apply、call、bind)javascript

原創文章,轉摘請註明出處:蘇福:http://www.cnblogs.com/susufufu/p/5850180.htmlhtml

本文參考MDN作的詳細整理,方便你們參考[MDN](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript)java

Function 構造器會建立一個新的 Function 對象。 在 JavaScript 中每一個函數都是一個Function對象。web

構造器數組

 new Function ([arg1[, arg2[, ...argN]],] functionBody)瀏覽器

  • arg1, arg2, ... arg被函數使用的參數的名稱必須是有效的JavaScript標識符的字符串
  • functionBody 一個含有包括函數定義的JavaScript語句的字符串。
  • 以調用函數的方式調用Function的構造函數 (不是用new關鍵字) 跟以構造函數來調用是同樣的.

例:var adder = new Function("a", "b", "return a + b"); // 建立了一個能返回兩個參數和的函數閉包

       adder(2, 6); //  8app

使用Function構造器生成的Function對象是在函數建立時解析的。這比你使用函數聲明或者函數表達式(function)並在你的代碼中調用更爲低效,由於使用後者建立的函數是跟其餘代碼一塊兒解析的dom

全部被傳遞到構造函數中的參數,都將被視爲將被建立的函數的參數,而且是相同的標示符名稱和傳遞順序。函數

使用Function構造器生成的函數,並不會在建立它們的上下文中建立閉包;它們通常在全局做用域中被建立。當運行這些函數的時候,它們只能訪問本身的本地變量和全局變量,不能訪問Function構造器被調用生成的上下文的做用域。這和使用帶有函數表達式代碼的 eval 不一樣。

屬性:

Function.length

length 是函數對象的一個屬性值,指明該函數指望多少個參數,即形參的個數。數量不包括剩餘參數。相比之下,  arguments.length 是函數被調用時實際傳參的個數。

  • Function 構造器自己也是個Function。他的 length 屬性值爲 1 。該屬性 Writable: false, Enumerable: false, Configurable: true.
  • Function  原型對象的 length 屬性值爲 0 。

方法

Function.prototype.call(thisArg[, arg1[, arg2[, ...]]])

在一個對象thisArg的上下文中應用另外一個對象的方法;參數可以以列表形式傳入,即在使用一個指定的this值(thisArg)和若干個指定的參數值的前提下調用某個函數或方法。

  • 須要注意的是,指定的this值並不必定是該函數執行時真正的this值,若是這個函數處於非嚴格模式下,則指定爲nullundefinedthis值會自動指向全局對象(瀏覽器中就是window對象),同時值爲原始值(數字,字符串,布爾值)的this會指向該原始值的自動包裝對象。
call方法的應用:
  • 使用call方法調用函數而且指定上下文的'this'
function greet() {
  var reply = [this.person, 'Is An Awesome', this.role].join(' ');
  console.log(reply);
}
var a = {
  person: 'Douglas Crockford', role: 'Javascript Developer'
};
greet.call(a); // Douglas Crockford Is An Awesome Javascript Developer
  • 使用call方法調用父構造函數:在一個子構造函數中,你能夠經過調用父構造函數的 call 方法來實現繼承
function Food(name, price) {
  Product.call(this, name, price);
  this.category = 'food';
}

 

  • 使用call方法調用匿名閉包函數,以達到綁定this和傳參數的目的
for(var i =0;i<10;i++){
  setTimeout(function(){
    console.log(i);
  }.call(null,i),0)
}
//能夠順利輸出0~9,若是不用call,則i的最終值,輸出10個10

 

Function.prototype.apply(thisArg[, argsArray])

在一個對象的上下文中應用另外一個對象的方法;參數可以以數組形式傳入。

  • thisArg 函數運行時指定的 this 值。須要注意的是,指定的 this 值並不必定是該函數執行時真正的 this 值,若是這個函數處於非嚴格模式下,則指定爲 null 或 undefined 時會自動指向全局對象(瀏覽器中就是window對象),同時值爲原始值(數字,字符串,布爾值)的 this 會指向該原始值的自動包裝對象。
  • argsArray 一個數組或者類數組對象(ES5新增,Chrome 14 以及 Internet Explorer 9 仍然不接受類數組對象。若是傳入類數組對象,它們會拋出異常。),其中的數組元素將做爲單獨的參數傳給函數。若是該參數的值爲null 或 undefined,則表示不須要傳入任何參數。從ECMAScript 5 開始可使用類數組對象。瀏覽器兼容性請參閱本文底部內容。
  • 使用 apply和call, 你能夠只寫一次這個方法而後在另外一個對象中繼承它,而不用在新對象中重複寫該方法。
  • 可使用 arguments  對象做爲 argsArray 參數。 arguments 是一個函數的局部變量。 它能夠被用做被調用對象的全部未指定的參數。 這樣,你在使用apply函數的時候就不須要知道被調用對象的全部參數。 你可使用arguments來把全部的參數傳遞給被調用對象。

使用apply和內置函數

聰明的apply用法容許你在某些原本須要寫成遍歷數組變量的任務中使用內建的函數。在接下里的例子中咱們會使用Math.max/Math.min來找出一個數組中的最大/最小值。

var numbers = [5, 6, 2, 3, 7];
var max = Math.max.apply(null, numbers);
var min = Math.min.apply(null, numbers);
  • 當你對一個方法傳入很是多的參數 (好比超過1W多個參數) 時, 就很是有可能會致使越界問題, 這個臨界值是根據不一樣的 JavaScript 引擎而定的 (JavaScript 核心中已經作了硬編碼  參數個數限制在65536),由於這個限制(實際上也是任何用到超大棧空間的行爲的天然表現)是未指定的. 有些引擎會拋出異常.  更糟糕的是其餘引擎會直接限制傳入到方法的參數個數,致使參數丟失.
  • 若是你的參數數組可能很是大, 那麼推薦使用下面這種策略來處理: 將參數數組切塊後循環傳入目標方法:
function minOfArray(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]);

 

Function.prototype.bind(thisArg[, arg1[, arg2[, ...]]])

bind()方法會建立一個新函數(新函數與被調函數具備相同的函數體)稱爲綁定函數。

  • 當調用這個綁定函數時,綁定函數會以建立它時傳入 bind()方法的第一個參數做爲 this,傳入 bind()方法的第二個以及之後的參數做爲綁定函數的預設參數,加上綁定函數運行時自己的參數按照順序做爲原函數的參數來調用原函數.
  • 一個綁定函數也能使用new操做符建立對象:這種行爲就像把原函數當成構造器。提供的 this 值被忽略,同時調用時的參數被提供給模擬函數。
function Point(x, y) {
  this.x = x;
  this.y = y;
}
Point.prototype.add = function() {
  return this.x + this.y;
};
var p = new Point(1, 2);
console.log(p); //Point { x: 1, y: 2 }
var YAxisPoint = Point.bind({}, 5);
var x = new YAxisPoint(6);
console.log(x); //Point { x: 5, y: 6 }
console.log(x.add()); //11 
bind用法:

bind() 最簡單的用法是爲原函數建立一個綁定函數,綁定到指定的對象上,使這個函數不論怎麼調用都有一樣的 this 值

this.x = 9;
var module = {
  x: 81,
  getX: function() { return this.x; }
};
module.getX(); // 返回 81
var retrieveX = module.getX;
retrieveX(); // 返回 9, 在這種狀況下,"this"指向所有做用域
// 建立一個新函數,將"this"綁定到module對象
// 新手可能會被全局的x變量和module裏的屬性x所迷惑
var boundGetX = retrieveX.bind(module);
boundGetX(); // 返回 81

bind()的另外一個最簡單的用法是使一個函數擁有預設的初始參數

function list() {
  return Array.prototype.slice.call(arguments);
}
var list1 = list(1, 2, 3); // [1, 2, 3]
var leadingThirtysevenList = list.bind(undefined, 37);
var list2 = leadingThirtysevenList(); // [37]
var list3 = leadingThirtysevenList(1, 2, 3); // [37, 1, 2, 3]
function foo(x,y){
  return y ? x+y : foo.bind(void 0,x)
}
console.log(foo(1,4)); //3
console.log(foo(1)(4)); //3

配合 setTimeout,在默認狀況下,使用 window.setTimeout() 時,this 關鍵字會指向 window (或全局)對象。當使用類的方法時,須要 this 引用類的實例,你可能須要顯式地把 this 綁定到回調函數以便繼續使用實例。

function LateBloomer() {
  this.petalCount = Math.ceil(Math.random() * 12) + 1;
}
LateBloomer.prototype.bloom = function() {
  window.setTimeout(this.declare.bind(this), 1000);
};
LateBloomer.prototype.declare = function() {
  console.log('I am a beautiful flower with ' +
  this.petalCount + ' petals!');
};
var flower = new LateBloomer();
flower.bloom(); // 一秒鐘後, 調用\'declare\'方法

Function.prototype.toString()

獲取函數的實現源碼的字符串。覆蓋了 Object.prototype.toString 方法。

相關文章
相關標籤/搜索