1.函數(shù)聲明和函數(shù)表達(dá)式有什么區(qū)別
- 函數(shù)聲明:function命令聲明的代碼區(qū)塊蝠嘉,就是一個(gè)函數(shù)缘回。function命令后面是函數(shù)名秽誊,函數(shù)名后面是一對圓括號错览,里面是傳入函數(shù)的參數(shù)理澎。函數(shù)體放在大括號里面逞力。
function print(s) {
console.log(s);
}
上面的代碼命名了一個(gè)print函數(shù),以后使用print()這種形式糠爬,就可以調(diào)用相應(yīng)的代碼寇荧。這叫做函數(shù)的聲明(Function Declaration)。
- 函數(shù)表達(dá)式:除了用function命令聲明函數(shù)执隧,還可以采用變量賦值的寫法揩抡。
var print = function(s) {
console.log(s);
};
這種寫法將一個(gè)匿名函數(shù)賦值給變量户侥。這時(shí),這個(gè)匿名函數(shù)又稱函數(shù)表達(dá)式(Function Expression)捅膘,因?yàn)橘x值語句的等號右側(cè)只能放表達(dá)式添祸。
采用函數(shù)表達(dá)式聲明函數(shù)時(shí),function命令后面不帶有函數(shù)名寻仗。如果加上函數(shù)名刃泌,該函數(shù)名只在函數(shù)體內(nèi)部有效,在函數(shù)體外部無效署尤。
var print = function x(){
console.log(typeof x);
};
x
// ReferenceError: x is not defined
print()
// function
2.什么是變量的聲明前置耙替?什么是函數(shù)的聲明前置
- 變量聲明前置:JavaScript引擎的工作方式是,先解析代碼曹体,獲取所有被聲明的變量俗扇,然后再一行一行地運(yùn)行。這造成的結(jié)果箕别,就是所有的變量的聲明語句铜幽,都會被提升到代碼的頭部,這就叫做變量提升(hoisting)串稀。
console.log(a);
var a = 1;
上面代碼首先使用console.log方法除抛,在控制臺(console)顯示變量a的值。這時(shí)變量a還沒有聲明和賦值母截,所以這是一種錯(cuò)誤的做法到忽,但是實(shí)際上不會報(bào)錯(cuò)。因?yàn)榇嬖谧兞刻嵘蹇埽嬲\(yùn)行的是下面的代碼喘漏。
var a;
console.log(a);
a = 1;
最后的結(jié)果是顯示undefined,表示變量a已聲明华烟,但還未賦值翩迈。
請注意,變量提升只對var命令聲明的變量有效盔夜,如果一個(gè)變量不是用var命令聲明的帽馋,就不會發(fā)生變量提升。
- 函數(shù)聲明前置:JavaScript引擎將函數(shù)名視同變量名比吭,所以采用function命令聲明函數(shù)時(shí)绽族,整個(gè)函數(shù)會像變量聲明一樣,被提升到代碼頭部衩藤。所以吧慢,下面的代碼不會報(bào)錯(cuò)。
f();
function f() {}
表面上赏表,上面代碼好像在聲明之前就調(diào)用了函數(shù)f检诗。但是實(shí)際上匈仗,由于“變量提升”,函數(shù)f被提升到了代碼頭部逢慌,也就是在調(diào)用之前已經(jīng)聲明了悠轩。但是,如果采用賦值語句定義函數(shù)攻泼,JavaScript就會報(bào)錯(cuò)火架。
f();
var f = function (){};
// TypeError: undefined is not a function
上面的代碼等同于下面的形式。
var f;
f();
f = function () {};
上面代碼第二行忙菠,調(diào)用f的時(shí)候何鸡,f只是被聲明了,還沒有被賦值牛欢,等于undefined骡男,所以會報(bào)錯(cuò)。因此傍睹,如果同時(shí)采用function命令和賦值語句聲明同一個(gè)函數(shù)隔盛,最后總是采用賦值語句的定義。
3.arguments 是什么
由于JavaScript允許函數(shù)有不定數(shù)目的參數(shù)拾稳,所以我們需要一種機(jī)制吮炕,可以在函數(shù)體內(nèi)部讀取所有參數(shù)。這就是arguments對象的由來熊赖。
arguments對象包含了函數(shù)運(yùn)行時(shí)的所有參數(shù),arguments[0]就是第一個(gè)參數(shù)虑椎,arguments[1]就是第二個(gè)參數(shù)震鹉,以此類推。這個(gè)對象只有在函數(shù)體內(nèi)部捆姜,才可以使用传趾。
var f = function(one) {
console.log(arguments[0]);
console.log(arguments[1]);
console.log(arguments[2]);
}
f(1, 2, 3)
// 1
// 2
// 3
需要注意的是,雖然arguments很像數(shù)組泥技,但它是一個(gè)對象浆兰。數(shù)組專有的方法(比如slice和forEach),不能在arguments對象上直接使用珊豹。
4.函數(shù)的"重載"怎樣實(shí)現(xiàn)
函數(shù)重載是指具有不同參數(shù)列表的同名函數(shù)簸呈。
JS中如果同一個(gè)函數(shù)被多次聲明,后面的聲明就會覆蓋前面的聲明店茶。
要實(shí)現(xiàn)重載蜕便,可以使用arguments參數(shù)
function overLoading() {
// 根據(jù)arguments.length,對不同的值進(jìn)行不同的操作
switch(arguments.length) {
case 0:
/*操作1的代碼寫在這里*/
break;
case 1:
/*操作2的代碼寫在這里*/
break;
case 2:
/*操作3的代碼寫在這里*/
break;
}
}
5.立即執(zhí)行函數(shù)表達(dá)式是什么贩幻?有什么作用
在Javascript中轿腺,一對圓括號()是一種運(yùn)算符两嘴,跟在函數(shù)名之后,表示調(diào)用該函數(shù)族壳。比如憔辫,print()就表示調(diào)用print函數(shù)。
有時(shí)仿荆,我們需要在定義函數(shù)之后贰您,立即調(diào)用該函數(shù)。這時(shí)赖歌,你不能在函數(shù)的定義之后加上圓括號枉圃,這會產(chǎn)生語法錯(cuò)誤。
function(){ /* code */ }();
// SyntaxError: Unexpected token (
產(chǎn)生這個(gè)錯(cuò)誤的原因是庐冯,function這個(gè)關(guān)鍵字即可以當(dāng)作語句孽亲,也可以當(dāng)作表達(dá)式。
// 語句
function f() {}
// 表達(dá)式
var f = function f() {}
為了避免解析上的歧義展父,JavaScript引擎規(guī)定返劲,如果function關(guān)鍵字出現(xiàn)在行首,一律解釋成語句栖茉。因此篮绿,JavaScript引擎看到行首是function關(guān)鍵字之后,認(rèn)為這一段都是函數(shù)的定義吕漂,不應(yīng)該以圓括號結(jié)尾亲配,所以就報(bào)錯(cuò)了。
解決方法就是不要讓function出現(xiàn)在行首惶凝,讓引擎將其理解成一個(gè)表達(dá)式吼虎。最簡單的處理,就是將其放在一個(gè)圓括號里面苍鲜。
(function () { /* code */ }());
// 或者
(function(){ /* code */ })();
上面兩種寫法都是以圓括號開頭思灰,引擎就會認(rèn)為后面跟的是一個(gè)表示式,而不是函數(shù)定義語句混滔,所以就避免了錯(cuò)誤洒疚。這就叫做“立即執(zhí)行函數(shù)表達(dá)式”(Immediately-Invoked Function Expression),簡稱IIFE坯屿。
注意油湖,上面兩種寫法最后的分號都是必須的。如果省略分號领跛,遇到連著兩個(gè)IIFE肺魁,可能就會報(bào)錯(cuò)。
推而廣之隔节,任何讓解釋器以表達(dá)式來處理函數(shù)定義的方法鹅经,都能產(chǎn)生同樣的效果寂呛,比如下面三種寫法。
var i = function(){ return 10; }();
true && function(){ /* code */ }();
0, function(){ /* code */ }();
甚至像下面這樣寫瘾晃,也是可以的贷痪。
!function(){ /* code */ }();
~function(){ /* code */ }();
-function(){ /* code */ }();
+function(){ /* code */ }();
通常情況下,只對匿名函數(shù)使用這種“立即執(zhí)行的函數(shù)表達(dá)式”蹦误。它的目的有兩個(gè):一是不必為函數(shù)命名劫拢,避免了污染全局變量;二是IIFE內(nèi)部形成了一個(gè)單獨(dú)的作用域强胰,可以封裝一些外部無法讀取的私有變量舱沧。
6.求n!,用遞歸來實(shí)現(xiàn)
function factorial(n){
if(n===1||n===0){
return 1;
}else if(n<0){
return "負(fù)數(shù)無階乘";
}
return n*factorial(n-1);
}
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, '男');
//'name:饑人谷' 'age:2' 'sex:男'
getInfo('小谷', 3);
//'name:饑人谷' 'age:2' 'sex:undefined'
getInfo('男');
//'name:男' 'age:undefined' 'sex:undefined'
8. 寫一個(gè)函數(shù)熟吏,返回參數(shù)的平方和?
function sumOfSquares() {
var a = 0;
for (i in arguments) {
a = a + arguments[i]*arguments[i];
}
return a
}
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);
// undefined 在執(zhí)行console.log(a)時(shí)牵寺,由于變量提升,a只聲明恩脂,未定義
//Uncaught ReferenceError: b is not defined 代碼中未聲明變量b
10. 如下代碼的輸出帽氓?為什么
sayName('world');
sayAge(10);
function sayName(name){
console.log('hello ', name);
}
var sayAge = function(age){
console.log(age);
};
//hello world 由于函數(shù)名提升,函數(shù)定義可以放在執(zhí)行代碼后
//Uncaught TypeError: sayAge is not a function
//var sayAge = function(age) 只進(jìn)行sayAge變量名提升俩块,sayAge(10)前函數(shù)未定義
11. 如下代碼輸出什么? 寫出作用域鏈查找過程偽代碼
var x = 10
bar()
function foo() {
console.log(x)
}
function bar(){
var x = 30
foo()
}
1.
globalContext = {
AO: {
x: 10
foo: function
bar: function
},
Scope: null
}
bar.[[scope]] = globalContext.AO
foo.[[scope]] = globalContext.AO
2.
barContext = {
AO:{
x:10
}
scope:globalContext.AO
}
3.
fooContext = {
AO:{
}
scope:globalContext.AO
}
}
在 globalContext.AO找到x:10
輸出:10
12. 如下代碼輸出什么? 寫出作用域鏈查找過程偽代碼
var x = 10;
bar()
function bar(){
var x = 30;
function foo(){
console.log(x)
}
foo();
}
1.
globalContext = {
AO: {
x: 10
bar: function
},
Scope: null
}
bar.[[scope]] = globalContext.AO
2.
batContext = {
AO: {
x: 30
foo: function
},
Scope:globalContext.AO
}
foo.[[scope]] = barContext.AO
3.
fooContext = {
AO: {
},
Scope:barContext.AO
}
從barContext.AO中得x:30黎休,輸出為30
13. 以下代碼輸出什么? 寫出作用域鏈的查找過程偽代碼
var x = 10;
bar()
function bar(){
var x = 30;
(function (){
console.log(x)
})()
}
1.
globalContext = {
AO: {
x: 10
bar: function
},
Scope: null
}
bar.[[scope]] = globalContext.AO
2.
barContext = {
AO: {
x: 30
},
scope:globalContext.AO
}
//(function (){
// console.log(x)
// })() 為立即執(zhí)行函數(shù)表達(dá)式,會屏蔽全局變量的污染玉凯,調(diào)用當(dāng)前bar的AO势腮,此時(shí)x=30,因此輸出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)
//undefined
//5
//1
//6
//20
//200
1.
globalContext = {
AO: {
a: 1
fn: function
fn3: function
},
Scope: null
}
fn[[scope]] = globalContext.AO
fn3[[scope]] = globalContext.AO
2.
fnContext = {
AO: {
a: undefined,然后為5嫉鲸,a++變6撑蒜,fn2后變20
fn2: function
},
scope: globalContext.AO
}
fn2.[[scope]] = fnContext.AO
3.
fn2Context = {
AO: {
},
scope:fnContext.AO
}
4.
fn3Context = {
AO: {
},
scope: globalContext.AO
}