Function對象(apply、call、bind)
原創文章,轉摘請注明出處:蘇福:http://www.cnblogs.com/susufufu/p/5850180.html
本文參考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 不同。
屬性:
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值,如果這個函數處於非嚴格模式下,則指定為null和undefined的this值會自動指向全局對象(瀏覽器中就是window對象),同時值為原始值(數字,字符串,布爾值)的this會指向該原始值的自動包裝對象。
- 使用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() 最簡單的用法是為原函數創建一個綁定函數,綁定到指定的對象上,使這個函數不論怎么調用都有同樣的 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\'方法
獲取函數的實現源碼的字符串。覆蓋了 Object.prototype.toString 方法。