Function 構(gòu)造函數(shù)創(chuàng)建一個(gè)新的Function對(duì)象。 在 JavaScript 中, 每個(gè)函數(shù)實(shí)際上都是一個(gè)Function對(duì)象。
語(yǔ)法
new Function ([arg1[, arg2[, ...argN]],] functionBody)
參數(shù)
arg1, arg2, ... argN
被函數(shù)使用的參數(shù)的名稱必須是合法命名的。參數(shù)名稱是一個(gè)有效的JavaScript標(biāo)識(shí)符的字符串,或者一個(gè)用逗號(hào)分隔的有效字符串的列表;例如“×”,“theValue”,或“A,B”。
functionBody
一個(gè)含有包括函數(shù)定義的JavaScript語(yǔ)句的字符串。
描述
使用Function構(gòu)造器生成的Function對(duì)象是在函數(shù)創(chuàng)建時(shí)解析的。這比你使用函數(shù)聲明或者函數(shù)表達(dá)式(function)并在你的代碼中調(diào)用更為低效,因?yàn)槭褂煤笳邉?chuàng)建的函數(shù)是跟其他代碼一起解析的。
所有被傳遞到構(gòu)造函數(shù)中的參數(shù),都將被視為將被創(chuàng)建的函數(shù)的參數(shù),并且是相同的標(biāo)示符名稱和傳遞順序。
以調(diào)用函數(shù)的方式調(diào)用Function的構(gòu)造函數(shù) (不是用new關(guān)鍵字) 跟以構(gòu)造函數(shù)來(lái)調(diào)用是一樣的。
屬性和方法
全局的Function對(duì)象沒有自己的屬性和方法, 但是, 因?yàn)樗旧硪彩呛瘮?shù),所以它也會(huì)通過(guò)原型鏈從Function.prototype上繼承部分屬性和方法。
原型對(duì)象
Function.arguments
以數(shù)組形式獲取傳入函數(shù)的所有參數(shù)。此屬性已被arguments替代。(該特性已經(jīng)從 Web 標(biāo)準(zhǔn)中刪除)
Function.caller
獲取調(diào)用函數(shù)的具體對(duì)象。(非標(biāo)準(zhǔn))
Function.length
獲取函數(shù)的接收參數(shù)個(gè)數(shù)。
Function.displayName
獲取函數(shù)的display name。(非標(biāo)準(zhǔn))
Function.prototype.constructor
聲明函數(shù)的原型構(gòu)造方法
***示例
1.傳入?yún)?shù)調(diào)用Function構(gòu)造函數(shù)
// 創(chuàng)建了一個(gè)能返回兩個(gè)參數(shù)和的函數(shù)
const adder = new Function("a", "b", "return a + b");
// 調(diào)用函數(shù)
console.log(adder(2, 6));//8
參數(shù)"a"和"b"是參數(shù)的名字,在函數(shù)體中被使用,"return a + b"。
幾乎很少看到這樣做的,這比使用函數(shù)聲明或者函數(shù)表達(dá)式(function)并在你的代碼中調(diào)用更為低效。
2.Function構(gòu)造器生成的函數(shù),在全局作用域中被創(chuàng)建
// 1、f()函數(shù)返回的function e()是閉包.
var n = 1;
function f() {
var n = 2;
function e() {
return n;
}
return e;
}
console.log(f()()); //2
// 2、f()函數(shù)返回的function e()是全局作用域函數(shù)
var n = 1;
function f() {
var n = 2;
var e = new Function("return n;");
return e;
}
console.log(f()()); //1
方法
1、Function?.prototype?.apply()
apply() 方法調(diào)用一個(gè)具有給定this值的函數(shù),以及作為一個(gè)數(shù)組(或類似數(shù)組對(duì)象)提供的參數(shù)。
注意:call()方法的作用和 apply() 方法類似,區(qū)別就是call()方法接受的是參數(shù)列表,而apply()方法接受的是一個(gè)參數(shù)數(shù)組。
1.1語(yǔ)法
func.apply(thisArg, [argsArray])
1.2參數(shù)
thisArg
可選的。在 func 函數(shù)運(yùn)行時(shí)使用的 this 值。請(qǐng)注意,this可能不是該方法看到的實(shí)際值:如果這個(gè)函數(shù)處于非嚴(yán)格模式下,則指定為 null 或 undefined 時(shí)會(huì)自動(dòng)替換為指向全局對(duì)象,原始值會(huì)被包裝。
argsArray
可選的。一個(gè)數(shù)組或者類數(shù)組對(duì)象,其中的數(shù)組元素將作為單獨(dú)的參數(shù)傳給 func 函數(shù)。如果該參數(shù)的值為 null 或 undefined,則表示不需要傳入任何參數(shù)。從ECMAScript 5 開始可以使用類數(shù)組對(duì)象。 瀏覽器兼容性 請(qǐng)參閱本文底部?jī)?nèi)容。
1.3返回值
調(diào)用有指定this值和參數(shù)的函數(shù)的結(jié)果
1.4描述
在調(diào)用一個(gè)存在的函數(shù)時(shí),你可以為其指定一個(gè) this 對(duì)象。 this 指當(dāng)前對(duì)象,也就是正在調(diào)用這個(gè)函數(shù)的對(duì)象。 使用 apply, 你可以只寫一次這個(gè)方法然后在另一個(gè)對(duì)象中繼承它,而不用在新對(duì)象中重復(fù)寫該方法。
apply 與 call() 非常相似,不同之處在于提供參數(shù)的方式。apply 使用參數(shù)數(shù)組而不是一組參數(shù)列表。apply 可以使用數(shù)組字面量(array literal),如 fun.apply(this, ['eat', 'bananas']),或數(shù)組對(duì)象, 如 fun.apply(this, new Array('eat', 'bananas'))。
你也可以使用 arguments對(duì)象作為 argsArray 參數(shù)。 arguments 是一個(gè)函數(shù)的局部變量。 它可以被用作被調(diào)用對(duì)象的所有未指定的參數(shù)。 這樣,你在使用apply函數(shù)的時(shí)候就不需要知道被調(diào)用對(duì)象的所有參數(shù)。 你可以使用arguments來(lái)把所有的參數(shù)傳遞給被調(diào)用對(duì)象。 被調(diào)用對(duì)象接下來(lái)就負(fù)責(zé)處理這些參數(shù)。
從 ECMAScript 第5版開始,可以使用任何種類的類數(shù)組對(duì)象,就是說(shuō)只要有一個(gè) length 屬性和(0..length-1)范圍的整數(shù)屬性。例如現(xiàn)在可以使用 NodeList 或一個(gè)自己定義的類似 {'length': 2, '0': 'eat', '1': 'bananas'} 形式的對(duì)象。
1.5示例
用 apply 將數(shù)組添加到另一個(gè)數(shù)組
let array = ["a", "b"];
let elements = [0, 1, 2];
let newArr = array.concat.apply(array, elements);
console.log(newArr); // ["a", "b", 0, 1, 2]
array.push.apply(array, elements);
console.log(array); // ["a", "b", 0, 1, 2]
使用apply和內(nèi)置函數(shù)
聰明的apply用法允許你在某些本來(lái)需要寫成遍歷數(shù)組變量的任務(wù)中使用內(nèi)建的函數(shù)
/* 找出數(shù)組中最大/小的數(shù)字 */
let numbers = [5, 6, 2, 3, 7];
/* 應(yīng)用(apply) Math.min/Math.max 內(nèi)置函數(shù)完成 */
let max = Math.max.apply(null, numbers);
console.log(max);
/* 基本等同于 Math.max(numbers[0], ...) 或 Math.max(5, 6, ..) */
let min = Math.min.apply(null, numbers);
console.log(min);
JavaScript引擎的參數(shù)長(zhǎng)度是有限制的,所以使用apply得注意參數(shù)的個(gè)數(shù)??梢圆捎脤?shù)數(shù)組切塊后循環(huán)傳入目標(biāo)方法:
function minOfArray(arr) {
let min = Infinity;
let QUANTUM = 2;
for (let i = 0, len = arr.length; i < len; i += QUANTUM) {
let 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]);
console.log("min:"+min); //min:2
2、Function?.prototype?.call()
call() 方法使用一個(gè)指定的 this 值和單獨(dú)給出的一個(gè)或多個(gè)參數(shù)來(lái)調(diào)用一個(gè)函數(shù)。
3.1語(yǔ)法
fun.call(thisArg, arg1, arg2, ...)
3.2參數(shù)
thisArg
在 fun 函數(shù)運(yùn)行時(shí)指定的 this 值。需要注意的是,指定的 this 值并不一定是該函數(shù)執(zhí)行時(shí)真正的 this 值,如果這個(gè)函數(shù)在非嚴(yán)格模式下運(yùn)行,則指定為 null 和 undefined 的 this 值會(huì)自動(dòng)指向全局對(duì)象(瀏覽器中就是 window 對(duì)象),同時(shí)值為原始值(數(shù)字,字符串,布爾值)的 this 會(huì)指向該原始值的自動(dòng)包裝對(duì)象。
arg1, arg2, ...
指定的參數(shù)列表
3.3返回值
使用調(diào)用者提供的 this 值和參數(shù)調(diào)用該函數(shù)的返回值。若該方法沒有返回值,則返回 undefined。
3.4描述
call() 允許為不同的對(duì)象分配和調(diào)用屬于一個(gè)對(duì)象的函數(shù)/方法。
call() 提供新的 this 值給當(dāng)前調(diào)用的函數(shù)/方法。你可以使用 call 來(lái)實(shí)現(xiàn)繼承:寫一個(gè)方法,然后讓另外一個(gè)新的對(duì)象來(lái)繼承它(而不是在新對(duì)象中再寫一次這個(gè)方法)。
3.5示例
1.使用 call 方法調(diào)用父構(gòu)造函數(shù)
在一個(gè)子構(gòu)造函數(shù)中,你可以通過(guò)調(diào)用父構(gòu)造函數(shù)的 call 方法來(lái)實(shí)現(xiàn)繼承,類似于 Java 中的寫法。下例中,使用 Food 和 Toy 構(gòu)造函數(shù)創(chuàng)建的對(duì)象實(shí)例都會(huì)擁有在 Product 構(gòu)造函數(shù)中添加的 name 屬性和 price 屬性,但 category 屬性是在各自的構(gòu)造函數(shù)中定義的。
function Product(name, price) {
this.name = name;
this.price = price;
}
function Food(name, price) {
Product.call(this, name, price);
this.category = "food";
}
function Toy(name, price) {
Product.call(this, name, price);
this.category = "toy";
}
var cheese = new Food("feta", 5);
console.log(cheese.name + "的價(jià)格為" + cheese.price + "屬于" + cheese.category);//feta的價(jià)格為5屬于food
var fun = new Toy("robot", 40);
console.log(fun.name + "的價(jià)格為" + fun.price + "屬于" + fun.category);//robot的價(jià)格為40屬于toy
2.使用 call 方法調(diào)用匿名函數(shù)
在下例中的 for 循環(huán)體內(nèi),我們創(chuàng)建了一個(gè)匿名函數(shù),然后通過(guò)調(diào)用該函數(shù)的 call 方法,將每個(gè)數(shù)組元素作為指定的 this 值執(zhí)行了那個(gè)匿名函數(shù)。這個(gè)匿名函數(shù)的主要目的是給每個(gè)數(shù)組元素對(duì)象添加一個(gè) print 方法,這個(gè) print 方法可以打印出各元素在數(shù)組中的正確索引號(hào)。
let animals = [
{ species: "Lion", name: "King" },
{ species: "Whale", name: "Fail" }
];
for (let i = 0; i < animals.length; i++) {
(function(i) {
this.print = function() {
console.log(i + "項(xiàng):" + this.species + "_" + this.name);
};
this.print();
}.call(animals[i], i));
//0項(xiàng):Lion_King
//1項(xiàng):Whale_Fail
}
當(dāng)然,這里不是必須得讓數(shù)組元素作為 this 值傳入那個(gè)匿名函數(shù)(普通參數(shù)就可以),目的是為了演示 call 的用法。
3.使用 call 方法調(diào)用函數(shù)并且指定上下文的 this
function greet() {
let reply = [
this.animal,
"typically sleep between",
this.sleepDuration
].join(" ");
console.log(reply);
}
let obj = {
animal: "cats",
sleepDuration: "12 and 16 hours"
};
greet.call(obj); // cats typically sleep between 12 and 16 hours
例子中,當(dāng)調(diào)用 greet 方法的時(shí)候,該方法的this值會(huì)綁定到 obj 對(duì)象。
4.使用 call 方法調(diào)用函數(shù)并且不指定第一個(gè)參數(shù)(argument)
var sData = "white";
function display() {
console.log("sData value is %s ", this.sData);
}
display.call({sData:'yellow'}); // sData value is yellow
display.call(); // sData value is white
3、Function?.prototype?.bind()
bind()方法創(chuàng)建一個(gè)新的函數(shù),在調(diào)用時(shí)設(shè)置this關(guān)鍵字為提供的值。并在調(diào)用新函數(shù)時(shí),將給定參數(shù)列表作為原函數(shù)的參數(shù)序列的前若干項(xiàng)。
3.1語(yǔ)法
function.bind(thisArg[, arg1[, arg2[, ...]]])
3.2參數(shù)
thisArg
調(diào)用綁定函數(shù)時(shí)作為this參數(shù)傳遞給目標(biāo)函數(shù)的值。
如果使用new(new 運(yùn)算符創(chuàng)建一個(gè)用戶定義的對(duì)象類型的實(shí)例或具有構(gòu)造函數(shù)的內(nèi)置對(duì)象的實(shí)例)運(yùn)算符構(gòu)造綁定函數(shù),則忽略該值。當(dāng)使用bind在setTimeout中創(chuàng)建一個(gè)函數(shù)(作為回調(diào)提供)時(shí),作為thisArg傳遞的任何原始值都將轉(zhuǎn)換為object。如果bind函數(shù)的參數(shù)列表為空,執(zhí)行作用域的this將被視為新函數(shù)的thisArg。
arg1, arg2, ...
當(dāng)目標(biāo)函數(shù)被調(diào)用時(shí),預(yù)先添加到綁定函數(shù)的參數(shù)列表中的參數(shù)。
3.3返回值
返回一個(gè)原函數(shù)的拷貝,并擁有指定的this值和初始參數(shù)。
3.4描述
bind() 函數(shù)會(huì)創(chuàng)建一個(gè)新綁定函數(shù)(bound function,BF)。綁定函數(shù)是一個(gè)exotic function object(怪異函數(shù)對(duì)象,ECMAScript 2015中的術(shù)語(yǔ)),它包裝了原函數(shù)對(duì)象。調(diào)用綁定函數(shù)通常會(huì)導(dǎo)致執(zhí)行包裝函數(shù)。
綁定函數(shù)具有以下內(nèi)部屬性:
- [[BoundTargetFunction]]- 包裝的函數(shù)對(duì)象
- [[BoundThis]] - 在調(diào)用包裝函數(shù)時(shí)始終作為this值傳遞的值。
- [[BoundArguments]] - 列表,在對(duì)包裝函數(shù)做任何調(diào)用都會(huì)優(yōu)先用列表元素填充參數(shù)列表。
- [[Call]] - 執(zhí)行與此對(duì)象關(guān)聯(lián)的代碼。通過(guò)函數(shù)調(diào)用表達(dá)式調(diào)用。內(nèi)部方法的參數(shù)是一個(gè)this值和一個(gè)包含通過(guò)調(diào)用表達(dá)式傳遞給函數(shù)的參數(shù)的列表。
當(dāng)調(diào)用綁定函數(shù)時(shí),它調(diào)用[[BoundTargetFunction]]上的內(nèi)部方法[[Call]],就像這樣Call(boundThis, args)。其中,boundThis是[[BoundThis]],args是[[BoundArguments]]**加上通過(guò)函數(shù)調(diào)用傳入的參數(shù)列表。
綁定函數(shù)也可以使用new運(yùn)算符構(gòu)造,它會(huì)表現(xiàn)為目標(biāo)函數(shù)已經(jīng)被構(gòu)建完畢了似的。提供的this值會(huì)被忽略,但前置參數(shù)仍會(huì)提供給模擬函數(shù)。
3.5示例
1.創(chuàng)建綁定函數(shù)
bind() 最簡(jiǎn)單的用法是創(chuàng)建一個(gè)函數(shù),不論怎么調(diào)用,這個(gè)函數(shù)都有同樣的 this 值。JavaScript新手經(jīng)常犯的一個(gè)錯(cuò)誤是將一個(gè)方法從對(duì)象中拿出來(lái),然后再調(diào)用,期望方法中的 this 是原來(lái)的對(duì)象(比如在回調(diào)中傳入這個(gè)方法)。如果不做特殊處理的話,一般會(huì)丟失原來(lái)的對(duì)象?;谶@個(gè)函數(shù),用原始的對(duì)象創(chuàng)建一個(gè)綁定函數(shù),巧妙地解決了這個(gè)問(wèn)題:
var x = 9; // 在瀏覽器中,this指向全局的 "window" 對(duì)象
var module = {
x: 81,
getX: function() {
return this.x;
}
};
console.log(module.getX()); // 81
var retrieveX = module.getX;
// 返回9 - 因?yàn)楹瘮?shù)是在全局作用域中調(diào)用的
console.log(retrieveX());//9
// 創(chuàng)建一個(gè)新函數(shù),把 'this' 綁定到 module 對(duì)象
// 新手可能會(huì)將全局變量 x 與 module 的屬性 x 混淆
var boundGetX = retrieveX.bind(module);
console.log(boundGetX()); // 81
2.偏函數(shù)
bind()的另一個(gè)最簡(jiǎn)單的用法是使一個(gè)函數(shù)擁有預(yù)設(shè)的初始參數(shù)。只要將這些參數(shù)(如果有的話)作為bind()的參數(shù)寫在this后面。當(dāng)綁定函數(shù)被調(diào)用時(shí),這些參數(shù)會(huì)被插入到目標(biāo)函數(shù)的參數(shù)列表的開始位置,傳遞給綁定函數(shù)的參數(shù)會(huì)跟在它們后面。
function list() {
return Array.prototype.slice.call(arguments);
}
function addArguments(arg1, arg2) {
return arg1 + arg2;
}
let list1 = list(1, 2, 3); // [1, 2, 3]
console.log(list1);
let result1 = addArguments(1, 2); // 3
console.log(result1);
// 創(chuàng)建一個(gè)函數(shù),它擁有預(yù)設(shè)的第一個(gè)參數(shù)
let leadingThirtySevenList = list.bind(null, 37);
let list2 = leadingThirtySevenList();
console.log(list2); // [37]
// 創(chuàng)建一個(gè)函數(shù),它擁有預(yù)設(shè)的第一個(gè)參數(shù)
let addThirtySeven = addArguments.bind(null, 37);
let list3 = leadingThirtySevenList(1, 2, 3);
console.log(list3); // [37, 1, 2, 3]
//let 37 + 5 = 42
var result2 = addThirtySeven(5);
console.log(result2); // 37 + 5 = 42
//已經(jīng)有一個(gè)參數(shù)37,所以第二個(gè)10會(huì)忽略掉
var result3 = addThirtySeven(5, 10);
console.log(result3); // 37 + 5 = 42
3.配合 setTimeout
在默認(rèn)情況下,使用 window.setTimeout() 時(shí),this 關(guān)鍵字會(huì)指向 window (或global)對(duì)象。當(dāng)類的方法中需要 this 指向類的實(shí)例時(shí),你可能需要顯式地把 this 綁定到回調(diào)函數(shù),就不會(huì)丟失該實(shí)例的引用。
function LateBloomer() {
this.petalCount = Math.ceil(Math.random() * 30) + 1;
}
// 在 1 秒鐘后聲明 bloom
LateBloomer.prototype.bloom = function() {
window.setTimeout(this.declare.bind(this), 1000);
};
LateBloomer.prototype.declare = function() {
console.log(
"I am a IT, " + this.petalCount + " olds!"
);
};
var flower = new LateBloomer();
flower.bloom(); // 一秒鐘后, 調(diào)用'declare'方法
4.作為構(gòu)造函數(shù)使用的綁定函數(shù)綁定函數(shù)自動(dòng)適應(yīng)于使用 new操作符去構(gòu)造一個(gè)由目標(biāo)函數(shù)創(chuàng)建的新實(shí)例。當(dāng)一個(gè)綁定函數(shù)是用來(lái)構(gòu)建一個(gè)值的,原來(lái)提供的 this 就會(huì)被忽略。不過(guò)提供的參數(shù)列表仍然會(huì)插入到構(gòu)造函數(shù)調(diào)用時(shí)的參數(shù)列表之前。
function Point(x, y) {
this.x = x;
this.y = y;
}
Point.prototype.toString = function() {
return this.x + "," + this.y;
};
let YAxisPoint = Point.bind(null, 0);
let axisPoint = new YAxisPoint(5);
console.log(axisPoint.x); // 0
console.log(axisPoint.y); // 5
console.log(axisPoint.toString()); // '0,5'
不需要做特別的處理就可以用 new操作符創(chuàng)建一個(gè)綁定函數(shù)。也就是說(shuō),不需要做特別處理就可以創(chuàng)建一個(gè)可以被直接調(diào)用的綁定函數(shù),即使你更希望綁定函數(shù)是用 new操作符來(lái)調(diào)用。
var emptyObj = {};
var mYAxisPoint = Point.bind(emptyObj, 0);
// 仍然能作為一個(gè)普通函數(shù)來(lái)調(diào)用
mYAxisPoint(13);
console.log(emptyObj.x + ',' + emptyObj.y);//0,13
如果你希望一個(gè)綁定函數(shù)要么只能用 new操作符,要么只能直接調(diào)用,那你必須在目標(biāo)函數(shù)上顯式規(guī)定這個(gè)限制。
5.快捷調(diào)用
在你想要為一個(gè)需要特定的 this 值的函數(shù)創(chuàng)建一個(gè)捷徑(shortcut)的時(shí)候,bind() 也很好用。
使用apply:
let arguments = 'abcde';
// let slice = Array.prototype.slice;
console.log(slice.apply(arguments));//["a", "b", "c", "d", "e"]
使用bind:
let unboundSlice = Array.prototype.slice;
let slice = Function.prototype.apply.bind(unboundSlice);
console.log(slice(arguments));//["a", "b", "c", "d", "e"]
用 bind()可以使這個(gè)過(guò)程變得簡(jiǎn)單。上面示例中,slice 是 Function.prototype 的 apply() 方法的綁定函數(shù),并且將 Array.prototype 的 slice() 方法作為 this 的值。這意味著我們壓根兒用不著上面那個(gè) apply()調(diào)用了。