1.函數聲明和函數表達式的區別?
- 在ECMAScript中,創建函數的最常用的兩個方法是函數表達式和函數聲明,兩者期間的區別是有點暈,因為ECMA規范只明確了一點:函數聲明必須帶有標示(Identifier)(就是大家常說的函數名稱),而函數表達式則可以省略這個標示符:
- 函數聲明:
function 函數名稱(參數:可選){函數體} - 函數表達式:
function 函數名稱(可選)(參數:可選){函數體} - 小結:
- 若不聲明函數的名稱,它就是函數表達式
- 若同時聲明了函數名稱,ECMAScript是通過上下文來區分的,如果function foo(){}是作為賦值表達式的一部分的話,例如:
var fun1=function foo(){}
,那它就是一個函數表達式。如果function foo(){}被包含在一個函數體內,或者位于程序的最頂部的話,那它就是一個函數聲明。 - 特殊的函數表達式,如:
(function foo(){})
它是表達式的原因是因為括號 ()是一個分組操作符,它的內部只能包含表達式,同理如,JSON字符串通常被包含在一個圓括號里:eval('(' + json + ')'),這樣做的原因就是因為分組操作符,也就是這對括號,會讓解析器強制將JSON的花括號解析成表達式。
2.什么是變量的聲明前置?什么是函數的聲明前置?
-
所謂的變量聲明前置就是在一個作用域塊中,所有的變量都被放在塊的開始出聲明,下面舉個例子你就能明白了
1.變量a 在函數外面
var a=1;
function fun1(){
console.log(a);// 1
}
main();//輸出 1var a = 1; function fun1(){ console.log(a); var a = 2; } main()//輸出undefined
2.同樣是變量a在函數外層,為什么出現undefined,這是因為JS在執行時候,會自動將變量聲明前置,解析步驟如下:
var a=1;
function fun1(){
var a;// a 此時為undefined
console.log(a);//輸出結果為undefined
a=2;//a 被賦值為2,但沒有輸出
}
fun1();//undefined
- 和變量的聲明會前置一樣,函數聲明同樣會前置.
1.如果我們使用函數表達式那么規則和變量一樣;
console.log(fun1) //undefined
var fun1= function(){}
2.我們使用函數聲明的方式,那么即使函數寫在最后也可以在前面語句調用,前提是函數聲明部分已經被下載到本地
fun1();//結果為1
function fun1(){
console.log(1); //1
}
3.arguments 是什么?
- arguments 是是JavaScript里的一個內置對象,它很古怪,也經常被人所忽視,但實際上是很重要的。所有主要的js函數庫都利用了arguments對象。所以agruments對象對于javascript程序員來說是必需熟悉的。所有的函數都有屬于自己的一個arguments對象,它包括了函所要調用的參數。他不是一個數組,如果用typeof arguments,返回的是'object'。雖然我們可以用調用數據的方法來調用arguments。比如length,還有index方法。但是數 組的push和pop對象是不適用的
- 在函數內部,你可以使用arguments對象獲取到該函數的所有傳入參數
function info(name,sex,age){
console.log(name);//"小明"
console.log(sex);//"男"
console.log(age);//18
console.log(arguments[0]);//"小明"
console.log(arguments[1]);//"男"
console.log(arguments[2]);//18
console.log(arguments);//"小明","男",18
console.log(arguments.length)//3
}
info("小明","男",18);//
info("小明","男");// “小明”,“男”,undefined 2
瀏覽器輸出結果:
![3BXV%DI)S@R8EB2@$NL26F.png
4. 函數的重載怎樣實現
- 重載是很多面向對象語言實現多態的手段之一,在靜態語言中確定一個函數的手段是靠方法簽名——函數名+參數列表,也就是說相同名字的函數參數個數不同或者順序不同都被認為是不同的函數,稱為函數重載
- 在JavaScript中沒有函數重載的概念,函數通過名字確定唯一性,參數不同也被認為是相同的函數,后面的覆蓋前面的
- 在js中,我們實現重載常用的方式有:
1、根據傳入參數的類型執行不同的操作。
2、利用參數中特殊的參數值進行不同的操作。
3、根據參數的個數進行重載。
這里對第3種實現方式進行說明
function f(length) {
var len= arguments.length;
if(1 == len) {
var width = arguments[1];
alert("高為:"+length+",寬為:"+width);
} else {
alert("高為:"+length);
}
}
f(10);// 高為10
f(10,10);高為10,寬為10
你就可以給函數f()傳入一個參數也可以傳入兩個參數了
5. 立即執行函數表達式是什么?有什么作用
- 立即執行函數就是聲明一個匿名函數,馬上調用這個匿名函數
- 立即執行函數表達式有兩種寫法
- 第一種
(function(){ }()); - 第二種
(function(){ })();
- 只有一個作用:創建一個獨立的作用域。這個作用域里面的變量,外面訪問不到(即避免「變量污染」),例如:
var liList = ul.getElementsByTagName('li')
for(var i=0; i<6; i++){
liList[i].onclick = function(){
alert(i) // 為什么 alert 出來的總是 6,而不是0、
1、2、4、5
}
}
這是因為i貫穿了整個作用域,而在js中除了函數內有作用域,{ }里的內容不是作用域,因而for運行完后i的值為6,而用戶也是在for運行完才點擊,此時i為6。
解決整個問題就要立即執行函給每個li創造獨立的作用域
var liList = ul.getElementsByTagName('li')
for(var i=0; i<6; i++){
!function(li){
liList[li].onclick = function(){
alert(li) // 0、1、2、3、4、5
}
}(i)
}
在立即執行函數執行的時候,i 的值被賦值給 ii,此后 ii 的值一直不變。i 的值從 0 變化到 5,對應 6 個立即執行函數,這 6 個立即執行函數里面的 ii 「分別」是 0、1、2、3、4、5。
6. 什么是函數的作用域鏈
- 作用域鏈是存儲變量對象的集合(環境棧),保證對執行環境有權訪問的所有變量和函數的有序訪問,也就是用于標識符解析(變量訪問)。
- 在javascript沒有塊級作用域,是由函數來劃分的。變量和函數的作用域是在定義時決定而不是執行時決定,也就是說詞法作用域取決于源碼,通過靜態分析就能確定,因此詞法作用域也叫做靜態作用域(with和eval除外)。當定義了一個函數,當前的作用域鏈就保存起來,并且成為函數的內部狀態的一部份。在最頂級作用域鏈僅由全局對象組成,而不和詞法作用域相關,然而,當定義一個嵌套的函數時,作用域鏈就包括外面的包含函數。這意味著嵌套函數可以訪問包含函數的所有參數和局部變量。盡管當一個函數定義時作用域鏈就固定了,但作用域鏈中定義的屬性還沒有固定。作用域鏈是活的,并且函數被調用時,可以訪問任何當前的綁定。
var a;//全局作用域
function b(){
var c ;//c位于函數b的作用域
function d(){
var e; //e位于函數d的作用域
alert(a);
}
} - 當alert(a)時,jsJS引擎沿著d的作用域, b的作用域, 全局作用域的順序進行查找,這三個作用域組成的有序集合就成為作用域鏈