1.函數聲明和函數表達式有什么區別 (*)
函數聲明
function printName(){
console.log('1');
}
printName();
函數表達式
var printName = function(){
console.log('1');
};
printName();
函數聲明:函數調用可以發生在函數聲明之前,例如下面這種情況下不會報錯
printName();//輸出console.log('1')
function printName(){
console.log('1');
}
//正常,因為‘提升’了函數聲明,函數調用可在函數聲明之前
函數表達式:函數調用只能在函數表達式后面,不能再函數表達式前面,例如
printName();//瀏覽器提示Uncaught TypeError: printName is not a function(…)
var printName = function(){
console.log('1');
};
//報錯,變量printName還未保存對函數的引用,函數調用必須在函數表達式之后
瀏覽器提示Uncaught TypeError: printName is not a function(…).原因:類似變量提升,函數作為一個變量賦值給printName,等價于
var printName;//此時printName為undefined
printName();
printName = function(){
console.log('1');
};
所以函數聲明和函數表達式不同之處在于,
一、Javascript引擎在解析javascript代碼時會‘函數聲明提升’(Function declaration Hoisting)當前執行環境(作用域)上的函數聲明,而函數表達式必須等到Javascirtp引擎執行到它所在行時,才會從上而下一行一行地解析函數表達式,
二、函數表達式后面可以加括號立即調用該函數,函數聲明不可以,只能以fnName()形式調用
2.什么是變量的聲明前置?什么是函數的聲明前置 (**)
變量的聲明前置
JavaScript引擎的工作方式是,先解析代碼,獲取所有被聲明的變量,然后再一行一行地運行。這造成的結果,就是所有的變量的聲明語句,都會被提升到代碼的頭部,這就叫做變量提升
我們寫了一個賦值語句
var a = 2;
實際上執行過程是解釋器在未執行的時候先解析出變量聲明,然后給他初始值 undefined ,然后才逐句執行程序
var a;
a = 2;
這樣看起來沒什么區別,但是在多語句的情況下會有差別,我們知道一個變量如果不存在我們就使用會報錯
console.log(xxx); // Uncaught ReferenceError: xxx is not defined
我們在使用一個變量之前必須聲明變量,但是由于變量提升,我們如果聲明了變量,即使在聲明語句前使用也是可以的,只不過其值是初始值 undefined
var xxx = 2;```
函數的聲明前置
>和變量的聲明會前置一樣,函數聲明同樣會前置,如果我們使用函數表達式那么規則和變量一樣
console.log(fn); //undefined
var fn = function(){}
#### 3.arguments 是什么 (*)
>在函數內部,可以使用 arguments 對象獲取到該函數的所有傳入參數
function printPersonInfo(name, age, sex){
console.log('name : '+name);
console.log('age : '+age);
console.log('sex : ' +sex);
console.log(arguments);
}
printPersonInfo('liu', 21, 'boy')
name : liu
age : 22
sex : boy
["liu", 22, "boy"]
printPersonInfo('liu', 21, 'boy')
name : liu
age : boy
sex : undefined
["liu", "boy"]
#### 4.函數的重載怎樣實現 (**)
>重載是很多面向對象語言實現多態的手段之一,在靜態語言中確定一個函數的手段是靠方法簽名——函數名+參數列表,也就是說相同名字的函數參數個數不同或者順序不同都被認為是不同的函數,稱為函數重載
>在JavaScript中沒有函數重載的概念,函數通過名字確定唯一性,參數不同也被認為是相同的函數,后面的覆蓋前面的,這是不是意味著JavaScript不能通過重載功能實現一個函數,參數不同功能不同呢?
在JavaScript中,函數調用沒必要把所有參數都傳入,只要你函數體內做好處理就行,但前提是傳的參數永遠被當做前幾個
function printPeopleInfo(name, age, sex){
if(name){
console.log(name);
}
if(age){
console.log(age);
}
if(sex){
console.log(sex);
}
}
printPeopleInfo('Byron', 26);
printPeopleInfo('Byron', 26, 'male');
#### 5.立即執行函數表達式是什么?有什么作用 (***)
立即執行函數表達式```( function(){…} )()和( function (){…} () )```
var fnName=function(){
alert('Hello World');
}();
//函數表達式后面加括號,當javascript引擎解析到此處時能立即調用函數
function fnName(){
alert('Hello World');
}();
//不會報錯,但是javascript引擎只解析函數聲明,忽略后面的括號,函數聲明不會被調用
function(){
console.log('Hello World');
}();
//語法錯誤,雖然匿名函數屬于函數表達式,但是未進行賦值操作,
//所以javascript引擎將開頭的function關鍵字當做函數聲明,報錯:要求需要一個函數名
>在理解了一些函數基本概念后,回頭看看( function(){…} )()和( function (){…} () )這兩種立即執行函數的寫法,最初我以為是一個括號包裹匿名函數,并后面加個括號立即調用函數,當時不知道為什么要加括號,后來明白,要在函數體后面加括號就能立即調用,則這個函數必須是函數表達式,不能是函數聲明
(function(a){
console.log(a); //firebug輸出123,使用()運算符
})(123);
(function(a){
console.log(a); //firebug輸出1234,使用()運算符
}(1234));
!function(a){
console.log(a); //firebug輸出12345,使用!運算符
}(12345);
+function(a){
console.log(a); //firebug輸出123456,使用+運算符
}(123456);
-function(a){
console.log(a); //firebug輸出1234567,使用-運算符
}(1234567);
var fn=function(a){
console.log(a); //firebug輸出12345678,使用=運算符
}(12345678)
>可以看到輸出結果,在function前面加!、+、 -甚至是逗號等到都可以起到函數定義后立即執行的效果,而()、!、+、-、=等運算符,都將函數聲明轉換成函數表達式,消除了javascript引擎識別函數表達式和函數聲明的歧義,告訴javascript引擎這是一個函數表達式,不是函數聲明,可以在后面加括號,并立即執行函數的代碼。
>加括號是最安全的做法,因為!、+、-等運算符還會和函數的返回值進行運算,有時造成不必要的麻煩。
>不過這樣的寫法有什么用呢?
>javascript中沒用私有作用域的概念,如果在多人開發的項目上,你在全局或局部作用域中聲明了一些變量,可能會被其他人不小心用同名的變量給覆蓋掉,根據javascript函數作用域鏈的特性,可以使用這種技術可以模仿一個私有作用域,用匿名函數作為一個“容器”,“容器”內部可以訪問外部的變量,而外部環境不能訪問“容器”內部的變量,所以( function(){…} )()內部定義的變量不會和外部的變量發生沖突,俗稱“匿名包裹器”或“命名空間”。
>JQuery使用的就是這種方法,將JQuery代碼包裹在( function (window,undefined){…jquery代碼…} (window)中,在全局作用域中調用JQuery代碼時,可以達到保護JQuery內部變量的作用。
參考http://dengo.org/archives/1004
#### 6.什么是函數的作用域鏈 (****)
>作用域是針對變量的,比如我們創建了一個函數,函數里面又包含了一個函數,那么現在就有三個作用域
全局作用域==>函數1作用域==>函數2作用域
作用域的特點就是,先在自己的變量范圍中查找,如果找不到,就會沿著作用域往上找。
如:
var a = 1;
function b(){
var a = 2;
function c(){
var a = 3;
console.log(a);
}
c();
}
b();
最后打印出來的是3,因為執行函數c()的時候它在自己的范圍內找到了變量a所以就不會越上繼續查找,如果在函數c()中沒有找到則會繼續向上找,一直會找到全局變量a,這個查找的過程就叫作用域鏈。
>不知道你有沒有疑問,函數c為什么可以在函數b中查找變量a,因為函數c是在函數b中創建的,也就是說函數c的作用域包括了函數b的作用域
,當然也包括了全局作用域,但是函數b不能向函數c中查找變量,因為
作用域只會向上查找
參考:
http://www.cnblogs.com/pssp/p/5204324.html
http://www.cnblogs.com/dolphinX/p/3280876.html
### 代碼
##### 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('男');
getInfo('hunger', 28, '男');輸出
name:hunger
age:28
sex:男
["hunger", 28, "男"]
name:valley
getInfo('hunger', 28);輸出
name:hunger
age:28
sex:undefined
["hunger", 28]
name:valley
getInfo('男');輸出
name:男
age:undefined
sex:undefined
["男"]
name:valley
##### 2.寫一個函數,返回參數的平方和?如 (難度**)
function sumOfSquares(){
var sum=0;
for (var i = 0; i < arguments.length; i++) {
sum+=arguments[i]*arguments[i];
}
return sum;
}
sumOfSquares(2,3,4); // 29
sumOfSquares(1,3); // 10
##### 3.如下代碼的輸出?為什么 (難度*)
console.log(a); //undefined
var a = 1;
console.log(b); //報錯Uncaught ReferenceError: b is not defined(…)
a輸出undefined是因為變量提升,b沒有定義,類似如下代碼
var a;
console.log(a); //undefined
a = 1;
console.log(b);
##### 4.如下代碼的輸出?為什么 (難度*)
sayName('world'); //輸出 'hello ', 'world'
sayAge(10); //報錯 瀏覽器提示Uncaught TypeError: sayAge is not a function(…)
function sayName(name){
console.log('hello ', name);
}
var sayAge = function(age){
console.log(age);
};
Javascript引擎在解析javascript代碼時會‘函數聲明提升’(Function declaration Hoisting)當前執行環境(作用域)上的函數聲明,而函數表達式必須等到Javascirtp引擎執行到它所在行時,才會從上而下一行一行地解析函數表達式,
##### 5.如下代碼的輸出?為什么 (難度**)
function fn(){}
var fn = 3;
console.log(fn);//輸出3
當在同一個作用域內定義了名字相同的變量和方法的話,無論其順序如何,變量的賦值會覆蓋方法的賦值
##### 6.如下代碼的輸出?為什么 (難度***)
function fn(fn2){
console.log(fn2);//1
var fn2 = 3;
console.log(fn2);//2
console.log(fn);//3
function fn2(){
console.log('fnnn2');
}//4
}
fn(10);
輸出
//1
function fn2(){
console.log('fnnn2');
}
//2
3
//3
function fn(fn2){
console.log(fn2);
var fn2 = 3;
console.log(fn2);
console.log(fn);
function fn2(){
console.log('fnnn2');
}
}
在函數體內```console.log(fn2);//1```與
function fn2(){
console.log('fnnn2');
}//4
在同一作用于內,在解析javascript代碼時會‘函數聲明提升’(Function declaration Hoisting)當前執行環境(作用域)上的函數聲明,因此
console.log(fn2);//1
輸出
function fn2(){
console.log('fnnn2');
}
然后```var fn2=3;```覆蓋函數聲明,下面```console.log(fn2);```
輸出3
console.log(fn);
輸出函數fn自身
##### 7.如下代碼的輸出?為什么 (難度***)
var fn = 1;
function fn(fn){
console.log(fn);
}
console.log(fn(fn));
輸出:報錯fn不是一個函數
首先,這種情況
var fn = 1;
function fn(fn){
console.log(fn);
}
console.log(fn);//輸出1;原因:同一個作用域內定義了名字相同的變量和方法的話,無論其順序如何,變量的賦值會覆蓋方法的賦值
如果改為
var fn = 1;
function fn(fn){
console.log(fn);
}
console.log(fn());//輸出:報錯fn不是一個函數
所以console.log(fn());無論傳不傳參數都會報錯
##### 8..如下代碼的輸出?為什么 (難度**)
//作用域
console.log(j);
console.log(i);
for(var i=0; i<10; i++){
var j = 100;
}
console.log(i);
console.log(j);
輸出結果
undefined
undefined
10
100
原因:
//作用域
console.log(j);//循環體內變量提升undefined
console.log(i);//循環體內變量提升undefined
for(var i=0; i<10; i++){
var j = 100;
}//循環結束i=10;j=100;
console.log(i);//10
console.log(j);//100
for(var i=0; i<10; i++){
var j = 100;
}
for循環相當于
var i=0;
if(0<10){ var j = 100;}
i+=1;//i=1
if(1<10){ var j = 100;}
i+=1;//i=2
if(2<10){ var j = 100;}
i+=1;//i=3
.......................
if(9<10){ var j = 100;}
i+=1;//i=10
if(10<10){ var j = 100;}
理解js語句是一行一行執行的就很好理解
##### 9.如下代碼的輸出?為什么 (難度****)
fn();//1.
var i = 10;
var fn = 20;
console.log(i);//2.
function fn(){
console.log(i);
var i = 99;
fn2();
console.log(i);
function fn2(){
i = 100;
}
}
預想輸出
>fn()//1.
undefined
100
99
console.log(i);//2
10
實際輸出結果
fn()//1.輸出
undefined
100
console.log(i);//2.輸出
10
分析:
function fn(){
console.log(i);//變量提升i為undefined
var i = 99;
fn2();//函數執行但不返回結果
console.log(i);//執行fn2();后i值變為100
function fn2(){
i = 100;
}
console.log(i);//2.輸出
10//作用域原因,函數內的i不影響外部的i
##### 10.如下代碼的輸出?為什么 (難度*****)
var say = 0;
(function say(n){
console.log(n);
if(n<3) return;
say(n-1);
}( 10 ));
console.log(say);
自執行函數輸出結果為
10 9 8 7 6 5 4 3 2
當n=2時跳出自執行
console.log(say);輸出結果為0
自己沒做對,不知道自執行函數運行機制,根據結果分析出來的,自執行函數只運行自己不影響外部任何變量,運行完結束返回結果,擁有自己獨立的作用域,“容器”內部可以訪問外部的變量,而外部環境不能訪問“容器”內部的變量
例如
var say = 0;
var i=4;
(function say(n){
console.log(n+i);
if(n<3) return;
say(n-1);
}( 10 ));
console.log(say);
輸出結果為14 13 12 11 10 9 8 7 6 0
而
var say = 0;
(function say(n){
var i=4;
console.log(n+i);
if(n<3) return;
say(n-1);
}( 10 ));
console.log(say);
console.log(i);
輸出結果為
14 13 12 11 10 9 8 7 6//自執行函數輸出結果console.log(n+i);
0// console.log(say);輸出結果
Uncaught ReferenceError: i is not defined// console.log(i);輸出結果
本文版權屬 饑人谷_劉曉東 所有,轉載務必注明出處