iT邦幫忙

2021 iThome 鐵人賽

DAY 22
2
Modern Web

初學者跪著學JavaScript系列 第 22

初學者跪著學JavaScript Day22 : 我要原型繼承,constructor又不走丟

一日客語:中文:晚上 客語:暗晡am buˊ  

function Plant() {}
function Apple() {}
Apple.prototype.shape = 'circle';

Apple.prototype = new Plant();//現在Apple的prototype是放 Plant的實例
const myapple = new Apple();
console.log(myapple.constructor);//[Function: Plant]

使用原型繼承會讓constructor指向Plant function,使myapple的constructor不是Apple function,從程式碼的寫法來說明明是建構函式Apple所建立的實例,但顯示的建構子會是plant function。
原本constructor可以判斷由哪一個函式建立,現在卻不是預期的樣子

如果使用原型繼承又要讓constructor是Apple?


Object.defineProperty()

語法:Object.defineProperty(obj, prop, descriptor)

對物件設定新屬性或修改物件屬性回傳設定好的物件

Object.defineProperty(定義的物件, 屬性,屬性描述子)

屬性描述子(property descriptor)是什麼?

物件內的每一個屬性都有property descriptor

  1. configurable
  2. enumerable
  3. value
  4. writable
  5. get
  6. set
property descriptor 說明
configurable 物件的property 是否可以被修改/刪除
enumerable 物件的property是否被for/in 迴圈Object.keys()
writable 物件的property的值是否能被改變
value 設定屬性值
get 定義取值getter function 可以取得屬性值,不能和value/writable一起設定
set 定義設值setter function 可以設定屬性指派的值,不能和value/writable一起設定

所以descriptor也是一個物件

property descriptor的 writable

這個屬性的值可以修改嗎
true:可以修改
false:不可以修改

const apple = {};

Object.defineProperty(apple, 'color', {
    value: 'red',
    writable: false,
});

apple.color = 'gold';
console.log(apple.color);//red
const apple = {};

Object.defineProperty(apple, 'color', {
    value: 'red',
    writable: true,
});

apple.color = 'gold';
console.log(apple.color);//gold

property descriptor的enumerable

是否可以被for/in迭代列出

目標:設定apple的shape屬性,是不可以被for/in迭代列出
作法:把property descriptor的enumerable設成false

let apple = {};
Object.defineProperty(apple, 'color', { value: 'red', enumerable: true });
Object.defineProperty(apple, 'shape', { value: 'circle', enumerable: false });
Object.defineProperty(apple, 'size', { value: 'small', enumerable: true });
Object.defineProperty(apple, 'price', { value: 10000, enumerable: true });

for (let i in apple) {
    console.log(i);
}
//color
//size
//price

property descriptor的getter

取值時,啟動get,return 出fruit[0]//apple出去

const map = {
    fruit: ['apple', 'orange', 'banana'],
    get eat() {
        return this.fruit[0];
    },
    set eat(value) {
        this.fruit[0] = value;
    },
};

console.log(map.eat);//apple


property descriptor的setter

賦值時,啟動set,會把value(water)賦值到fruit[0]

const map = {
    fruit: ['apple', 'orange', 'banana'],
    get eat() {
        return this.fruit[0];
    },
    set eat(value) {
        this.fruit[0] = value;
    },
};

map.eat = 'water';

//現在fruit: ['water', 'orange', 'banana']

使用這個方式解決constructor

function Go() {}
function Run() {}
Go.prototype.school = 'sleep';
Run.prototype = new Go();
const student = new Run();

console.log(student.constructor);//[Function: Go]

解決方式:就是在property descriptor 讓Apple.prototype的constructor設定為 Apple,且不讓他被改寫也讓他不要出現在for-in迴圈

function Plant() {}
function Apple() {}
Apple.prototype.shape = 'circle';

Apple.prototype = new Plant();
const myapple = new Apple();
Object.defineProperty(Apple.prototype, 'constructor', {
    value: Apple,
    enumerable: false,
    writable: false,
});
console.log(myapple.constructor); //Function: Apple]

這樣方式既可以繼承,constructor也是指向當初建立的函式

學完一整套原型鏈~讚

資料參考:
忍者開發技巧探秘第二版
mdn


上一篇
初學者跪著學JavaScript Day21 : 原型畢露(下)
下一篇
初學者跪著學JavaScript Day23 : 閉包簡單用
系列文
初學者跪著學JavaScript30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言