任務17-函數和作用域


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);輸出結果

本文版權屬 饑人谷_劉曉東 所有,轉載務必注明出處
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,106評論 6 542
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,441評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,211評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,736評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,475評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,834評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,829評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,009評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,559評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,306評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,516評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,038評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,728評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,132評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,443評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,249評論 3 399
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,484評論 2 379

推薦閱讀更多精彩內容