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: undefinedfunction 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)
//29function 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