標籤:
Function對象(apply、call、bind)
本文參考MDN做的詳細整理,方便大家參考[MDN](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript)
Function 構造器會建立一個新的 Function 對象。 在 JavaScript 中每個函數都是一個Function對象。
構造器
new Function ([arg1[, arg2[, ...argN]],] functionBody)
- arg1, arg2, ... argN 被函數使用的參數的名稱必須是有效JavaScript標識符的字串
- functionBody 一個含有包括函數定義的JavaScript語句的字串。
- 以調用函數的方式調用Function的建構函式 (不是用new關鍵字) 跟以建構函式來調用是一樣的.
例:var adder = new Function("a", "b", "return a + b"); // 建立了一個能返回兩個參數和的函數
adder(2, 6); // 8
使用Function構造器產生的Function對象是在函數建立時解析的。這比你使用函式宣告或者函數運算式(function)並在你的代碼中調用更為低效,因為使用後者建立的函數是跟其他代碼一起解析的。
所有被傳遞到建構函式中的參數,都將被視為將被建立的函數的參數,並且是相同的標示符名稱和行程順序。
使用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值,如果這個函數處於非strict 模式下,則指定為null和undefined的this值會自動指向全域對象(瀏覽器中就是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 值,如果這個函數處於非strict 模式下,則指定為 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(); // 返回 81var 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)); //3console.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 方法。
原生JS:Function對象(apply、call、bind)詳解