JS 函數(shù)

  • 函數(shù)聲明和函數(shù)表達(dá)式有什么區(qū)別 (*)
    • 解析器會(huì)率先讀取函數(shù)聲明,并使其在執(zhí)行任何代碼之前可以訪問(wèn);函數(shù)表達(dá)式則必須等到解析器執(zhí)行到它所在的代碼行才會(huì)被執(zhí)行。
    • 函數(shù)聲明:
console.log(sum(10,10));
function sum(num1, num2) {
      return num1 + num2;
}

上述代碼完全可以運(yùn)行。因?yàn)樵诖a執(zhí)行之前,解析器就已經(jīng)通過(guò)一個(gè)名為函數(shù)聲明提升的過(guò)程,讀取并將函數(shù)聲明添加到執(zhí)行環(huán)境中。在執(zhí)行sum(10,10)時(shí)JavaScript引擎會(huì)聲明函數(shù)并將它們放到源代碼樹的頂部。等價(jià)于:

function sum(num1, num2) {
      return num1 + num2;
}//函數(shù)放置在頂部被聲明
console.log(sum(10,10));
  • 函數(shù)表達(dá)式:
console.log(sum(10,10));
var sum = function(num1, num2) {
      return num1 + num2;
}

上述代碼運(yùn)行將會(huì)出錯(cuò),因?yàn)楹瘮?shù)處在一個(gè)var初始化語(yǔ)句中,而不是一個(gè)函數(shù)聲明。等價(jià)于

var sum;
console.log(sum(10,10));
sum = function(num1, num2) {
      return num1 + num2;

在執(zhí)行到函數(shù)所在的語(yǔ)句前,變量sum中不會(huì)保存有對(duì)函數(shù)的引用,這時(shí)var sum = undefined;那么alert(undefined(10,10))是沒(méi)有任何意義的,所以會(huì)出錯(cuò)。

  • 什么是變量的聲明前置?什么是函數(shù)的聲明前置 (**)
    • 變量的聲明前置:
      JavaScript引擎的工作方式是先解析代碼,獲取所有被聲明的變量,然后再一行一行地運(yùn)行。所有的變量聲明語(yǔ)句都會(huì)被提升到代碼頂部。
console.log(a);
var a = 123;

上述代碼輸出為undefined,因?yàn)樵趫?zhí)行時(shí)先解析了var a,a聲明后沒(méi)有賦值所以為undefined,然后再執(zhí)行console語(yǔ)句,最后給a賦值為3??梢詫懗?

var a;
console.log(a);
a = 123;
  • 函數(shù)的聲明前置:
    JavaScript引擎將函數(shù)名視同變量名,所以采用function
    命令聲明函數(shù)時(shí),整個(gè)函數(shù)會(huì)像變量聲明一樣,被提升到代碼頂部。
fn();
function fn() {}

上述代碼不會(huì)報(bào)錯(cuò),因?yàn)樵趂n()執(zhí)行前,函數(shù)function fn() {}就被提升到代碼的頂部,在執(zhí)行fn()前就被聲明了??梢詫懗?

function fn() {}
fn();

如果采用賦值語(yǔ)句(函數(shù)表達(dá)式)定義函數(shù)就會(huì)報(bào)錯(cuò):

fn();
var fn = function (){};
//error

上述代碼在執(zhí)行時(shí)變量fn只是被聲明了并沒(méi)有被賦值為函數(shù),可以寫成:

var fn;
fn();
fn = function (){}

這里的fn被聲明后為undefined,執(zhí)行到fn()時(shí)其實(shí)是undefined(),這個(gè)是沒(méi)有意義的所以會(huì)報(bào)錯(cuò)。

  • arguments 是什么 (*)
    • arguments對(duì)象:
      它與數(shù)組類似但不是Array實(shí)例。arguments可以使用類似數(shù)組的語(yǔ)法訪問(wèn)它每一個(gè)元素(第一個(gè)元素為arguments[0],第二個(gè)元素為arguments[1]),也可以用length屬性來(lái)確定傳遞了多少參數(shù)。
function say() {
      console.log("Hello " + arguments[0] + ", " + arguments[1]);
}
say("world", "morning"); //Hello world, morning
  • arguments對(duì)象的length屬性:
    通過(guò)訪問(wèn)arguments對(duì)象的length屬性可以知道有多少個(gè)參數(shù)傳遞給了函數(shù)。
function howManyArgs() {
      console.log(arguments.length);
}
howManyArgs("str", 123);//2
howManyArgs();//0
howManyArgs(123);//1
howManyArgs("str", 321, "str1");//3
  • 我們可以利用arguments對(duì)象的length屬性讓函數(shù)能夠接受不同個(gè)數(shù)的參數(shù)并分別實(shí)現(xiàn)不同的功能。
function add() {
      if(arguments.length == 1) {
             console.log(arguments[0] + 10);
       }  else if (arguments.length == 2){
             console.log(arguments[0] + arguments[1]);
       }
}
add(10); //20
add(20, 30); //50
  • 上述代碼中當(dāng)只傳遞給函數(shù)一個(gè)參數(shù)時(shí)給這個(gè)參數(shù)加上10,當(dāng)傳遞給函數(shù)兩個(gè)參數(shù)時(shí)則這兩個(gè)參數(shù)相加。arguments對(duì)象可以與命名的參數(shù)一起使用,所以我們還可以寫成下面這種形式
function add(num1, num2) {
      if(arguments.length == 1) {
             console.log(num1 + 10);
       }  else if (arguments.length == 2){
             console.log(arguments[0] +num2);
       }
}
add(10); //20
add(20, 30); //50
  • 這里我們可以知道num1與arguments[0]的值相同,它們可以互換。上例代碼也告訴我們,不管你給函數(shù)命名了多少參數(shù),JavaScript是不會(huì)管你給函數(shù)傳遞了多少參數(shù)的。
  • arguments的值與對(duì)應(yīng)函數(shù)中命名參數(shù)的值保持同步:
function add(num1, num2) {
        arguments[1] = 10;
        console.log(arguments[0] + num2);
}
add(20, 30); //30
  • 上述代碼每次執(zhí)行到add()函數(shù)都會(huì)重寫第二個(gè)參數(shù),將第二個(gè)參數(shù)改寫為10。arguments對(duì)象的值會(huì)對(duì)應(yīng)到命名參數(shù)(arguments[1]與num2的值相同)所以修改了arguments[1]也就修改了num2,結(jié)果就是它們都變成了10。這里arguments[1]與num2的值雖然相同,但它們的內(nèi)存空間是不同并且獨(dú)立的,只是它們的值會(huì)同步。
  • 如果我們?cè)谶@里只傳遞了一個(gè)參數(shù),那么arguments[1]設(shè)置的值不會(huì)反映到命名參數(shù)里。因?yàn)閍rguments對(duì)象的長(zhǎng)度只是由傳遞到函數(shù)的參數(shù)個(gè)數(shù)決定(add(20, 30)),不是由定義函數(shù)時(shí)命名的參數(shù)個(gè)數(shù)決定(function add(num1, num2) {})。


    只傳遞一個(gè)參數(shù)但賦值arguments[1]
  • 這個(gè)圖里我們可以發(fā)現(xiàn),執(zhí)行函數(shù)時(shí)只傳遞了一個(gè)參數(shù),在這樣的情況下沒(méi)有傳遞值的命名參數(shù)(num2)被自動(dòng)賦為undefined值。與只聲明了變量但又沒(méi)有初始化賦值一樣。即使我們給arguments[1]賦值為10,num2的值仍然還是undefined(只是值同步訪問(wèn)的空間是獨(dú)立的)。
  • 函數(shù)的重載怎樣實(shí)現(xiàn) (**)
    • 我們可以利用檢查傳入函數(shù)中參數(shù)的類型和數(shù)量來(lái)做出不同的反應(yīng),用來(lái)模仿重載
    • 以通過(guò)處理傳入的參數(shù)來(lái)實(shí)現(xiàn)類似重載的效果
function printPeopleInfo(name, age, sex) { 
if (name) { 
console.log(name); 
} 
if (age) { 
console.log(age); 
} 
if (sex) { 
console.log(sex); 
}
}
printPeopleInfo('Byron', 26);
printPeopleInfo('Byron', 26, 'male');
  • 通過(guò)遍歷 arguments來(lái)實(shí)現(xiàn)類似重載的效果
function sum(){
var sum = 0;
for (var i = 0; i < arguments.length; i++) { 
sum = sum + arguments[i];
}
return sum;
}
console.log(sum(1,3,5));
  • 根據(jù)傳入?yún)?shù)的不同類型來(lái)實(shí)現(xiàn)重載
function addOrSay(num1, num2) {
      for (var i = 0; i < arguments.length; i++) {
              if(arguments.length == 1 && !isNaN(arguments[i])) {
                     console.log(num1 + 10);
               }  else if (arguments.length == 2 && !isNaN(arguments[i])) {
                     console.log(arguments[0] +num2);
               }  else {
                     console.log(arguments[i]) ;
               }
       }
}
addOrSay(10);//10
addOrSay(20, 30);//50
addOrSay("Hello");//Hello
不同類型重載
  • 立即執(zhí)行函數(shù)表達(dá)式是什么?有什么作用 (***)
    • 立即執(zhí)行函數(shù)表達(dá)式(IIFE),是指定義函數(shù)后立即執(zhí)行函數(shù);為了避免歧義,javascript規(guī)定,如果function關(guān)鍵字出現(xiàn)在行首,一律解釋為語(yǔ)句,下面是兩種立即執(zhí)行函數(shù)表達(dá)式的寫法,它們都是以圓括號(hào)開頭的,javascript引擎就會(huì)認(rèn)為后面是一個(gè)表達(dá)式而不是函數(shù)定義的語(yǔ)句
    • 立即執(zhí)行函數(shù)的語(yǔ)法錯(cuò)誤
function() {}()//這里出錯(cuò)是因?yàn)闆](méi)把明確告訴圓括號(hào)運(yùn)算符這里是一個(gè)表達(dá)式。
                      解析器把這段語(yǔ)句當(dāng)成了一個(gè)函數(shù)聲明。
                      函數(shù)聲明又必須要有標(biāo)識(shí)符作為函數(shù)名稱。
function fn() {}()//加上標(biāo)識(shí)符結(jié)果成了聲明了函數(shù)fn  結(jié)果出錯(cuò)。                       
function fn() {}(1) //1   末尾的括號(hào)作為運(yùn)算符,又必須要提供表達(dá)式做為參數(shù)。
  • 寫成下列方式直接調(diào)用時(shí)可以的
var fn = function() {}()//這樣調(diào)用函數(shù)是可以的
  • 那么當(dāng)要使用立即執(zhí)行函數(shù)時(shí)可以使用括號(hào)來(lái)引導(dǎo)解析器,指明括號(hào)運(yùn)算符附近是一個(gè)表達(dá)式
 ( function() {}() );
 [ function() {}() ];//當(dāng)括號(hào)出現(xiàn)在匿名函數(shù)的末尾想要調(diào)用函數(shù)時(shí),
                          它會(huì)默認(rèn)將函數(shù)當(dāng)成是函數(shù)聲明。 function fn() {}
 ( function() {} )();//默認(rèn)為函數(shù)表達(dá)式 var fn = function() {}
  • 也可以使用一元運(yùn)算符來(lái)寫立即執(zhí)行函數(shù)引導(dǎo)解析器
    ~ function() {}();
    ! function() {}();
    + function() {}();
    - function() {}();
  • 甚至可以寫成這樣
delete function() {}();
typeof function() {}();
void function() {}();
new function() {}();
new function() {};
var f = function() {}();
--
1, function() {}();
1 ^ function() {}();
1 > function() {}();
  • 什么是函數(shù)的作用域鏈 (****)
    • 全局:在web瀏覽器中,全局執(zhí)行環(huán)境就是window對(duì)象,所有的全局變量和函數(shù)都是作為window對(duì)象的屬性(變量)和方法(函數(shù))創(chuàng)建的。
    • 函數(shù):每個(gè)函數(shù)都有自己的執(zhí)行環(huán)境,由{}與全局的window對(duì)象分開。
    • 作用域鏈:當(dāng)代碼在一個(gè)環(huán)境中執(zhí)行時(shí),會(huì)創(chuàng)建變量對(duì)象的一個(gè)作用域鏈保證對(duì)執(zhí)行環(huán)境有權(quán)訪問(wèn)的所有變量和函數(shù)的有序訪問(wèn)。
var color = "blue";
function changeColor() {
      if (color === "blue") {
          color = "red";
      }  else  {
          color = "blue";
      }
}
changeColor();
console.log("Color is now " + color);//Color is now red

在上述代碼中,changeColor()函數(shù)的使用了全局環(huán)境下的變量color。函數(shù)可以在內(nèi)部訪問(wèn)color就是因?yàn)榭梢栽谶@個(gè)作用域鏈中找到它。

var color = "blue";
function changeColor() {
         var anotherColor = "red";
         console.log(anotherColor, color);
         function swapColor() {
             var tempColor = anotherColor;
             anotherColor = color;
             color = tempColor;
             //這里可以訪問(wèn)color, anotherColor, tempColor
             console.log(tempColor, anotherColor, color);
          }
          //這里可以訪問(wèn)color, anotherColor,
          swapColor();
}
//這里只能訪問(wèn)color
console.log( color);
changeColor();
運(yùn)行效果
  • 以上代碼共有三個(gè)執(zhí)行環(huán)境:
    1 window全局環(huán)境:有一個(gè)變量color和一個(gè)函數(shù)changeColor()
    2 changeColor()的局部環(huán)境:有一個(gè)變量anotherColor和一個(gè)swapColor()函數(shù),但也可以訪問(wèn)全局環(huán)境中的變量color
    3 swapColor()的局部環(huán)境:只有一個(gè)變量tempColor,在這個(gè)函數(shù)內(nèi)可以訪問(wèn)到全局環(huán)境下的變量color,也可以訪問(wèn)changeColor()局部環(huán)境的anotherColor,因?yàn)槠渌麅蓚€(gè)環(huán)境是它的父執(zhí)行環(huán)境。


    作用域鏈?zhǔn)疽鈭D
  • 參考:作用域鏈

  • 代碼
    1 以下代碼輸出什么? (難度**)

  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

2 寫一個(gè)函數(shù),返回參數(shù)的平方和?如 (難度**)

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

3 如下代碼的輸出?為什么 (難度*)

 console.log(a); 
 var a = 1;
 console.log(b);
//undefined   在瀏覽器中變量a被提升至頂部先被聲明,執(zhí)行到console.log(a)語(yǔ)句時(shí)變量a還沒(méi)有被賦值。
//error   在整個(gè)全局中b并沒(méi)有被賦值也沒(méi)有被聲明。

4 如下代碼的輸出?為什么 (難度*)

    sayName('world');
    sayAge(10);
    function sayName(name){
        console.log('hello ', name);
    }
    var sayAge = function(age){
        console.log(age);
    };
//hello world  聲明函數(shù)function sayName(name)被提升到頂部進(jìn)行聲明
所以執(zhí)行sayName('world');時(shí)可以正常運(yùn)行。
//error  函數(shù)表達(dá)式var sayAge = function(age){}中var sayAge被提升到頂部進(jìn)行聲明
這時(shí)候sayAge為undefined,執(zhí)行到sayAge(10);時(shí)為undefined(10)沒(méi)有意義。

5 如下代碼的輸出?為什么 (難度**)

function fn(){}
    var fn = 3;
    console.log(fn);//3  聲明完變量fn,又聲明函數(shù)fn,最后賦值3給變量fn覆蓋了

6 如下代碼的輸出?為什么 (難度**)

 function fn(fn2){
     console.log(fn2);
     var fn2 = 3;
     console.log(fn2);//3;
     console.log(fn);
     function fn2(){
        console.log('fnnn2');
    }
 }
fn(10);
/* function fn2(){
        console.log('fnnn2');
    } */  執(zhí)行fn(10)時(shí),參數(shù)10被傳遞到函數(shù)fn中并賦值給了fn2,
//在函數(shù)fn中提升聲明變量fn2,并且提升聲明函數(shù)fn2(){},這時(shí)候參數(shù)fn2的值被聲明函數(shù)fn2覆蓋,所以輸出的是fn2函數(shù)
//3  執(zhí)行完console.log(fn2); fn2變成變量并賦值為3
/* function fn(fn2){
   console.log(fn2);
   var fn2 = 3;
   console.log(fn2);//3;
   console.log(fn);
   function fn2(){
        console.log('fnnn2');
    }
 } */  執(zhí)行console.log(fn);輸出函數(shù)fn

7 如下代碼的輸出?為什么 (難度***)

    var fn = 1;
    function fn(fn){
         console.log(fn);
    }
    console.log(fn(fn));//error  
    typeof(fn);//number 

在JavaScript中上述代碼的變量和函數(shù)聲明會(huì)提升到最頂部最先聲明,然后再賦值,所以可以寫成:

    var fn;
     function fn(fn){
         console.log(fn);
    }
    fn = 1; 
    console.log(fn(fn));//這個(gè)時(shí)候fn是一個(gè)number,所以不能調(diào)用函數(shù)fn了。

8 如下代碼的輸出?為什么 (難度**)

    //作用域
    console.log(j);//undefined  //for循環(huán)不處在函數(shù)中聲明的變量i和變量j仍然是全局變量,但是沒(méi)賦值。
    console.log(i);//undefined  //同上
    for(var i=0; i<10; i++){
        var j = 100;
    }
    console.log(i);//10  //變量i在for循環(huán)中的i<10;i++遞加到10。
    console.log(j);//100  //變量j在for循環(huán)中被賦值為100。

9 如下代碼的輸出?為什么 (難度****)

    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 fn;
function fn(){
/*statement*/
}
fn()//在這里執(zhí)行fn(),下面看fn函數(shù)中是什么樣的
    function fn(){
        var i;
        function fn2(){
            i = 100;
        }
    }
        console.log(i);//變量i只被聲明輸出undefined
        i = 99;
        fn2(); //這里執(zhí)行函數(shù)fn2,變量i在函數(shù)fn2中賦值為100并返回
        console.log(i); // 輸出100
    }
i = 10;
fn = 20;
console.log(i); //i被賦值為10,輸出10

10 如下代碼的輸出?為什么 (難度5*****)

    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 */

上述代碼可以寫成

    var say;
    (function say(n){
        console.log(n);
        if(n<3) return;
        say(n-1);
    }( 10 ));//立即執(zhí)行函數(shù),在聲明時(shí)就開始給函數(shù)say傳入?yún)?shù)10并執(zhí)行say函數(shù)
當(dāng)執(zhí)行多次say(n-1)后n=2時(shí)被return返回并跳出函數(shù)say不在執(zhí)行say(n-1)
    say = 0//say變量被賦值0并輸出。
    console.log(say);
/* 10
     9
     8
     7
     6
     5
     4
     3
     2
     0 */

本博客版權(quán)歸 本人和饑人谷所有,轉(zhuǎn)載需說(shuō)明來(lái)源

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

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

  • 1. 函數(shù)聲明和函數(shù)表達(dá)式有什么區(qū)別 (*) 函數(shù)在JS中有三種方式來(lái)定義:函數(shù)聲明(function decla...
    進(jìn)擊的阿群閱讀 485評(píng)論 0 1
  • 工廠模式類似于現(xiàn)實(shí)生活中的工廠可以產(chǎn)生大量相似的商品,去做同樣的事情,實(shí)現(xiàn)同樣的效果;這時(shí)候需要使用工廠模式。簡(jiǎn)單...
    舟漁行舟閱讀 8,140評(píng)論 2 17
  • 一、問(wèn)答1、函數(shù)聲明和函數(shù)表達(dá)式有什么區(qū)別 ()函數(shù)聲明和函數(shù)表達(dá)式都是聲明函數(shù)的方法。函數(shù)聲明:function...
    崔敏嫣閱讀 410評(píng)論 0 0
  • 概念 1、函數(shù)聲明和函數(shù)表達(dá)式有什么區(qū)別? ECMAScript規(guī)定了三種聲明函數(shù)方式: 構(gòu)造函數(shù)首先函數(shù)也是對(duì)象...
    周花花啊閱讀 578評(píng)論 1 1
  • 函數(shù)聲明和函數(shù)表達(dá)式有什么區(qū)別? 函數(shù)聲明和函數(shù)表達(dá)式是EMACScript規(guī)定的兩種不同的聲明函數(shù)的方法。1.函...
    LeeoZz閱讀 454評(píng)論 0 1

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