函數與作用域

函數聲明和函數表達式有什么區(qū)別?

函數聲明:function functionName() {}
函數表達式:var fn = function() {}

  • 函數聲明會提前,函數表達式可以省略標識符(函數名)。

函數聲明不必放到調用的前面,函數表達式聲明必須放到調用的前面。

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

在一個作用域下,var 聲明的變量和function 聲明的函數會前置.前置到當前作用域的開頭。
變量聲明的前置:

console.log(i);
var i = 1;     //undefined

代碼執(zhí)行過程:

var i;.   //提前在程序最開始,聲明一個變量i 未賦值 所以是 undefined
console.log(i);
i = 1;   //console.log(i)在 i = 1 ;賦值語句之前,而變量聲明前置所以打印的時候i是undefined;

函數的聲明前置:

test(1)
function test(x){
  console.log(x);
}     // 1

通過函數聲明的方式建立的函數表達式會提前,這段代碼過程:

function test(x){};
test(1)

輸出的結果為1;因為function test(x){} 整個函數 已經提前到了test(1)之前。
但是,同樣的方法,使用函數表達式,其出現的結果是報錯,因為其調用在其聲明之前。
如下例子:

func1();    // 輸出:我是函數聲明

func2();    // 報錯 

console.log(a); // 輸出:undefined

function func1() {
    console.log("我是函數聲明");
}

var func2 = function() {
    console.log("我是函數表達式");
}

var a = 10;

arguments 是什么?

arguments是一個類數組對象。代表傳給一個function的參數列表。
?arguments對象是函數內部的本地變量;arguments 已經不再是函數的屬性了。可以在函數內部通過使用 arguments 對象來獲取函數的所有參數。這個對象為傳遞給函數的每個參數建立一個條目,條目的索引號從0開始。它包括了函所要調用的參數。object對象。類數組。

函數的"重載"怎樣實現?

首先,要明確,在JS中,沒有重載。同名函數會覆蓋。但可以在函數體針對不同的參數調用執(zhí)行相應的邏輯。
如下例:

function printPeopleInfo(name, age, sex){
    if(name){
      console.log(name);
    }

    if(age){
      console.log(age);
    }

    if(sex){
      console.log(sex);
    }
  }
  printPeopleInfo('Byron', 26);//Byron 26
  printPeopleInfo('Byron', 26, 'male');//Byron 26 male

javascript不能支持函數的重載,如下:

function f(length) 
{ 
   alert("高為:"+length); 
} 

function f(length,width) 
{ 
   alert("高為:"+length+",寬為:"+width); 
}

上面那段代碼其實是行不通的,因為函數定義時的參數個數和函數調用時的參數個數沒有任何關系。在函數中可以用f.arguments[0]和f.arguments[1]得到調用時傳入的第一和第二個參數,所以定義function(length),后面用f(10,10)調用是沒有問題的。所以在上面這段代碼中,第二個函數是永遠不可能被調用到的,那么,要怎樣才能實現像函數重載那樣的功能呢?
那就是在函數定義中用f.arguments.length判斷一下調用時傳入的參數個數。然后對不同的情況采用不同的處理方式。 如下:

function f() 
{ 
    var len= arguments.length; 
    if(2 == len) 
    { 
        var length = arguments[0]; 
        var width = arguments[1]; 
        f2(length,width); 
    } 
    else 
    { 
        var length = arguments[0]; 
        f1(length); 
    } 
} 

function f1(length) 
{ 
    alert("高為:"+length); 
} 

function f2(length,width) 
{ 
    alert("高為:"+length+",寬為:"+width); 
}

*這樣,你就可以給函數f()傳入一個參數也可以傳入兩個參數了,比如f(10)和f(10,10); *

  • 這樣雖然可以實現重載,但也不是很好用,我們可以根據具體情況在一個函數中實現重載,如果要重載的兩個函數相差較大,那就保留兩個函數,而如果兩個函數的實現基本差不多,那么可以在一個函數中進行判斷,處理不同的部分,而不需要像上面那樣寫成三個函數,如下:
function f(length) 
{ 
    var len= arguments.length; 
    if(1 == len) 
    { 
        var width = arguments[1]; 
        alert("高為:"+length+",寬為:"+width); 
    } 
    else 
    { 
        alert("高為:"+length); 
    } 
}

立即執(zhí)行函數表達式是什么?有什么作用

(function(){
    var a = 1;
})()

作用: 隔離作用域。
??JavaScript有很多致命缺陷,比如JavaScript中沒有命名空間,而且只有function代碼塊內部可以隔離變量作用域,自調用匿名函數就用來防止變量彌散到全局,以免各種js庫沖突。

求n!,用遞歸來實現

function fn(n) {
                if(n < 0){
                alert("error")
                }
        else if(n == 0 || n == 1)  {
            return 1;
        }
        return n * fn(n-1);
    }
    fn(5);

以下代碼輸出什么?

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('饑人谷', 2, '男');
    getInfo('小谷', 3);
    getInfo('男');
輸出結果如下:
name: 饑人谷
age: 2
sex: 男
['饑人谷',2,'男']
name valley

name:小谷
age:2
sex: undefined
['小谷','3']
name valley

name:男
age:undefined
sex:undefined
['男']
name valley

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

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

如下代碼的輸出?為什么

console.log(a);
var a = 1;
console.log(b);
//  undefined 
//  ReferenceError: b is not defined

console.log(a)輸出的是undefined、因為變量在當前作用域下被前置,結果在console.log(a)前面的var a;雖然聲明了但是沒有賦值,結果為undefined.
console.log(b) b完全是沒有聲明也沒有賦值,結果是報錯.//ReferenceError: b is not defined
*/

如下代碼的輸出?為什么

    sayName('world');
    sayAge(10);
    function sayName(name){
        console.log('hello ', name);
    }
    var sayAge = function(age){
        console.log(age);
    };
// hello world
// TypeError: sayAge is not a function

sayNme('world');會返回'hello world'.
sayAge(10);因為是使用表達式定義,所以,當調用在定義之前就會報錯,sayAge is not a function.

如下代碼輸出什么? 寫出作用域鏈查找過程偽代碼

var x = 10
bar() 
function foo() {
  console.log(x)
}
function bar(){
  var x = 30
  foo()
}
/*   10
1.
globalContext = {
  AO: {
    x: 10, foo: function, bar: function
  },

foo.[[scope]] = globalContext.AO
bar.[[scope]] = globalContext.AO
}
2.調用bar()
barContext = {
  AO: {
    x: 30
  }
  Scope: bar.[[scope]] = globalContext.AO
}
3.調用foo()
fooContext = {
  AO: {}
  Scope: foo.[[scope]] = globalContext.AO
} */

如下代碼輸出什么? 寫出作用域鏈查找過程偽代碼

var x = 10;
bar() 
function bar(){
  var x = 30;
  function foo(){
    console.log(x) 
  }
  foo();
}   
輸出: 30
1. 
globalContext={
  AO:{
    x:10, 
    bar : function
  },
  Scope:null
}
  bar.[[scope]]=globalContext.AO
2. 調用bar
barContext={
  AO:{
    x: 30,
    foo:function
  },
Scope: bar.[[scope]]=globalContext.AO
}
foo.[[scope]]=barContext.AO
3. 調用foo
fooContext={
  AO:{},
  Scope: foo.[[scope]]=barContext.AO
}

以下代碼輸出什么? 寫出作用域鏈的查找過程偽代碼

var x = 10;
bar() 
function bar(){
  var x = 30;
  (function (){
    console.log(x)
  })()
}
輸出: 30
1.
globalContext={
  AO:{
    x: 10,
    bar:function
 }
    Scope: null
}
bar.[[scope]]=globalContext.AO
2.調用bar
barContext={
  AO:{
    x:30, 
    function(){}
  }
Scope:bar.[[scope]]=globalContext.AO
}
function().[[scope]]=barContext.AO
3. 調用function()
functionContext={
  AO:{}
  Scope:function().[[scope]]=barContext.AO
}

以下代碼輸出什么? 寫出作用域鏈查找過程偽代碼

var a = 1;

function fn(){
  console.log(a)
  var a = 5
  console.log(a)
  a++
  var a
  fn3()
  fn2()
  console.log(a)

  function fn2(){
    console.log(a)
    a = 20
  }
}

function fn3(){
  console.log(a)
  a = 200
}

fn()
console.log(a)
輸出: undefined 5 1 6 20 200
1.
globalContext={
  AO:{
    a:200,
    fn:function,
    fn3:function
  }
Scope: null
}
fn.[[scope]]=globalContext.AO
fn3.[[scope]]=globalContext.AO
2. 調用fn
fnContext={
  AO:{
  a:20, 
  fn2:function  
}
Scope: fn.[[scope]]=globalContext.AO
}
fn2.[[scope]]=fnContext.AO
3. 調用fn3
fn3Context={
  AO:{}
  Scope:fn3.[[scope]]=globalContext.AO
}
4. 調用fn2
fn2Context={
  AO:{}
 Scope:fn2.[[scope]]=fnContext.AO
}

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

推薦閱讀更多精彩內容

  • 1.函數聲明和函數表達式有什么區(qū)別 函數就是一段可以反復調用的代碼塊。函數還能接受輸入的參數,不同的參數會返回不同...
    徐國軍_plus閱讀 498評論 0 0
  • 函數聲明和函數表達式有什么區(qū)別 函數聲明語法:function functionName(arg0,arg1,ar...
    _Dot912閱讀 597評論 0 3
  • 函數聲明和函數表達式有什么區(qū)別? 函數聲明和函數表達式是EMACScript規(guī)定的兩種不同的聲明函數的方法。1.函...
    LeeoZz閱讀 354評論 0 1
  • 1.函數聲明和函數表達式有什么區(qū)別 function命令聲明的代碼區(qū)塊,就是一個函數。function命令后面是函...
    饑人谷_Leon閱讀 292評論 0 0
  • 聲明前置和作用域也是JS 部分面試常考點 1.函數聲明和函數表達式有什么區(qū)別 函數聲明:使用function關鍵字...
    湖衣閱讀 210評論 0 0