函式與作用域

weixin_34357887發表於2017-04-18

1. 函式宣告和函式表示式有什麼區別

  • 使用function關鍵字宣告一個函式時,宣告不必放到呼叫的前面。
    //函式宣告 function sayHello(){ console.log('hello') } //函式呼叫 sayHello()
    函式表示式是把一個匿名函式function()賦給一個變數,宣告必須放到呼叫的前面。
    //函式宣告 var sayHello = function(){ console.log('hello'); } //函式呼叫 sayHello()
  • 函式宣告必須有識別符號,也就是常說的函式名,函式表示式可以省略函式名。
  • 用函式宣告建立的函式可以隨時呼叫,而用函式表示式建立的函式是在執行時進行賦值,且要等到表示式賦值完成後才能呼叫。

2. 什麼是變數的宣告前置?什麼是函式的宣告前置

  • 在一個作用域下,var 宣告的變數和function 宣告的函式會前置
    console.log(a);//undefined
    var a = 3; console.log(a);//3
    sayHello(); function sayHello(){ console.log('hello'); }
    執行順序為:
    var a function sayHello() console.log(a) //undefined
    a = 3 console.log(a) //3
    sayHello()
  • 函式內部的宣告前置。
    var a = 1; function main() { console.log(a); var a = 2; } main()
    輸出undefined,函式的執行順序如下,變數宣告被提前了。
    var a = 1; function main() { var a; console.log(a); //此時a為undefined
    a = 2; }
  • 解析器在向執行環境載入資料時,會率先讀取函式宣告,並使其在執行任何程式碼之前可用,所以存在函式宣告提升,對程式碼求值的時候,js引擎在第一遍會宣告函式並將它們放到原始碼樹的頂部,所以即使呼叫函式的語句在宣告函式的前面也能正常執行。但是對於函式表示式,它必須等到解析器執行到它所在的程式碼行,才會真正被解釋執行。

3. arguments 是什麼

arguments是一個類陣列物件,在函式內部,可以使用arguments物件獲取到該函式的所有傳入引數。

4. 函式的"過載"怎樣實現

JS沒有過載! 同名函式會覆蓋。 但可以在函式體針對不同的引數呼叫執行相應的邏輯。例如:
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');
可以在函式體內部使用if語句,來根據傳遞的引數,執行不同的語句,來模擬函式的過載。

5. 立即執行函式表示式是什麼?有什麼作用

表示式:(function(){ })();
立即執行函式就是,宣告一個匿名函式,馬上呼叫這個匿名函式。立即執行函式可以建立一個獨立的作用域,這個作用域裡面的變數,外面訪問不到(即避免變數汙染)。

6. 求n!,用遞迴來實現

function factor(n){ if(n === 1) { return 1 } return n * factor(n-1) } factor(5) n=5

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('飢人谷', 2, '男'); 
    getInfo('小谷', 3); 
    getInfo('男'); 
  • function getInfo(name, age, sex) { console.log('name:',name); console.log('age:', age); console.log('sex:', sex); } getInfo('飢人谷', 2, '男');結果://name: 飢人谷 age: 2 sex: 男
    getInfo('小谷', 3);結果://name: 小谷 age: 3 sex: undefined
    getInfo('男');結果://name: 男 age: undefined sex: undefined

  • function getInfo(name, age, sex) { console.log(arguments); } getInfo('飢人谷', 2, '男'); 結果:類陣列 ["飢人谷", 2, "男"]
    getInfo('小谷', 3); 結果:類陣列 ["小谷", 3]
    getInfo('男');結果:類陣列 ["男"]

  • function getInfo(name, age, sex) { arguments[0] = 'valley'; console.log('name', name); } getInfo('飢人谷', 2, '男'); 結果:name valley
    getInfo('小谷', 3); 結果:name valley
    getInfo('男'); 結果:name valley

8. 寫一個函式,返回引數的平方和?

  • function sumOfSquares(num1,num2,num3) { sum = num1*num1+num2*num2+num3*num3; return sum; } var result = sumOfSquares(2,3,4); console.log(result) //29

  • function sumOfSquares(num1,num2) { sum = num1*num1+num2*num2; return sum; } var result2 = sumOfSquares(1,3); console.log(result2) //10

9. 如下程式碼的輸出?為什麼

console.log(a); //結果:undefined
var a = 1;
console.log(b); //結果:報錯,b沒有定義。
因為變數宣告會前置,實際順序如下:
var a console.log(a); a = 1; console.log(b);

10. 如下程式碼的輸出?為什麼

sayName('world'); //結果:hello world
sayAge(10); //結果:報錯,sayAge不是一個函式。
function sayName(name){ console.log('hello ', name); } var sayAge = function(age){ console.log(age); };
因為函式宣告會前置,但函式表示式只會在執行時才會執行函式,實際順序如下:
function sayName(name){ console.log('hello ', name); } //宣告函式
var sayAge //宣告變數
sayName("name") //呼叫函式
sayAge(10); // 呼叫函式,但按順序函式還未宣告。
sayAge = function(age){ console.log(age); };

11. 如下程式碼輸出什麼? 寫出作用域鏈查詢過程虛擬碼

var x = 10 bar() function foo() { console.log(x) } function bar(){ var x = 30 foo() }
結果:10
作用域鏈查詢過程:
globalContext { AO: { x: 10, foo: function bar: function }, Scope: null }
//foo.[[scope]] = globalContext.AO
//bar.[[scope]] = globalContext.AO

barContext { AO: { x: 30 foo: function }, Scope: bar.[[scope]] }

fooContext { AO: {}, Scope: foo.[[scope]] }
console.log(x)先從foo 執行上下文中的AO裡找,找不到再從foo的[[scope]]裡找,找到後即呼叫,所以 console.log(x)是 10

12. 如下程式碼輸出什麼? 寫出作用域鏈查詢過程虛擬碼

var x = 10; bar() function bar(){ var x = 30; function foo(){ console.log(x) } foo(); }
結果:30
作用域鏈查詢過程:
globalContext { AO: { x: 10 bar: function }, Scope: null } //bar.[[scope]] = globalContext.AO barContext { AO: { x: 30 foo: function }, Scope: bar.[[scope]] } //foo.[[scope]] = barContext.AO fooContext { AO: {}, Scope: foo.[[scope]] }
console.log(x)先從foo 執行上下文中的AO裡找,找不到再從foo的[[scope]]裡找,即是bar的執行上下文,所以 console.log(x)是 30

13. 以下程式碼輸出什麼? 寫出作用域鏈的查詢過程虛擬碼

var x = 10; bar() function bar(){ var x = 30; (function (){ console.log(x) })() }
結果:30
作用域鏈的查詢過程:
globalContext { AO: { x: 10 bar: function }, Scope: null } //bar.[[scope]] = globalContext.AO barContext { AO: { x: 30 function: function }, Scope: bar.[[scope]] } //function.[[scope]] = barContext.AO functionContext { AO: {}, Scope: function.[[scope]] }
console.log(x)先從function 執行上下文中的AO裡找,找不到再從function的[[scope]]裡找,即是bar的執行上下文,所以 console.log(x)是 30

14. 以下程式碼輸出什麼? 寫出作用域鏈查詢過程虛擬碼

var a = 1; function fn(){ console.log(a) //僅宣告,未賦值,undefined var a = 5 console.log(a) //賦值為5 a++ //自增變為6 var a
fn3() //先執行fn3裡的console.log(a),為1
fn2() //再執行fn2裡的console.log(a),為6
console.log(a) //經過fn2,a變為20
function fn2(){ console.log(a) //即fnContext.AO = globalContext.AO中的a a = 20 } }
function fn3(){
console.log(a) //即globalContext.AO中的a
a = 200 //globalContext.AO中的a變為200
}
fn()
console.log(a) //呼叫globalContext.AO中的a = 200
結果:
作用域鏈的查詢過程:
globalContext { AO: { x: 1 fn: function fn3: function }, Scope: null } //fn.[[scope]] = globalContext.AO //fn3.[[scope]] = globalContext.AO fnContext { AO: { x: 1 //之後a賦值5,再之後a++
fn2: function }, Scope: fn.[[scope]] //即globalContext.AO } //fn2.[[scope]] = fnContext.AO fn3Context { AO: { }, Scope: fn3.[[scope]] //即globalContext.AO
}
結果:
undefined
5
1
6
20
200

相關文章