函數(shù)聲明和函數(shù)表達(dá)式的區(qū)別
//函數(shù)聲明
function wscat(type){
return type="wscat";
}
//函數(shù)表達(dá)式
var oaoafly =function (type){
return type="oaoafly";
}
區(qū)別
- 函數(shù)聲明不管在哪里定義访圃,該函數(shù)都可以進(jìn)行調(diào)用。而函數(shù)表達(dá)式的值必須是在函數(shù)表達(dá)式賦值完成后秧倾,該函數(shù)才能調(diào)用馁龟。
- 對于函數(shù)聲明崩侠,js解析器會(huì)優(yōu)先讀取,確保在所有 代碼執(zhí)行前聲明已經(jīng)被解析坷檩。而函數(shù)表達(dá)式却音,如同其他基本類型的變量一樣,只在執(zhí)行到某一句的時(shí)候也會(huì)對其進(jìn)行解析淌喻。
聲明前置
變量聲明前置
變量聲明前置就是在一個(gè)作用域塊中僧家,所有的變量都被放在塊的開始聲明。
var a=1;
function main(){
console.log(a);
var a=2;
}
main()//輸出undefined;
為什么會(huì)輸出undefined呢裸删?是因?yàn)槟_本在運(yùn)行的時(shí)候會(huì)自動(dòng)將變量聲明前置解析如下:
var a=1;
function main(){
var a;
console.log(a);
a=2;
}//所以八拱,我們在寫js的時(shí)候應(yīng)該盡量將變量聲明放在作用域的開始地方。
函數(shù)聲明前置
使用function關(guān)鍵字就可以聲明一個(gè)函數(shù),它的特征是函數(shù)聲明提升肌稻,執(zhí)行代碼前會(huì)先讀取函數(shù)聲明拱烁,即函數(shù)聲明不必放在調(diào)用的前面夕玩。
var a=3;
console.log(a);
sayHello();
function sayHello (){
console.log("hello");
}
解析為
var a;
function sayHello(){}
然后:conslole.log(a);
a=3;
sayHello();
對于函數(shù)表達(dá)式,和var一個(gè)變量沒什么區(qū)別。
arguments
可以在函數(shù)體內(nèi)讀取所有的參數(shù)酵熙,這就是argunments對象的由來凌彬。
arguments對象的使用
- 通過方括號(hào)語法訪問每一個(gè)元素
var fun=function(one){
console.log(arguments[0]);
console.log(arguments[1]);
console.log(arguments[2]);
}
fun(1,2,3)
//1
//2
//3
- 通過length屬性变屁,查看到底要幾個(gè)參數(shù)
function fun(){
return argunments.length;
}
fun(1,2,3,)
- 參數(shù)賦值(嚴(yán)格模式下不容許)
var fun=function (a,b){
argunments[1]=2;
return a+b;
}
fun(1,1)//3
重載
js沒有重載议惰,但是可以在js中實(shí)現(xiàn)重載的功能,即傳遞不同的實(shí)參腹泌,可以自動(dòng)調(diào)用匹配嘶卧。
function print(name,age,sex){
if(name){
console.log(name);
}
if(age){
console.log(age);
}
if(sex){
console.log(sex);
}
}
print("byyu",26)
立即執(zhí)行函數(shù)
最常用的兩種寫法:
(function (){/*code*/}());
(function (){/*code*/})();
為什么第一種執(zhí)行而不報(bào)錯(cuò)呢?因?yàn)樵趈s里,括號(hào)內(nèi)部不能包含語句凉袱,當(dāng)解析器對代碼進(jìn)行解析的時(shí)候芥吟,先碰到了(),然后碰到function關(guān)鍵字就會(huì)自動(dòng)將()里面的代碼識(shí)別為函數(shù)表達(dá)式而不是函數(shù)聲明专甩。
作用
模擬塊作用域
js只有函數(shù)作用域钟鸵,在同時(shí)調(diào)用多個(gè)庫的情況下,很容易造成對象或者變量的覆蓋涤躲。
liba.js
var num=1;
libb.js
var num=2;
如果在頁面上同時(shí)引用liba,js和liba,js兩個(gè)庫棺耍。必然導(dǎo)致num的覆蓋,為此:
liba.js
(function(){
var num=1;
})();
libb,js
(function(){
var num=2;
})();
經(jīng)過改造之后种樱,兩個(gè)庫的代碼就完全獨(dú)立烈掠,互不影響了。
用遞歸實(shí)現(xiàn) n缸托!
function factor(n){
if (n==1){
return 1
}
return n*factor(n-1);
}
factor(100)
代碼輸出
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取董,undefined,['小谷棍苹,'3'],name:valley;
getInfo('男');name:男茵汰,undefined,undefined,['男']枢里,name:valley;
寫一個(gè)函數(shù),返回參數(shù)的平方和
function sumOfSquares(){
var result=0;
for(i=0;i<arguments.length;i++){
result=result+argument[i]+argument[i];
}
return result;
}
var demo=sumOfSquares(1,3,4);
console.log(demo);
代碼輸出(變量提升)
console.log(a);
var a = 1;
console.log(b);
console.log(a)
輸出undefined;因?yàn)閍變量提升,相當(dāng)于
var a;
console.log(a);
只聲明了沒有賦值栏豺,為undefined彬碱。
console.log(b)會(huì)報(bào)錯(cuò),因?yàn)榧葲]有聲明也沒有賦值奥洼。
代碼輸出(函數(shù)聲明前置)
sayName('world');
sayAge(10);
function sayName(name){
console.log('hello ', name);
}
var sayAge = function(age){
console.log(age);
};
sayName函數(shù)會(huì)輸出'hello''world',sayAge會(huì)報(bào)錯(cuò)
原因:第一個(gè)是函數(shù)聲明巷疼,會(huì)函數(shù)聲明前置,所以函數(shù)聲明不必放在調(diào)用的前面灵奖。而第二個(gè)是函數(shù)表達(dá)式嚼沿,相當(dāng)于普通變量,所以函數(shù)調(diào)用必須放在函數(shù)聲明的后面瓷患,否則會(huì)報(bào)錯(cuò)骡尽。
作用域鏈(查找過程偽代碼)
var x = 10
bar()
function foo() {
console.log(x)
}
function bar(){
var x = 30
foo()
}//輸出10
原因:
globalcontext={
Ao{ x:10;
foo:function;
bar:function;}
scope:null;
}}
foo.[[scope]]=globalcontext.Ao;
bar.[[scope]]=globalcontext.Ao;
barcontext={
Ao{
x:30;}
scope:globalcontext.Ao;
}
foocontext={
Ao{};
scope:globalcontext.Ao;
}//調(diào)用foo時(shí)候會(huì)先從Ao里面找,Ao里面為空尉尾,會(huì)向globalcontext.Ao里面找爆阶,找到x=10,所以會(huì)打印出10。
作用域鏈(查找過程偽代碼)
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. barcontext={
Ao:{
x:30;
foo:function;
}
scope:bar.[[scope]]=globalcontext.Ao;
}
foo.[[scope]]=barcontext.Ao;
3. foocontext={
Ao:{};
scope:foo.[[scope]]=barcontext.Ao;
}//輸出30.調(diào)用foo()的時(shí)候沙咏,會(huì)在foo的Ao中查找辨图,可見里面沒有。所以會(huì)在scope里面找肢藐,barcontext.Ao里面有x=30;所以輸出的結(jié)果為30故河。
查找過程的偽代碼(立即執(zhí)行函數(shù))
var x = 10;
bar()
function bar(){
var x = 30;
(function (){
console.log(x)
})()//等價(jià)于function foo(){console.log(x);}
}
過程:
1. globalcontext={
Ao:{
x:10;
bar:function;
}
scope:null;
}
bar.[[scope]]=globalcontext.Ao;
2. barcontext={
Ao:{
x:30;
foo:function;
}
scope:globalcontext.Ao;
}
foocontext.[[scope]]=barcontext.Ao;
3. foocontext={
Ao:{};
scope:barcontext.Ao;
}//調(diào)用foo()函數(shù)時(shí),Ao中沒有值吆豹。所以在scope:barcontext.Ao查找;其中中x=30,所以輸出30
作用域鏈(查找過程偽代碼)
var a = 1;
function fn(){
console.log(a)//輸出undefined;因?yàn)樽兞柯暶髑爸胿ar a
var a = 5
console.log(a)輸出5鱼的,因?yàn)閍=5
a++
var a
fn3()
fn2()
console.log(a)//輸出6,因?yàn)閍++,a為6
function fn2(){
console.log(a)//輸出20痘煤,因?yàn)閒n2.[[scope]]=fncontext.Ao;并且a=20,將
fncontext.Ao中的a=6改為20凑阶;
a = 20
}
}
function fn3(){
console.log(a)//輸出200,因?yàn)閒n3中的AO為空衷快,所以會(huì)在
globalcontext.Ao;中找宙橱,又因?yàn)閍=200,將a=1改為200,所以a=200
a = 200
}
fn()
console.log(a)
過程:
globalcontext={
Ao:{
a:1;
fn3:function;
fn:function;
}
scope:null;
}
fn3.[[scope]]=globalcontext.Ao;
fn.[[scope]]=globalcontext.Ao;
fncontext={
Ao:{
a:6;
fn2:function;
scope:globalcontext.Ao;
}
fn2.[[scope]]=fncontext.Ao;
}
fn3context={
Ao:{};
scope:globalcontext.Ao;
}
fn2context={
Ao:{};
scope:fncontext.Ao;
}