摘要:當作構造函數來使用,作為普通函數來使用,當在全局作用域中調用一個函數時,對象總是指向對象。調用構造函數時會為實例添加一個指向最初原型的的指針,而把原型修改為另外一個對象就等于切斷了構造函數于最初原型之間的聯系。
ECMA-262 把對象定義為
無序屬性的集合,其屬性可以包含基本值、對象或者函數。
即對象是一組沒有特定順序的值。對象的每個屬性或方法都有一個名字,而每個名字都映射到一個值。正因為這樣,我們可以把 ECMAScript 的對象想象成散列表:無非就是一組名值對,其中值可以是數據或函數。
理解對象 屬性類型ECMAScript 第 5 版 在定義只有內部采用的特性(attribute)時, 描述了屬性(property)的各種特征。ECMAScript 定義這些特性是為了實現 JavaScript 引擎用的,因此在 JavaScript 中不能直接訪問它們。為表示特性是內部值,該規范把它們放在兩對方括號中,例如 [[Enuermerable]]。
ECMAScript 中只有兩種屬性:數據屬性和訪問器屬性。
數據屬性包含一個數據值的位置。在這個位置可以讀取和寫入值。數據屬性有描述其行為的特性4個:
[[Configurable]]: 表示能否通過 delete 刪除屬性從而重新定義屬性,能否修改屬性的特性,或者能否把屬性修改為訪問器屬性。
[[Enumberable]]: 表示能否通過 for-in 循環返回屬性。
[[Writable]]: 表示能否修改屬性的值。
[[value]]:包含這個屬性的數據值。讀取屬性值的時候,從這個位置讀;寫入屬性值的時候,把新值保存在這個位置。
要修改屬性默認的特性,必須用 ECMAScript 5 的 Object.defineProperty() 方法。這個方法接受三個參數`: 屬性所在的對象,屬性名,和一個描述符對象。其中,描述符(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"
一旦把屬性定義為不可配置特性就不能再把它變為可配置。
在調用 Object.defineProperty() 方法時,如果不指定, configurable, enumerable 和 writable 特性的默認值為 false。
訪問器屬性
訪問器屬性不包含數據值,他們包含一對 getter 和 setter 函數(不過不是必須的),在讀取訪問器屬性時,會調用 getter 函數,這個函數負責返回有效的值。在寫入訪問器屬性時,會調用 setter 函數并傳入新值,此函數負責決定如何處理數據。訪問器也有4個特性:
[[Configurable]]: 表示能否通過 delete 刪除屬性從而重新定義屬性,能否修改屬性的特性,或者能否把屬性修改為訪問器屬性。
[[Enumberable]]: 表示能否通過 for-in 循環返回屬性。
[[get]]: 在讀取屬性時調用的函數。默認值為 undefined。
[[set]]: 在寫入屬性時調用的函數。默認值為 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 變為 2 這是使用訪問器屬性的常見方式。即設置一個屬性的值會導致其他屬性發生變化。
定義多個屬性Object.definePorperties() 接受兩個對象參數:第一個要添加或修改其屬性的對象,第二個對象的屬性 與第一個對象中要添加或修改的屬性一一對應。
讀取屬性的特性Object.getOwnPropertyDescriptor() 方法獲取給定屬性的描述符。
此方法接受2個參數:屬性所在的對象和要讀取其描述符的屬性名稱。
返回值是一個對象。
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創建對象
最簡單的可以用 Object 構造函數,或者對象字面量來創建單個對象,但這樣使用一個接口創建很多對象,會產生大量的重復代碼。
jsvar paddingme = new Object(); //用 Object 構造函數 創建對象 var paddingme = {}; //對象字面量創建對象工廠模式
工廠模式是用函數來封裝以特定接口創建對象的細節。
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");
工廠模式雖然解決了創建多個相似對象的問題,但沒有解決對象識別的問題(即怎樣知道一個對象的類型)。
構造函數模式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
構造函數模式與工廠模式不同的是:
- 沒有顯式地創建對象;
- 沒有 return 語句;
- 直接將屬性和方法賦給了 this 方法。
另按照慣例,構造函數首字母都應該大寫。
創建 Person 新實例,經過了以下4個新步驟:
創建一個新對象;
將構造函數的作用域賦給新對象(因此 this 就指向了這個新對象);
執行構造函數中的代碼(為這個新對象添加屬性);
返回新對象。
創建自定義的構造函數意味著將來可以將它的實例標識為一種特殊的類型,而這正是構造函數模式勝過工廠模式的地方。
js //當作構造函數來使用 var person = new Person("paddingme",25,"F2ER"); person.sayName();//"paddingme" //作為普通函數來使用; Person("paddingme",25,"F2ER"); window.sayName();//"paddingme" //**當在全局作用域中調用一個函數時,this 對象總是指向 Global 對象。** //在另一個對象的作用域中調用 var o = new Object(); Person.call(o,"paddingme",25,"F2ER"); o.sayName();// "paddingme"
構造函數創建對象的問題在于:每個方法都要在每個實例上重新創建一遍,會導致不同的作用域鏈和標識符解析。不同實例上的同名函數是不相等的。
原型模式我們創建的每個函數都有一個 prototype(原型) 屬性,這個屬性是一個指針,指向一個對象,而這個對象的用途是包含可以由特定類型的所有實例共享的屬性和方法。 按照字面意思即 prototype 就是調用構造函數而創建的那個對象實例的原型對象。使用原型對象的好處就是可以讓所有對象實例共享它所包含的屬性和方法。換言之,不必在構造函數中定義實例的信息,而是可以將這些信息直接添加到原型對象中。
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"
理解原型對象
無論什么時候,只要創建一個新函數,就會根據一組特定的規則為該函數創建一個 prototype 屬性,這個屬性指向函數的原型對象。在默認情況下,所有原型對象都會自動獲得一個 constructor 屬性。這個屬性包含一個指向所在函數的指針。 創建了自定義的構造函數之后,其原型對象默認只會取得 constructor 屬性;至于其他方法,都是從 Object 繼承而來的。當調用構造函數創建一個新實例后,該實例的內部將包含一個指針(內部屬性),指向構造函數的原型對象。ECMAScript 管此指針叫 [[prototype]]
Person 構造函數、 Person 的原型屬性以及 Person 現有的兩個實例之間的關系。在此,Person.prototype 指向了原型對象,而 Person.prototype.constructor 又指回了 Person。 原型對象中除了包含 constructor 屬性之外,還包括后來添加的其他屬性。 Person 的每個實例都包含一個內部屬性,該屬性指向了 Person.prototype;換句話說,它們與構造函數沒有直接的關系。
js alert(Person.prototype.isPrototype(person1));//true alert(Person.prototype.isPrototype(person2));//true
ECMASript 5 增加了Object.getPrototypeOf(),在所有支持的實現中,這個方法返回[[Prototype]] 的值。
jsalert(Object.getPrototypeOf(person1) == Person.prototype); // true alert(Object.getPrototypeOf(person1).name); // "PaddingMe"
每當代碼讀取某個對象的某個屬性時,都會執行一次搜索,目標是具有給定名字的屬性。搜索首先從對象實例本身開始。若在實例中找到了具有給定名字的屬性,則返回該屬性的值。若沒有,則繼續搜索指針指向的原型鍍錫i昂,在原型對象中查找是否具有給定名字的屬性。若在原型對象中找到此屬性,則返回該屬性的值。
原型最初只包括 constructor 屬性,而該屬性也是共享的,因此可以通過對象實例訪問
當為對象實例添加一個屬性時,這個屬性就會屏蔽原型對象中同名屬性,換句話說,添加這個屬性只會阻止我們訪問原型對象中的屬性,而不會修改那個屬性。
使用 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 循環時, 返回的是所有能夠通過對象訪問的、可枚舉(enumerated)屬性,其中既包括存在于實例中的屬性,也包括存在于原型中的屬性。屏蔽了原型中不可枚舉屬性的實例屬性也會返回,因為根據規定,所有開發人員定義的屬性都是可枚舉的——只有在 IE8 以及更早版本中例外。
要去的對象上所有可枚舉的實例屬性,可使用 ECMAScript 5 中的 Object.keys() 方法。此方法要接受一個對象作為參數,返回一個包含所有可枚舉屬性的字符串組。
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 構造函數。
jsfunction Person(){ } Person.prototype = { constructor: Person, name : "PaddingMe", age : 25, job : "F2ER", sayName : function() { alert(this.name); } };
原型的動態性
對原型對象所在的任何修改都能夠立即從實例上反映出來—— 即使是先創建了實例后修改原型也是一樣的。但若是重寫整個原型對象,情況就不一樣了。
調用構造函數時會為實例添加一個指向最初原型的 [[prototype]] 的指針,而把原型修改為另外一個對象就等于切斷了構造函數于最初原型之間的聯系。實例中的指針只指向原型,而不指向構造函數。
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
原型對象的問題
所有實例在默認情況下都將取得相同的屬性值。原型模式的最大問題是由其共享的本性所導致。
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組合使用構造函數模式和原型模式
構造函數模式用于定義實例屬性,而原型模式用于定義方法和共享的屬性。
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動態原型模式
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();寄生構造函數模式
寄生(parasitic)構造函數模式的基本思想是:創建一個函數,該函數的作用僅僅是封裝創建對象的代碼,然后再返回新創建的對象;但從表面上看,有很像典型的構造函數。
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"
關于寄生構造函數模式需要說明的是:
返回的對象與構造函數或者與構造函數的原型屬性之間沒有關系,亦即構造函數返回的都喜愛那個與在構造函數外部建立的對象沒什么不同。所以不能依賴 instanceof 操作符來確定對象模型。
所謂穩妥對象是指沒有公共屬性,而且其方法也引用 this 的對象。穩妥對象最適合在一些安全的環境中(這些環境中會禁止使用 this 和 new),或者在防止數據被其他應用程序(如 Mashup 程序)改動時使用。
穩妥構造函數模式與寄生構造函數模式不一樣的是:
- 新創建對象的實例方法不引用 this;
- 不使用 new 操作符調用構造函數。
jsfunction Person(name,age,job) { var o = new Object(); o.sayName = function (){ alert(name); }; return o; }
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/85476.html
摘要:對象有狀態對象具有狀態,同一對象可能處于不同狀態之下。中對象獨有的特色對象具有高度的動態性,這是因為賦予了使用者在運行時為對象添改狀態和行為的能力。小結由于的對象設計跟目前主流基于類的面向對象差異非常大,導致有不是面向對象這樣的說法。 筆記說明 重學前端是程劭非(winter)【前手機淘寶前端負責人】在極客時間開的一個專欄,每天10分鐘,重構你的前端知識體系,筆者主要整理學習過程的一些...
摘要:對象有狀態對象具有狀態,同一對象可能處于不同狀態之下。中對象獨有的特色對象具有高度的動態性,這是因為賦予了使用者在運行時為對象添改狀態和行為的能力。小結由于的對象設計跟目前主流基于類的面向對象差異非常大,導致有不是面向對象這樣的說法。 筆記說明 重學前端是程劭非(winter)【前手機淘寶前端負責人】在極客時間開的一個專欄,每天10分鐘,重構你的前端知識體系,筆者主要整理學習過程的一些...
摘要:對象有狀態對象具有狀態,同一對象可能處于不同狀態之下。中對象獨有的特色對象具有高度的動態性,這是因為賦予了使用者在運行時為對象添改狀態和行為的能力。小結由于的對象設計跟目前主流基于類的面向對象差異非常大,導致有不是面向對象這樣的說法。 筆記說明 重學前端是程劭非(winter)【前手機淘寶前端負責人】在極客時間開的一個專欄,每天10分鐘,重構你的前端知識體系,筆者主要整理學習過程的一些...
摘要:子類繼承自父類的方法可以重新定義即覆寫,被調用時會使用子類定義的方法什么是多態青蛙是一個對象,金魚也是一個對象,青蛙會跳,金魚會游,定義好對象及其方法后,我們能用青蛙對象調用跳這個方法,也能用金魚對象調用游這個方法。 1、專用術語 面向對象編程程序設計簡稱:OOP,在面向對象編程中常用到的概念有:對象、屬性、方法、類、封裝、聚合、重用與繼承、多態。 2、什么是對象? 面向對象編程的重點...
摘要:即另外,注意到構造函數里的屬性,都沒有經過進行初始化,而是直接使用進行綁定。并且在模式下,構造函數沒有使用進行調用,也會導致報錯。調用構造函數千萬不要忘記寫。 1. 基礎 JavaScript不區分類和實例的概念,而是通過原型來實現面向對象編程。Java是從高級的抽象上設計的類和實例,而JavaScript的設計理念,聽起來就好比Heros里的Peter,可以復制別人的能力。JavaS...
閱讀 1894·2021-11-24 11:16
閱讀 3257·2021-09-10 10:51
閱讀 3180·2021-08-03 14:03
閱讀 1261·2019-08-29 17:03
閱讀 3238·2019-08-29 12:36
閱讀 2219·2019-08-26 14:06
閱讀 493·2019-08-23 16:32
閱讀 2662·2019-08-23 13:42