變量聲明前置,立即執(zhí)行函數(shù),作用域鏈

1、函數(shù)聲明和函數(shù)表達(dá)式有什么區(qū)別

函數(shù)聲明和函數(shù)表達(dá)式都有聲明前置的效果,但是:

  • 函數(shù)表達(dá)式的聲明前置效果更像變量的聲明前置效果,相當(dāng)于只是一個(gè)變量名被前置了,而這個(gè)變量的值并沒有被前置,不能在前面調(diào)用,會報(bào)錯(cuò)。


  • 函數(shù)聲明則相當(dāng)于整個(gè)函數(shù)都被前置了,可以在聲明之前調(diào)用。


2、什么是變量的聲明前置?什么是函數(shù)的聲明前置

  • 什么是變量的聲明前置?
    JavaScript引擎的工作方式是,先解析代碼,獲取所有被聲明的變量,然后再一行一行地運(yùn)行。這造成的結(jié)果,就是所有的變量的聲明語句,都會被提升到代碼的頭部,然后給他初始值undefined,然后才逐句執(zhí)行程序,這就叫做“變量提升”,也即“變量的聲明前置”。


  • 什么是函數(shù)的聲明前置?
    和變量的聲明會前置一樣,函數(shù)聲明同樣會前置,如果我們使用函數(shù)表達(dá)式那么規(guī)則和變量一樣,如下圖:



    如果我們使用函數(shù)聲明的方式,那么即使函數(shù)寫在最后也可以在前面語句調(diào)用,前提是函數(shù)聲明部分已經(jīng)被下載到本地。

3、arguments 是什么

是一個(gè)長的很像數(shù)組的對象,可以通過該對象獲取到函數(shù)的所有傳入?yún)?shù)。


4、函數(shù)的重載怎樣實(shí)現(xiàn)

在JavaScript中沒有函數(shù)重載的概念,函數(shù)通過名字確定唯一性,參數(shù)不同也被認(rèn)為是相同的函數(shù),后面的覆蓋前面的。函數(shù)調(diào)用沒必要把所有參數(shù)都傳入,只要你函數(shù)體內(nèi)做好處理就行,但前提是傳的參數(shù)永遠(yuǎn)被當(dāng)做前幾個(gè)。


5、立即執(zhí)行函數(shù)表達(dá)式是什么?有什么作用

立即執(zhí)行函數(shù)表達(dá)式有多種寫法:(function(){})();, 或(function(){}()); ,或!function(){}();,或void function(){}(); , 如下圖所示:


作用:創(chuàng)建一個(gè)獨(dú)立的作用域。這個(gè)作用域里面的變量,外面訪問不到(即避免變量污染)。
比如下面這個(gè)例子:不管單擊哪個(gè)li,alert出來的總是6,而不是0、1、2、3、4、5。因?yàn)槲覀円欢ㄊ窃趂or循環(huán)結(jié)束后才單擊的,而i是貫穿整個(gè)作用域的,所以,i總是6.

<ul>
    <li>li1</li>
    <li>li2</li>
    <li>li3</li>
    <li>li4</li>
    <li>li5</li>
    <li>li6</li>
</ul>
<script type="text/javascript">
     var liList=document.getElementsByTagName("li");
     for(var i=0; i<liList.length; i++){
         liList[i].onclick=function(){
             alert(i);
         }
      }
</script>

這里我們就可以用立即執(zhí)行函數(shù)給每個(gè)li創(chuàng)造一個(gè)獨(dú)立的作用域來解決這個(gè)問題。



當(dāng)然也有其他解決方法


6、什么是函數(shù)的作用域鏈

JavaScript中所有的量都是存在于某一個(gè)作用域中的,在試圖訪問一個(gè)變量時(shí)JS引擎會從當(dāng)前作用域開始向上查找直到Global全局作用域停止。
如下圖:當(dāng)alert(A)時(shí), JS引擎沿著D的作用域, B的作用域, 全局作用域的順序進(jìn)行查找,這三個(gè)作用域組成的有序集合就成為作用域鏈。


7.以下代碼輸出什么?

 function getInfo(name, age, sex){ 
    console.log('name:',name);
    console.log('age:', age);
    console.log('sex:', sex); 
    console.log(arguments); 
    arguments[0] = 'valley'; 
    console.log('name', name); 
}
getInfo('hunger', 28, '男'); 
getInfo('hunger', 28); 
getInfo('男');

輸出:

name:hunger;
age:28;
sex:男;
["hunger", 28, "男"]
name:valley;

name:hunger;
age:28;
sex:undefined;
["hunger", 28]
name:valley;

name:"男";
age:undefined;
sex:undefined;
["男"]
name:valley;

8.寫一個(gè)函數(shù),返回參數(shù)的平方和

 function sumOfSquares(){
     var sum=0;
     for(var i=0; i<arguments.length; i++){
        sum=sum+Math.pow(arguments[i],2);
     }
        console.log(sum);
 }
sumOfSquares(3,5);   //34
sumOfSquares(3,5,-2);  //38
sumOfSquares(3,5,-2,"hello");   //NaN

9.如下代碼的輸出?為什么

console.log(a);  //underfined,因?yàn)樽兞刻嵘木壒?,初始值undefined
var a = 1;  
console.log(b);  //報(bào)錯(cuò),因?yàn)閎沒有聲明

10.如下代碼的輸出?為什么

sayName('world');     //輸出hello world,因?yàn)槭褂煤瘮?shù)聲明聲明的函數(shù),相當(dāng)于整個(gè)函數(shù)都被前置了,可以在聲明之前就被調(diào)用
sayAge(10);  //會報(bào)錯(cuò),因?yàn)槭褂帽磉_(dá)式聲明的函數(shù),只是函數(shù)名被前置了,所以在聲明之前調(diào)用會報(bào)錯(cuò)
function sayName(name){ 
    console.log('hello ', name); 
} 
var sayAge = function(age){ 
    console.log(age); 
};

11.如下代碼的輸出?為什么

function fn(){} 
var fn = 3; 
console.log(fn); //3,因?yàn)楫?dāng)在同一個(gè)作用域內(nèi)定義了名字相同的變量和方法的話,無論其順序如何,變量的賦值會覆蓋方法的賦值

12.如下代碼的輸出?為什么

function fn(fn2){
    console.log(fn2); 
    var fn2 = 3; 
    console.log(fn2); 
    console.log(fn);
    function fn2(){
       console.log('fnnn2');
   } 
} 
fn(10); 

輸出

 function fn2(){
       console.log('fnnn2');
   } 

3

function fn(fn2){ 
    console.log(fn2); 
    var fn2 = 3; 
    console.log(fn2);
     console.log(fn); 
    function fn2(){ 
        console.log('fnnn2'); 
  } 
}

因?yàn)椋?*當(dāng)函數(shù)執(zhí)行有命名沖突的時(shí)候,函數(shù)執(zhí)行時(shí)載入順序是變量、函數(shù)、參數(shù) **,所以原代碼等同于如下代碼:

function fn(fn2){
    var fn2;  //變量聲明前置
    function fn2(){   //函數(shù)聲明前置
        console.log('fnnn2'); 
    }
     console.log(fn2);   //打印fn2()函數(shù)
     fn2 = 3;
     console.log(fn2);   //打印數(shù)字3
     console.log(fn);     //向上尋找,打印fn()函數(shù)
}
fn(10);

13 .如下代碼的輸出?為什么

var fn = 1; 
function fn(fn){ 
    console.log(fn); 
} 
console.log(fn(fn)); 

執(zhí)行報(bào)錯(cuò)。代碼等同于:

var fn;   //變量聲明前置
function fn(fn){   //函數(shù)聲明前置
    console.log(fn); 
}
fn=1;  //fn賦值為1
console.log(fn(fn));   //報(bào)錯(cuò),因?yàn)榇藭r(shí)的fn是數(shù)字1

14.如下代碼的輸出?為什么

 //作用域
console.log(j);   //undefined,變量聲明前置導(dǎo)致
console.log(i);   //undefined,變量聲明前置導(dǎo)致
for(var i=0; i<10; i++){ 
    var j = 100; 
} 
console.log(i);  //10,因?yàn)閕=9時(shí),j=100,然后再執(zhí)行i++,i=10,此時(shí)不滿足條件i<10,跳出循環(huán)。
console.log(j);  //100,j一直等于100.

15.如下代碼的輸出?為什么

fn();   
var i = 10; 
var fn = 20;
console.log(i); 
function fn(){ 
    console.log(i); 
    var i = 99; 
    fn2(); 
    console.log(i); 
    function fn2(){
         i = 100; 
    } 
}

會依次打?。簎ndefined,100,10,分析如下;


var i, fn;
function fn(){ 
    var i;
    function fn2(){
         i = 100; 
    } 
    console.log(i);   //undefined,因?yàn)榇藭r(shí)fn2()函數(shù)沒有被調(diào)用,所以i沒有被賦值,打印undefined
    i = 99; 
    fn2();    //此處調(diào)用了fn2()函數(shù),但是由于fn2()函數(shù)沒有返回值,所以此處沒有打印,但是i已經(jīng)被賦值為100,并且是全局變量
    console.log(i);   //打印出i的值100
   
}
fn();
i=10;  //此處i被重新賦值為10
fn=20;
console.log(i); //打印重新賦值后的i值

16.如下代碼的輸出?為什么

var say = 0; 
(function say(n){ 
    console.log(n); 
    if(n<3) return;
     say(n-1); 
}( 10 )); 
console.log(say);

依次輸出:10,9,8,7,6,5,4,3,2,0
括號內(nèi)是一個(gè)立即執(zhí)行函數(shù),創(chuàng)建了一個(gè)獨(dú)立的作用域,這個(gè)作用域里的變量,外面訪問不到。第一次傳入?yún)?shù)10,打印10,由于n=10不滿足條件(n<3),于是執(zhí)行函數(shù)say(n-1),打印9,接著是8,7......3,2,當(dāng)n=2時(shí),滿足條件(n<3),于是跳出函數(shù),執(zhí)行console.log(say);,打印0。


? 本文歸饑人谷和本人所有,如需轉(zhuǎn)載請注明來源。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容