一、問答
(一)函數(shù)聲明和函數(shù)表達(dá)式有什么區(qū)別 ?
函數(shù)聲明表示方法的例子:
function printName(){
alert("shengming");
}
printName();
函數(shù)表達(dá)式的例子:
var printName=function(){
alert("biaodashi");
}
那么他們之間有什么區(qū)別呢?看下面的例子
console.log( shengMing );
console.log( biaodashi );
function shengMing(){
alert("shengming")
};
var biaodashi=function biaoDaShi(){
alert("biaodashi")
};
console.log( shengMing );
console.log( biaodashi );
運(yùn)行后:

由以上可以看出,即使聲明函數(shù)shengMing()是在后面聲明的,但在前面也可調(diào)用(第一個(gè)console.log( shengMing );能夠正常運(yùn)行),但是表達(dá)函數(shù)則不行(第一個(gè)console.log( biaodashi ); 運(yùn)行結(jié)果顯示undefined);對(duì)于函數(shù)聲明語句,函數(shù)名稱和函數(shù)體均提前聲明了,可以在聲明之前調(diào)用它;但對(duì)于函數(shù)表達(dá)式,只有函數(shù)變量聲明提前了,但是函數(shù)的初始化代碼仍然在原來的位置;
(二)什么是變量的聲明前置?什么是函數(shù)的聲明前置?
- 變量的聲明前置,是指即使我們沒有在使用變量前先聲明這個(gè)變量,但是由于變量提升(即變量聲明的前置)的存在,我們可以在聲明前使用該變量,只不過這個(gè)變量的默認(rèn)值是undefined,但解析器不會(huì)報(bào)錯(cuò);例如:

- 同理,對(duì)于函數(shù)也存在函數(shù)的聲明前置的情況,其實(shí)第一題就說明了這一點(diǎn),下面再舉個(gè)例子:

當(dāng)然對(duì)于表達(dá)式函數(shù)也適用,例如下面的例子:

(三)arguments 是什么 ?
arguments 是一個(gè)類數(shù)組對(duì)象。代表傳給一個(gè)function的參數(shù)列表。
可以在函數(shù)內(nèi)部通過使用 arguments 對(duì)象來獲取函數(shù)的所有參數(shù)。這個(gè)對(duì)象為傳遞給函數(shù)的每個(gè)參數(shù)建立一個(gè)條目,條目的索引號(hào)從 0 開始。arguments 對(duì)象并不是一個(gè)真正的Array。它類似于數(shù)組,但沒有數(shù)組所特有的屬性和方法,除了 length。例如,它沒有 pop 方法。不過可以將其轉(zhuǎn)換成數(shù)組。另外:arguments 對(duì)象僅在函數(shù)內(nèi)部有效,在函數(shù)外部調(diào)用 arguments 對(duì)象會(huì)出現(xiàn)一個(gè)錯(cuò)誤。如果你調(diào)用一個(gè)函數(shù),當(dāng)這個(gè)函數(shù)的參數(shù)數(shù)量比它顯式聲明的參數(shù)數(shù)量更多的時(shí)候,你就可以使用 arguments 對(duì)象。這個(gè)技術(shù)對(duì)于參數(shù)數(shù)量是一個(gè)可變量的函數(shù)來說比較有用。 你可以用 arguments.length 來得到參數(shù)的數(shù)量,然后可以用 arguments object 來對(duì)每個(gè)參數(shù)進(jìn)行處理。 (想要得到當(dāng)一個(gè)函數(shù)定義時(shí)的該函數(shù)的參數(shù)數(shù)量, 請使用 Function.length 屬性。)
arguments的屬性如下:
arguments.callee
指向當(dāng)前執(zhí)行的函數(shù)。
arguments.caller
指向調(diào)用當(dāng)前函數(shù)的函數(shù)。
arguments.length
指向傳遞給當(dāng)前函數(shù)的參數(shù)數(shù)量。
- 下面舉個(gè)使用arguments的例子:

在聲明上述函數(shù)時(shí)未使用參數(shù),即其不包含命名的參數(shù),這說明在函數(shù)中命名的參數(shù)只提供便利,但不是必須的。
- 下面再舉個(gè)使用arguments.length的例子:

(四)函數(shù)的重載怎樣實(shí)現(xiàn) ?
由于js不能像傳統(tǒng)意義上那樣實(shí)現(xiàn)重載,因此我們可以通過使用arguments的方法實(shí)現(xiàn)非完美的重載;
如下面的例子:

(五)立即執(zhí)行函數(shù)表達(dá)式是什么?有什么作用 ?
類似于

類似于上述函數(shù)的寫法叫做「立即執(zhí)行函數(shù)」也叫「自執(zhí)行匿名函數(shù)」(self-executing anonymous function);「立即執(zhí)行函數(shù)表達(dá)式」(Immediately-Invoked Function Expression,簡稱IIFE);
其中一般推薦使用第一種寫法,但是目前很多比較好的js library 使用的都是第二種方式。 比如: web 圖形繪制的: git , draw2d ,....
立即執(zhí)行函數(shù)的作用有:
1、模擬塊作用域;js沒有java、c等語言的塊作用域,只有函數(shù)作用域,因此在同時(shí)調(diào)用多個(gè)庫時(shí)很容易出現(xiàn)變量或?qū)ο蟊桓采w的情況;
2、解決閉包沖突;
3、模擬單例;
(六)什么是函數(shù)的作用域鏈?
函數(shù)對(duì)象和其他對(duì)象一樣,擁有可以通過代碼訪問的屬性和一系列供js引擎訪問的內(nèi)部屬性,其中一個(gè)內(nèi)部屬性是[scope],該屬性包括了函數(shù)被創(chuàng)建的作用域中的對(duì)象集合,這個(gè)集合稱為函數(shù)的作用域鏈,它決定了哪些能夠被函數(shù)訪問;
舉個(gè)例子:

二、代碼
1.以下代碼輸出什么?
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('男');
運(yùn)行結(jié)果會(huì)如下:
/* getInfo('hunger', 28, '男')運(yùn)行結(jié)果:
name:hunger
age:28
sxe:男
{valley, 28, 男}
name valley
*/
/* getInfo('hunger', 28)運(yùn)行結(jié)果:
name:hunger
age:28
sxe:undefind
{valley,28}
name valley
*/
/* getInfo('男')運(yùn)行結(jié)果:
name:男
age:undefind
sxe:undefind
{valley}
name valley
*/

2.寫一個(gè)函數(shù),返回參數(shù)的平方和?如
function sumOfSquares(){
}
sumOfSquares(2,3,4); // 29
sumOfSquares(1,3); // 10
function sumOfSquares(){
for (var i = 0,x = 0; i < arguments.length; i++) {
x=x+arguments[i]*arguments[i];
}
console.log(x);
}

3.如下代碼的輸出?為什么 ?
console.log(a);
var a = 1;
console.log(b);
上述代碼相當(dāng)于:
var a;
console.log(a);
a = 1;
console.log(b);
輸出為 undefined;b is not defined; 由于變量提升的存在,導(dǎo)致console.log(a)時(shí),不會(huì)出現(xiàn)a is not defined,但是由于console.log(a)之前a未賦值,所以才會(huì)導(dǎo)致出現(xiàn) undefined; 而對(duì)于console.log(b)語句,由于b既未聲明也未賦值,所以才會(huì)報(bào)錯(cuò)b is not defined錯(cuò)誤;

4.如下代碼的輸出?為什么 ?
sayName('world'); //hello world (解釋:該函數(shù)為聲明函數(shù),存在提前聲明的情況;)
sayAge(10);/*sayAge is not a function (解釋:該函數(shù)為表達(dá)式函數(shù),
不存在提前聲明的情況,所以會(huì)報(bào)錯(cuò))*/
function sayName(name){
console.log('hello ', name);
}
var sayAge = function(age){
console.log(age);
};
上述代碼相當(dāng)于:
var sayAge;
function sayName(name){
console.log('hello ', name);
}
sayName('world');
sayAge(10);// sayAge is not a function
sayAge = function(age){
console.log(age);
};

5.如下代碼的輸出?為什么 ?
function fn(){}
var fn = 3;
console.log(fn);//3
上述代碼相當(dāng)于:
var fn; //fn 此時(shí)為普通變量
function fn(){} //fn此時(shí)為函數(shù)名
fn = 3; /*fn此時(shí)值等于3,因?yàn)樵谕粋€(gè)作用域內(nèi)定義了名字相同的變量和方法的話,無論其順序如
何,變量的賦值會(huì)覆蓋方法的賦值;*/
console.log(fn); //打印時(shí)輸出3

6.如下代碼的輸出?為什么 ?
function fn(fn2){
console.log(fn2);
var fn2 = 3;
console.log(fn2);
console.log(fn);
function fn2(){
console.log('fnnn2');
}
}
fn(10);
上述代碼相當(dāng)于:
function fn(fn2){
var fn2;
function fn2(){
console.log('fnnn2');
}
console.log(fn2); //
/* 由于同個(gè)作用域下,變量聲明和函數(shù)聲明存在命名沖突時(shí),變量聲明前置要比函數(shù)聲明前置的優(yōu)先級(jí)底,因此此時(shí)打印出的是函數(shù)*/
fn2 = 3;
console.log(fn2); //3
/*此時(shí)fn2被賦值3了,因?yàn)樵谕粋€(gè)作用域中,定義了同一個(gè)名字的變量
和方法時(shí),無論順序如何,變量的賦值會(huì)覆蓋方法的賦值*/
console.log(fn); // 此時(shí)會(huì)打印函數(shù)fn本身
}
fn(10);
因此結(jié)果為

如果將此題改成下面的代碼,結(jié)果將是怎樣的呢?
function fn(fn2){
var fn2=200;
function fn2(){
console.log('fnnn2');
}
console.log(fn2);
var fn2 = 3;
console.log(fn2);
}
fn(10);
其實(shí)上面的代碼相當(dāng)于
function fn(fn2){
var fn2=200;
function fn2(){
console.log('fnnn2');
}
console.log(fn2); //200 此時(shí)并沒有輸出函數(shù)fn2,為什么呢?
/*因?yàn)橥粋€(gè)作用域中,定義了名字相同的變量和方法的話,無論順序如何
變量的賦值會(huì)覆蓋方法的賦值;
*/
var fn2 = 3;
console.log(fn2); //3
}
fn(10);
輸出結(jié)果:

如果再將代碼改成:
function fn(fn2){
var fn2=200;
function fn2(){
console.log('fnnn2');
}
console.log(fn2);
fn2 = 3;
console.log(fn2);
}
fn(10);
console.log(fn2); //此處會(huì)報(bào)錯(cuò),因?yàn)橛捎诤瘮?shù)作用域的存在,此處的fn2并未定義變量且賦值;
則結(jié)果為

7.如下代碼的輸出?為什么?
var fn = 1;
function fn(fn){
console.log(fn);
}
console.log(fn(fn));
上述代碼相對(duì)于
var fn ;
function fn(fn){
console.log(fn);
};
fn = 1;
console.log(fn(fn)); //報(bào)錯(cuò) fn is not a function
/* 由于同一個(gè)作用域中,定義同一個(gè)名字的變量和方法時(shí),變量賦值會(huì)覆蓋方法的賦值,因此此時(shí)解析器
并不能夠識(shí)別fn為函數(shù),但是在打印時(shí)又以函數(shù)的方式打印,因此會(huì)報(bào)錯(cuò)*/
其實(shí)上面的例子可簡化成這樣的理解

打印a(a) ,解析器會(huì)把它當(dāng)作成要打印一個(gè)函數(shù),但是a并不是一個(gè)函數(shù),因此會(huì)報(bào)錯(cuò)。
8.如下代碼的輸出?為什么 ?
//作用域
console.log(j);
console.log(i);
for(var i=0; i<10; i++){
var j = 100;
}
console.log(i);
console.log(j);
其實(shí)上面的代碼相當(dāng)于
var j ;
var i ;
console.log(j); // undefined
/*解釋:由于變量提升的存在,導(dǎo)致此處提示j未被賦值,但不抱錯(cuò)*/
console.log(i); //undefined
/*解釋:同理,由于變量提升的存在,
導(dǎo)致此處提示i未被賦值,但不抱錯(cuò)*/
for(var i=0; i<10; i++){
var j = 100;
}
console.log(i); //10 (解釋:for語句執(zhí)行完后,i值為10)
console.log(j); // 100 (解釋:for語句執(zhí)行完后,j值為100)

9.如下代碼的輸出?為什么 ?
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;
}
}
上述代碼相當(dāng)于:
var i;
var fn;
function fn(){
var i;
function fn2(){
i = 100;
}
console.log(i); //undefinded
/*此時(shí)i并未被賦值,因此為undefinded*/
var i = 99;
fn2(); //執(zhí)行后i為100;
console.log(i); //100
/*此時(shí)打印i,i的值為100*/
}
fn();
var i = 10;
var fn = 20;
console.log(i); //10
/*雖然i在function內(nèi)部為100但由于函數(shù)作用域的存在及i的外部賦值,在外部的為10;*/

10.如下代碼的輸出?為什么?
var say = 0;
(function say(n){
console.log(n);
if(n<3) return;
say(n-1);
}( 10 )); //輸出 10,9,8,7,6,5,4,3,2
/*該函數(shù)為立即執(zhí)行函數(shù),因此會(huì)馬上執(zhí)行,當(dāng)n為2時(shí),因?yàn)?<3,因此會(huì)立即跳出該函數(shù)*/
console.log(say); //輸出0
/*因?yàn)樵谠撟饔糜蛑?,變量say已經(jīng)被賦值了0,在同一個(gè)作用域中,變量和方法同名時(shí),無論順序如何,
變量的賦值會(huì)覆蓋方法的賦值,更何況say()為立即執(zhí)行函數(shù);
*/

如果將題中代碼改成
var say = 0;
var n=10086;
function say(n){
console.log(n);
if(n<3) return;
say(n-1);
};
console.log(say(n));
console.log(say(n)); /*此時(shí)解析器以為要執(zhí)行say(n)函數(shù),但由于同一個(gè)作用域下,若存在同
名的變量和方法,變量的賦值會(huì)覆蓋方法的賦值,因此say還是被看成值為0的變量,而say(n)又不是一個(gè)函
數(shù),因此會(huì)報(bào)錯(cuò)說say不是一個(gè)函數(shù)
*/
上述代碼執(zhí)行后將會(huì)報(bào)錯(cuò)。

補(bǔ)充:作用域鏈相關(guān)資料
**本文版權(quán)歸本人即簡書筆名:該賬戶已被查封 所有,如需轉(zhuǎn)載請注明出處。謝謝! *