原型與繼承(1) - 建構式函式 & 閉包


Posted by TempuraEngineer on 2022-08-13

目錄


建構函式(constructor function)

建構函式是用來建立實例(物件)的函式,和class一樣以大寫開頭

相對於class,優點是效能較好所有引擎都支援,缺點則是初始化後物件裡面可能會有一些你不想要的方法

function Person(name, skill) {
    this.name = name;
    this.skill = skill;
}

const Alex = new Person('Alex', 'programming');

Alex.name; // Alex

另外需要注意的是this在建構函式裡並沒有指向,直到使用new建立一個物件,this才指向該物件

In a constructor function this does not have a value. It is a substitute for the new object.
The value of this will become the new object when a new object is created.

function Person(name, skill) {
    this.name = name;
    this.skill = skill;
}

// 沒有使用new關鍵字建立實例,stranger是undefined
const stranger = Person('unknown', 'secret'); 

stranger.name; // TypeError: Cannot read properties of undefined (reading 'name')


非共同的屬性

為了讓this指到該函式,所以不要用箭頭函式,不然會指到window

clock.saySomethingRidiculous = function(content){
    return `${this.name}: ${content}`;
}

clock.saySomethingRidiculous('粗暴言論大可不必');
Alex.saySomethingRidiculous('粗暴言論大可不必'); // Alex.saySomethingRidiculous is not a function

但如果人分為普通人、政治人物2種,前者要有saySomethingRidiculous,後者則不呢?

不成要加好多次saySomethingRidiculous🤔?


call & 繼承

透過繼承子建構函式可有和其父一樣的屬性,並幫子建構函式定義只有他才有的屬性,如此就能解決上一段最後的問題

function Person(name, skill) {
    this.name = name;
    this.skill = skill;
}

function Politician(name, skill) {
    // 使用call將Person的指向改變
    // 使用call是因為call會立刻呼叫,且它能接師多個參數
    Person.call(this, name, skill);
}

// 將共同的方法加在prototype上,這樣可以避免降低效能、減少記憶體使用量
Politician.prototype.saySomethingRidiculous = function(content){
    return `${this.name}: ${content}`;
}

const Alex = new Person('Alex', 'programming');
const clock = new Politician('時鐘', '報數');

Alex.saySomethingRidiculous('粗暴言論大可不必'); // Alex.saySomethingRidiculous is not a function
clock.saySomethingRidiculous('粗暴言論大可不必');


閉包

閉包是一個函式,它會回傳一個函式或物件。藉此建出一個獨立作用域(scope),且在函式執行完後作用域仍然存在

A closure is a function having access to the parent scope, even after the parent function has closed.

因為閉包的作用域是獨立的,所以可以避免全域汙染但也因此會降低效能、消耗更多記憶體

each function instance manages its own scope and closure. Therefore, it is unwise to unnecessarily create functions within other functions if closures are not needed for a particular task, as it will negatively affect script performance both in terms of processing speed and memory consumption.


模擬私有

在ES6的class以前,原生Javascript並沒有私有概念,私有某屬性或方法只能被同class的屬性或方法取用

但閉包仍可以模擬私有,只要不return屬性、方法,就會保持私有

function count() {
    // num是私有變數,不能從外部存取
    let num = 0;

    // 內部的匿名函式可以取用外部函式作用域裡的num
    // 它會被return,所以是公有方法,可以從外部呼叫
    return function(){ 
        num++; 
        return num;
    }
}

const add = count();
const addNumber = count();

add(); // 1
add(); // 2

// addNumber不受add影響
addNumber(); // 1


模擬繼承

閉包的繼承方式和建構函式幾乎相同,都需要使用bind或call來改變指向

function Person(name, useSkill){
    return {
        name,
        useSkill,
    }
}

function Politician(name, useSkill, party){
    // 使用call改變this指向來繼承Person的屬性
    const proto = Person.call(this, name, useSkill);

    // 新增Politician才有的屬性
    proto.party = party;

    proto.saySomethingRidiculous = function(content){
        return `${this.name}: ${content}`;
    }

    return proto;
}

function count() {
    let num = 0;

    return function(){ 
        num++; 
        return num;
    }
}

function response(condition, negativeRes, positiveRes){
    const add = count();

    return function(){
        const num = add();

        return num > condition? negativeRes : positiveRes;
    }
}

const Alex = Person('Alex', response(4, 'Alex累了,想去睡了', 'Alex正在寫程式'));
const clock = Politician('clock', response(2, `就說我負責了,還想怎樣`,'我負責'), 'Dqp');

for(let i = 0; i < 5; i++){
    console.log(`${Alex.useSkill()} | ${clock.useSkill()}`);
}

// Alex正在寫程式 | 我負責
// Alex正在寫程式 | 我負責
// Alex正在寫程式 | 就說我負責了,還想怎樣
// Alex正在寫程式 | 就說我負責了,還想怎樣
// Alex累了,想去睡了 | 就說我負責了,還想怎樣


參考資料

W3C - Object Constructors
MDN - Object.prototype.constructor
javascript 一定得懂的物件概念(2)

MDN - Closure
為什麼我們需要閉包


#constructor function #closure #建構函式 #閉包







Related Posts

七天學會 swift - NSPredicate Day4

七天學會 swift - NSPredicate Day4

地圖嵌入網頁- MapBox

地圖嵌入網頁- MapBox

認識rem單位

認識rem單位


Comments