1. 函數(shù)聲明和函數(shù)表達式有什么區(qū)別
JavaScript函數(shù)是指一個特定代碼塊,可能包含多條語句,可以通過名字來供其它語句調(diào)用以執(zhí)行函數(shù)包含的代碼語句。
ECMAScript規(guī)定了三種聲明函數(shù)方式:
構(gòu)造函數(shù)(不推薦):
首先函數(shù)也是對象的一種,我們可以通過其構(gòu)造函數(shù),使用new來創(chuàng)建一個函數(shù)對象。
var sayHello = new Function("console.log('hello world');");函數(shù)聲明:
使用function關(guān)鍵字可以聲明一個函數(shù)。
//函數(shù)聲明
function sayHello(){
console.log('hello');
}
//函數(shù)調(diào)用
sayHello();
- 函數(shù)表達式:
var sayHello = function(){
console.log('hello');
}
//函數(shù)調(diào)用
sayHello();
區(qū)別:
函數(shù)聲明:function functionName(){} ; //函數(shù)聲明會提前,聲明不必放到調(diào)用的前面。
函數(shù)表達式:var fn = function(){} ; //函數(shù)表達式可以省略函數(shù)名,聲明必須放到調(diào)用的前面。
2. 什么是變量的聲明前置?什么是函數(shù)的聲明前置?
- 變量聲明前置
JavaScript引擎的工作方式是,先解析代碼,獲取所有被聲明的變量,然后再一行一行地運行。這造成的結(jié)果,就是所有的變量的聲明語句,都會被提升到代碼的頭部。
例如:
console.log(a);
var a = 3;
console.log(a);
經(jīng)過變量的聲明前置,可以理解為:
var a;
console.log(a); // undefined
a = 3 ;
console.log(a); // 3
注意:
var重復(fù)聲明一個已經(jīng)存在的變量,原變量值不變。
不寫var會聲明一個全局的變量,在編程中應(yīng)該要避免的,即使真的需要全局變量,也應(yīng)該在最外層作用域使用var聲明。
- 函數(shù)聲明前置
JavaScript引擎將函數(shù)名視同變量名,所以采用function聲明函數(shù)時,整個函數(shù)會像var聲明變量一樣,被提升到代碼頭部。所以,形如下面的代碼是不會報錯的。
sayHello();
function sayHello(){
console.log('hello');
}
聲明前置后,即:
function sayHello(){
console.log('hello');
}
sayHello(); //'hello'
3. arguments 是什么?
arguments是一個類數(shù)組對象。代表傳給一個function的參數(shù)列表。
可以在函數(shù)內(nèi)部通過使用 arguments 對象來獲取函數(shù)的所有參數(shù)。這個對象為傳遞給函數(shù)的每個參數(shù)建立一個條目,條目的索引號從0開始:arguments[0]就是第一個參數(shù),arguments[1]就是第二個參數(shù),以此類推。這個對象只有在函數(shù)體內(nèi)部,才可以使用。
function printPersonInfo(name, age, sex){
//console.log(name);
//console.log(age);
//console.log(sex);
console.log(arguments);
}
4. 函數(shù)的"重載"怎樣實現(xiàn)的?
- 重載是很多面向?qū)ο笳Z言實現(xiàn)多態(tài)的手段之一,在靜態(tài)語言中確定一個函數(shù)的手段是靠方法簽名——函數(shù)名+參數(shù)列表,也就是說相同名字的函數(shù)參數(shù)個數(shù)不同或者順序不同都被認為是不同的函數(shù),稱為函數(shù)重載。
- 在JavaScript中沒有函數(shù)重載的概念,函數(shù)通過名字確定唯一性,參數(shù)不同也被認為是相同的函數(shù),后面的覆蓋前面的,但可以在函數(shù)體針對不同的參數(shù)調(diào)用執(zhí)行相應(yīng)的邏輯。
function userInfo(name,age,sex){
if(name){
console.log(name);
}
if(age){
console.log(age);
}
if(sex){
console.log(sex);
}
}
userInfo("andy",22); //參數(shù)依次傳遞值,缺少的參數(shù)返回的是undefined
userInfo("andrea",20,"female");
5. 立即執(zhí)行函數(shù)表達式是什么?有什么作用?
立即執(zhí)行函數(shù)表達式(Immediately-Invoked Function Expression),簡稱IIFE。表示定義函數(shù)之后,立即調(diào)用該函數(shù)。
(function(){
var a = 1;
})()
console.log(a); //undefined
其他寫法:
(function fn1() {});
- 當圓括號出現(xiàn)在匿名函數(shù)的末尾想要調(diào)用函數(shù)時,它會默認將函數(shù)當成是函數(shù)聲明。
- 當圓括號包裹函數(shù)時,它會默認將函數(shù)作為表達式去解析,而不是函數(shù)聲明。
- 作用:隔離作用域。
6. 求n!,用遞歸來實現(xiàn)
function factor(n){
if(n === 1 || n === 0){
return 1;
}
if(n > 1){
return n*factor(n-1);
}else{
console.log('Input Error');
}
}
factor(5); //120
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('谷哥', 20, '女');
getInfo('小谷', 30);
getInfo('男');
答案如下:
getInfo('谷哥', 20, '女');
name : 谷哥
age : 20
sex : 女
[name:谷哥, age:20,sex:女]
name valley
name valley
getInfo('小谷', 30);
name:小谷
age:30
sex:undefined
[name:小谷,age:30,sex:undefined]
name valley
name valley
getInfo('男');
name:男
age:undefined
sex:undefined
[name:男,age:undefined,sex:undefined]
name valley
name valley
8. 寫一個函數(shù),返回參數(shù)的平方和
function sumOfSquares(){
var sum = 0;
for(var i = 0 ; i < arguments.length ; i++){
sum = 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
9. 如下代碼的輸出是什么?為什么?
console.log(a);
var a = 1;
console.log(b);
變量聲明前置后,即:
var a
console.log(a) ; //undefined
a = 1
console.log(b) ; // b is not defined 沒有聲明變量b
10. 如下代碼的輸出是什么?為什么?
sayName('world');
sayAge(10);
function sayName(name){
console.log('hello ', name);
}
var sayAge = function(age){
console.log(age);
};
函數(shù)聲明前置后,而函數(shù)表達式則不會前置,所以結(jié)果即:
sayAge(10); // sayAge is not a function(報錯)。 (因為函數(shù)表達式不會被前置)
function sayName(name){
console.log('hello ', name);
}
var sayAge = function(age){
console.log(age);
};
sayName('world'); // hello world
11. 如下代碼的輸出是什么?寫出作用域鏈查找過程偽代碼
var x = 10;
bar() ;
function foo() {
console.log(x);
}
function bar(){
var x = 30;
foo();
}
聲明提前,如下:
var x;
function foo() {
console.log(x);
}
function bar(){
var x ;
x = 30;
foo();
}
x = 10 ;
bar() ;
作用域鏈代碼,如下:
1.
globalContext = {
AO {
x : 10
foo : function
bar : function
},
Scope: null
}
foo.[[scope]] = globalContext.AO
bar.[[scope]] = globalContext.AO
全局變量 x = 10
2. 調(diào)用函數(shù)bar()
barContext = {
AO:{
x : 30
foo : function
},
Scope:bar.[[scope]] = globalContext.AO
}
函數(shù)內(nèi)局部變量 x = 30
3. 調(diào)用函數(shù)foo()
fooContext = {
AO:{ };
},
Scope:foo.[[scope]] = globalContext.AO
//因為作用域鏈的關(guān)系, 全局變量 x = 10 , 所以 console.log(x) 的結(jié)果也是 10 。
12. 如下代碼的輸出是什么?寫出作用域鏈查找過程偽代碼
var x = 10;
bar() ;
function bar(){
var x = 30;
function foo(){
console.log(x) ;
}
foo();
}
聲明提前,如下:
var x;
function bar(){
var x;
function foo(){
console.log(x);
}
x = 30;
foo();
}
x = 10 ;
bar() ;
作用域鏈代碼,如下:
1.
globalContext = {
AO:{
x : 10;
bar : function
},
Scope: null
}
bar.[[scope]] = globalContext.AO
2. 調(diào)用函數(shù)bar()
barContext = {
AO:{
x : 30;
foo: function
},
Scope:bar.[[scope]] = globalContext.AO
}
foo.[[scope]] = barContext.AO
3. 調(diào)用函數(shù)foo()
fooContext = {
AO:{},
}
foo.[[scope]] = barContext.AO
//調(diào)用foo時會在barContext.AO中找到x = 30。
13. 如下代碼的輸出是什么?寫出作用域鏈查找過程偽代碼
var x = 10;
bar() ;
function bar(){
var x = 30;
(function (){
console.log(x)
})();
}
聲明提前,如下:
var x ;
function bar(){
var x = 30 ;
(function (){
console.log(x)
})();
}
x = 10 ;
bar() ;
作用域鏈代碼,如下:
1.
globalContext = {
AO:{
x:10
bar:function
},
Scope:null
}
bar.[[scope]] = globalContext.AO
2.調(diào)用bar()
barContext = {
AO:{
x:30
function
},
Scope:bar.[[scope]] = globalContext.AO
}
function.[[scope]] = barContext.AO
3.調(diào)用立即執(zhí)行函數(shù)
functionContext = {
AO:{},
Scope:function.[[scope]] = barContext.AO
}
調(diào)用bar函數(shù)時,由于它的活動對象中 x=30,所以當自執(zhí)行函數(shù)執(zhí)行時,會先在它自己的AO中查找,找不到再向bar()函數(shù)的AO中查找,所以結(jié)果為30。
14. 如下代碼的輸出是什么?寫出作用域鏈查找過程偽代碼
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);
作用域鏈代碼,如下:
1.
globalContext = {
AO:{
a:1
fn:function
fn3:function
},
Scope:null
}
fn.[[scope]] = globalContext.AO
fn3.[[scope]] = globalContext.AO
2.調(diào)用fn()
fnContext = {
AO:{
a:undefined
fn2:function
},
Scope:fn.[[scope]] = globalContext.AO
}
fn2.[[scope]] = fnContext.AO
3.
fn3Context = {
AO:{
a:200
},
Scope:fn3Context.[[scope]] = globalContext.AO
}
fn2ConText = {
AO:{
a:20
},
Scope:fn2ConText.[[scope]] = fnContext.AO
}
開始執(zhí)行
console.log(a); //undefined 打印
var a = 5; //fnContext中a變成5
console.log(a); //5
a++ ; //fnContext.AO中a變?yōu)?
調(diào)用fn3()
fn3()中
console.log(a); //globalContext.AO中的a值為1,打印
a = 200; //globalContext.AO中的a變?yōu)?00
調(diào)用fn2()
console.log(a); //fnContext.AO中的a值為6,打印
a = 20; //fnContext.AO中的a變?yōu)?0
繼續(xù)執(zhí)行fn()
console.log(a); //fnContext.AO中的a值為20,打印
fn()結(jié)束
console.log(a); //globalContext.AO中a值為200,打印
輸出的結(jié)果: undefined 5 1 6 20 200