摘要:這樣每個實例獨享自己的屬性,并和其他同類型的實例共享方法構造函數原型以上這種方式定義的類型,可以通過來判斷一個實例是否是類型的實際上是通過實例的原型鏈來判斷一個對象是否某個類型的實例的,具體的細節后面會詳細介紹。
JavaScript面向對象編程 如何定義自定義類型
首先需要明確,JavaScript并不是傳統意義上的OO語言,它并沒有class的概念,
而是包含了另一套異常強大的原型機制。它的類型體系、繼承體系都建立在原型基礎之上。
為了迎合傳統的OO開發者,JavaScript語言的設計者通過這套原型體系模擬了傳統面向對象語言的編碼風格。
簡單來說,建立一個自定義類型只需要編寫類型的構造函數即可:
javascriptfunction Person(name, age) { this.name = name; this.age = age; } // 實例化 Person 類型 Person person = new Person("John", 34);
Person構造函數與一般的函數沒有任何區別,只是調用方式不太一樣,
通過使用new關鍵字,改變了一般函數調用的行為,有點類似于下面這樣:
1. Object obj = new Object();
2. Person.call(obj, "John", 34);
3. obj.__proto__ = Person.prototype;
4. return obj;
序號2中call函數調用的作用是改變執行Person函數時的this為obj,
序號3的作用是設置新建實例的原型(一個實例的__proto__屬性是這個實例的原型)。
記住,所有函數(如這里的Person)的prototype屬性默認都是一個Object實例。
所以序號3執行后,person.__proto__正是Person.prototype,
這解釋了為什么所有的引用類型都派生自Object。
除此之外,從上面的介紹還應該意識到Person的所有實例的__proto__屬性都是Person.prototype。
上面定義的屬性都是實例的屬性,也可以直接為某個類型添加類型的屬性(記住,方法也是一個對象),
而這個屬性無法通過類型的實例訪問到,如下面的代碼:
javascriptPerson.country = "Canada";
另一個例子是ECMAScript 5引入的Object類型的getPrototypeOf方法,它可以獲得一個實例的原型變量:
javascriptObject.getPrototypeOf = function(instance) { // some code.. };
如果想定義同一個類型所有實例共享的屬性(比如方法),可以定義在類型的原型中:
javascriptPerson.prototype.logName = function() { console.log(this.name); };
需要注意,通過Person的實例只能讀取原型中的屬性,而不能重寫;
如果嘗試重寫,實際上是在實例中定義了一個同名的屬性,從而屏蔽了原型中的屬性:
javascript// 并沒有改變 Person.prototype.logName的值 person.logName = function() { // some code.. }
造成屏蔽的原因是當使用對象.屬性時,
是從對象開始查找屬性,如果沒有找到再在其原型中查找,
如果還沒有找到,再查找其原型的原型,以此類推在原型鏈上不斷向上查找,
第一次查找到屬性后查找過程就結束了。
如果想恢復被屏蔽的原型屬性,可以使用delete操作符:
javascriptdelete person.logName;
最佳的實踐是將實例的屬性定義在構造函數中,將方法定義在原型中。
這樣每個實例獨享自己的屬性,并和其他同類型的實例共享方法:
javascript// 構造函數 function Person(name, age) { this.name = name; this.age = age; } // 原型 Person.prototype.logName = function() { console.log(this.name); }
以上這種方式定義的Person類型,可以通過instanceof來判斷一個實例是否是Person類型的:
javascriptPerson person = new Person("John", 34); console.log(john instanceof Person); // true
實際上instanceof是通過實例的原型鏈來判斷一個對象是否某個類型的實例的,具體的細節后面會詳細介紹。
這里首先介紹一下如何獲得一個實例的原型對象:
1. isPrototypeOf()你可以判斷一個對象是否在另一個對象的原型鏈上出現:
Person person = new Person("John", 34); console.log(Person.prototype.isPrototypeOf(person)); // true
2. Object.getPrototypeOf()你可以得到一個對象的原型。
這個方法是ECMAScript 5引入的,某些IE瀏覽器并不支持:
// get the prototype of person instance Object.getPrototypeOf(person);
3. __proto__JavaScript中每個對象都有一個指向其原型的內部屬性,
在某些瀏覽器(如Chrome)中可以使用它們。
既然可以將屬性定義在實例本身或它的原型鏈中,那么可不可以判斷某個屬性具體是在哪里定義呢?當然可以:
1. hasOwnProperty()如果屬性在實例本身出現,則返回true:
console.log(person.hasOwnProperty("name")); // true console.log(person.hasOwnProperty("logName")); // false
2. in操作符,如果屬性在實例或其原型鏈中出現,則返回true
alert("logName" in person); // true
利用in操作符,我們還可以枚舉出一個實例和它原型鏈中所有可枚舉的屬性:
for (var propertyName in person) { // log all property name and its value console.log(propertyName + " " + person[propertyName]); }
最后來介紹一下instanceof的原理,假設執行下面的代碼:
javascriptfunction Person(name, age) { this.name = name; this.age = age; } function Student(name, age, school) { Person.call(this, name, age); this.school = school; } // Student 繼承了 Person Student.prototype = new Person(); Student student = new Student("Adam", 30, " School");
關于繼承的細節之后再詳細討論,這里只需要明確我們將Student類型的原型賦值為一個Person實例。
此時student的原型鏈可以表示為:
student.__proto__ ==> Person實例(假設為person)
person.__proto__ ==> Object實例(假設為object)
object.__proto__ ==> null(到頂了)
也許會有人疑惑person的原型為什么是Object實例,
這是因為所有的方法(比如這里的Person、Student)的prototype屬性默認都是一個Object類型的實例,
這也證明了為什么所有的內置類型和自定義類型無一例外全部都派生自Object類型。
當執行下面的代碼時:
javascriptconsole.log(student instanceof Student); // true
實際上是判斷Student.prototype是否在student的原型鏈中出現,如果出現了則返回true。
這里Student.prototype是person,是student的原型,所以返回true。
再看:
javascriptconsole.log(student instanceof Person); // true
同理因為存在Person.prototype === student.__proto__.__proto__,所以返回true。
看到這里相信你應該對prototype和__proto__的關系有了比較清楚的理解了。
它們之間的關系可以總結為:
繼承__proto__:
__proto__ is the actual object that is used in the lookup chain to resolve methods.
It is a property that all objects have.
This is the property which is used by the JavaScript engine for inheritance.
According to ECMA specifications it is supposed to be an internal property,
however most vendors allow it to be accessed and modified.
prototype:
prototype is a property belonging only to functions.
It is used to build __proto__ when the function
happens to be used as a constructor with the new keyword.
可以說JavaScript是Python的另一個極端
——There"s always more than one way to do it.
實現繼承也不例外,不同的實現模式有不同的使用場景,
各有優勢和不足,這里只介紹一個最常被使用的模式——組合繼承模式,直接看例子:
javascript/* * 基類,定義屬性 */ function Person(name, age) { this.name = name; this.age = age; } /* * 基類,定義方法 */ Person.prototype.selfIntroduce = function () { console.log("name: " + this.name); console.log("age: " + this.age); } /* * 子類,定義子類的屬性 */ function Student(name, age, school) { // 調用基類的構造函數 Person.call(this, name, age); this.school = school; } // 使子類繼承基類 Student.prototype = new Person(); /* * 定義子類的方法 */ Student.prototype.goToSchool = function() { // some code.. } /* * 擴展并調用了超類的方法 */ Student.prototype.selfIntroduce = function () { Student.prototype.__proto__.selfIntroduce.call(this); console.log("school: " + this.school); } var student = new Student("John", 22, "My School"); student.selfIntroduce();
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/85833.html
摘要:我們目前正處于一個新興的區塊鏈開發行業中。,一種在以太坊開發人員中流行的新的簡單編程語言,因為它是用于開發以太坊智能合約的語言。它是全球至少萬開發人員使用的世界上最流行的編程語言之一。以太坊,主要是針對工程師使用進行區塊鏈以太坊開發的詳解。 我們目前正處于一個新興的區塊鏈開發行業中。區塊鏈技術處于初期階段,然而這種顛覆性技術已經成功地風靡全球,并且最近經歷了一場與眾不同的繁榮。由于許多...
摘要:入門,第一個這是一門很新的語言,年前后正式公布,算起來是比較年輕的編程語言了,更重要的是它是面向程序員的函數式編程語言,它的代碼運行在之上。它通過編輯類工具,帶來了先進的編輯體驗,增強了語言服務。 showImg(https://segmentfault.com/img/bV1xdq?w=900&h=385); 新的一年不知不覺已經到來了,總結過去的 2017,相信小伙們一定有很多收獲...
摘要:入門,第一個這是一門很新的語言,年前后正式公布,算起來是比較年輕的編程語言了,更重要的是它是面向程序員的函數式編程語言,它的代碼運行在之上。它通過編輯類工具,帶來了先進的編輯體驗,增強了語言服務。 showImg(https://segmentfault.com/img/bV1xdq?w=900&h=385); 新的一年不知不覺已經到來了,總結過去的 2017,相信小伙們一定有很多收獲...
摘要:入門,第一個這是一門很新的語言,年前后正式公布,算起來是比較年輕的編程語言了,更重要的是它是面向程序員的函數式編程語言,它的代碼運行在之上。它通過編輯類工具,帶來了先進的編輯體驗,增強了語言服務。 showImg(https://segmentfault.com/img/bV1xdq?w=900&h=385); 新的一年不知不覺已經到來了,總結過去的 2017,相信小伙們一定有很多收獲...
閱讀 3844·2021-09-06 15:00
閱讀 2177·2019-08-30 15:53
閱讀 3282·2019-08-23 16:44
閱讀 950·2019-08-23 15:19
閱讀 1397·2019-08-23 12:27
閱讀 4196·2019-08-23 11:30
閱讀 589·2019-08-23 10:33
閱讀 374·2019-08-22 16:05