---
# System prepended metadata

title: JS 核心 23 - 原始型別的包裹物件與原型的關聯、課後練習
tags: ['JS 核心 ', 'JS ', ' JavaScript']

---

---
title: 'JS 核心 23 - 原始型別的包裹物件與原型的關聯、課後練習'
tags: JS 核心 ,JS , JavaScript
description: 2021/02/19
---

JS 核心 -- 原始型別的包裹物件與原型的關聯
===

## 原型 與 純值的關係

### 宣告純值的方法一 : 使用包裹物件把字串a變大寫  
定義純值 : 先宣告一個變數，並把他指向純值  
> toUpperCase() 的方法源自於 String 的原型
```
var a = 'a';
console.log(a);  // a
console.log(a.toUpperCase());  // A (使用包裹物件的方式把字串變大寫)
```

### 宣告純值的方法二 : 在 String 上新增新的原型方法

(第 1 行) String 本身是一個函式，必須搭配 new 運算子才能轉為建構式使用  
> 以建構式建立一個看似純值的東西，但其實建立出來的是一個物件，非純值  
> 不建議以建構式方式建立純值，浪費效能

```typescript=
var b = new String('bcde'); // 此方式定義的是物件型別 (可看到包裹物件的所有方法)，非純值
console.log(b);             // (如下左圖) b 有一個原型，裡面有所有方法
console.dir(String);      // (如下右圖) String 本身是一個函式，有屬於自己的 prototype
console.dir(String.prototype === b.__proto__);  // true

// 在 String 這個建構函式上新增新的原型方法
String.prototype.lastText = function() {   // 修改包裹物件的原型方法
  return this[this.length - 1];            // e  ( 取出最後一個字元 )
}
console.log(b.lastText());                 // 取用 String 的原型方法
```

![](https://i.imgur.com/RD1zaje.png)

<span class="green">**在 String 這個建構函式上新增新的原型方法**</span> 

![](https://i.imgur.com/I38qOTR.png)

### 原型 與 純值

* 純值會有一個包裹物件，包裹物件就是他的原始型別
    * 以數字來說，包裹物件就是 number
    * 以字串來說，包裹物件就是 string
* 原型是共用的，當原型加入其他方法後，此純值的原型也會跟著套用

> 在純值對應的包裹物件新增方法，則純值就可以使用我們新增的方法

```
// var num = 5;  
var num = new Number(5);
Number.prototype.secondPower = function() {
  return this * this;   // 新增平方的方法
}
console.log(num);
console.dir(Number);
console.log(num.secondPower());  // 25 (num 可套用上面所新增的 secondPower 方法)
```
![](https://i.imgur.com/8AbaDDg.png)

### 建構式 : 日期 - 嘗試在 Date 原型新增方法

new Date() 是建構式，Date 本身也是函式

```typescript=
var date = new Date();  // new Date() 是建構式，Date 本身也是函式
console.log(date);      // Fri Feb 19 2021 11:07:50 GMT+0800 (GMT+08:00)
console.dir(Date);      // Date 本身是函式，有許多原型方法供使用
```

可以直接在原型上新增一個方法，用來取得所有時間

```typescript=4
Date.prototype.getFullDate = function() {  // 在 Date 函式的 prototype 新增方法
  var dd = String(this.getDate());
  var mm = String(this.getMonth() + 1);
  var yyyy = this.getFullYear();

  var today = yyyy + '/' + mm + '/' + dd;  // 組裝日期
  return today;
}
console.log(date.getFullDate());           // 2021/02/19
```
**Q : yyyy 也是 number 型態，為什麼不用使用 String()，轉換成字串 ?**  
A : + 這個符號除了數值相加以外，另外具有字串串接的功能，相加過程中，如果數值遇到了 '字串' 型別，就會一律轉為字串型別做串接，因此不需要另外轉型勒。

---
## :pencil2: 繼承與原型鍊 - 章節作業

練習必須透過 Object.create 方式實作多層繼承的原型，可以自行思考一個多層級的主題撰寫。
```
// 底層是冰箱、電視，有各自的方法(可以冷藏 xxx 、可以觀看 xxx)
// 上一層是家電，會耗電
// 最頂層是物件
Refrigerator <
             |- HomeAppliance < Object
Television   <
```
* 條件：
    * 必須使用 Object.create
    * 結構必須正確
    * 每個原型需有獨立的方法
```
function HomeAppliance(callName){  // 建構函式
  this.callName = callName || '家電';
}

function Refrigerator(name, color, use){
  HomeAppliance.call(this, '冰箱');  
  this.name = name;
  this.color = color || '白色';
  this.use = use || '可能壞掉了';
}
Refrigerator.prototype = Object.create(HomeAppliance.prototype);
Refrigerator.prototype.constructor = Refrigerator;

function Television(name, color, use){
  HomeAppliance.call(this, '電視');  
  this.name = name;
  this.color = color || '黑色';;
  this.use = use || '可能壞掉了'
}
Television.prototype = Object.create(HomeAppliance.prototype);
Television.prototype.constructor = Television;
Television.prototype.ok = function () {
  return this.name + this.callName +  '要修理了';
}

var ref = new Refrigerator('國際牌', '白色', '可以冷藏')
var tv = new Television('東元', '黑色', '可以觀看')

console.log(ref);      
// Refrigerator {callName: "冰箱", name: "國際牌", color: "白色", use: "可以冷藏"}
console.log(tv);       
// Television {callName: "電視", name: "東元", color: "黑色", use: "可以觀看"}
console.log(tv.ok());  // 東元電視要修理了
```

## :pencil2: 繼承與原型鍊 - 課後練習

以下範例需貼至「開發人員工具」的 "console" 才可執行。

```
function sayHi () {
  var name = 'Casper';
    return name;
}
sayHi.name = 'Mark';
sayHi.__proto__.hello = function() {
  return this.name;
}
console.dir(sayHi);  // (如下圖) 來看一下 sayHi 的函式內容
console.log(sayHi.hello()); // sayHi
```
1. 透過 function 所建立的函式，會帶上一個 name 的屬性，因此 console.dir(sayHi) 可以看到 name: "sayHi"。
2. 這個 name 屬性有點特別，他是不可被變更的，可以使用以下方法檢視 Object.getOwnPropertyDescriptor(sayHi, 'name') 得到結果會是不可寫入。

![](https://i.imgur.com/HKw0uVZ.png)




## :memo: 學習回顧  
:::info
* 純值會有一個包裹物件，包裹物件就是他的原始型別
    * 以數字來說，包裹物件就是 number
    * 以字串來說，包裹物件就是 string
* 原型是共用的，當原型加入其他方法後，此純值的原型也會跟著套用
* **" 加 "** 運算子功能
    * 數值相加
    * 字串串接
    * 相加過程中，如果數值遇到了 '字串' 型別，就會一律轉為字串型別做串接
* function 會自動帶上一個 name 的屬性，此屬性不可寫入。
:::



<style>
.red {
  color: red;
}
.green {
  color: green;
}
</style>