摘要:在基于原型的面向對象方式中,對象則是依靠構造函數和原型構造出來的。來看下面的例子優點與單純使用構造函數不一樣,原型對象中的方法不會在實例中重新創建一次,節約內存。
我們所熟知的面向對象語言如 C++、Java 都有類的的概念,類是實例的類型模板,比如Student表示學生這種類型,而不表示任何具體的某個學生,而實例就是根據這個類型創建的一個具體的對象,比如zhangsan、lisi,由類生成對象體現了抽象模板到具體化的過程,這叫做基于類的面向對象方式,而 JavaScript 沒有類的概念,是基于原型的面向對象方式(雖然 Es6 增加了 class,實質是對原型方式的封裝)。總結起來就是以下兩點:
在基于類的面向對象方式中,對象(object)依靠類(class)來產生。
在基于原型的面向對象方式中,對象(object)則是依靠構造函數(constructor)和原型(prototype)構造出來的。
面向對象語言的第一個特性毫無疑問是封裝,在 JS 中,封裝的過程就是把一些屬性和方法放到對象中“包裹”起來,那么我們要怎么去封裝屬性和方法,或者說怎么去創建對象呢(后文統一說創建對象)?下面用逐步推進的方式闡述:
對象字面量 --> 工廠模式 --> 構造函數 --> 原型模式 --> 構造函數+原型模式對象字面量
JS中創建對象最原始的方式有兩種:
對象字面量
var person = { name: "leon", age: "20", greeting: function() { alert("Hi!"); } }
為Object實例添加屬性方法
var person = new Object(); person.name = "leon"; person.age = "20"; person.greeting = function() { alert("Hi!"); };
優點:代碼簡單
缺點: 創建多個對象會產生大量的代碼,編寫麻煩,且并沒有實例與原型的概念。
解決辦法:工廠模式。
工廠模式工廠模式是編程領域一種廣為人知的設計模式,它抽象了創建具體對象的過程。JS 中創建一個函數,把創建新對象、添加對象屬性、返回對象的過程放到這個函數中,用戶只需調用函數來生成對象而無需關注對象創建細節,這叫工廠模式:
function createPerson(name, age) { var person = new Object(); person.name = name; person.age = age; person.greeting = function() { alert("Hi!"); }; return person; } var person1 = createPerson("leon", "20");
優點:工廠模式解決了對象字面量創建對象代碼重復問題,創建相似對象可以使用同一API。
缺點:因為是調用函創建對象,無法識別對象的類型。
解決辦法:構造函數
構造函數JS 中構造函數與其他函數的唯一區別,就在于調用它的方式不同。任何函數,只要通過new 操作符來調用,那它就可以作為構造函數。來看下面的例子:
function Person(name, age) { this.name = name; this.age = age; this.greeting = function() { alert("Hi!"); }; // return this; } var person1 = new Person("leon", "20"); var person2 = new Person("jack", "21");
通過構造函數new一個實例經歷了四步:
創建一個新對象;
將構造函數內的this綁定到新對象上;
為新對象添加屬性和方法;
返回新對象(JS 引擎會默認添加 return this;)。
而通過構造函數創建的對象都有一個constructor屬性,它是一個指向構造函數本身的指針,因此就可以檢測對象的類型啦。:
alert(person1.constructor === Person) //true alert(person1 instanceof Person) // true
但是仍然存在問題:
alert(person1.greeting == person2.greeting) //false
同一個構造函數中定義了greeting(),而不同實例上的同名函數卻是不相等的,意味著這兩個同名函數的內存空間不一致,也就是構造函數中的方法要在每個實例上重新創建一次。這顯然是不劃算的。
優點:解決了類似對象創建問題,且可以檢測對象類型。
缺點:構造函數方法要在每個實例上新建一次。
解決辦法:原型模式。
原型模式終于講到了原型模式,JS 中每個構造函數都有一個prototype屬性,這個屬性是一個指針,指向原型對象,而這個原型對象包含了這個構造函數所有實例共享的屬性和方法。而實例對象中有一個proto屬性,它指向原型對象,也就是構造函數.prototype == 原型對象 == 對象._proto_,那么對象就可以獲取到原型對象中的屬性和方法啦。同時,所有對象中都有一個constructor屬性,原型對象的constructor指向其對應的構造函數。
使用原型,就意味著我們可以把希望實例共享的屬性和方法放到原型對象中去,而不是放在構造函數中,這樣每一次通過構造函數new一個實例,原型對象中定義的方法都不會重新創建一次。來看下面的例子:
function Person() { } Person.prototype.name = "leon"; Person.prototype.age = "20"; Person.prototype.greeting = function() { alert("Hi!"); }; var person1 = new Person(); var person2 = new Person(); alert(person1.name); //"leon" alert(person2.name); //"leon" alert(person1.greeting == person2.greeting); //true
優點:與單純使用構造函數不一樣,原型對象中的方法不會在實例中重新創建一次,節約內存。
缺點:使用空構造函數,實例 person1 和 person2 的 name都一樣了,我們顯然不希望所有實例屬性方法都一樣,它們還是要有自己獨有的屬性方法。并且如果原型中對象中有引用類型值,實例中獲得的都是該值的引用,意味著一個實例修改了這個值,其他實例中的值都會相應改變。
解決辦法:構造函數+原型模式組合使用。
另外 JS 中還定義了一些與原型相關的屬性,這里羅列一下:
Object.getPrototypeOf(),取得實例的原型對象。
Object.getPrototypeOf(person1);
isPrototypeOf(),判斷是不是一個實例的原型對象。
Person.prototype.isPrototypeOf(person1);
hasOwnProperty(),檢測一個屬性是否存在于實例中
person1.hasOwnProperty("name");
in,判斷一個屬性是否存在于實例和原型中。
"name" in person1;構造函數+原型模式
最后一種方式就是組合使用構造函數和原型模式,構造函數用于定義實例屬性,而共享屬性和方法定義在原型對象中。這樣每個實例都有自己獨有的屬性,同時又有對共享方法的引用,節省內存。
function Person(name, age) { this.name = name; this.age = age; } Person.prototype = { constructor: Person, nationality: "China", greeting: function() { alert(this.name); } } var person1 = new Person("leon", "20"); var person2 = new Person("jack", "21"); alert(person1.greeting == person2.greeting) //true
上面代碼中用對象字面量的形式重寫了原型對象,這樣相當于創建了一個新的對象,那么它的constructor屬性就會指向Object,這里為了讓它繼續指向構造函數,顯示的寫上了constructor: Person
這種構造函數與原型模式混成的模式,是目前在 JS 中使用最為廣泛的一種創建對象的方法。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/96489.html
摘要:很多情況下,通常一個人類,即創建了一個具體的對象。對象就是數據,對象本身不包含方法。類是相似對象的描述,稱為類的定義,是該類對象的藍圖或原型。在中,對象通過對類的實體化形成的對象。一類的對象抽取出來。注意中,對象一定是通過類的實例化來的。 showImg(https://segmentfault.com/img/bVTJ3H?w=900&h=385); 馬上就要到七夕了,離年底老媽老爸...
摘要:很多情況下,通常一個人類,即創建了一個具體的對象。對象就是數據,對象本身不包含方法。類是相似對象的描述,稱為類的定義,是該類對象的藍圖或原型。在中,對象通過對類的實體化形成的對象。一類的對象抽取出來。注意中,對象一定是通過類的實例化來的。 showImg(https://segmentfault.com/img/bVTJ3H?w=900&h=385); 馬上就要到七夕了,離年底老媽老爸...
摘要:很多情況下,通常一個人類,即創建了一個具體的對象。對象就是數據,對象本身不包含方法。類是相似對象的描述,稱為類的定義,是該類對象的藍圖或原型。在中,對象通過對類的實體化形成的對象。一類的對象抽取出來。注意中,對象一定是通過類的實例化來的。 showImg(https://segmentfault.com/img/bVTJ3H?w=900&h=385); 馬上就要到七夕了,離年底老媽老爸...
摘要:更形象的我們還可以將面向對象理解為一種宗教信仰。這就導致面向對象教的程序員們在寫時就很難受。所以為了滿足信仰面向對象教的需求通過構造函數的形式模擬了偽類。這個套路的核心就是類那么里沒有類所以其實是通過構造函數來模擬的偽類。 JS面向對象之一 【概述】 在學習JS的面向對象之前,我們應該先自問這樣幾個問題: 面向對象是什么意思? 學習面向對象的核心是什么? 為什么要學習面向對象?(它的...
摘要:為什么要面向對象你需要知道的面向對象面向對象并不是針對一種特定的語言,而是一種編程范式。后端傳遞過來顯示工人完成狀態的字段代表未完成,代表已完成。其實這就是如何消除代碼副作用的問題將副作用隔離。 為什么要面向對象? 你需要知道的面向對象 面向對象并不是針對一種特定的語言,而是一種編程范式。但是每種語言在設計之初,都會強烈地支持某種編程范式,比如面向對象的Java,而Javascript...
閱讀 2305·2021-09-28 09:45
閱讀 3596·2021-09-24 09:48
閱讀 2256·2021-09-22 15:49
閱讀 3093·2021-09-08 16:10
閱讀 1586·2019-08-30 15:54
閱讀 2317·2019-08-30 15:53
閱讀 3012·2019-08-29 18:42
閱讀 2864·2019-08-29 16:19