摘要:面向對象的程序設計理解對象前言最近在細讀高級程序設計,對于我而言,中文版,書中很多地方翻譯的差強人意,所以用自己所理解的,嘗試解讀下。總結如果英語水平足夠好的話,建議看英文原版書籍或者國外大師的博客。
JS面向對象的程序設計_理解對象 前言:最近在細讀Javascript高級程序設計,對于我而言,中文版,書中很多地方翻譯的差強人意,所以用自己所理解的,嘗試解讀下。
如有紕漏或錯誤,會非常感謝您的指出。文中絕大部分內容引用自《JavaScript高級程序設計第三版》。
面向對象(Object-Oriented, OO)的計算機語言有一個標志,就是它們都有一個類class的概念,通過類可以創建任意多個具有相同屬性和方法的對象。ECMAScript中沒有類的概念,因而它的對象也與基于類的計算機語言中的對象不同。
ECMA-262把對象定義為: “無序屬性的集合,其屬性可以包含基本值、對象或者函數。” 也就是說對象是一組沒有特定順序的值。對象的每個屬性或方法都有一個名字,而每個名字都映射(map)到一個值。所以我們可以把ECMAScript中的對象想象成散列表,換句話說,就是名值對,其中值可以是數據或者函數。每個對象都是基于引用類型創建的,這個值可以是原生類型,也可以是開發人員定義的類型。
理解對象如果我們要創建自定義對象,最簡單的方式就是創建一個Object類型的實例對象,然后再為它添加屬性和方法。
//使用new關鍵字從Object類型中實例化出一個對象。 var person = new Object(); person.name = "Shaw"; person.age = "Secret"; person.job = "Front-end Engineer"; person.sayName = function(){ console.log(this.name); // 這里的this指向person,call調用它的就是person }
上述代碼,創建了一個person對象,并為它添加了三個屬性(name, age, job)和一個方法 sayName()。其中,sayName() 方法用于顯示this.name(被解析為person.name)的值。
現在,對象字面量成為創建這種對象的首選模式。
// 之前的例子,可以寫成這樣 var person = { name: "Shaw", age: "Secret", job: "Front-end Engineer", sayName: function(){ console.log(this.name) } }
這些屬性在創建時都帶有一些特性值(attribute ), Javascript通過特性來定義屬性(property)的行為。描述特性的字符叫做descriptor。
屬性特性 PropertyAttribute特性(attribute)描述了屬性(property)的各種特征(也可以理解為特性值),ECMA-262第5版定義這些特性是為了實現JavaScript引擎用的,因此在JavaScript中不能直接訪問到它們。為了表示特性是內部值, 該規范把它們放在兩對方括號中,[[Enumerable]]。
ECMAScript定義對象的屬性分為兩種:數據屬性和訪問器屬性。
對象屬性的數據特性
數據特性包含一個數據值的位置。在這個位置可以讀取和寫入值(I/O)。
數據特性的描述符(property attribute descriptor)
[[Configurable]], 可配置的: 表示能否通過delete刪除屬性從而重新定義屬性,能否修改屬性的特性或能否把屬性修改為訪問器屬性。像上面例子那樣直接在對象上定義的屬性,這些屬性的這個特性默認值為true(可以刪改定義屬性)。
[[Enumerable]], 枚舉: 表示能否通過for-in循環返回屬性。像上面例子那樣直接在對象上定義的屬性,對象的屬性的這個Enumerable特性值默認為true。
[[Writable]], 改寫: 表示能否修改屬性的值。像上面例子那樣直接在對象上定義的屬性,對象的屬性的這個writable特性值默認為true。
[[value]], 輸入讀取屬性值:讀取屬性值的時候,從這個位置開始讀; 寫入屬性的時候,把新值保存在這個位置。這個特性的默認值為undefined。
對于上面的那個例子中直接在對象上定義的屬性, 屬性們的特性值[[Configurable]]、[[Enumberable]]、[[Writable]]的默認值都為true,而[[value]]特性被設置為指定的值。
//例子 var person = { name: "Shaw" //偽代碼 //name: { value: "Shaw"} }; //這里創建一個名為name的屬性,為它指定的值是"Shaw" //也就是說, [[value]]特性被設置為“Shaw”,而對這個值的任何修改都將反應在這個位置。
要修改屬性默認的特性,必須使用ECMAScript5的Object.defineProperty()方法。 這個方法接收三個參數: 屬性所在的對象、屬性的名字和一個描述符對象。 其中, 描述符(descriptor)對象的屬性必須是: configurable、enumerable、writable和value。 設置其中的一或多個值,可以修改對應的特性值。請看下面的例子:
var person = {}; Object.defineProperty(person, "name", { writable: false, value: "Shaw" }); console.log(person.name); //"Shaw" person.name = "roc"; console.log(person.name); // 還是"Shaw", 因為通過Object.defineProperty()方法,設置該屬性的特性writable的值為false,即不可改寫。
這個例子創建一個名為name的屬性,它的值“Shaw"是不可改寫的,即只能讀取。如果嘗試為它指定新值,賦值操作會被忽略;在嚴格模式下,賦值操作會拋出錯誤。
類似的規則也適用于屬性的可配置特性。例如:
var person = {}; Object.defineProperty(person, "name", { configurable: false, value: "Shaw" }); delete person.name; // false, 因為configurable為false; console.log(person.name);
把屬性的特性configurable設置為false,意味著該屬性是不可配置的,也就是不能從對象中刪除這個屬性。如果對這個屬性調用delete, 在非嚴格模式下什么都不會發生, 在嚴格模式下, 會拋出一個錯誤。 ==注意==, 一旦把屬性的特性定義為不可配置的,就不能再把它變回可配置了。此時,再調用Object.defineProperty()方法,會拋出錯誤。
var person = {}; Object.defineProperty(person, "name", { configurable: false, value: "Shaw" //不指定的話, 其余的特性值為默認值,也就是說特性writable的默認值為 false, //特性 enumerable的默認值為 false }) //Uncaught TypeError: Cannot redefine property: name Object.defineProperty(person, "name", { configurable: true, value: "Shaw" })
換句說,可以多次調用Object.defineProperty()方法來修改該屬性, 但是設置屬性的特性configurable的值為false(不可配置)后,其結果是不可逆的, 就會有限制了。
在調用Object.defineProperty()方法來創建一個新的屬性時候,如果不指定,configurable、writable、enumerable特性的默認值都為false。
如果調用Object.defineProperty()方法只是修改已定義的屬性的特性, 則無此限制。 因為已經定義的屬性的特性configurable、enumerable、writable默認都為true。比如之前的那個例子:
var person = { name: "Shaw" // 偽代碼 /*propertyAttribute = { configurable(descriptor): true, enumerable(descriptor): true, writable(descriptor): true, value(descriptor): "Shaw"*/ } }
多數情況下, 我們可能都用不到Object.defineProperty()提供的這些高級功能。
不過,理解這些概念對理解JavaScript對象有很大的幫助。
對象屬性的訪問器特性
顧名思義就是定義了對象內部,該屬性的特性是有權限訪問或設置對象內部其他屬性。
訪問器屬性不包含數據值;它們包含一對兒getter和setter函數。
在讀取訪問器屬性時,會調用getter函數,這個函數負責返回有效的值; 在寫入訪問器屬性時,會調用setter函數并傳入新值,這個函數負責如何處理數據。 訪問器屬性有如下4個特性:
[[configurable]],是否可配置。 表示能否通過delete刪除屬性從而重新定義屬性,能否修改屬性的特性。或者能否把屬性修改為數據屬性。對于直接在對象上定義的屬性, 這個屬性的該特性的默認值為true。
[[Enumerable]], 是否可枚舉。 表示能否通過for-in循環返回屬性。 對于直接在對象上定義的屬性,這個屬性的該特性的默認值為true。
[[Get]], 讀取屬性函數。 讀取屬性時調用的函數。默認值為undefined。
[[Set]], 設置屬性函數。 設置屬性時調用的函數。默認值為undefined。
訪問器屬性不能直接定義, 必須使用Object.defineProperty()來定義。
var cat = { _age: 1, adoptionYear: 2016 }; Object.defineProperty(cat, "age", { get: function() { return this._age; }, set: function(newAge) { if(newAge>1) { this._age = newAge; //5 this.adoptionYear += newAge - 1; } } }); window.alert(cat.age); //1 cat.age = 5; window.alert(cat.age); //5 window.alert(cat.adoptionYear); // 2020
以上代碼創建了一個cat對象,并給它定義兩個默認的屬性: _age和adoptionYear。 _age 是一種約定俗成的記號,表示只能通過對象屬性的訪問器特性方法才能訪問到的屬性(可以想象成對象的成員屬性)。 而屬性year的訪問器特性則包含一個get函數和set函數。get函數返回_age的值, set函數通過計算來確定貓咪的收養年份。因此,把age的屬性值修改為5,而adoptionYear變為2020,即已經收養了四年。這是使用訪問器屬性的常見方法,即設置一個屬性的值然后改變另外一個屬性的值。
在這個定義對象屬性的訪問器特性之前,Object.defineProperty(object, "property", { get: function() {} , set: function(){}}) 。 要創建對象屬性的訪問器特性,一般都使用兩個非標準方法: __defineGetter__() 和 __defineSetter__(), ==注意這里是雙下劃線==。 這兩個方法最初是由Firefox引入的,后來Safari 3,Chrome 1和Opera9.5也給出相同的實現。 使用這兩個遺留的方法,也可以實現對象屬性的訪問器特性設置。
// 使用例子如下 var cat = { _age: 1, adoptionYear: 2016 } cat.__defineGetter__("age", function(){ return this._age; }); cat.__defineSetter__("age", function(newAge){ if(newAge > 1) { this._age = newAge; this.adoptionYear += this._age - 1; } }) alert(cat.age); //1 alert(cat.adoptionYear); //2016 cat.age = 5; alert(cat.age); //5 alert(cat.adoptionYear); //2020同時定義對象多個屬性的特性的方法
為對象同時定義多個屬性特性的可能性很大,ECMAScript5又定義了一個Object.defineProperties(object, ObjectDescriptor)方法,用這個方法可以通過一次性定義對象的多個屬性特性。 該方法接收兩個對象參數: 第一個對象參數是要添加和修改屬性特性值的對象,第二個對象參數的屬性與第一個對象中要添加或修改的屬性一一對應。
// 還是代碼比較直觀 var cat = {}; Object.defineProperties(cat, { _name: { writable: true, value: "Meow" }, _age: { writable: true, value: 1 }, adoptionYear: { writable: true, value: 1 }, age: { get: function() { return this._age; }, set: function(newAge) { if(newAge <= 1) { this._age = 1; }; this._age = newAge; this.adoptionYear += newAge -1; } } }) alert(cat.age); // 1 cat.age = 3; alert(cat.age); // 3 alert(cat.adoptionYear); // 3 從小就收養了。讀取屬性的特性
ECMAScript5定義了Object.getOwnPropertyDescriptor(object, "property")方法, 該方法取得給定屬性的特性描述符。
這個方法接收兩個參數:
第一個參數為: 屬性所在的對象。
第二個參數為: 需要讀取其特性值(PropertyAttribute) 的對象屬性。
該方法的返回值是一個對象,所以可以通過.語法讀取到值,如果是屬性的訪問器特性,返回的這個對象的屬性有:configurable、enumerable、get, set; 如果是數據屬性, 這個對象的屬性有configurable、enumerable、writable和value。
var person = {}; Object.defineProperties(person, { _name: { writable: true, value: "請使用person.name = ?,給我定義一個名字呀!" }, _age: { writable: true, value: "請使用person.age = ?,給我定義一個年齡呀!" }, _gender: { writable: true, value: "請使用person.gender = ?,給我定義一個性別呀!" }, name: { get: function(){ return this._name; }, set: function(newName) { this._name = newName; } }, age: { get: function(){ return this._age; }, set: function(newAge) { this._age = newAge; } }, gender: { get: function(){ return this._gender; }, set: function(newGender) { this._gender = newGender; } } }); var objPropAttrDescriptor = Object.getOwnPropertyDescriptor(person, "_age"); alert(objPropAttrDescriptor.value); //請使用person.gender = ?,給我定義一個性別呀! alert(objPropAttrDescriptor.configurable); //false; alert(typeof objPropAttrDescriptor.get); // undefined; var objPropAttrDescriptor = Object.getOwnPropertyDescriptor(person, "age"); alert(objPropAttrDescriptor.value);// undefined; alert(objPropAttrDescriptor.configurable); //false; alert(typeof objPropAttrDescriptor.get); // function
在JavaScript中,可以針對任何對象 - 包括DOM和BOM對象,使用Object.getOwnPropertyDescript(object, "objProperty")方法,。
總結如果英語水平足夠好的話,建議看英文原版書籍或者國外大師的博客。畢竟翻譯過來的文字,很多東西都變了味, 而且看英文,會讓你注意力更加集中,不容易跑神。
接下來我會定期翻譯一些國外的精品博客。 立個Flag,促使自己進步。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/98150.html
摘要:是完全的面向對象語言,它們通過類的形式組織函數和變量,使之不能脫離對象存在。而在基于原型的面向對象方式中,對象則是依靠構造器利用原型構造出來的。 JavaScript 函數式腳本語言特性以及其看似隨意的編寫風格,導致長期以來人們對這一門語言的誤解,即認為 JavaScript 不是一門面向對象的語言,或者只是部分具備一些面向對象的特征。本文將回歸面向對象本意,從對語言感悟的角度闡述為什...
摘要:很多情況下,通常一個人類,即創建了一個具體的對象。對象就是數據,對象本身不包含方法。類是相似對象的描述,稱為類的定義,是該類對象的藍圖或原型。在中,對象通過對類的實體化形成的對象。一類的對象抽取出來。注意中,對象一定是通過類的實例化來的。 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); 馬上就要到七夕了,離年底老媽老爸...
摘要:目錄導語理解對象和面向對象的程序設計創建對象的方式的繼承機制原型對象原型鏈與原型對象相關的方法小結導語前面的系列文章,基本把的核心知識點的基本語法標準庫等章節講解完本章開始進入核心知識點的高級部分面向對象的程序設計,這一部分的內容將會對對象 目錄 導語 1.理解對象和面向對象的程序設計 2.創建對象的方式 3.JavaScript的繼承機制 3.1 原型對象 3.2 原型鏈 3.3 與...
摘要:首先,需要來理清一些基礎的計算機編程概念編程哲學與設計模式計算機編程理念源自于對現實抽象的哲學思考,面向對象編程是其一種思維方式,與它并駕齊驅的是另外兩種思路過程式和函數式編程。 JavaScript 中的原型機制一直以來都被眾多開發者(包括本人)低估甚至忽視了,這是因為絕大多數人沒有想要深刻理解這個機制的內涵,以及越來越多的開發者缺乏計算機編程相關的基礎知識。對于這樣的開發者來說 J...
閱讀 633·2021-11-24 09:39
閱讀 3478·2019-08-30 15:53
閱讀 2509·2019-08-30 15:44
閱讀 3237·2019-08-30 12:54
閱讀 2206·2019-08-29 12:23
閱讀 3304·2019-08-26 14:05
閱讀 2101·2019-08-26 13:36
閱讀 3429·2019-08-26 13:33