# JS - Object ###### tags: `Language-js` ## 前言 #### 我想用『點-線-面』的方式理解js中物件的關係。 1. 點 : 單一一個物件的特性。 - 物件的宣告。 - 物件的屬性和方法。 - 屬性描述器。 - 物件包裹器。 2. 線 : ```Prototype-Based```的物件繼承。 - ```Prototype-Based```的概念。 - ```Javascript```下的物件繼承。 - ```__proto__```和```prototype```。 4. 面 : 深入ES6的class語法。 - ```ES6 class```的語法。 - ```ES6 class```下的物件繼承。 - ```ES6 class```和```ES5 prototype```的不同。 ## 一 . 點 : Js中的物件 ### (一) 物件的宣告 #### 方法一 : 最基本的方法,用大括號包住。 ```javascript= let obj={ name: "student", age : 20, say : function(){ console.log("hello" + this.name); } } ``` #### 方法二 : 模擬建構式的方法產生。 - step 1 : 新建一個空的物件存在 - step 2 : 在將物件傳入function call,並將function的this指向物件。 ```javascript= function obj(name, age){ this.name=name; this.age=age; } let myobj=new obj('steven',20); // step 1 : myobj={} // step 2 : obj.call(myobj,'steven',20) ``` --- ### (二). 物件的屬性和方法 #### 1. 定義屬性和方法 : - ```Attribute``` : 變數,存放object的某種特質。 - ```Method``` : 函式,可以使object的某種特性顯示,或表現特定行為。 #### 2. 新增屬性和方法 : - 方法一 : 用```[obj].[attrname]```設定value。 - 方法二 : 用```Object.defineproperty```設定屬性描述器。 ```javascript= let obj={ name: "student", age : 20, say : function(){ console.log("hello" + this.name); } } obj.newthing='new!!'; // 新增newthing屬性在obj中。 ``` #### 3. 刪除屬性和方法 : - 方法 : 直接用『delete 物件.屬性名』即可。 ```javascript= let obj={ name: "student", age : 20, say : function(){ console.log("hello" + this.name); } } delete obj.newthing; // 刪除newthing屬性在obj中。 ``` #### 4. 檢查屬性和方法 : - 方法一 : ```'屬性名' in 物件```,會向上檢查原型鍊 - 方法二 : ``` 物件.hasOwnProperty('屬性名')```,不會上檢查原型鍊。 --- ### (三). 物件的屬性描述器 #### 屬性描述器 : ES5後加入,用來描述js的物件中『每一個』屬性的特性。 - 每一個屬性都有下面六個描述器喔,不是一個物件只有一個。 - ```value``` : 代表屬性的值。 - ```writable``` : 代表屬性是否可以改變,否的話為唯讀屬性。 - ```enumerable``` : 代表屬性是否可以被for-in迭代讀出。 - ```configurable``` : 代表屬性的wirtable、enumerable、configurable是否可以被改變,也代表是否可以被刪除。 - ```get``` : 函式,定義輸出函式。 - ```set``` : 函式,定義輸入函式。 - 設定方法 : ```Object.defineProperty(obj, 'prop', descriptor)``` - 呼叫原生函式的方法。 - 注意 : descriptor必須為物件。 ```javascript= let obj={}; Object.defineProperty(obj, 'name', { value: 'steven', writable: false, enumerable: true, configurable: true }) ``` #### 預設屬性描述器 : - 若用一般的變數定義或用new定義 : 則屬性描述器都為true。 - 若用Object.defineProperty定義 : 未清楚描寫時,都為false。 ```javascript= let obj={}; Object.defineProperty(obj, 'name', { value: 'steven' }) // writable enumerable configurable 都為false。 let obj={ name: 'steven' } // writable enumerable configurable 都為true。 ``` #### set和get函式 : set和get必須同時存在,且有set、get時,不能有value和wirtable性質。 - set和get是再定義另外一個變數存放這個屬性的值。 - 注意讀入和讀出的方法。 ```javascript= let obj={}; Object.defineProperty(obj,'attribute',{ get: function(){ console.log("name : "+ this.name); }, set: function(name){ this.name=name; } }); obj.attribute='steven'; // 設定atrribute這個屬性為steven。 obj.attribute; // 直接輸出atrribute這個屬性。 ``` --- ### (四) . 物件包裹器 #### 1. JS的原生函式 - JS的內建模擬ctor : 我們之前常常看到,運用JS時有基本類別的名稱為ctor。 - Number()。 - String()。 - Object()。 - 用new的觀念去理解 : 是一個內建函式,new出來的當然是物件拉。 ```javascript= let str=new String("steven"); typeof str; //Object ``` #### 2. 原生包裹器 - 基礎型別可以有屬性的存取 : - js在我們存取基礎型別時,會先將它轉成物件。 - 在存取完後,在轉回基礎型別。 ```javascript= let str='string'; console.log(str.length); // step 1 : str=new String('string'); // step 2 : console.log(str.length); // step 3 : str=nll; // step 4 : str='string' ``` - 易有錯誤 : 可以存取基礎型別的時候設定屬性,但該行結束後,那個屬性就消失了。 ```javascript= var str = 'Hello'; typeof str; // "string" var strObj = new String('Hello'); typeof strObj; // "object" // 像這樣,分別為物件與基本型別的 string 設定「屬性」 strObj.color = 'red'; str.color = 'red'; console.log( strObj.color ); // 'red' console.log( str.color ); // undefined ``` --- ## 二 . 線 : JS的原型與繼承關係 ### (一) . ```Prototype-based```的概念 #### 1. ```Class-based```的```OOP``` : - 概念 :由抽象的類別樹,經由```new```去實作出物件的存在。 - ```Constructor```時 : 建構一個物件,會先從其繼承的跟物件開始建構,再到當前物件。 ![](https://i.imgur.com/xW9g5oy.png =420x) #### 2. ```Prototype-based```的```OOP``` : - 概念 : 沒有類別的存在,新建出的物件直接連上原有的繼承關係。 - ```Constructor```時 : 僅建構當前物件,再將其連接上已經存在的物件樹上面。 ![](https://i.imgur.com/b6SAXiQ.png =350x) #### 3. ```JS```的原型鍊: - 實作 : ```Prototype-based```,已經在環境中定義好許多物件。 - 原型鍊 : 如上圖,JS的物件以父子關係存在的一串關係,就稱為原型鍊。 - 原型 : 代表物件的父物件。 - 單一繼承 : 每一個物件的原型唯一,所以```JS```沒有多繼承。 ```javascript= /* * /------/ * | Root | age : 15 * /------/ * ↗︎ ↖︎ * /----/ /----/ value : 20 * | A | | B | function : Method * /----/ /----/ * ↑ * /----/ * | C | function : Method * /----/ * */ ``` - 特性 : - 可以繼承```attribute```和```method``` : 可以沿著原型鍊找。 - 可以```Override``` : 可以定義同原型```function name```的```function```,重新定義```function```行為。 --- ### (二) . ```Javascript```下的物件繼承。 #### 1.```setPrototypeOf```和```__proto__``` : - ```Object.setPrototypeOf``` : 將兩個物件以繼承關係相連。 - 參數 : ```setPrototypeOf(obj, prototype)```。 - 功用 : 將```obj```物件的原型設為```prototype```。 ```javascript= let father = {fatherVal : "father"}; let Child = {childVal : "Child"}; Object.setPrototypeOf(Child, father); console.log(Child.fatherVal); // "father" /* * father child * /------------/ /-----------/ * | fatherVal | <--- | childVal | * /------------/ /-----------/ * * */ ``` - ```__proto__```屬性 : ```JS```繼承關係的關鍵屬性。 - 功用 : 指向**原型物件,代表物件的原型鍊查詢的方向**。 - ```Object.getPrototypeOf(obj)``` : 推薦使用,```__proto__```不一定支援。 ```javascript= let father = {fatherVal : "father"}; let Child = {childVal : "Child"}; Object.setPrototypeOf(Child, father); console.log(Child.__proto__ === father); // "ture" ``` #### 2. ```function ctor```和```prototype``` : - ```function ctor``` : 模擬```ctor```的行為。 - 因:```new```關鍵字特性是會自動回傳一個新的物件。 - 果:可以用```function```當作一個類別的```ctor```。 ```javascript= function Createor(name){ this.name = name; } let Me = new Createor("steven"), other = new Createor("Other") ``` - ```prototype```屬性 : 連結```function ctor``` 建構出來的物件的```__proto__```屬性。 - ```JS```自動生成的```prototype```物件 : 當一個```function```定義時,```JS```會自動定義一個物件,以```function.prototype```的方式連上function。 - ```JS```的```OOP```機制:**當```new```使用時,新增物件自動連上```function prototype```**。 ```javascript= function Createor(name){ this.name = name; } let Me = new Createor("steven") console.log(Createor.prototype === Me.__proto__) /* /----------/ /-------------/ * | Createor | prototype | Createor | * | function | -------> | Prototype | * /----------/ /-------------/ * | ⥣ __proto__ * | new /-----/ * |----------------> | Me | * /-----/ * / ``` #### 3. ```JS```的```OOP```使用技巧 - ```Function```的```prototype```是同```function ctor```的共同```__proto__```。 - 概念 : 可以視為代表『類別』的實體物件。 - 小錯誤 : 若改動```Function```的```prototype```,同此```function ctor```的物件都會受影響。 ```javascript= function Createor(age){ this.age = age; } Createor.prototype.name = "steven"; let Me = new Createor(20),Other = new Createor(19); Createor.prototype.name = "Test"; ``` - 應該將同類(```class```)物件的```method```定義在```function ctor```中 - 因 : ```new```關鍵字會對每一次使用新增一個物件。 - 果 : 若將```method```寫在裡面,會造成每一個物件都是不同```method```,非共用一個。 ```javascript= /* * Bad Example * */ function Createor(){ this.method = function(){}; } let Me = new Createor(), Other = new Createor(); console.log(Me.method === Other.method); // false /* * Good Example * */ function Createor(){ } let Me = new Createor(), Other = new Createor(); Createor.method = function(){} console.log(Me.method === Other.method); // true ``` --- ### (三) . ```Proto```和```Prototype```屬性 #### 1. ```Proto```屬性 : 物件都有。 - 指向 : 建構子的prototype。 - 自訂物件 : 指向自訂建構子的prototytpe。 - 內建物件 : 不一定有proto屬性,有的話多指向Object.prototype。 #### 2. ```Prototype```屬性 : 建構子(函式)才有。 - 指向 : prototype物件。 - 自訂函式 : 系統會自動產生函式的prototytpe。 - 內建函式 : 指向內建的prototype。 - ```Number()```指向``` Number.prototype。``` 。 - ```String()```指向``` String.prototype。``` 。 - ```Boolean()```指向``` Boolean.prototype。``` 。 - ```Object()```指向``` Object.prototype。``` 。 - ```Array()```指向``` Array.prototype。``` 。 - ```Function()```指向``` Function.prototype。``` 。 #### 3. $Summary$```JS```的繼承機制 : - **用proto屬性將父子關係建立**。 - **用prototype屬性建立新物件的父子關係**。 - ```PitFall``` : **這種方法只適建立單層的原型鍊,非多層**。 - 情況 : 如果我今天想要多重繼承呢。 - 問題 : **如果我接上```prototype```的話,不可能在```prototype```設定屬性(因為是共用的)。如果我連上```object```的話,我需要用```setPrototypeOf```(因為```function ctor```不會幫你連上)**。 ```javascript= /* * /----------/ /--------/ * | Person | | Person | * | Prototype| | object | * /----------/ /--------/ * ? ⬉ ⬈ ? setPrototypeOf * /----------/ /-------------/ * | Student | prototype | Student | * | function | -------> | Prototype | * /----------/ /-------------/ * | ⥣ __proto__ * | new /-----/ * |----------------> | Me | * /-----/ * / ``` --- ## 三 . 面 : ES6的Class語法 ### (一) . ```ES6 class```語法 #### 1. 物件的宣告 - $class$ : 定義一個function name,且method不用用『,』隔開。 - $constructor$ : 定義function name的實際行為 - $other\ function$ : 不是constructor的函式, **都會變成class.prototype的**。 ```javascript= class Person(){ constructor(name, age){ this.name=name; this.age=age; } say(){ console.log("Name: "+this.name+ "\n age : "+ this.age); } } ``` #### 2. 物件的繼承 - $extend$ : 使function prototype的proto屬性指向指定父物件。 - 本function prototype的proto屬性預設為Object.prototype。 - extend關鍵字使得可以自動指向需要的父物件。 - $super$ : 用於override,可以指定呼叫父物件的方法。 - 在OOP中,常常遇到父與子有同樣的名稱的函式屬性。 - super可以指定呼叫父物件的同名函式屬性,也可以呼叫ctor。 - $super$使用的限制 : 只可以在物件方法中使用。 - 物件方法 : 方法就是一個函式。 - 屬性方法 : 視為屬性,但是存著方法。 - 注意 : **一般宣告函式的方法,在class不可以用**。 ```javascript= class Person(){ //物件方法 say(){// do some thing} //屬性方法 say : function (){// do some thing} // Error function say(){} } ``` ### (二) . ```ES6 class```下的物件繼承 ```javascript= class Person(){ constructor(name, age){ this.name=name; this.age=age; } say(){ console.log("Name: "+this.name+ "\n age : "+ this.age); } } /////// 上下相同 function Person(name, age){ this.name=name; this.age=age; } Person.prototype.say=function(){ console.log("Name: "+this.name+ "\n age : "+ this.age); } ``` ### (三) . ```ES6```和```ES5```的繼承差異