# 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```時 : 建構一個物件,會先從其繼承的跟物件開始建構,再到當前物件。

#### 2. ```Prototype-based```的```OOP``` :
- 概念 : 沒有類別的存在,新建出的物件直接連上原有的繼承關係。
- ```Constructor```時 : 僅建構當前物件,再將其連接上已經存在的物件樹上面。

#### 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```的繼承差異