什么是arguments?
它是JS的一個(gè)內(nèi)置對(duì)象,常被人們所忽略,但實(shí)際上確很重要,JS不像JAVA是顯示傳遞參數(shù),JS傳的是形參,可以傳也可以不傳,若方法里沒有寫參數(shù)卻傳入了參數(shù),該如何拿到參數(shù)呢,答案就是arguments了,在一些插件里通常這樣使用。
每一個(gè)函數(shù)都有一個(gè)arguments對(duì)象,它包括了函數(shù)所要調(diào)的參數(shù),通常我們把它當(dāng)作數(shù)組使用,用它的length得到參數(shù)數(shù)量,但它卻不是數(shù)組,使用instanceof查看下,若使用push添加數(shù)據(jù)將報(bào)錯(cuò),代碼如下:
(function(){
console.log([] instanceof Array)
console.log(arguments instanceof Array)
if(arguments.push) arguments.push('test')
})()
創(chuàng)建一個(gè)靈活的格式化函數(shù)
上面說了arguments可以使用函數(shù)使用數(shù)量不定的參數(shù),下面看看它的一個(gè)實(shí)際應(yīng)用:
function format(string) {
var args = arguments;
var pattern = new RegExp("%([1-" + arguments.length + "])", "g");
return String(string).replace(pattern, function(match, index) {
return args[index];
});
};
format("And the %1 want to know whose %2 you %3", "papers", "shirt", "wear");
這里我借用了別人的一個(gè)例子,一個(gè)模板字符串,可以使用%1到%9等9個(gè)占位符,然后提供9個(gè)參數(shù)給這些占位符,最后替換生成真正的字符串。
上面的代碼返回:“And the papers want to know whose shirt you wear”
把a(bǔ)rguments轉(zhuǎn)換成一個(gè)真正的數(shù)組
有時(shí)我們希望將它轉(zhuǎn)換成真正的數(shù)組使用,可以使用下面的代碼:
var args = Array.prototype.slice.call(arguments);
現(xiàn)在args就是一個(gè)標(biāo)準(zhǔn)的js數(shù)組了,可以使用數(shù)組的標(biāo)準(zhǔn)方法了。
通過arguments對(duì)象封裝format函數(shù)
arguments允許我們?nèi)?zhí)行所有類型的js方法,下面通過一個(gè)makeFunc函數(shù),展示了函數(shù)允許我們?nèi)ヌ峁┮粋€(gè)函數(shù)引用和這個(gè)函數(shù)的所有參數(shù),它將返回一個(gè)匿名函數(shù)去調(diào)用你規(guī)定的函數(shù)(就是閉包),也提供了匿名函數(shù)調(diào)用時(shí)所附帶的參數(shù)。
function makeFunc() {
var args = Array.prototype.slice.call(arguments);
var func = args.shift();
return function() {
return func.apply(null, args.concat(Array.prototype.slice.call(arguments)));
};
}
第一個(gè)arguments給makeFunc提供了你調(diào)用的函數(shù)的引用,它將第一個(gè)參數(shù)從arguments數(shù)組里移除,然后makeFunc返回了一個(gè)匿名函數(shù)去運(yùn)行規(guī)定的方法。
apply的第一個(gè)參數(shù)是函數(shù)調(diào)用的范圍,主要是函數(shù)內(nèi)部關(guān)聯(lián)部分所指向的,這里設(shè)為null,它的arguments是一個(gè)數(shù)組,即匿名函數(shù)調(diào)用時(shí)傳入的參數(shù),匿名函數(shù)將傳入的參數(shù)串聯(lián)到原參數(shù)對(duì)象args里組成完整的匿名函數(shù)所需要參數(shù)。
你需要輸出一個(gè)模板,總是相同位置的字符發(fā)生改變,這樣就可以使用makeFunc去生成一個(gè)模板函數(shù),傳入不同的參數(shù)多次調(diào)用生成不同的內(nèi)容了。
var func = makeFunc(format, "I like %1 not %2.");
func("js", "java");
func("java", "python");
每一次調(diào)用func,它會(huì)同時(shí)調(diào)用format函數(shù)和第一個(gè)arguments,然后填充已有的模板。
執(zhí)行結(jié)果如下:
"I like js not java."
"I like java not python."
這樣封裝format是不是很酷,不過arguments還有更多驚喜。
ES6重構(gòu)makeFunc
es6中不推薦使用arguments,那就要使用es6中提供的語(yǔ)法...rest
Rest就是為解決傳入的參數(shù)數(shù)量不一定, rest parameter(Rest 參數(shù)) 本身就是數(shù)組,數(shù)組的相關(guān)的方法都可以用。
下面是重構(gòu)后的代碼:
function format(string, ...args) {
var pattern = new RegExp("%([1-" + args.length + "])", "g");
return String(string).replace(pattern, function(match, index) {
return args[index-1];
});
};
format("And the %1 want to know whose %2 you %3", "papers", "shirt", "wear");
function makeFunc(...args) {
var func = args.shift();
return function(...rest) {
return func.apply(null, args.concat(rest));
};
}
var func = makeFunc(format, "I like %1 not %2.");
func("js", "java");
func("java", "python");
創(chuàng)建引用自身的函數(shù)
arguments.callee包括了一個(gè)函數(shù)的引用去創(chuàng)建一個(gè)arguments對(duì)象,它能讓一個(gè)匿名函數(shù)很方便的指向本身。
下面的Repeat是一個(gè)承載了一個(gè)函數(shù)引用和兩個(gè)數(shù)字的函數(shù),第一個(gè)數(shù)字是調(diào)用次數(shù),第二個(gè)是間隔時(shí)間,單位毫秒。
function repeat(fn, times, delay) {
return function() {
if(times-- > 0) {
fn.apply(null, arguments);
var args = Array.prototype.slice.call(arguments);
var self = arguments.callee;
setTimeout(function(){self.apply(null,args)}, delay);
}
};
}
Repeat函數(shù)使用了arguments.callee方法從變量self獲取一個(gè)引用,指向運(yùn)行原始指令的函數(shù)。這樣,匿名函數(shù)就可以再次調(diào)用自身,看看下面的調(diào)用:
var somethingWrong = repeat(function(s){console.log(s)}, 3, 2000);
somethingWrong("Can you hear me, major tom?");
ES6重構(gòu)repeat
現(xiàn)在已經(jīng)不推薦使用arguments.callee();
原因:訪問 arguments 是個(gè)很昂貴的操作,因?yàn)樗莻€(gè)很大的對(duì)象,每次遞歸調(diào)用時(shí)都需要重新創(chuàng)建。影響現(xiàn)代瀏覽器的性能,還會(huì)影響閉包。
其實(shí)很簡(jiǎn)單,給內(nèi)部函數(shù)一個(gè)名字即可:
function repeat(fn, times, delay) {
return function _fn(...args) {
if(times-- > 0) {
fn.apply(null, args);
// 現(xiàn)在arguments.callee被棄用了, 給內(nèi)部函數(shù)一個(gè)名字即可:
setTimeout(function(){_fn.apply(null, args)}, delay);
}
};
}
var somethingWrong = repeat((s)=>{console.log(s)}, 3, 2000);
somethingWrong("Can you hear me, major tom?");
可以看到somethingWrong函數(shù)的結(jié)果被打印了3次,每隔2秒。
arguments還有很多驚喜,非常值得我們?nèi)チ私猓瑲g迎加入前端交流群。
學(xué)習(xí)交流,請(qǐng)加群:867541922