函數(shù)的定義和調(diào)用
arguments
arguments盲再,它只在函數(shù)內(nèi)部起作用周崭,并且永遠(yuǎn)指向當(dāng)前函數(shù)的調(diào)用者傳入的所有參數(shù)哑诊,可以獲得調(diào)用者傳入的所有參數(shù)撒璧。arguments類似Array但它不是一個Array:
function foo(x) {
console.log(x); // 10
for (var i = 0; i < arguments.length; i++) {
console.log(arguments[i]); // 10, 20, 30
}
}
foo(10, 20, 30);
那在什么情況下才會真正用到arguments呢透葛,下面舉個栗子:
函數(shù)的重載:
有時候函數(shù)根據(jù)傳入的參數(shù)個數(shù)的不同,來執(zhí)行不同的處理:
// 如果用戶傳入一個參數(shù)卿樱,求平方
function sum(a){
console.log(a*a);
}
//如果用戶傳入兩個參數(shù)僚害,就求和
function sum(a,b){
console.log(a+b);
}
sum(4); //?
sum(4,5); //繁调?
上述例子中本意是想讓同名函數(shù)sum()根據(jù)參數(shù)不同輸出不同結(jié)果萨蚕,但是sum是函數(shù)名字,本質(zhì)也是個變量蹄胰,
第二個會覆蓋第一個岳遥,所以上述的正確輸出答案是:NaN,9.所以這樣顯然不可以。
如果用arguments,就簡單多了裕寨。
如下:
function calc(){
//如果用戶傳入一個參數(shù)浩蓉,求平方
if(arguments.length===1){
console.log(arguments[0]*arguments[0]);
}else if(arguments.length===2){
//如果用戶傳入兩個參數(shù),就求和
console.log(arguments[0]+arguments[1]);
}
}
calc(4); //16
calc(4, 5); //9
多個參數(shù)的求和:
function add(){
//arguments:[]
//遍歷arguments中每個元素宾袜,并累加
for(var i=0,sum=0;i<arguments.length;sum+=arguments[i++]);
return sum;//返回和
}
console.log(add(1, 2, 3)); //6
console.log(add(1, 2, 3, 4, 5, 6)); //21
這就是JS利用arguments重載的效果捻艳,簡單理解就是一個函數(shù)重復(fù)利用.
arguments.length是有實參決定,即函數(shù)調(diào)用時候里面的參數(shù)個數(shù)決定试和!
arguments對象中還定義了callee屬性讯泣,用來引用當(dāng)前正在執(zhí)行的函數(shù)纫普,例如在遞歸中使用:
function factorial(num) {
// 參數(shù)的callee是函數(shù)自身
console.log(arguments.callee === foo); // true
if(num <=1 ) {
return 1;
} else {
return num*arguments.callee(num-1);
}
}
console.log(factorial(5));//120
需要注意的是如果一個函數(shù)的參數(shù)有3個,傳遞的實參只有2個,那么arguments[2]和第三個參數(shù)是不共享的.
function foo(x, y, z) {
// 聲明的函數(shù)參數(shù)數(shù)量arguments (x, y, z)
console.log(foo.length); // 3
// 真正傳進(jìn)來的參數(shù)個數(shù)(only x, y)
console.log(arguments.length); // 2
// 參數(shù)共享
console.log(x === arguments[0]); // true
console.log(x); // 10
arguments[0] = 20;
console.log(x); // 20
x = 30;
console.log(arguments[0]); // 30
// 不過阅悍,沒有傳進(jìn)來的參數(shù)z好渠,和參數(shù)的第3個索引值是不共享的
z = 40;
console.log(arguments[2]); // undefined
arguments[2] = 50;
console.log(z); // 40
}
foo(10, 20);
rest參數(shù)
ES6標(biāo)準(zhǔn)引入了rest參數(shù):
function foo(a, b, ...rest) {
console.log('a = ' + a);
console.log('b = ' + b);
console.log(rest);
}
foo(1, 2, 3, 4, 5);
// 結(jié)果:
// a = 1
// b = 2
// Array [ 3, 4, 5 ]
foo(1);
// 結(jié)果:
// a = 1
// b = undefined
// Array []
rest參數(shù)只能寫在最后,前面用 ... 標(biāo)識节视,從運行結(jié)果可知拳锚,傳入的參數(shù)先綁定a、b寻行,多余的參數(shù)以數(shù)組形式交給變量rest霍掺,所以,不再需要arguments我們就獲取了全部參數(shù)拌蜘。
如果傳入的參數(shù)連正常定義的參數(shù)都沒填滿杆烁,也不要緊,rest參數(shù)會接收一個空數(shù)組(注意不是undefined)简卧。
變量的作用域
我們知道在JavaScript中兔魂, 在函數(shù)內(nèi)var一個變量,則該變量的作用域為整個函數(shù)體举娩,在函數(shù)體外不可引用該變量析校;
不同的函數(shù)體聲明同一個變量,相互不會影響铜涉,各自的作用域只在各自所在函數(shù)體內(nèi)智玻。也就是說不同函數(shù)內(nèi)部的同名變量互相獨立,互不影響芙代;
上面描述了平行關(guān)系的函數(shù)內(nèi)部變量及作用域的特點吊奢,JavaScript可以存在嵌套函數(shù),此時的內(nèi)部函數(shù)可以訪問外部函數(shù)定義的變量链蕊,外部則不可訪問外部;
'use strict';
function foo() {
var x = 1;
function bar() {
var y = x + 1;
}
var z = y + 1; // foo() 時這里會因為無法訪問y而報錯
}
如果內(nèi)部函數(shù)和外部函數(shù)的變量名重名事甜,則內(nèi)部函數(shù)的變量將“屏蔽”外部函數(shù)的變量。由此說明JavaScript的函數(shù)在查找變量時從自身函數(shù)定義開始滔韵。
'use strict';
function foo() {
var i = 'a';
function fuc() {
var i = 'b';
console.log('fuc', i);
}
console.log('foo', i);
fuc();
}
foo();
// 'foo', a
// 'fuc', b
變量提升
JavaScript的函數(shù)定義有個特點逻谦,它會先掃描整個函數(shù)體的語句,把所有申明的變量“提升”到函數(shù)頂部:
'use strict';
function foo() {
var i = 'Hello, ' + j;
alert(i);
var j = 'Bob';
}
foo(); // Hello, undefined
JavaScript引擎自動提升了變量j的聲明陪蜻,但不會提升變量j的賦值;
因此邦马,我們在函數(shù)內(nèi)部定義變量時,請嚴(yán)格遵守“在函數(shù)內(nèi)部優(yōu)先申明所有變量”這一規(guī)則宴卖。
全局作用域
不在任何函數(shù)內(nèi)定義的變量就具有全局作用域;
JavaScript實際上有一個默認(rèn)(唯一一個)全局作用域 window;
全局作用域的變量實際上被綁定到window的一個屬性:
'use strict';
var animal = 'White cat';
alert(animal); // 'White cat'
alert(window.animal); // 'White cat'
全局作用域有哪些需要注意的點
1滋将、名字空間
全局變量會綁定到window上,如果再不同的js文件里定義了相同的全局變量(包括頂層函數(shù)症昏,不在任何函數(shù)體內(nèi)的變量等)随闽,都會引起沖突,而且在項目中很難被發(fā)現(xiàn)肝谭。為減少這種沖突掘宪,我們可以把所以的變量和函數(shù)綁定在一個全局變量中蛾扇。
2、局部作用域
在for循環(huán)等語句塊中是無法定義具有局部作用域的變量的:
'use strict';
function foo() {
for (var i=0; i<10; i++) {
//
}
i += 10; // 仍然可以引用變量i
}
ES6引入了新的關(guān)鍵字let魏滚,用let替代var可以申明一個塊級作用域的變量镀首,用來解決以上所訴問題。
3鼠次、常量
在ES6之前更哄,我們通常用全部大寫的變量來表示一個常量,ES6標(biāo)準(zhǔn)引入了新的關(guān)鍵字const來定義常量腥寇,const與let都具有塊級作用域成翩。
this指針
在一個方法內(nèi)部,this是一個特殊變量赦役,它始終指向當(dāng)前對象捕传。
沒有綁定在對象上的函數(shù),它的this指針指向全局對象扩劝,也就是window庸论。
不知道設(shè)計者在設(shè)計之初是作何考慮的,ECMA想要優(yōu)化這個“特性”棒呛,在strict模式下讓函數(shù)的this指向undefined聂示。但并沒有根本解決this指向的問題。
因此有時我們需要用一個變量比如 that 來提前捕獲this簇秒。
參考資料:廖雪峰的官方網(wǎng)站:https://www.liaoxuefeng.com/wiki/001434446689867b27157e896e74d51a89c25cc8b43bdb3000