最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

深入理解javascript函數(shù)參數(shù)與閉包

 更新時間:2016年12月12日 16:17:50   作者:unclekeith  
函數(shù)是javascript的一等對象,想要學(xué)好javascript,就必須深刻理解函數(shù)。本文對javascript函數(shù)參數(shù)與閉包進行詳細分析介紹。需要的朋友一起來看下吧

最近在學(xué)習(xí)javascript的函數(shù),函數(shù)是javascript的一等對象,想要學(xué)好javascript,就必須深刻理解函數(shù)。本人把學(xué)習(xí)的過程整理成文章,一是為了加深自己函數(shù)的理解,二是給讀者提供學(xué)習(xí)的途徑,避免走彎路。內(nèi)容有些多,但都是筆者對于函數(shù)的總結(jié)。

1.函數(shù)參數(shù)

  1.1:參數(shù)是什么

  1.2:參數(shù)的省略

  1.3:參數(shù)默認值

  1.4:參數(shù)傳遞方式

  1.5:同名參數(shù)

  1.6:arguments對象

2.閉包

  2.1:閉包定義

  2.2:立即調(diào)用的函數(shù)表達式(IIFE, Immediately invoked function expression)

1.函數(shù)參數(shù)

 1.1:參數(shù)是什么

 在定義一個函數(shù)時,有時候需要為函數(shù)傳遞額外的數(shù)據(jù),不同的外部數(shù)據(jù)會得到不同的結(jié)果,這種外部數(shù)據(jù)就叫做參數(shù)。

 function keith(a){
 return a+a;
 }
 console.log(keith(3)); //6

上面代碼中,給keith函數(shù)傳遞了參數(shù)a,并且返回了a+a表達式。

1.2:參數(shù)的省略

函數(shù)參數(shù)不是必須的,javascript規(guī)范允許省略調(diào)用時傳遞的實際參數(shù)。

 function keith(a, b, c) {
 return a;
 }
 console.log(keith(1, 2, 3)); //1
 console.log(keith(1)); //1
 console.log(keith()); // 'undefined'

上面代碼中,keith函數(shù)定義了三個參數(shù),但是在調(diào)用時無論傳遞了多少個參數(shù),javascript都不會報錯。被省略的參數(shù)的默認值就變?yōu)閡ndefined。了解函數(shù)定義與函數(shù)作用域 的都知道,函數(shù)的length屬性會返回參數(shù)個數(shù)。需要注意的是,length屬性與實際參數(shù)的個數(shù)無關(guān),只是返回形式參數(shù)的個數(shù)。

(實際參數(shù):調(diào)用時傳遞的參數(shù)。     形式參數(shù):定義時傳遞的參數(shù)。)

但是沒有辦法省略只靠前的元素,而保留靠后的元素。如果一定要省略靠前的元素,只有顯示傳入undefined。

 function keith(a, b) {
 return a;
 }
 console.log(keith(, 1)); //SyntaxError: expected expression, got ','
 console.log(keith(undefined, 2)); //'undefined'

上面代碼中,如果省略了第一個參數(shù),瀏覽器就會報錯。如果給第一個參數(shù)傳遞undefined,則不會報錯。

1.3:默認值

在JavaScript中,函數(shù)參數(shù)的默認值是undefined。然而,在某些情況下設(shè)置不同的默認值是有用的。一般策略是在函數(shù)的主體測試參數(shù)值是否為undefined,如果是則賦予一個值,如果不是,則返回實際參數(shù)傳遞的值。

 function keith(a, b) {
 (typeof b !== 'undefined') ? b = b: b = 1;
 return a * b;
 }
 console.log(keith(15)); //15
 console.log(keith(15, 2)) //30

上面代碼中,做了個判斷。當(dāng)在調(diào)用時沒有傳入b參數(shù),則默認為1。

從ECMAScript 6開始,定義了默認參數(shù)(default parameters)。使用默認參數(shù),在函數(shù)體的檢查就不再需要了。

 function keith(a, b = 1) {
 return a * b;
 }
 console.log(keith(15)); //15
 console.log(keith(15, 2)) //30

1.4:參數(shù)傳遞方式

函數(shù)參數(shù)的傳遞方式有兩種,一個是傳值傳遞,一個是傳址傳遞。

當(dāng)函數(shù)參數(shù)是原始數(shù)據(jù)類型時(字符串,數(shù)值,布爾值),參數(shù)的傳遞方式為傳值傳遞。也就是說,在函數(shù)體內(nèi)修改參數(shù)值,不會影響到函數(shù)外部。

 var a = 1;
 function keith(num) {
 num = 5;
 }
 keith(a);
 console.log(a); //1

上面代碼中,全局變量a是一個原始類型的值,傳入函數(shù)keith的方式是傳值傳遞。因此,在函數(shù)內(nèi)部,a的值是原始值的拷貝,無論怎么修改,都不會影響到原始值。

但是,如果函數(shù)參數(shù)是復(fù)合類型的值(數(shù)組、對象、其他函數(shù)),傳遞方式是傳址傳遞(pass by reference)。也就是說,傳入函數(shù)的是原始值的地址,因此在函數(shù)內(nèi)部修改參數(shù),將會影響到原始值。

 var arr = [2, 5];
 function keith(Arr) {
 Arr[0] = 3;
 }
 keith(arr);
 console.log(arr[0]); //3

上面代碼中,傳入函數(shù)keith的是參數(shù)對象arr的地址。因此,在函數(shù)內(nèi)部修改arr第一個值,會影響到原始值。

注意,如果函數(shù)內(nèi)部修改的,不是參數(shù)對象的某個屬性,而是替換掉整個參數(shù),這時不會影響到原始值。

 var arr = [2, 3, 5];
 function keith(Arr) {
 Arr = [1, 2, 3];
 }
 keith(arr);
 console.log(arr); // [2,3,5]

上面代碼中,在函數(shù)keith內(nèi)部,參數(shù)對象arr被整個替換成另一個值。這時不會影響到原始值。這是因為,形式參數(shù)(Arr)與實際參數(shù)arr存在一個賦值關(guān)系。

1.5:同名參數(shù)

如果有同名參數(shù),則取最后面出現(xiàn)的那個值,如果未提供最后一個參數(shù)的值,則取值變成undefined。

 function keith(a, a) {
 return a;
 }
 console.log(keith(1, 3)); //3
 console.log(keith(1)); //undefined

如果想訪問同名參數(shù)中的第一個參數(shù),則使用arguments對象。

 function keith(a, a) {
 return arguments[0];
 }
 console.log(keith(2));  //2

1.6 arguments對象

JavaScript 中每個函數(shù)內(nèi)都能訪問一個特別變量 arguments。這個變量維護著所有傳遞到這個函數(shù)中的參數(shù)列表。

arguments 對象包含了函數(shù)運行時的所有參數(shù),arguments[0]就是第一個參數(shù),arguments[1]就是第二個參數(shù),以此類推。這個對象只有在函數(shù)體內(nèi)部,才可以使用。

可以訪問arguments對象的length屬性,判斷函數(shù)調(diào)用時到底帶幾個參數(shù)。

 function keith(a, b, c) {
 console.log(arguments[0]); //1
 console.log(arguments[2]); //3
 console.log(arguments.length); //4
 }
 keith(1, 2, 3, 4);

arguments對象與數(shù)組的關(guān)系

arguments 對象不是一個數(shù)組(Array)。 盡管在語法上它有數(shù)組相關(guān)的屬性 length,但它不從 Array.prototype 繼承,實際上它是一個類數(shù)組對象。因此,無法對 arguments 變量使用標準的數(shù)組方法,比如 push, pop 或者 slice。但是可以使用數(shù)組中的length屬性。

通常使用如下方法把arguments對象轉(zhuǎn)換為數(shù)組。

var arr = Array.prototype.slice.call(arguments);

2.閉包

2.1:閉包定義

要理解閉包,需要先理解全局作用域和局部作用域的區(qū)別。函數(shù)內(nèi)部可以訪問全局作用域下定義的全局變量,而函數(shù)外部卻無法訪問到函數(shù)內(nèi)部定義(局部作用域)的局部變量。

var a = 1;
function keith() {
 return a;
 var b = 2;
 }
 console.log(keith()); //1
 console.log(b); //ReferenceError: b is not defined

上面代碼中,全局變量a可以在函數(shù)keith內(nèi)部訪問??墒蔷植孔兞縝卻無法在函數(shù)外部訪問。

如果需要得到函數(shù)內(nèi)部的局部變量,只有通過在函數(shù)的內(nèi)部,再定義一個函數(shù)。

 function keith(){
 var a=1;
 function rascal(){
 return a;
 }
 return rascal;
 }
 var result=keith();
 console.log(result()); //1
 function keith(){
 var a=1;
 return function(){
 return a;
 };
 }
 var result=keith();
 console.log(result()) //1

上面代碼中,兩種寫法相同,唯一的區(qū)別是內(nèi)部函數(shù)是否是匿名函數(shù)。函數(shù)rascal就在函數(shù)keith內(nèi)部,這時keith內(nèi)部的所有局部變量,對rascal都是可見的。但是反過來就不行,rascal內(nèi)部的局部變量,對keith就是不可見的。這就是JavaScript語言特有的”鏈式作用域”結(jié)構(gòu)(chain scope),子對象會一級一級地向上尋找所有父對象的變量。所以,父對象的所有變量,對子對象都是可見的,反之則不成立。函數(shù)keith的返回值就是函數(shù)rascal,由于rascal可以讀取keith的內(nèi)部變量,所以就可以在外部獲得keith的內(nèi)部變量了。

閉包就是函數(shù)rascal,即能夠讀取其他函數(shù)內(nèi)部變量的函數(shù)。由于在JavaScript語言中,只有函數(shù)內(nèi)部的子函數(shù)才能讀取內(nèi)部變量,因此可以把閉包簡單理解成“定義在一個函數(shù)內(nèi)部的函數(shù)”。閉包最大的特點,就是它可以“記住”誕生的環(huán)境,比如rascal記住了它誕生的環(huán)境keith,所以從rascal可以得到keith的內(nèi)部變量。

閉包可以使得它誕生環(huán)境一直存在??聪旅嬉粋€例子,閉包使得內(nèi)部變量記住上一次調(diào)用時的運算結(jié)果。

 function keith(num) {
 return function() {
 return num++;
 };
 }
 var result = keith(2);
 console.log(result()) //2
 console.log(result()) //3
 console.log(result()) //4

上面代碼中,參數(shù)num其實就相當(dāng)于函數(shù)keith內(nèi)部定義的局部變量。通過閉包,num的狀態(tài)被保留了,每一次調(diào)用都是在上一次調(diào)用的基礎(chǔ)上進行計算。從中可以看到,閉包result使得函數(shù)keith的內(nèi)部環(huán)境,一直存在。

通過以上的例子,總結(jié)一下閉包的特點:

1:在一個函數(shù)內(nèi)部定義另外一個函數(shù),并且返回內(nèi)部函數(shù)或者立即執(zhí)行內(nèi)部函數(shù)。

2:內(nèi)部函數(shù)可以讀取外部函數(shù)定義的局部變量

3:讓局部變量始終保存在內(nèi)存中。也就是說,閉包可以使得它誕生環(huán)境一直存在。

閉包的另一個用處,是封裝對象的私有屬性和私有方法。

 function Keith(name) {
 var age;
 function setAge(n) {
 age = n;
 }
 function getAge() {
 return age;
 }
 return {
 name: name,
 setAge: setAge,
 getAge: getAge
 };
 }
 var person = Keith('keith');
 person.setAge(21);
 console.log(person.name); // 'keith'
 console.log(person.getAge()); //21

2.2:立即調(diào)用的函數(shù)表達式(IIFE)

通常情況下,只對匿名函數(shù)使用這種“立即執(zhí)行的函數(shù)表達式”。它的目的有兩個:一是不必為函數(shù)命名,避免了污染全局變量;二是IIFE內(nèi)部形成了一個單獨的作用域,可以封裝一些外部無法讀取的私有變量。

循環(huán)中的閉包

一個常見的錯誤出現(xiàn)在循環(huán)中使用閉包,假設(shè)我們需要在每次循環(huán)中調(diào)用循環(huán)序號

 for(var i=0;i<10;i++){
 setTimeout(function(){
 console.log(i); //10
 }, 1000)
 }

上面代碼中,不會符合我們的預(yù)期,輸出數(shù)字0-9。而是會輸出數(shù)字10十次。

當(dāng)匿名函數(shù)被調(diào)用的時候,匿名函數(shù)保持著對全局變量 i 的引用,也就是說會記住i循環(huán)時執(zhí)行的結(jié)果。此時for循環(huán)結(jié)束,i 的值被修改成了10。

為了得到想要的效果,避免引用錯誤,我們應(yīng)該使用IIFE來在每次循環(huán)中創(chuàng)建全局變量 i 的拷貝。

for(var i = 0; i < 10; i++) {
 (function(e) {
 setTimeout(function() {
 console.log(e); //1,2,3,....,10
 }, 1000);
 })(i);
 }

外部的匿名函數(shù)會立即執(zhí)行,并把 i 作為它的參數(shù),此時函數(shù)內(nèi) e 變量就擁有了 i 的一個拷貝。當(dāng)傳遞給 setTimeout 的匿名函數(shù)執(zhí)行時,它就擁有了對 e 的引用,而這個值是不會被循環(huán)改變的。

以上就是本文的全部內(nèi)容,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作能帶來一定的幫助,同時也希望多多支持腳本之家!

相關(guān)文章

  • Layui點擊圖片彈框預(yù)覽的實現(xiàn)方法

    Layui點擊圖片彈框預(yù)覽的實現(xiàn)方法

    今天小編就為大家分享一篇Layui點擊圖片彈框預(yù)覽的實現(xiàn)方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2019-09-09
  • javascript使用shift+click實現(xiàn)選擇和反選checkbox的方法

    javascript使用shift+click實現(xiàn)選擇和反選checkbox的方法

    這篇文章主要介紹了javascript使用shift+click實現(xiàn)選擇和反選checkbox的方法,涉及javascript針對鍵盤按鍵的判斷及checkbox的操作技巧,需要的朋友可以參考下
    2015-05-05
  • js類型轉(zhuǎn)換與引用類型詳解(Boolean_Number_String)

    js類型轉(zhuǎn)換與引用類型詳解(Boolean_Number_String)

    本篇文章主要是對js中的類型轉(zhuǎn)換與引用類型(Boolean_Number_String)進行了詳細的介紹,需要的朋友可以過來參考下,希望對大家有所幫助
    2014-03-03
  • 一次JavaScript正則的詭異經(jīng)歷記錄

    一次JavaScript正則的詭異經(jīng)歷記錄

    正則表達式是用于匹配字符串中字符組合的模式,下面這篇文章主要給大家介紹了一次JavaScript正則的詭異經(jīng)歷記錄,文中通過實例介紹的非常詳細,需要的朋友可以參考下
    2022-04-04
  • 如何遍歷對象的屬性?

    如何遍歷對象的屬性?

    如何遍歷對象的屬性?...
    2006-09-09
  • JS實戰(zhàn)例子之實現(xiàn)自動打字機動效

    JS實戰(zhàn)例子之實現(xiàn)自動打字機動效

    什么是打字機效果呢?打字機效果即為文字逐個輸出,實際上就是一種Web動畫,下面這篇文章主要給大家介紹了關(guān)于JS實戰(zhàn)例子之實現(xiàn)自動打字機動效的相關(guān)資料,需要的朋友可以參考下
    2023-01-01
  • javascript實現(xiàn)動態(tài)模態(tài)綁定grid過程代碼

    javascript實現(xiàn)動態(tài)模態(tài)綁定grid過程代碼

    這篇文章主要分享javascript實現(xiàn)動態(tài)模態(tài)綁定grid過程代碼,比較實用,需要的朋友可以參考下
    2014-09-09
  • 最新評論

    洮南市| 莱芜市| 铜川市| 广丰县| 子洲县| 隆德县| 阜南县| 从化市| 吉木萨尔县| 资源县| 花莲县| 鄂伦春自治旗| 蒙自县| 本溪| 北安市| 土默特左旗| 九江市| 都昌县| 枞阳县| 太谷县| 平乡县| 盱眙县| 克山县| 股票| 宣汉县| 英德市| 松阳县| 长治县| 瑞丽市| 平度市| 京山县| 海口市| 浮梁县| 耿马| 福清市| 祥云县| 桃源县| 宁蒗| 崇阳县| 固阳县| 信阳市|