摘要:在中有四種調用模式方法調用模式函數調用模式構造器調用模式和調用模式。構造一個的實例目標就是結合前綴來調用的函數,被稱為構造函數。
正文久違的博文,貌似距離我上一篇也算是有些年歲(加班的日子真是度日如年啊T^T)了,所以呢,現在是時候回歸正道了,還是歡迎各位IT道友多多交(tu)流(cao)哈!
首先,說到 JavaScript 函數,我們就要先理解下一些很可能被忽視的小概念:函數對象 和 函數字面量。
函數對象我們知道,在JavaScript中 函數 就是 對象。對象是“名/值”的集合,并擁有一個連到原型對象的隱藏連接。其中,對象字面量產生的對象連接到 Object.prototype,而函數對象連接到 Function.prototype (注:該原型對象本身連接到 Object.prototype )。每個函數在創建時,都附有兩個附加的隱藏屬性: 函數的上下文 和 實現函數行為的代碼 。
另外,每個函數對象在創建時,也隨帶有一個 prototype 屬性,他的值是一個擁有 constructor 屬性,而且其值即為該函數的對象。這和隱藏連接到 Function.prototype 完全不同,而這個令人費解的構造過程的意義,我先埋個坑,以后在繼承篇的相關文章中再來填好了。
因為函數是對象,所以它可以像其他值一樣被使用,比如,可以存放在變量、對象和數組中,可以被當做參數傳遞給其他函數,也可以在函數中返回函數,而且,更因為函數是對象,因此 函數也可以擁有方法。
函數字面量函數對象可以通過函數字面量來創建:
var add = function (a, b) { return a + b; }
函數字面量包括四個部分:
第一部分,是 保留字 function。
第二部分,是 函數名,它可以省略不寫。函數可以用它的名字來 遞歸 地調用自己。此名字也能被調試器和開發工具來識別函數(如:FireBug、Chrome console 等)。如果沒有給函數命名,比如上面的例子,它會認為是 匿名函數。
第三部分,是包圍在圓括號中的一組 參數,其中每個參數之間用逗號隔開,這些參數(也稱形式參數,即形參)將被定義為函數中的變量,但是,它們不像普通變量那樣被初始化為 undefined,而是在該函數被調用時初始化為實際提供的參數的值(也稱實際參數,即實參)。
第四部分,是包圍在花括號中的一組語句,這些語句就是 函數主體,它們在函數被調用時執行。
函數字面量可以出現在任何允許表達式出現的地方。當然,函數也可以嵌套在其他函數中,這樣的話,一個內部函數不僅可以訪問自己的參數和變量,同時也可以方便地訪問它被嵌套的那個外部函數的參數和變量。
通過函數字面量創建的函數對象包含一個連到外部上下文的連接,這被稱為 閉包。它是 JavaScript 強大表現力的根基。而關于閉包的詳細原理和使用方法,以后會發布一些專門的文章進行說明,敬請期待 ( ^_^ ) ~~
調用葷割線之后,接下來就是本文的重頭戲 -- 關鍵字 this 上場。眾所周知,這個老(son)伙(of)計(bit ch)可以說是JavaScript中的一大深坑,至于如何華麗麗地跳出這個坑,還請各位搬好板凳,備好瓜子,聽我慢慢道來。
當我們調用一個函數時,將暫停當前函數的執行,將傳遞控制器與參數給新函數。然而,除了聲明時定義的形參,每個函數接收兩個附加的參數:this 和 arguments。參數 this 在面向對象編程中是非常重要的,它的值取決于調用的模式。在JavaScript中有四種調用模式:方法調用模式、函數調用模式、構造器調用模式和apply調用模式。
調用運算符,就是跟在任何一個函數值的表達式之后的一對圓括號,它可以包含零個或者多個用逗號隔開的表達式,每個表達式產生一個參數值,每個參數值被賦予函數聲明時定義的形式參數名,而當實際參數(arguments)的個數與形式參數(parameters)的個數不匹配時,不會導致運行時報錯。比如說,如果實參值過多,超出的參數值將被忽略,如果實參值過少,缺失的值將會被替換為 undefined。并且,對參數值不會進行類型檢查,即任何類型的值都可以被傳遞給參數。
方法調用模式當一個函數被保存為對象的一個屬性時,我們稱之為方法。當一個方法被調用時,this 會被綁定到該對象,即this就是該對象。如果一個調用表達式包含一個屬性存取表達式(即一個 . 點表達式 或者 [subscript] 下標表達式),那么它將被當做一個方法來調用。
// 創建 myObject。它有一個 value 屬性 和一個 increment 方法 // increment 方法接收一個可選的參數,若參數不是數字型,則默認使用數字 1。 var myObject = { value: 0, increment: function (inc) { this.value += typeof inc === "number" ? inc : 1; } }; // 不傳參 myObject.increment(); console.log(myObject.value); // 1 // 傳非數字型 myObject.increment("a"); console.log(myObject.value); // 2 // 傳數字型 myObject.increment(2); console.log(myObject.value); // 4
方法可以使用 this 去訪問對象,所以它能從對象中取值或者修改該對象。this 到對象的綁定,發生在調用的時候。這個“超級”遲綁定(very late binding)使得函數可以對 this 高度復用。通過 this 可取得它們所屬對象的上下文的方法,稱為公共方法。
函數調用模式當一個函數并非一個對象的屬性(即方法)時,那么它將被當做一個函數來調用。
function add (a, b) { return a + b; } var sum = add(3,4); console.log(sum); // 7
當函數以此模式調用時,this 被綁定到全局對象(即 window 對象),這是語言設計上的一個重大的錯誤啊!!如果設計正確的話,當內部函數被調用時,this 應該仍然綁定到外部函數的 this 變量才對。這個錯誤設計的后果是,方法不能利用內部函數來幫助他工作,因為內部函數的 this 被綁定了錯誤的值,或者說綁定了我們不想要的值,所以不能共享該方法對于對象的訪問權。不過,幸運的是,有一個很容易的解決方案:如果該方法定義一個變量并給它賦值為this,那么內部函數就可以通過那個變量訪問到 this,而這個變量我們通常命名為 that。
function add (a, b) { return a + b; } // 給 myObject 增加一個double方法 myObject.double = function () { var that = this; console.log(that); var helper = function () { that.value = add(that.value, that.value); }; // 以函數的形式調用 helper helper(); }; // 以方法的形式調用 double myObject.double(); console.log(myObject.getValue()); // 8構造器調用模式
JavaScript 是一門基于原型繼承的語言,這就意味著對象可以直接從其它對象繼承屬性或方法,而該語言也是無類別的。
如果在一個函數前面加上一個 new 來調用,那么將會創建一個隱藏連接到該函數的 prototype 成員的新對象(或者稱之為該對象的實例),同時,this 將會被綁定到那個新對象(實例)上。
然而,new 前綴也會改變 return 語句的行為,這個我們以后再做詳細解析。
Quo.prototype.get_status = function () { return this.status; }; // 構造一個 Quo 的實例 var myQuo = new Quo("success"); console.log(myQuo.get_status()); // success
目標就是結合 new 前綴來調用的函數,被稱為構造函數。按照約定,它們保存在以首字母大寫命名的變量里。如果調用構造函數時,沒有在前面加上 new,可能會發生非常糟糕的事情(如,實例無法調用該原型對象的方法,等),這樣既沒有編譯時警告,也沒有運行時警告,所以加 new 前綴和大寫約定,是非常、非常、非常重要的(重要話,說三遍)。
然并卵,實際使用中,我們并不推薦這種形式的構造器函數,以后將在JavaScript的繼承篇為各位提供更好的解決方案。
Apply 調用模式因為 JavaScript 是一門函數式的面向對象的編程語言,所以函數可以擁有方法。
apply 方法讓我們構建一個參數數組并用其去調用函數,它也允許我們選擇 this 的取值。apply 方法接收兩個參數,第一個是將被綁定給 this 的值,第二個就是一個參數數組。
// 1.構造一個帶有兩個數字的數組,將之相加 var arr = [3, 4]; var sum = add.apply(null, arr); console.log(sum); // 7 // 2.構造一個含有 status 成員的對象 var statusObj = { status: "right" }; var status = Quo.prototype.get_status.apply(statusObj); console.log(status); // right
這里第二個例子的代碼,通過了 apply 方法替換 Quo 對象中的 this 指針。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/85982.html
摘要:前言由于最近的項目用到了一些的代碼,所以我帶著好奇心,認真閱讀了這本書,粗略地了解語言的基本結構和特性,對于一些不熟悉的新概念,以記錄的形式加強印象,也是對學習的反思總結。 前言 由于最近的項目用到了一些js的代碼,所以我帶著好奇心,認真閱讀了這本書,粗略地了解js語言的基本結構和特性,對于一些不熟悉的新概念,以記錄的形式加強印象,也是對學習的反思總結。 一、字面量(literals...
摘要:對之前看高級程序設計時沒有注意到的一些知識點,結合本書做以補充語法注釋源于的型既可以出現在字符串字面量中,也可能出現在正則表達式字面量中,如故一般建議使用型注釋保留字語句變量參數屬性名運算符和標記等標識符不允許使用保留字,此外在對象字面量中 對之前看《JavaScript高級程序設計》時沒有注意到的一些知識點,結合本書做以補充 語法 注釋 源于PL/I的/* */型既可以出現在字符串字...
摘要:但采用構造器調用模式,即是使用了前綴去調用一個函數時,函數執行的方式會改變。對象包含構造器需要構造一個新的實例的所有信息。構造器的變量和內部函數變成了該實例的私有成員。 JavaScript 是一門弱類型語言,從不需要類型轉換。對象繼承關系變得無關緊要。對于一個對象來說重要的時它能夠做什么,而不是它從哪里來。 閱讀《javascript語言精粹》筆記! 偽類 js的原型存...
摘要:調用函數時,被綁定到全局對象。如果使用構造器調用有前綴,且返回不是一個對象,則返回該新對象。閉包會導致原有作用域鏈不釋放,造成內存泄漏。當采用構造器調用模式,函數執行的方式會被修改。 內容 ECMAScript核心語法結構:1.語法2.對象3.函數4.繼承5.數組6.正則表達式7.方法8.附錄A-毒瘤9.附錄B-糟粕 一、語法 1.類型、值和變量 1) 類型:區分數據類型 在JS中使...
摘要:調用函數時,被綁定到全局對象。如果使用構造器調用有前綴,且返回不是一個對象,則返回該新對象。閉包會導致原有作用域鏈不釋放,造成內存泄漏。當采用構造器調用模式,函數執行的方式會被修改。 內容 ECMAScript核心語法結構:1.語法2.對象3.函數4.繼承5.數組6.正則表達式7.方法8.附錄A-毒瘤9.附錄B-糟粕 一、語法 1.類型、值和變量 1) 類型:區分數據類型 在JS中使...
閱讀 3723·2021-11-24 09:39
閱讀 1870·2021-11-16 11:45
閱讀 616·2021-11-16 11:45
閱讀 1029·2021-10-11 10:58
閱讀 2475·2021-09-09 11:51
閱讀 1941·2019-08-30 15:54
閱讀 687·2019-08-29 13:13
閱讀 3466·2019-08-26 12:18