摘要:當(dāng)作構(gòu)造函數(shù)來使用,作為普通函數(shù)來使用,當(dāng)在全局作用域中調(diào)用一個函數(shù)時,對象總是指向?qū)ο?。調(diào)用構(gòu)造函數(shù)時會為實例添加一個指向最初原型的的指針,而把原型修改為另外一個對象就等于切斷了構(gòu)造函數(shù)于最初原型之間的聯(lián)系。
ECMA-262 把對象定義為
無序?qū)傩缘募希鋵傩钥梢园局怠ο蠡蛘吆瘮?shù)。
即對象是一組沒有特定順序的值。對象的每個屬性或方法都有一個名字,而每個名字都映射到一個值。正因為這樣,我們可以把 ECMAScript 的對象想象成散列表:無非就是一組名值對,其中值可以是數(shù)據(jù)或函數(shù)。
理解對象 屬性類型ECMAScript 第 5 版 在定義只有內(nèi)部采用的特性(attribute)時, 描述了屬性(property)的各種特征。ECMAScript 定義這些特性是為了實現(xiàn) JavaScript 引擎用的,因此在 JavaScript 中不能直接訪問它們。為表示特性是內(nèi)部值,該規(guī)范把它們放在兩對方括號中,例如 [[Enuermerable]]。
ECMAScript 中只有兩種屬性:數(shù)據(jù)屬性和訪問器屬性。
數(shù)據(jù)屬性包含一個數(shù)據(jù)值的位置。在這個位置可以讀取和寫入值。數(shù)據(jù)屬性有描述其行為的特性4個:
[[Configurable]]: 表示能否通過 delete 刪除屬性從而重新定義屬性,能否修改屬性的特性,或者能否把屬性修改為訪問器屬性。
[[Enumberable]]: 表示能否通過 for-in 循環(huán)返回屬性。
[[Writable]]: 表示能否修改屬性的值。
[[value]]:包含這個屬性的數(shù)據(jù)值。讀取屬性值的時候,從這個位置讀;寫入屬性值的時候,把新值保存在這個位置。
要修改屬性默認的特性,必須用 ECMAScript 5 的 Object.defineProperty() 方法。這個方法接受三個參數(shù)`: 屬性所在的對象,屬性名,和一個描述符對象。其中,描述符(descriptor)對象的屬性必須是:configurable、enumerable、writable 和 value。
jsvar person = {}; Object.defineProperty(person,"name",{ writable: false, value: "Nicholas", configurable: false }); alert(person.name);//"Nicholas" person.name = "PaddingMe"; alert(person.name);//"Nicholas" delete person.name; alert(person.name);//"Nicholas"
一旦把屬性定義為不可配置特性就不能再把它變?yōu)榭膳渲谩?/p>
在調(diào)用 Object.defineProperty() 方法時,如果不指定, configurable, enumerable 和 writable 特性的默認值為 false。
訪問器屬性
訪問器屬性不包含數(shù)據(jù)值,他們包含一對 getter 和 setter 函數(shù)(不過不是必須的),在讀取訪問器屬性時,會調(diào)用 getter 函數(shù),這個函數(shù)負責(zé)返回有效的值。在寫入訪問器屬性時,會調(diào)用 setter 函數(shù)并傳入新值,此函數(shù)負責(zé)決定如何處理數(shù)據(jù)。訪問器也有4個特性:
[[Configurable]]: 表示能否通過 delete 刪除屬性從而重新定義屬性,能否修改屬性的特性,或者能否把屬性修改為訪問器屬性。
[[Enumberable]]: 表示能否通過 for-in 循環(huán)返回屬性。
[[get]]: 在讀取屬性時調(diào)用的函數(shù)。默認值為 undefined。
[[set]]: 在寫入屬性時調(diào)用的函數(shù)。默認值為 undefined。
訪問器不能直接定義,必須使用 object.defineProperty() 來定義。
var book = { _year: 2004, edition: 1 }; Object.defineProperty(book, "year", { get: function() { return this._year; }, set: function() { if (newValue > 2004) { this._year = newValue; this.edition += newValue -2004; } } }); book.year = 2005; alert(book.edition); //2
_year 前面的下劃線用于表示只能通過對象方法訪問的屬性。book.edition 變?yōu)?2 這是使用訪問器屬性的常見方式。即設(shè)置一個屬性的值會導(dǎo)致其他屬性發(fā)生變化。
定義多個屬性Object.definePorperties() 接受兩個對象參數(shù):第一個要添加或修改其屬性的對象,第二個對象的屬性 與第一個對象中要添加或修改的屬性一一對應(yīng)。
讀取屬性的特性Object.getOwnPropertyDescriptor() 方法獲取給定屬性的描述符。
此方法接受2個參數(shù):屬性所在的對象和要讀取其描述符的屬性名稱。
返回值是一個對象。
jsvar book = {}; Object.defineProperties(book,{ _year: {value: 2004}, edition: {value: 1}, year: { get: function() { return this._year; }, set: function() { if (newValue > 2004) { this._year = newValue; this.edition += newValue -2004; } } } }); var descriptor = Object.getOwnPropertyDescriptor(book,"_year"); alert(descriptor.value); //2004 alert(descriptor.configurable);//false alert(typeof descriptor.get); // "undefined" var descriptor = Object.getOwnPropertyDescriptor(book,"year"); alert(descriptor.value); //undefined alert(descriptor.enumerable);//false alert(typeof descriptor.get); //function創(chuàng)建對象
最簡單的可以用 Object 構(gòu)造函數(shù),或者對象字面量來創(chuàng)建單個對象,但這樣使用一個接口創(chuàng)建很多對象,會產(chǎn)生大量的重復(fù)代碼。
jsvar paddingme = new Object(); //用 Object 構(gòu)造函數(shù) 創(chuàng)建對象 var paddingme = {}; //對象字面量創(chuàng)建對象工廠模式
工廠模式是用函數(shù)來封裝以特定接口創(chuàng)建對象的細節(jié)。
jsfunction createPerson(name,age,job) { var o = new Object(); o.name = name; o.age = age; o.job = job; o.sayName = function() { alert(this.name); }; return o; } var person = createPerson("PaddingMe",25,"front-end developer");
工廠模式雖然解決了創(chuàng)建多個相似對象的問題,但沒有解決對象識別的問題(即怎樣知道一個對象的類型)。
構(gòu)造函數(shù)模式js function Person(name, age, job) { this.name = name; this.age = age; this.job = job; this.sayName = function() { alert(this.name); }; } var person1 = new Person("PaddingMe",25,"front-end developer"); alert(person1.constructor == Person);//true alert(person1 instanceof Person);//true alert(person1 instanceof Object);//true
構(gòu)造函數(shù)模式與工廠模式不同的是:
- 沒有顯式地創(chuàng)建對象;
- 沒有 return 語句;
- 直接將屬性和方法賦給了 this 方法。
另按照慣例,構(gòu)造函數(shù)首字母都應(yīng)該大寫。
創(chuàng)建 Person 新實例,經(jīng)過了以下4個新步驟:
創(chuàng)建一個新對象;
將構(gòu)造函數(shù)的作用域賦給新對象(因此 this 就指向了這個新對象);
執(zhí)行構(gòu)造函數(shù)中的代碼(為這個新對象添加屬性);
返回新對象。
創(chuàng)建自定義的構(gòu)造函數(shù)意味著將來可以將它的實例標識為一種特殊的類型,而這正是構(gòu)造函數(shù)模式勝過工廠模式的地方。
js //當(dāng)作構(gòu)造函數(shù)來使用 var person = new Person("paddingme",25,"F2ER"); person.sayName();//"paddingme" //作為普通函數(shù)來使用; Person("paddingme",25,"F2ER"); window.sayName();//"paddingme" //**當(dāng)在全局作用域中調(diào)用一個函數(shù)時,this 對象總是指向 Global 對象。** //在另一個對象的作用域中調(diào)用 var o = new Object(); Person.call(o,"paddingme",25,"F2ER"); o.sayName();// "paddingme"
構(gòu)造函數(shù)創(chuàng)建對象的問題在于:每個方法都要在每個實例上重新創(chuàng)建一遍,會導(dǎo)致不同的作用域鏈和標識符解析。不同實例上的同名函數(shù)是不相等的。
原型模式我們創(chuàng)建的每個函數(shù)都有一個 prototype(原型) 屬性,這個屬性是一個指針,指向一個對象,而這個對象的用途是包含可以由特定類型的所有實例共享的屬性和方法。 按照字面意思即 prototype 就是調(diào)用構(gòu)造函數(shù)而創(chuàng)建的那個對象實例的原型對象。使用原型對象的好處就是可以讓所有對象實例共享它所包含的屬性和方法。換言之,不必在構(gòu)造函數(shù)中定義實例的信息,而是可以將這些信息直接添加到原型對象中。
js function Person() {} Person.prototype.name = "PaddingMe"; Person.prototype.age = 29; Person.prototype.job = "Front-end Engineer"; Person.prototype.sayName = function () { alert(this.name); }; var person1 = new Person(); person1.sayName(); //"PaddingMe" var person2 = new Person(); person2.sayName(); //"PaddingMe"
理解原型對象
無論什么時候,只要創(chuàng)建一個新函數(shù),就會根據(jù)一組特定的規(guī)則為該函數(shù)創(chuàng)建一個 prototype 屬性,這個屬性指向函數(shù)的原型對象。在默認情況下,所有原型對象都會自動獲得一個 constructor 屬性。這個屬性包含一個指向所在函數(shù)的指針。 創(chuàng)建了自定義的構(gòu)造函數(shù)之后,其原型對象默認只會取得 constructor 屬性;至于其他方法,都是從 Object 繼承而來的。當(dāng)調(diào)用構(gòu)造函數(shù)創(chuàng)建一個新實例后,該實例的內(nèi)部將包含一個指針(內(nèi)部屬性),指向構(gòu)造函數(shù)的原型對象。ECMAScript 管此指針叫 [[prototype]]
Person 構(gòu)造函數(shù)、 Person 的原型屬性以及 Person 現(xiàn)有的兩個實例之間的關(guān)系。在此,Person.prototype 指向了原型對象,而 Person.prototype.constructor 又指回了 Person。 原型對象中除了包含 constructor 屬性之外,還包括后來添加的其他屬性。 Person 的每個實例都包含一個內(nèi)部屬性,該屬性指向了 Person.prototype;換句話說,它們與構(gòu)造函數(shù)沒有直接的關(guān)系。
js alert(Person.prototype.isPrototype(person1));//true alert(Person.prototype.isPrototype(person2));//true
ECMASript 5 增加了Object.getPrototypeOf(),在所有支持的實現(xiàn)中,這個方法返回[[Prototype]] 的值。
jsalert(Object.getPrototypeOf(person1) == Person.prototype); // true alert(Object.getPrototypeOf(person1).name); // "PaddingMe"
每當(dāng)代碼讀取某個對象的某個屬性時,都會執(zhí)行一次搜索,目標是具有給定名字的屬性。搜索首先從對象實例本身開始。若在實例中找到了具有給定名字的屬性,則返回該屬性的值。若沒有,則繼續(xù)搜索指針指向的原型鍍錫i昂,在原型對象中查找是否具有給定名字的屬性。若在原型對象中找到此屬性,則返回該屬性的值。
原型最初只包括 constructor 屬性,而該屬性也是共享的,因此可以通過對象實例訪問
當(dāng)為對象實例添加一個屬性時,這個屬性就會屏蔽原型對象中同名屬性,換句話說,添加這個屬性只會阻止我們訪問原型對象中的屬性,而不會修改那個屬性。
使用 hasOwnProperty() 方法可以檢測一個屬性是存在于實例中,還是存在于原型中。
這個方法(它繼承于 Object)只在給定屬性存在于對象實例中,才會返回 true。
jsfunction Person() {} Person.prototype.name = "PaddingMe"; Person.prototype.age = 29; Person.prototype.job = "Front-end Engineer"; Person.prototype.sayName = function () { alert(this.name); }; var person1 = new Person(); var person2 = new Person(); alert(person1.hasOwnProperty("name"));//false person2.name = "hhb"; alert(person2.hasOwnProperty("name"));//true delete person2.name; alert(person2.hasOwnProperty("name"));//false
原型與 in 操作符
在多帶帶使用時,in 操作符會在通過對象能夠訪問給定屬性時返回 true,無論該屬性是在實例中還是原型中。與 hasOwnProperty() 一起使用可以確定該屬性到底是在對象中,還是存在于原型中。
jsfunction hasPrototypeProperty(){ return !object.hasOwnProperty(name) && (name in object); }
在使用 for-in 循環(huán)時, 返回的是所有能夠通過對象訪問的、可枚舉(enumerated)屬性,其中既包括存在于實例中的屬性,也包括存在于原型中的屬性。屏蔽了原型中不可枚舉屬性的實例屬性也會返回,因為根據(jù)規(guī)定,所有開發(fā)人員定義的屬性都是可枚舉的——只有在 IE8 以及更早版本中例外。
要去的對象上所有可枚舉的實例屬性,可使用 ECMAScript 5 中的 Object.keys() 方法。此方法要接受一個對象作為參數(shù),返回一個包含所有可枚舉屬性的字符串組。
jsfunction Person() {} Person.prototype.name = "PaddingMe"; Person.prototype.age = 29; Person.prototype.job = "Front-end Engineer"; Person.prototype.sayName = function () { alert(this.name); }; var keys = Object.keys(Person.prototype); alert(keys);// "name,age,job,sayName" var p1 = new Person(); p1.name = "hhb"; p1.age = 25; var p1keys = Object.keys(p1); alert(p1keys);// "name,age"
想要得到所有的實例屬性,無論它是否可枚舉,可以使用 Object.getOwnPropertyNames() 方法。
jsvar keys = Object.getOwnPropertyNames(Person.prototype); alert(keys);// "constructor,name,age,job,sayName"
更簡單的原型方法
用對象自變量來重寫真哥哥原型對象。
jsfunction Person(){ } Person.prototype = { name : "PaddingMe", age : 25, job : "F2ER", sayName : function() { alert(this.name); } };
注意 constructor 屬性不再指向 Person,而是指向了 Object 構(gòu)造函數(shù)。
jsfunction Person(){ } Person.prototype = { constructor: Person, name : "PaddingMe", age : 25, job : "F2ER", sayName : function() { alert(this.name); } };
原型的動態(tài)性
對原型對象所在的任何修改都能夠立即從實例上反映出來—— 即使是先創(chuàng)建了實例后修改原型也是一樣的。但若是重寫整個原型對象,情況就不一樣了。
調(diào)用構(gòu)造函數(shù)時會為實例添加一個指向最初原型的 [[prototype]] 的指針,而把原型修改為另外一個對象就等于切斷了構(gòu)造函數(shù)于最初原型之間的聯(lián)系。實例中的指針只指向原型,而不指向構(gòu)造函數(shù)。
jsfunction Person() { } var friend = new Person(); Person.prototype = { constuctor : Person, name : "PaddingMe", age : 25, sayName : function(){ alert(this.name); } }; friend.sayName(); //error
原生對象的原型
jsString.prototype.startsWith = function (text) { return this.indexOf(text) == 0; } var msg = "Hello world!"; alert(msg.startsWith("Hello")); //true
原型對象的問題
所有實例在默認情況下都將取得相同的屬性值。原型模式的最大問題是由其共享的本性所導(dǎo)致。
jsfuntion Person(){ } Person.prototype = { constuctor: Person, name: "PaddingMe", age: 26, job: "F2ER", friends: ["hw","wjj"], sayName: function() { alert(this.name); } }; var person1 = new Person(); var person2 = new Person(); Person1.friends.push("ex"); alert(person1.friends);//"hw,wjj,ex" alert(person2.friends);//"hw,wjj,ex" alert(person1.friends == person2.friends);//true組合使用構(gòu)造函數(shù)模式和原型模式
構(gòu)造函數(shù)模式用于定義實例屬性,而原型模式用于定義方法和共享的屬性。
jsfunction Person(name,age,job) { this.name = name; this.age = age; this.job = job; this.friends = ["hw","wjj"]; } Person.prototype = { constructor: Person, sayName: function() { alert(this.name); } } var person1 = new Person("winter",59,"Full-Stack Enginner"); var person2 = new Person("PaddingMe",25,"F2ER"); person1.friends.push("ex"); alert(person1.friends); // "hw,wjj,ex" alert(person2.friends); // "hw,wjj" alert(person1.friends == person2.friends); //false alert(person1.sayName == person2.sayName); //true動態(tài)原型模式
jsfunction Person(name,age,job) { this.name = name; this.age = age; this.job = job; if(typeof this.sayName != "funtion") { Person.prototype.sayName = function(){ alert(this.name); }; } } var friend = new Person("PaddingMe",25,"F2ER"); friend.sayName();寄生構(gòu)造函數(shù)模式
寄生(parasitic)構(gòu)造函數(shù)模式的基本思想是:創(chuàng)建一個函數(shù),該函數(shù)的作用僅僅是封裝創(chuàng)建對象的代碼,然后再返回新創(chuàng)建的對象;但從表面上看,有很像典型的構(gòu)造函數(shù)。
jsfunction Person(name,age,job) { var o = new Object(); o.name = name; o.age = age; o.job = job; o.sayName = function() { alert(this.name); } return o; } var friend = new Person("PaddingMe",25,"F2ER"); friend.sayName;// "PaddingMe"
js function SpecialArray() { var values = new Array(); values.push.apply(values,arguments); values.toPipedString = function() { return this.join("|"); } } var colors = new SpecialArray("red","blue","green"); alert(colors.toPipedString());//"red|blue|green"
關(guān)于寄生構(gòu)造函數(shù)模式需要說明的是:
返回的對象與構(gòu)造函數(shù)或者與構(gòu)造函數(shù)的原型屬性之間沒有關(guān)系,亦即構(gòu)造函數(shù)返回的都喜愛那個與在構(gòu)造函數(shù)外部建立的對象沒什么不同。所以不能依賴 instanceof 操作符來確定對象模型。
所謂穩(wěn)妥對象是指沒有公共屬性,而且其方法也引用 this 的對象。穩(wěn)妥對象最適合在一些安全的環(huán)境中(這些環(huán)境中會禁止使用 this 和 new),或者在防止數(shù)據(jù)被其他應(yīng)用程序(如 Mashup 程序)改動時使用。
穩(wěn)妥構(gòu)造函數(shù)模式與寄生構(gòu)造函數(shù)模式不一樣的是:
- 新創(chuàng)建對象的實例方法不引用 this;
- 不使用 new 操作符調(diào)用構(gòu)造函數(shù)。
jsfunction Person(name,age,job) { var o = new Object(); o.sayName = function (){ alert(name); }; return o; }
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/85476.html
摘要:對象有狀態(tài)對象具有狀態(tài),同一對象可能處于不同狀態(tài)之下。中對象獨有的特色對象具有高度的動態(tài)性,這是因為賦予了使用者在運行時為對象添改狀態(tài)和行為的能力。小結(jié)由于的對象設(shè)計跟目前主流基于類的面向?qū)ο蟛町惙浅4?,?dǎo)致有不是面向?qū)ο筮@樣的說法。 筆記說明 重學(xué)前端是程劭非(winter)【前手機淘寶前端負責(zé)人】在極客時間開的一個專欄,每天10分鐘,重構(gòu)你的前端知識體系,筆者主要整理學(xué)習(xí)過程的一些...
摘要:對象有狀態(tài)對象具有狀態(tài),同一對象可能處于不同狀態(tài)之下。中對象獨有的特色對象具有高度的動態(tài)性,這是因為賦予了使用者在運行時為對象添改狀態(tài)和行為的能力。小結(jié)由于的對象設(shè)計跟目前主流基于類的面向?qū)ο蟛町惙浅4?,?dǎo)致有不是面向?qū)ο筮@樣的說法。 筆記說明 重學(xué)前端是程劭非(winter)【前手機淘寶前端負責(zé)人】在極客時間開的一個專欄,每天10分鐘,重構(gòu)你的前端知識體系,筆者主要整理學(xué)習(xí)過程的一些...
摘要:對象有狀態(tài)對象具有狀態(tài),同一對象可能處于不同狀態(tài)之下。中對象獨有的特色對象具有高度的動態(tài)性,這是因為賦予了使用者在運行時為對象添改狀態(tài)和行為的能力。小結(jié)由于的對象設(shè)計跟目前主流基于類的面向?qū)ο蟛町惙浅4螅瑢?dǎo)致有不是面向?qū)ο筮@樣的說法。 筆記說明 重學(xué)前端是程劭非(winter)【前手機淘寶前端負責(zé)人】在極客時間開的一個專欄,每天10分鐘,重構(gòu)你的前端知識體系,筆者主要整理學(xué)習(xí)過程的一些...
摘要:子類繼承自父類的方法可以重新定義即覆寫,被調(diào)用時會使用子類定義的方法什么是多態(tài)青蛙是一個對象,金魚也是一個對象,青蛙會跳,金魚會游,定義好對象及其方法后,我們能用青蛙對象調(diào)用跳這個方法,也能用金魚對象調(diào)用游這個方法。 1、專用術(shù)語 面向?qū)ο缶幊坛绦蛟O(shè)計簡稱:OOP,在面向?qū)ο缶幊讨谐S玫降母拍钣校簩ο?、屬性、方法、類、封裝、聚合、重用與繼承、多態(tài)。 2、什么是對象? 面向?qū)ο缶幊痰闹攸c...
摘要:即另外,注意到構(gòu)造函數(shù)里的屬性,都沒有經(jīng)過進行初始化,而是直接使用進行綁定。并且在模式下,構(gòu)造函數(shù)沒有使用進行調(diào)用,也會導(dǎo)致報錯。調(diào)用構(gòu)造函數(shù)千萬不要忘記寫。 1. 基礎(chǔ) JavaScript不區(qū)分類和實例的概念,而是通過原型來實現(xiàn)面向?qū)ο缶幊?。Java是從高級的抽象上設(shè)計的類和實例,而JavaScript的設(shè)計理念,聽起來就好比Heros里的Peter,可以復(fù)制別人的能力。JavaS...
閱讀 2033·2021-11-24 11:16
閱讀 3325·2021-09-10 10:51
閱讀 3333·2021-08-03 14:03
閱讀 1333·2019-08-29 17:03
閱讀 3305·2019-08-29 12:36
閱讀 2332·2019-08-26 14:06
閱讀 555·2019-08-23 16:32
閱讀 2844·2019-08-23 13:42