一、函數(shù)聲明和函數(shù)表達(dá)式有什么區(qū)別
- 區(qū)別:
用函數(shù)聲明創(chuàng)建的函數(shù)可以在定義之前就進(jìn)行調(diào)用例嘱;而用函數(shù)表達(dá)式創(chuàng)建的函數(shù)不能在被賦值之前進(jìn)行調(diào)用宁舰。 - 原因
函數(shù)聲明語(yǔ)句“被提前”到外部腳本或外部函數(shù)作用域的頂部,所以以這種方式聲明的函數(shù)蛮艰,可以被在它定義之前出現(xiàn)的代碼所調(diào)用。
而要使用一個(gè)以表達(dá)式方式定義的函數(shù)之前即寡,必須把它賦值給一個(gè)變量袜刷。變量的聲明提前了,但給變量賦值是不會(huì)提前的著蟹,所以,以表達(dá)式方式定義的函數(shù)在定義之前無(wú)法調(diào)用奸披。
二涮雷、什么是變量的聲明前置?什么是函數(shù)的聲明前置洪鸭?
- 變量聲明前置:
javascript的變量聲明具有hoisting機(jī)制膜钓,JavaScript引擎在執(zhí)行的時(shí)候卿嘲,會(huì)把所有變量的聲明都提升到當(dāng)前作用域的最前面拾枣。
var a = 1;
function main() {
console.log(a);
var a = 2;
}
main()//輸出undefined
//解析如下
function main() {
var a;
console.log(a);
a = 2;
}
main()
//所以輸出undefined```
* 函數(shù)的聲明前置
JavaScript的函數(shù)作用域是指在函數(shù)內(nèi)聲明的所有變量在函數(shù)體內(nèi)是始終可見(jiàn)的。這意味著變量可以先使用梅肤,后聲明。JavaScript的這一特性被非正式地稱為聲明提前(hoiosting)姨蝴,即JavaScript函數(shù)中所有變量的聲明都被“提前”至函數(shù)體的頂部。
>```
var scope = "global";
function f() {
console.log(scope);//輸出underfined授帕,而不是"global"
var scope = "local"; //變量在這里賦初始值浮梢,但變量在函數(shù)體內(nèi)任何地方均是有定義的。
console.log(scope);//輸出"local"
}
//上述過(guò)程等價(jià)于:將函數(shù)內(nèi)的變量聲明提前至函數(shù)頂部秕硝,但變量初始化(賦值)還在原來(lái)的位置。
var scope = "global";
function f() {
var scope奈偏;//在函數(shù)頂部聲明了局部變量
console.log(scope);//變量存在躯护,但值是underfined
scope = "local"; //初始化賦值
console.log(scope);//
}```
三、arguments 是什么
標(biāo)識(shí)符arguments是指向?qū)崊?duì)象的引用唁盏,實(shí)參對(duì)象是一個(gè)類數(shù)組對(duì)象(不能修改它检眯,也不能用push來(lái)添加新元素等。但是可以訪問(wèn)其中的元素锰瘸,并且同時(shí)具有.length屬性)。
在函數(shù)代碼中舞萄,使用特殊對(duì)象 arguments管削,無(wú)需明確指出參數(shù)名,就能訪問(wèn)它們崎弃。
例如,在函數(shù) sayHi() 中饲做,第一個(gè)參數(shù)是 message。用 arguments[0] 也可以訪問(wèn)這個(gè)值塞弊,即第一個(gè)參數(shù)的值(第一個(gè)參數(shù)位于位置 0泪姨,第二個(gè)參數(shù)位于位置 1,依此類推)奏候。
四唇敞、函數(shù)的"重載"怎樣實(shí)現(xiàn)
所謂重載,是同一函數(shù)名疆柔,但是參數(shù)類型或參數(shù)個(gè)數(shù)不同的函數(shù)旷档。
Javascript不像其他編程語(yǔ)言一樣具有函數(shù)簽名(函數(shù)簽名,簡(jiǎn)單的說(shuō)就是函數(shù)的接收參數(shù)類型和參數(shù)個(gè)數(shù))。所以Javascript是不能像其他語(yǔ)言一樣實(shí)現(xiàn)方法名相同鞋屈,參數(shù)個(gè)數(shù)不同的這類重載的。
利用arguments渠啊,可以實(shí)現(xiàn)JavaScript的重載权旷。
> function showMessage(){
if(arguments.length==1){
console.log(arguments[0]);
}else if( arguments.length==2){
console.log(arguments[0]+"說(shuō):"+arguments[1]);
}else{
return false;
}
}
showMessage("Hi!");
showMessage("張三","Hi 你妹");
五、立即執(zhí)行函數(shù)表達(dá)式是什么躲查?有什么作用
立即執(zhí)行函數(shù)就是當(dāng)我們?cè)诙x了函數(shù)之后需要立即執(zhí)行的函數(shù)译柏。
( function(){…} ) ()
( function (){…} () )```
是兩種javascript立即執(zhí)行函數(shù)的常見(jiàn)寫(xiě)法。
-
為什么這么寫(xiě):
首先怎静,要在函數(shù)體后面加括號(hào)就能立即調(diào)用,這個(gè)函數(shù)必須是函數(shù)表達(dá)式蚓聘,不能是函數(shù)聲明盟劫。
下面代碼:
function(){ /* code */ }(); // SyntaxError: Unexpected token```
報(bào)錯(cuò)了侣签,這是為何?這是因?yàn)樵趈avascript代碼解釋時(shí)影所,當(dāng)遇到function關(guān)鍵字時(shí),會(huì)默認(rèn)把它當(dāng)做是一個(gè)函數(shù)聲明阴幌,而不是函數(shù)表達(dá)式卷中,如果沒(méi)有把它顯視地表達(dá)成函數(shù)表達(dá)式,就報(bào)錯(cuò)了议忽,因?yàn)楹瘮?shù)聲明需要一個(gè)函數(shù)名十减,而上面的代碼中函數(shù)沒(méi)有函數(shù)名。(以上代碼帮辟,也正是在執(zhí)行到第一個(gè)左括號(hào)(時(shí)報(bào)錯(cuò),因?yàn)?前理論上是應(yīng)該有個(gè)函數(shù)名的壳繁。)
如果我們給它函數(shù)名荔棉,然后加上()立即調(diào)用,同樣也會(huì)報(bào)錯(cuò):
function foo(){ /* code */ }(); // SyntaxError: Unexpected token```
為什么會(huì)這樣渣触?在一個(gè)表達(dá)式后面加上括號(hào)壹若,表示該表達(dá)式立即執(zhí)行皂冰;而如果是在一個(gè)語(yǔ)句后面加上括號(hào)养篓,該括號(hào)完全和之前的語(yǔ)句不搭嘎,而只是一個(gè)分組操作符舶胀,用來(lái)控制運(yùn)算中的優(yōu)先級(jí)(小括號(hào)里的先運(yùn)算)碧注。所以以上代碼等價(jià)于:
function foo(){ /* code */ }
(); // SyntaxError: Unexpected token )```
相當(dāng)于先聲明了一個(gè)叫foo的函數(shù),之后進(jìn)行()內(nèi)的表達(dá)式運(yùn)算轩端,但是()(分組操作符)內(nèi)的表達(dá)式不能為空逝变,所以報(bào)錯(cuò)。
( function(){…} ) ()
( function (){…} () )```
為什么這樣就能立即執(zhí)行并且不報(bào)錯(cuò)呢骨田?因?yàn)樵趈avascript里态贤,括號(hào)內(nèi)部不能包含語(yǔ)句,當(dāng)解析器對(duì)代碼進(jìn)行解釋的時(shí)候悠汽,先碰到了(),然后碰到function關(guān)鍵字就會(huì)自動(dòng)將()里面的代碼識(shí)別為函數(shù)表達(dá)式而不是函數(shù)聲明茬高。
-
作用:
1. 創(chuàng)建只使用一次的函數(shù)假抄,并立即執(zhí)行它。
2. 創(chuàng)建閉包熏瞄,保存狀態(tài)谬以,隔離作用域。
3. 作為獨(dú)立模塊存在(例子如jQuery)邮丰,防止命名沖突,命名空間注入(模塊解耦)剪廉。
六、求n!淮野,用遞歸來(lái)實(shí)現(xiàn)
function recursion(n) {
if (n===1) {
return 1;
}else {
return n * recursion(n-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('饑人谷', 2, '男');
/*輸出
name: 饑人谷
age: 2
sex: 男
['饑人谷',2,'男']
name valley
*/
getInfo('小谷', 3);
/*
name: 小谷
age: 3
sex: undefined
['小谷',3]
name valley
*/
getInfo('男');
/*
name: 男
age: undefined
sex: undefined
['男']
name valley
*/
八舆吮、寫(xiě)一個(gè)函數(shù),返回參數(shù)的平方和色冀?
function sumOfSquares(){
var sum=0;
for(var i=0;i<arguments.length;i++){
sum=sum+arguments[i]*arguments[i];
}
console.log(sum);
}
var result = sumOfSquares(2,3,4)
var result2 = sumOfSquares(1,3)
console.log(result) //29
console.log(result) //10
九锋恬、如下代碼的輸出?為什么
console.log(a);
var a = 1;
console.log(b);
以上可重寫(xiě)為:
var a与学;//聲明前置
console.log(a);//輸出underfined
a = 1;
console.log(b);//ReferenceError: b is not defined
十、如下代碼的輸出晕窑?為什么
sayName('world');//輸出hello world卵佛,函數(shù)聲明前置
sayAge(10);//ReferenceError: sayAge is not defined
function sayName(name){
console.log('hello ', name);
}
var sayAge = function(age){
console.log(age);
};
十一、如下代碼輸出什么? 寫(xiě)出作用域鏈查找過(guò)程偽代碼
var x = 10
bar()
function foo() {
console.log(x)
}
function bar(){
var x = 30
foo()
}
//輸出10
/*
1. globalContext = {
AO: {
x: 10
foo: function
bar: function
}
Scope: null
}
foo.[[Scope]] = globalContext.AO
bar.[[Scope]] = globalContext.AO
//調(diào)用 bar()
2. barContext = {
AO: {
x: 30
}
Scope: globalContext.AO
}
//調(diào)用 foo()
3. fooContext = {
AO: {}
Scope: globalContext.AO
}
*/
十二疾牲、如下代碼輸出什么? 寫(xiě)出作用域鏈查找過(guò)程偽代碼
var x = 10;
bar()
function bar(){
var x = 30;
function foo(){
console.log(x)
}
foo();
}
//輸出30
/*
1. globalContext = {
AO: {
x: 10
bar: function
}
Scope: null
}
bar.[[Scope]] = globalContext.AO
//調(diào)用 bar()
2. barContext = {
AO: {
x: 30
foo: function
},
Scope: globalContext.AO
}
foo.[[Scope]] = barContext.AO
//調(diào)用 foo()
3. fooContext = {
AO: {}
Scope: barContext.AO
}
*/
十三说敏、以下代碼輸出什么? 寫(xiě)出作用域鏈的查找過(guò)程偽代碼
var x = 10;
bar()
function bar(){
var x = 30;
(function (){
console.log(x)
})()
}
//輸出30
/*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
}
*/
十四丢郊、以下代碼輸出什么医咨? 寫(xiě)出作用域鏈查找過(guò)程偽代碼
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)
重寫(xiě)為:
var a;
function fn(){
var a; //3.聲明fn()的局部變量a
var a;
function fn2(){ //4.聲明fn2()
console.log(a);//13.fn2()未定義變量a拟淮,尋找父級(jí)谴忧,輸出6
a=20; //14.把fn()的a賦值為20
}
console.log(a); //5.未賦值輸出underfined
a=5; //6.為fn()的局部變量a賦值為5
console.log(a); //7.輸出5
a++; //8.a=6
fn3() //9.調(diào)用fn3()
fn2() //12.調(diào)用fn2()
console.log(a) //15.輸出20
}
function fn3(){
console.log(a); //10.fn3()的作用域未定義變量a,尋找父級(jí)沾谓,輸出1
a=200 //11.將全局a賦值為200
}
a=1; //1.全局變量a賦值為1
fn() //2.調(diào)用fn()
console.log(a) //16.輸出200
//輸出
undefined
5
1
6
20
200
/*
1. globalContext = {
AO: {
a: 1 -> 200
fn: function
fn3: function
},
Scope: null
}
fn.[[Scope]] = globalContext.AO
fn3.[[Scope]] = globalContext.AO
//調(diào)用 fn()
2. fnContext = {
AO: {
a: undefined -> 5 -> 6 -> 20
fn2: function
}
Scope: globalContext.AO
}
fn2.[[Scope]] = fnContext.AO
//調(diào)用 fn3()
3. fn3Context = {
AO: {}
Scope: globalContext.AO
}
//調(diào)用 fn2()
3. fn2Context = {
AO: {}
Scope: fnContext.AO
}
*/