通常來說产舞,一個(gè)函數(shù)就是一個(gè)可以被外部代碼調(diào)用(或函數(shù)本身遞歸調(diào)用)的“子程序”檬寂。和程序本身一樣,一個(gè)函數(shù)的函數(shù)體是由一系列的語句組成的势木。
1.函數(shù)聲明和函數(shù)表達(dá)式有什么區(qū)別蛛倦?
syntax:
函數(shù)聲明:
<pre> function函數(shù)名稱(參數(shù):可選){函數(shù)體}</pre>
函數(shù)表達(dá)式:
<pre>functuion函數(shù)名稱(可選)(參數(shù):可選){函數(shù)體}</pre>
如果聲明了函數(shù)名稱的話,當(dāng)function 函數(shù)名稱(){}是作為賦值表達(dá)式的一部分的話啦桌,那它就是函數(shù)表達(dá)式溯壶;如果function函數(shù)名稱(){}被包含在一個(gè)函數(shù)體內(nèi),或者位于程序的最頂部的話甫男,那它就是一個(gè)函數(shù)聲明且改。
區(qū)別在于:
a.函數(shù)聲明會(huì)在任何表達(dá)式被解析和求值之前先被解析和求值,即使聲明在代碼的最后一行查剖,它也會(huì)在同作用域內(nèi)第一個(gè)表達(dá)式之前被解析/求值钾虐。示例:
<pre>
alert(fn());
function fn() {return 'Hello world!';}
</pre>
b.函數(shù)聲明在條件語句內(nèi)雖然可以用,但是沒有被標(biāo)準(zhǔn)化笋庄,也就是說不同的環(huán)境可能有不同的執(zhí)行結(jié)果效扫,所以這樣情況下,最好使用函數(shù)表達(dá)式:
c.函數(shù)聲明的syntax:
<pre>function 函數(shù)名(){}</pre>
函數(shù)表達(dá)式的syntax:
<pre>var 變量名=function 函數(shù)名(){};</pre>
函數(shù)聲明結(jié)尾沒有分號(hào)直砂,但是函數(shù)表達(dá)式以分號(hào)結(jié)尾菌仁。
2.什么是變量的聲明前置?什么是函數(shù)的聲明前置?
變量的聲明前置:JavaScript引擎的工作方式是静暂,先解析代碼济丘,獲取所有被聲明的變量,然后再一行一行地運(yùn)行洽蛀。這造成的結(jié)果就是所有的變量的聲明語句摹迷,都會(huì)被提升到代碼的頭部,也稱為變量提升郊供。
函數(shù)的聲明前置:函數(shù)聲明過程在整個(gè)程序執(zhí)行之前的預(yù)處理就完成了峡碉,所以只要在同一作用域,就可以訪問到驮审,即使在定義之前調(diào)用也可以鲫寄。
3.arguments 是什么 吉执?
arguments是一個(gè)類數(shù)組對(duì)象。代表傳給一個(gè)function的參數(shù)列表地来。是函數(shù)內(nèi)部的本地變量戳玫;arguments已經(jīng)不再是函數(shù)的屬性了∥窗撸可以在函數(shù)內(nèi)部通過使用arguments對(duì)象來獲取函數(shù)的所有參數(shù)咕宿。這個(gè)對(duì)象為傳遞給函數(shù)的每個(gè)參數(shù)簡(jiǎn)歷一個(gè)條目,條目的索引號(hào)從0開始颂碧。(MDN)
4.函數(shù)的重載怎樣實(shí)現(xiàn)?
JavaScript沒有重載函數(shù)這個(gè)概念荠列,但是可以通過arguments來模擬函數(shù)重載。實(shí)現(xiàn)代碼示例:
<pre>
function sum(){
var sum=0;
for(var i=0;i < arguments.length;i++){
sum+=arguments[i];
}
return sum;
}
sum(1,2);
</pre>
5.立即執(zhí)行函數(shù)表達(dá)式是什么载城?有什么作用 肌似?
立即執(zhí)行函數(shù)表達(dá)式是函數(shù)定義完直接調(diào)用。
syntax:
<pre>a.(function(){/code/}());</pre>
<pre>b.(function(){/code/})();</pre>
<pre>c.!function(){/code/}();</pre>
作用:
a.js沒有塊級(jí)作用域诉瓦,用來隔離作用域避免污染川队,或者截?cái)嘧饔糜蜴湥苊忾]包造成引用常量無法釋放睬澡;
b.利用立即執(zhí)行特性固额,返回需要的業(yè)務(wù)函數(shù)或?qū)ο螅苊饷看瓮ㄟ^條件判斷來處理煞聪。
6.什么是函數(shù)的作用域鏈 ?
javascript中斗躏,變量的作用域有全局作用域和局部作用域兩種。
a.全局作用域:在代碼任何地方都能訪問到的對(duì)象擁有全局作用域
b.局部作用域:一般只在固定的代碼片段內(nèi)可以訪問到昔脯。
c.函數(shù)作用域鏈:在JavaScript中啄糙,函數(shù)也是對(duì)象,實(shí)際上云稚,JavaScript里一切都是對(duì)象隧饼。函數(shù)對(duì)象和其它對(duì)象一樣,擁有可以通過代碼訪問的屬性和一系列僅供JavaScript引擎訪問的內(nèi)部屬性静陈。其中一個(gè)內(nèi)部屬性是[[Scope]]燕雁,由ECMA-262標(biāo)準(zhǔn)第三版定義,該內(nèi)部屬性包含了函數(shù)被創(chuàng)建的作用域中對(duì)象的集合鲸拥,這個(gè)集合被稱為函數(shù)的作用域鏈拐格,它決定了哪些數(shù)據(jù)能被函數(shù)訪問。
代碼題:
1.以下代碼輸出什么刑赶?
<pre>
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('hunger', 28, '男');
getInfo('hunger', 28);
getInfo('男');
</pre>
輸出結(jié)果:
<pre>
getInfo('hunger', 28, '男');
name:hunger
age:28
sex:男
["hunger",28,"男"]
name valley
</pre>
<pre>
getInfo('hunger', 28);
name:hunger
age:28
sex:undefined
["hunger",28]
name valley
</pre>
<pre>
getInfo('男');
name:男
age:unfined
sex:unfined
["男"]
name valley
</pre>
2.寫一個(gè)函數(shù)禁荒,返回參數(shù)的平方和?
<pre>
function sumOfSquares(){
var sum=0;
for(var i=0;i < arguments.length;i++)
{
sum+=arguments[i]*arguments[i];
}
console.log(sum);
}
sumOfSquares(2,3,4); // 29
sumOfSquares(1,3); // 10
</pre>
3.如下代碼的輸出角撞?為什么 ?
<pre>
console.log(a); // undefined,變量提升
var a = 1;
console.log(b);//報(bào)錯(cuò)呛伴,b is not defined
</pre>
4.如下代碼的輸出?為什么 ?
<pre>
sayName('world'); //函數(shù)sayName聲明提前
sayAge(10); //函數(shù)sayAge聲明
function sayName(name){
console.log('hello ', name); //輸出:hello world
}
var sayAge = function(age){ //函數(shù)表達(dá)式
console.log(age); //報(bào)錯(cuò)谒所,sayAge is not a function.函數(shù)表達(dá)式的聲明不能提前
};
</pre>
5.如下代碼輸出热康?為什么?
<pre>
function fn(){}
var fn = 3;
console.log(fn);
</pre>
可以寫成:
<pre>
var fn;
function fn(){}
fn=3;
console.log(fn);
</pre>
輸出結(jié)果為:3劣领,var fn變量提升姐军,function fn(){}函數(shù)聲明前置,第三行代碼給fn賦值3尖淘,使其作為變量輸出奕锌。
6.如下代碼輸出?為什么
<pre>
function fn(fn2){
console.log(fn2);
var fn2 = 3;
console.log(fn2);
console.log(fn);
function fn2(){
console.log('fnnn2');
}
}
fn(10);
</pre>
<pre>
解析:
a.fn(10)調(diào)用函數(shù)function fn(fn2){}將10賦值給fn2;
b.由于函數(shù)聲明前置村生,console.log(fn2)輸出
function fn2(){
console.log('fnnn2');
}
c.var fn2=3給fn2重新賦值,console.log(fn2)輸出3惊暴;
d.console.log(fn)輸出:
function fn(fn2){
console.log(fn2);
var fn2=3;
console.log(fn2);
console.log(fn);
function fn2(){
console.log('fnnn2');
}
}
</pre>
7.如下代碼的輸出?為什么?
<pre>
var fn = 1;
function fn(fn){
console.log(fn);
}
console.log(fn(fn));
</pre>
輸出結(jié)果:
<pre>
代碼相當(dāng)于:
var fn;
function fn(fn){
console.log(fn);
}
fn=1;
console.log(fn(fn));
報(bào)錯(cuò):fn is not a function
原因是:變量聲明提前和函數(shù)聲明前置趁桃,但是function fn(fn){}的聲明在前面辽话,之后給變量賦值,變量賦值會(huì)覆蓋函數(shù)的聲明卫病,因此會(huì)報(bào)錯(cuò)油啤。
</pre>
8.如下代碼的輸出?為什么?
<pre>
//作用域
console.log(j);
console.log(i);
for(var i=0; i<10; i++){
var j = 100;
}
console.log(i);
console.log(j);
</pre>
輸出結(jié)果:
<pre>
console.log(i);//undefined,變量提升蟀苛,因?yàn)闆]有賦值益咬,所以輸出是undefined
console.log(j);//undefined,變量提升,因?yàn)闆]有賦值帜平,所以輸出是undefined
for(var i=0;i<10;i++){
var j=100;
}//定義全局變量i和j
console.log(i);//10
console.log(j);//100
結(jié)果為:
undefined
undefined
10
100
</pre>
9.如下代碼的輸出幽告?為什么 ?
<pre>
fn();
var i = 10;
var fn = 20;
console.log(i);
function fn(){
console.log(i);
var i = 99;
fn2();
console.log(i);
function fn2(){
i = 100;
}
}
</pre>
輸出結(jié)果:
<pre>
a.調(diào)用函數(shù)fn(){};
b.運(yùn)行函數(shù)fn(){};
function fn(){
var i;
console.log(i);//變量提升,變量聲明但沒有賦值罕模,輸出undefined
i = 99;
fn2(); //調(diào)用函數(shù)fn2()评腺,函數(shù)聲明前置
console.log(i);//輸出100
function fn2(){
i = 100;
}
}
c.var i=10;給i賦值10,覆蓋之前的100淑掌,console.log(i)輸出10
所以結(jié)果為:
undefined
100
10
</pre>
10.如下代碼的輸出蒿讥?為什么 ?
<pre>
var say = 0;
(function say(n){
console.log(n);
if(n<3) return;
say(n-1);
}( 10 ));
console.log(say);
</pre>
輸出結(jié)果:
<pre>
(function say(n){
console.log(n);
if(n<3) return;
say(n-1);
}( 10 ));為立即執(zhí)行表達(dá)式;
輸出結(jié)果為:
10
9
8
7
6
5
4
3
2
最后的console.log(say)輸出全局變量say:0
</pre>
參考文章:
1.湯姆大叔的博客
2.Back to Basics: JavaScript Hoisting
3.為什么要有js立即執(zhí)行函數(shù)抛腕,存在的意義是什么芋绸?
4.JavaScript作用域鏈
版權(quán)歸本人所有,若有轉(zhuǎn)載担敌,請(qǐng)注明來源