一、問答
(一)函數(shù)聲明和函數(shù)表達式有什么區(qū)別 酝掩?
函數(shù)聲明表示方法的例子:
function printName(){
alert("shengming");
}
printName();
函數(shù)表達式的例子:
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 );
運行后:
由以上可以看出,即使聲明函數(shù)shengMing()是在后面聲明的期虾,但在前面也可調(diào)用(第一個console.log( shengMing );
能夠正常運行)原朝,但是表達函數(shù)則不行(第一個console.log( biaodashi );
運行結(jié)果顯示undefined);對于函數(shù)聲明語句镶苞,函數(shù)名稱和函數(shù)體均提前聲明了喳坠,可以在聲明之前調(diào)用它;但對于函數(shù)表達式茂蚓,只有函數(shù)變量聲明提前了壕鹉,但是函數(shù)的初始化代碼仍然在原來的位置;
(二)什么是變量的聲明前置?什么是函數(shù)的聲明前置聋涨?
- 變量的聲明前置晾浴,是指即使我們沒有在使用變量前先聲明這個變量,但是由于變量提升(即變量聲明的前置)的存在牛郑,我們可以在聲明前使用該變量怠肋,只不過這個變量的默認值是undefined,但解析器不會報錯淹朋;例如:
- 同理笙各,對于函數(shù)也存在函數(shù)的聲明前置的情況,其實第一題就說明了這一點础芍,下面再舉個例子:
當(dāng)然對于表達式函數(shù)也適用杈抢,例如下面的例子:
(三)arguments 是什么 ?
arguments 是一個類數(shù)組對象仑性。代表傳給一個function的參數(shù)列表惶楼。
可以在函數(shù)內(nèi)部通過使用 arguments 對象來獲取函數(shù)的所有參數(shù)。這個對象為傳遞給函數(shù)的每個參數(shù)建立一個條目诊杆,條目的索引號從 0 開始歼捐。arguments 對象并不是一個真正的Array。它類似于數(shù)組晨汹,但沒有數(shù)組所特有的屬性和方法豹储,除了 length。例如淘这,它沒有 pop 方法剥扣。不過可以將其轉(zhuǎn)換成數(shù)組巩剖。另外:arguments 對象僅在函數(shù)內(nèi)部有效,在函數(shù)外部調(diào)用 arguments 對象會出現(xiàn)一個錯誤钠怯。如果你調(diào)用一個函數(shù)佳魔,當(dāng)這個函數(shù)的參數(shù)數(shù)量比它顯式聲明的參數(shù)數(shù)量更多的時候,你就可以使用 arguments 對象晦炊。這個技術(shù)對于參數(shù)數(shù)量是一個可變量的函數(shù)來說比較有用鞠鲜。 你可以用 arguments.length 來得到參數(shù)的數(shù)量,然后可以用 arguments object 來對每個參數(shù)進行處理刽锤。 (想要得到當(dāng)一個函數(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ù)量。
- 下面舉個使用arguments的例子:
在聲明上述函數(shù)時未使用參數(shù)语稠,即其不包含命名的參數(shù)宋彼,這說明在函數(shù)中命名的參數(shù)只提供便利,但不是必須的仙畦。
- 下面再舉個使用arguments.length的例子:
(四)函數(shù)的重載怎樣實現(xiàn) 输涕?
由于js不能像傳統(tǒng)意義上那樣實現(xiàn)重載,因此我們可以通過使用arguments的方法實現(xiàn)非完美的重載慨畸;
如下面的例子:
(五)立即執(zhí)行函數(shù)表達式是什么莱坎?有什么作用 ?
類似于
類似于上述函數(shù)的寫法叫做「立即執(zhí)行函數(shù)」也叫「自執(zhí)行匿名函數(shù)」(self-executing anonymous function)寸士;「立即執(zhí)行函數(shù)表達式」(Immediately-Invoked Function Expression檐什,簡稱IIFE);
其中一般推薦使用第一種寫法弱卡,但是目前很多比較好的js library 使用的都是第二種方式乃正。 比如: web 圖形繪制的: git , draw2d ,....
立即執(zhí)行函數(shù)的作用有:
1、模擬塊作用域婶博;js沒有java瓮具、c等語言的塊作用域,只有函數(shù)作用域凡人,因此在同時調(diào)用多個庫時很容易出現(xiàn)變量或?qū)ο蟊桓采w的情況名党;
2、解決閉包沖突挠轴;
3传睹、模擬單例;
(六)什么是函數(shù)的作用域鏈忠荞?
函數(shù)對象和其他對象一樣蒋歌,擁有可以通過代碼訪問的屬性和一系列供js引擎訪問的內(nèi)部屬性帅掘,其中一個內(nèi)部屬性是[scope],該屬性包括了函數(shù)被創(chuàng)建的作用域中的對象集合堂油,這個集合稱為函數(shù)的作用域鏈修档,它決定了哪些能夠被函數(shù)訪問;
舉個例子:
二府框、代碼
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('男');
運行結(jié)果會如下:
/* getInfo('hunger', 28, '男')運行結(jié)果:
name:hunger
age:28
sxe:男
{valley, 28, 男}
name valley
*/
/* getInfo('hunger', 28)運行結(jié)果:
name:hunger
age:28
sxe:undefind
{valley,28}
name valley
*/
/* getInfo('男')運行結(jié)果:
name:男
age:undefind
sxe:undefind
{valley}
name valley
*/
2.寫一個函數(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)時系宜,不會出現(xiàn)a is not defined照激,但是由于console.log(a)之前a未賦值,所以才會導(dǎo)致出現(xiàn) undefined盹牧; 而對于console.log(b)語句俩垃,由于b既未聲明也未賦值,所以才會報錯b is not defined錯誤汰寓;
4.如下代碼的輸出口柳?為什么 ?
sayName('world'); //hello world (解釋:該函數(shù)為聲明函數(shù),存在提前聲明的情況有滑;)
sayAge(10);/*sayAge is not a function (解釋:該函數(shù)為表達式函數(shù)跃闹,
不存在提前聲明的情況,所以會報錯)*/
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 此時為普通變量
function fn(){} //fn此時為函數(shù)名
fn = 3; /*fn此時值等于3,因為在同一個作用域內(nèi)定義了名字相同的變量和方法的話睛榄,無論其順序如
何荣茫,變量的賦值會覆蓋方法的賦值;*/
console.log(fn); //打印時輸出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); //
/* 由于同個作用域下,變量聲明和函數(shù)聲明存在命名沖突時旨剥,變量聲明前置要比函數(shù)聲明前置的優(yōu)先級底咧欣,因此此時打印出的是函數(shù)*/
fn2 = 3;
console.log(fn2); //3
/*此時fn2被賦值3了,因為在同一個作用域中轨帜,定義了同一個名字的變量
和方法時魄咕,無論順序如何,變量的賦值會覆蓋方法的賦值*/
console.log(fn); // 此時會打印函數(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);
其實上面的代碼相當(dāng)于
function fn(fn2){
var fn2=200;
function fn2(){
console.log('fnnn2');
}
console.log(fn2); //200 此時并沒有輸出函數(shù)fn2毛萌,為什么呢?
/*因為同一個作用域中喝滞,定義了名字相同的變量和方法的話阁将,無論順序如何
變量的賦值會覆蓋方法的賦值;
*/
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); //此處會報錯右遭,因為由于函數(shù)作用域的存在做盅,此處的fn2并未定義變量且賦值;
則結(jié)果為
7.如下代碼的輸出窘哈?為什么?
var fn = 1;
function fn(fn){
console.log(fn);
}
console.log(fn(fn));
上述代碼相對于
var fn ;
function fn(fn){
console.log(fn);
};
fn = 1;
console.log(fn(fn)); //報錯 fn is not a function
/* 由于同一個作用域中吹榴,定義同一個名字的變量和方法時,變量賦值會覆蓋方法的賦值滚婉,因此此時解析器
并不能夠識別fn為函數(shù)图筹,但是在打印時又以函數(shù)的方式打印,因此會報錯*/
其實上面的例子可簡化成這樣的理解
打印a(a) 满哪,解析器會把它當(dāng)作成要打印一個函數(shù)婿斥,但是a并不是一個函數(shù),因此會報錯哨鸭。
8.如下代碼的輸出?為什么 ?
//作用域
console.log(j);
console.log(i);
for(var i=0; i<10; i++){
var j = 100;
}
console.log(i);
console.log(j);
其實上面的代碼相當(dāng)于
var j ;
var i ;
console.log(j); // undefined
/*解釋:由于變量提升的存在娇妓,導(dǎo)致此處提示j未被賦值像鸡,但不抱錯*/
console.log(i); //undefined
/*解釋:同理,由于變量提升的存在哈恰,
導(dǎo)致此處提示i未被賦值只估,但不抱錯*/
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
/*此時i并未被賦值,因此為undefinded*/
var i = 99;
fn2(); //執(zhí)行后i為100荠医;
console.log(i); //100
/*此時打印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ù),因此會馬上執(zhí)行娃胆,當(dāng)n為2時遍希,因為2<3,因此會立即跳出該函數(shù)*/
console.log(say); //輸出0
/*因為在該作用域中里烦,變量say已經(jīng)被賦值了0凿蒜,在同一個作用域中禁谦,變量和方法同名時,無論順序如何废封,
變量的賦值會覆蓋方法的賦值州泊,更何況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)); /*此時解析器以為要執(zhí)行say(n)函數(shù)虱饿,但由于同一個作用域下拥诡,若存在同
名的變量和方法,變量的賦值會覆蓋方法的賦值氮发,因此say還是被看成值為0的變量渴肉,而say(n)又不是一個函
數(shù),因此會報錯說say不是一個函數(shù)
*/
上述代碼執(zhí)行后將會報錯爽冕。
**本文版權(quán)歸本人即簡書筆名:該賬戶已被查封 所有仇祭,如需轉(zhuǎn)載請注明出處。謝謝颈畸! *