- 看透JavaScript:原理、方法與實踐
- 韓路彪
- 2377字
- 2020-11-28 15:50:45
4.5 三種子類型
前面介紹過ES中的function共有三種用法:作為對象使用、處理業務以及創建object類型的實例對象。跟這三種用法相對應的有三種子類型,分別是對象的屬性、變量(包括參數)和創建出來的object類型實例對象的屬性。這三種子類型是相互獨立的,而且也很容易區分。但是,很多新手往往會因為不了解它們之間的區別,經常將它們混淆,混淆之后就會帶來不必要的錯誤,因此一定要將它們區分清楚。下面將分別對這三種類型進行介紹。
4.5.1 function作為對象來使用
這種情況下,function對象的子類型就是對象自己的屬性,這時通過點操作符“.”(或者方括號操作符)使用,例如下面的例子。
function book(){} book.price = 161.0; book.getPrice = function () { return this.price; } console.log(book.getPrice()); //161
在這種情況下,function是作為object類型的對象來使用的。上面的例子中首先定義了function類型的book對象,然后給它添加了price屬性和getPrice方法,這時就可以直接使用點操作符來對其進行操作了。
4.5.2 function用于處理業務
這種情況下,function的子類型就是自己定義的局部變量(包括參數),這時的變量是在方法被調用時通過變量作用域鏈來管理的。變量作用域鏈的相關內容前面已經介紹過,這里就不再重述了。
4.5.3 function用于創建對象
這種情況下,對應的子類型是使用function創建的實例對象的屬性,主要包括在function中通過this添加的屬性,以及創建完成之后實例對象自己添加的屬性。另外,還可以調用function的prototype屬性對象所包含的屬性,例如前面用過的Car的例子。
function Car(color, displacement){ this.color = color; this.displacement = displacement;
} Car.prototype.logMessage = function(){ console.log(this.color+", "+this.displacement); } var car = new Car("black", "2.4T");
這個例子中創建的car對象就包含有color和displacement兩個屬性,而且還可以調用Car.prototype的logMessage方法。當然,創建完之后還可以使用點操作符給創建的car對象添加或者修改屬性,也可以使用delete刪除其中的屬性,例如下面的例子。
function Car(color, displacement){ this.color = color; this.displacement = displacement; } Car.prototype.logMessage = function(){ console.log(this.color+", "+this.displacement); } var car = new Car("black", "2.4T"); car.logColor = function () { console.log(this.color); } car.logColor(); //black car.color = "red"; car.logColor(); //red delete car.color; car.logColor(); //undefined
這個例子中,在創建完car對象后又給它添加了logColor方法,可以打印出car的color屬性。添加完logColor方法后直接調用就可以打印出car原來的color屬性值(black)。然后,將其修改為red,再打印就打印出了red。最后,使用delete刪除car的color屬性,這時再調用logColor方法就會打印出undefined。
4.5.4 三種子類型的關系
function的三種子類型是相互獨立的,它們只能在自己所對應的環境中使用而不能相互調用,例如下面的例子。
function log(msg){ console.log(msg); } function Bird(){ var name = "kitty"; this.type = "pigeon"; this.getName = function () { return this.name; } }
Bird.color="white"; Bird.getType = function () { return this.type; } Bird.prototype.getColor = function () { return this.color; } var bird = new Bird(); log(bird.getColor()); //undefined log(bird.getName()); //undefined log(Bird.getType()); //undefined
這個例子中的最后三條語句都會打印出undefined,下面分析其中的原因。
Bird作為對象時包含color和getType兩個屬性,作為處理業務的函數時包含一個名為name的局部變量,創建的實例對象bird具有type和getName兩個屬性,而且還可以調用Bird.prototype中的getColor屬性,getColor也可以看作bird的屬性,如表4-2所示。
表4-2 Bird的用法及子類型

每種用法中所定義的方法只能調用相應用法所對應的屬性,而不能交叉調用,從表4-2中的對應關系可以看出,getName、getColor和getType三個方法都獲取不到對應的值,所以它們都會輸出undefined。
另外,getName和getColor是bird的屬性方法,getType是Bird的屬性方法,如果用Bird對象調用getName或getColor方法或者使用bird對象調用getType方法都會拋出找不到方法的錯誤。
除了三種子類型不可以相互調用之外,還有一種情況也非常容易被誤解,那就是對象的屬性并沒有繼承的關系,例如下面的例子。
function obj(){} obj.v = 1; obj.func = { logV : function(){ console.log(this.v); } }; obj.func.logV();
這個例子中的obj是作為對象使用的,obj有一個屬性v和一個對象屬性func, func對象中又有一個logV方法,logV方法用于打印對象的v屬性。這里需要特別注意,logV方法打印的是func對象的v屬性,但是func對象并沒有v屬性,所以最后會打印出undefined。在這個例子中,雖然obj對象中包含v屬性,但是由于屬性不可以繼承,所以obj的func屬性對象中的方法不可以使用obj中的屬性v。這一點各位讀者一定要記住,并且不要和prototype的繼承以及變量作用域鏈相混淆。
多知道點
JS中的“公有屬性”“私有屬性”和“靜態屬性”
在有些資料中,可能會看到類似“公有屬性”“私有屬性”以及“靜態屬性”等名稱,其實這些是基于類的語言(例如Java、C++等)中的一些概念,由于JS并不是基于類的而是基于對象的語言,因此JS本身并沒有這些概念。所謂的“公有屬性”,一般指使用function對象創建出來的object實例對象所擁有的屬性,“私有屬性”一般指function的內部變量,“靜態屬性”一般指function對象自己的屬性。這跟基于類的語言的公有屬性、私有屬性的含義并不相同,而且這種叫法很容易讓人產生誤解,其實這是三種不同用法分別對應的三種不同的子類型。
當然,學習JS的目的是為了使用它來實現用戶需要的功能,而不是為了做理論上的研究,因此,如果大家習慣這種叫法也無所謂,關鍵是要理解清楚其本質的含義而不要混淆。
4.5.5 關聯三種子類型
ES中的三種子類型本來是相互獨立、各有各的使用環境的,但是,在一些情況下需要操作不屬于自己所對應環境的子類型,這時就需要使用一些技巧來實現了。
為了描述方便,本書將function作為對象使用時記作O(Object),作為函數使用時記作F(Function),創建出來的對象實例記作I(Instance),它們所對應的子類型分別記作op(object property)、v(variables)和ip(instance property),它們之間的調用方法如表4-3所示。
表4-3 function對象的用法及子類型

表4-3的縱向表頭表示function對象不同的用法,橫向表頭表示三種子類型,表格的主體表示在function相應用法中調用各種子類型的方法。因為function創建的實例對象在創建之前還不存在,所以function作為方法(F)和作為對象(O)使用時無法調用function創建的實例對象的屬性(ip)。調用參數可以在函數中將變量關聯到相應屬性,調用function作為對象(O)時的屬性可以直接使用function對象來調用,我們來看下面的例子。
function log(msg){ console.log(msg); } function Bird(){ var name = "kitty"; var type = "pigeon"; //將局部變量name關聯到新創建的對象的getName、setName屬性方法 this.getName = function () { return name; } this.setName = function (n) { name = n; } //將局部變量type關聯到Bird對象的getType屬性方法 Bird.getType = function () { return type; } //在業務處理中調用Bird對象的color屬性 log(Bird.color); //white, F調用op } Bird.color="white"; //在創建出的實例對象中調用Bird對象的color屬性 Bird.prototype.getColor = function () { return Bird.color; } var bird = new Bird(); log(bird.getColor()); //white, I調用op log(bird.getName()); //kitty, I調用v log(Bird.getType()); //pigeon, O調用v bird.setName("petter"); //I調用v log(bird.getName()); //petter, I調用v
上述代碼中已經添加了詳細的注釋,我們就不再多加解釋了。通過這個例子,你應該清楚三種子類型在不同環境(用法)中交叉調用的方式了。
- Advanced Machine Learning with Python
- Spring Cloud Alibaba核心技術與實戰案例
- Java EE 6 企業級應用開發教程
- 動手玩轉Scratch3.0編程:人工智能科創教育指南
- Learning Informatica PowerCenter 10.x(Second Edition)
- C語言從入門到精通(第4版)
- Hands-On GPU:Accelerated Computer Vision with OpenCV and CUDA
- Learning ELK Stack
- 量化金融R語言高級教程
- Mastering JavaScript Design Patterns(Second Edition)
- C# 8.0核心技術指南(原書第8版)
- Managing Microsoft Hybrid Clouds
- 零基礎學Scratch 3.0編程
- Drupal Search Engine Optimization
- TypeScript全棧開發