任務十七~函數

問答部分

一、函數聲明和函數表達式有什么區別?

  • 二者表示函數的方式不一樣,如下
  • 函數聲明(函數名稱不可少)
function funName(){
              statement;
          }
  • 函數表達式(函數名稱可以沒有,末尾有一個分號)
var funName = function(){
             statement;
         };
  • 二者在函數名提升的時候不一樣(函數名提升是指JS引擎在解析代碼時,整個函數會像變量提升一樣提到代碼頭部,但此時并未執行
  • 對于函數聲明,如下代碼
printName("jirengu");
function printName(name){
       console.log(name);
  }

此時顯示的結果如下圖所示

函數名提升

此時執行調用函數時不會報錯,因為函數名的提升,整個函數被提升到代碼頭部

  • 對于函數表達式,如下代碼
printName("jirengu");
var printName = function(name){
          console.log(name);
 };

此時顯示結果如下圖所示


函數表達式

這個時候會報錯是因為此時JS引擎是如下處理代碼的

var printName;
printName("jirengu");
printName = function(name){
            console.log(name);
   };

以上代碼中,第一行表示函數變量聲明提前,但還未被賦值,等于undefined;所以在執行第二行時則會發生錯誤;即函數表達式只是函數聲明提前,但是函數部分并未提前


二、什么是變量的聲明前置?什么是函數的聲明前置?

  • 變量的聲明前置是指Javascript引擎解析代碼,獲取所有被聲明的代碼變量,然后再一行一行的執行。這樣造成的結果是所有變量的聲明語句,都將被提升到代碼頭部,如下情況
console.log(a);
var a = 1;

此時并不會報錯,結果如下圖所示


變量的聲明前置

JS引擎在解析代碼時相當于以下過程

var a;//變量聲明提升到代碼頭部
console.log(a);
a = 1;
  • 函數聲明前置指JS引擎在解析代碼時,整個函數會像變量提升一樣提到代碼頭部,如下情況
printNumber(12);
function printNumber(num){
    console.log(num);
 }

此時不會報錯,顯示結果如下圖所示


函數聲明前置

此過程相當于一下代碼

function printNumber(num){
    console.log(num);
 }//函數聲明前置會讓整個函數提到代碼頭部
printNumber(12);

還有函數表達式的前置,見問題1


三、arguments 是什么?

  • arguments~函數運行時,有時需要提供外部數據,不同的外部數據會得到不同的結果,這種外部數據即為參數,比如如下代碼
function getSquare(x){
     return x * x;
}
console.log(getSquare(4));//16

其中x即為getSquare函數的參數,每次運行getSquare函數時,都要提供x值,否則不會得到結果

  • arguments不是必須的,JS中允許省略arguments,被省略的參數的值就變為undefined
  • 函數體內可以通過arguments對象來訪問這個數組,從而獲取傳遞給函數的每一個參數,如下代碼
function visitArg(){
                console.log(arguments[0]);
                console.log(arguments[1]);
                console.log(arguments[2]);
            }
 visitArg("ji","ren","gu");

顯示結果如下圖


arguments對象獲取函數的參數
  • 通過訪問arguments對象的length屬性可以獲知有多少個參數傳遞給了函數,如下
function howmanyArg(){
     console.log(arguments.length);
}
howmanyArg(1,2,3,4);
howmanyArg(12);
howmanyArg();
howmanyArg("hello",4,false);

輸出結果為下圖所示


arguments.length

四、函數的重載怎樣實現?

  • 在一些語言中(如Java)中,相同名字的函數參數個數不同或者順序不同都被認為是不同的函數,稱為函數重載。但是在Javascript中沒有函數的重載
  • 在Javascript中可以通過arguments來實現函數的重載,如下代碼
function sum(){
    var x = 0;
    for(var i = 0;i < arguments.length;i++){
             x += arguments[i];
     }
     return x;
  }
  console.log(sum(1,2,3));//6

五、立即執行函數表達式是什么?有什么作用 ?

  • 立即執行函數模式(沒有聲明前置)是一種語法,可以讓你的函數在定義后立即被執行,這種模式本質上就是函數表達式(命名的或者匿名的),在創建后立即執行,其代碼格式如下
//在最前最后加括號
(function atOnce(){
           console.log("abc");
}());
//在function外面加括號
(function atOnce(){
           console.log("abc");
})();
  • 立即執行函數表達式的作用:
  • 立即執行函數模式被廣泛使用,它可以幫你封裝大量的工作而不會在背后遺留任何全局變量
  • 定義的所有變量都會成員立即執行函數的局部變量,所以你不用擔心這些臨時變量會污染全局空間
  • 這種模式經常被使用在書簽工具(bookmarklets)中,因為書簽工具在任何頁面上運行并且保持全局命名空間干凈是非常必要的
  • 這種模式也可以讓你將獨立的功能封裝在自包含模塊中
    更多知識

六、什么是函數的作用域鏈?

  • 函數的作用域鏈~任何程序設計語言都有作用域的概念,簡單的說,作用域就是變量與函數的可訪問范圍,即作用域控制著變量與函數的可見性和生命周期。在JavaScript中,變量的作用域有全局作用域和局部作用域兩種
  • 全局作用域~在代碼中任何地方都能訪問到的對象擁有全局作用域,一般來說以下幾種情形擁有全局作用域
  • 最外層函數和在最外層函數外面定義的變量擁有全局作用域,如下
var num = 1;
function example(){
  var ourAuthor = "jirengu";
  function printName(){
      console.log(ourAuthor);
  }
  printName();
 }
//在這段代碼中,變量num和函數example是擁有全局作用域;
而在函數example里面的變量和函數并不擁有全局作用域
  • 所有末定義直接賦值的變量自動聲明為擁有全局作用域,如下
 function example(){
 var a = "jirengu";
 b ="quanju";
 console.log(a);
}
//在以上代碼中,變量b擁有全局作用域;而變量a則不是
  • 所有window對象的屬性擁有全局作用域
  • 局部作用域~和全局作用域相反,局部作用域一般只在固定的代碼片段內可訪問到,最常見的例如函數內部,所有在一些地方也會看到有人把這種作用域稱為函數作用域
  function example(){
    var author = "jirengu";
    function printName(){
        console.log(ourAuthor);
    }
    printName();
   }
//在以上代碼中,變量author和函數printName都只擁有局部變量
  • 作用域鏈~在JavaScript中,函數也是對象,實際上,JavaScript里一切都是對象。函數對象和其它對象一樣,擁有可以通過代碼訪問的屬性和一系列僅供JavaScript引擎訪問的內部屬性。其中一個內部屬性是[[Scope]],由ECMA-262標準第三版定義,該內部屬性包含了函數被創建的作用域中對象的集合,這個集合被稱為函數的作用域鏈,它決定了哪些數據能被函數訪問(如果變量在該作用域中沒有,則它會逐級向上尋找,直至最頂層)
  • 以上內容引用自JavaScript 開發進階:理解 JavaScript 作用域和作用域鏈

代碼部分

一、以下代碼輸出什么?

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("男");

輸出的結果為

//getInfo("hunger",28,"男");輸出結果為
name:hunger
age:28
sex:男
["hunger",28,"男"]
name valley

//getInfo("hunger",28);輸出結果為
name:hunger
age:28
sex:undefined
["hunger",28]
name valley

//getInfo("男");輸出結果為
name:男
age:undefined
sex:undefined
["男"]
name valley

在chrome上運行如下圖所示


代碼1運行效果圖

二、寫一個函數,返回參數的平方和?

function sumOfSquares(){
    var s = 0;
    for(var i = 0;i < arguments.length;i++){
        s += arguments[i] * arguments[i];
    }
    console.log(s);
}
sumOfSquares(2,3,4);//29
sumOfSquares(1,3);//10

在chrome中一下效果圖


代碼2運行效果圖

三、如下代碼的輸出?為什么?

console.log(a);//輸出結果為undefined
var a = 1;//輸出結果為1
console.log(b);// Uncaught ReferenceError: b is not defined

造成以上結果的原因是變量提升,即JS引擎在解析代碼時會把所有的變量聲明提到代碼頭部,再一行一行的執行代碼,所以第一行代碼輸出結果為undefined;而第三行代碼是因為b并不是變量,所以會報錯;以上代碼相當于以下過程

var a;//變量聲明提升
console.log(a);
a = 1;
console.log(b);

四、如下代碼的輸出?為什么?

sayName('world');
sayAge(10);
function sayName(name){
    console.log('hello ', name);
}
var sayAge = function(age){
    console.log(age);
};

以上代碼的輸出結果為

//sayName("world");輸出結果為hello world
//sayAge(10);輸出結果為Uncaught TypeError: sayAge is not a function

第一行代碼由于函數聲明提升,整個sayName函數提到代碼頭部,則在執行sayName("world")時輸出正常結果;第二行代碼由于sayAge是函數表達式,則js引擎在解析代碼時,只是把var sayAge提到代碼頭部,并未把整個函數部分提到代碼頭部,所以在執行時會報錯,以上內容相當于以下過程

function sayName(name){
    console.log('hello ', name);
}//函數聲明前置將整個函數提到代碼頭部
var sayAge;//函數表達式只是把var sayAge提到代碼頭部
sayName('world');//輸出結果為hello world
sayAge(10);//sayAge并不是一個函數,則會報錯
sayAge = function(age){
    console.log(age);
};//將右邊的函數賦值給sayAge

五、如下代碼的輸出?為什么?

function fn(){}
var fn = 3;
console.log(fn);//輸出結果為3

由于變量聲明前置和函數聲明前置,以上代碼相當于一下過程

var fn;//變量聲明前置,將var fn提到代碼頭部
function fn(){}//函數聲明前置
fn = 3;//將數值3賦值給fn
console.log(fn);//最終輸出結果為3

六、如下代碼的輸出?為什么?

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

最終出現的結果是

//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');
    }
 }

出現以上結果相當于js引擎在解析代碼時做了以下事情

function fn(fn2){
    var fn2;//變量聲明提升
    function fn2(){
         console.log('fnnn2');
     }//函數聲明提升
    console.log(fn2);//輸出結果為fn2函數
    fn = 3;//fn被重新賦值
    console.log(fn2);//輸出結果為3
    console.log(fn);//輸出結果為fn函數
 }
fn(10);

七、如下代碼的輸出?為什么?

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

以上代碼輸出結果為

//console.log(fn(fn));
Uncaught TypeError: fn is not a function

出現以上結果是因為JS引擎在解析代碼時做了以下事情

var fn;//變量聲明提升
function fn(fn){
     console.log(fn);
}//函數聲明提升
fn = 1;//把數值1賦值給函數fn
console.log(fn(fn));//會報錯,因為fn不是函數,已經被賦值為1

八、如下代碼的輸出?為什么?

//作用域
console.log(j);
console.log(i);
for(var i=0; i<10; i++){
    var j = 100;
}
console.log(i);
console.log(j);

最終輸出結果為

console.log(i);//輸出結果為10
console.log(j);//輸出結果為100

相當于以下過程

var i;//變量提升,將var i提到代碼頭部
var j;//變量提升,將var j也提到代碼頭部
console.log(i);//undefined,此時變量i還未賦值
console.log(j);//undefined,此時變量j也還未賦值
for(var i=0;i<10;i++){
     var j = 100;
}
console.log(i);//在for循環里執行完后,i為10
console.log(j)//在for循環執行后,j被賦值為100

九、如下代碼的輸出?為什么 ?

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;
    }
}

最終輸出結果為

undefined
100
10

相當于以下過程

var i;//變量提升,將var i提到代碼頭部
var fn;//變量提升,將var i提到代碼頭部
function fn(){
    var i;//函數體內部的變量提升
    function fn2(){
        i = 100;
    }//函數體內部的函數聲明提升
    console.log(i);//undefined,因為此時的i只是聲明,但是還未賦值
    i = 99;//把99賦值給變量i
    fn2();//執行fn2函數,執行完后結果是i的值為100
    console.log(i);//100
}//函數聲明提升,將整個函數提到代碼頭部
fn();//執行函數fn,執行完后,得到undefined和100l兩個結果
i = 10;//把10賦值給ifn = 20;//把20賦值給fn
console.log(i);//10

十、如下代碼的輸出?為什么?

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

最終輸出結果如下圖


代碼10運行效果圖
  • 首先,立即執行函數時沒有函數聲明前置的,則以上代碼會按照順序來執行,解釋如下
var say = 0;//把0賦值給變量say
(function say(n){
    console.log(n);//輸出給定的參數n~10,9,8,7,6,5,4,3,2
    if(n<3) return;//當n<3時,跳出函數,故當n為2時跳出函數
    say(n-1);
}( 10 ));
console.log(say);//輸出為0,因為立即執行函數執行完了就完了,這里是變量say

版權聲明:本教程版權歸鄧攀和饑人谷所有,轉載須說明來源!!!!

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 函數聲明和函數表達式有什么區別 (*)解析器會率先讀取函數聲明,并使其在執行任何代碼之前可以訪問;函數表達式則必須...
    coolheadedY閱讀 400評論 0 1
  • 工廠模式類似于現實生活中的工廠可以產生大量相似的商品,去做同樣的事情,實現同樣的效果;這時候需要使用工廠模式。簡單...
    舟漁行舟閱讀 7,842評論 2 17
  • 1.函數聲明和函數表達式有什么區別 (*) 區別: 函數聲明后面的分號可加可不加,不加也不影響接下來語句的執行,但...
    Sheldon_Yee閱讀 410評論 0 1
  • 一、函數聲明和函數表達式有什么區別?(*) ECMAScript里面規定了三種聲明函數的方式: 構造函數函數也是對...
    婷樓沐熙閱讀 478評論 0 2
  • Define: .new promise傳入的函數里面是立即執行的 . promise 是一個對象,里面有三個狀態...
    享悅moonlight閱讀 359評論 0 0