JavaScript Mixin類別概念

Read From

傳統物件導向 - 三大核心概念

  • 封裝
  • 繼承
  • 多型

傳統物件導向 - 封裝

  • 將資料與其行為打包的動作
    • 優點:可以隱藏私有資料

傳統物件導向 - 封裝

(封裝的另一層概念)

  • 類別 (藍圖)
    • 資料結構
    • 資料:
      • 應儲存的資訊
    • 行為:
      • 也就是方法
      • 行為才是程式邏輯中在意的點

傳統物件導向 - 封裝

  • 實體 (產品)
    • 利用建構器製作出
    • 經由 複製作業 將類別實體化為物件

傳統物件導向 - 封裝

  • 範例:String
    • 資料:隨意字元組合
    • 行為:計算長度,附加資料,搜尋...etc

傳統物件導向 - 繼承

  • 具體類別 承接於 抽象類別
  • e.x. Vehicle & Car
  • 同樣暗示著複製行為
    • 子類別並沒有與父類別連結在一起
    • 而是父類別將必要的東西複製給子類別

傳統物件導向 - 多型

  • 使用同一個操作介面,以操作不同的物件實例

傳統物件導向 - 多型

class Vehicle {
	engines = 1

	ignition() {
		output( "Turning on my engine." )
	}

	drive() {
		ignition()
		output( "Steering and moving forward!" )
	}
}

class SpeedBoat inherits Vehicle {
	engines = 2

	ignition() {
		output( "Turning on my ", engines, " engines." )
	}

	pilot() {
		inherited:drive()
		output( "Speeding through the water with ease!" )
	}
}

Which ignition will be called?

  • 相對多型
  • 絕對多型

JavaScript Mixin 類別設計

Mixin = Mixed in

將 父類別 的內容複製進(混入) 子類別中

JavaScript Mixin 類別設計

  • 明確 Mixins
    • 拷貝定義
    • 利用明確多型呼叫
  • 隱含 Mixins
    • 不拷貝定義
    • 只利用明確多型呼叫

JavaScript - 明確 Mixins

// vastly simplified `mixin(..)` example:
function mixin( sourceObj, targetObj ) {
	for (var key in sourceObj) {
		// only copy if not already present
		if (!(key in targetObj)) {
			targetObj[key] = sourceObj[key];
		}
	}

	return targetObj;
}

var Vehicle = {
	engines: 1,

	ignition: function() {
		console.log( "Turning on my engine." );
	},

	drive: function() {
		this.ignition();
		console.log( "Steering and moving forward!" );
	}
};

var Car = mixin( Vehicle, {
	wheels: 4,

	drive: function() {
		Vehicle.drive.call( this );
		console.log( "Rolling on all " + this.wheels + " wheels!" );
	}
} );
  • 拷貝定義
  • 利用明確多型呼叫

JavaScript - 明確 Mixins

// "Traditional JS Class" `Vehicle`
function Vehicle() {
	this.engines = 1;
}
Vehicle.prototype.ignition = function() {
	console.log( "Turning on my engine." );
};
Vehicle.prototype.drive = function() {
	this.ignition();
	console.log( "Steering and moving forward!" );
};

// "Parasitic Class" `Car`
function Car() {
	var car = new Vehicle();
	car.wheels = 4;

        // vehDrive = Vehicle::drive()
	var vehDrive = car.drive;
	car.drive = function() {
		vehDrive.call( this );
		console.log( "Rolling on all " + this.wheels + " wheels!" );
	};

	return car;
}

var myCar = new Car();

myCar.drive();
// Turning on my engine.
// Steering and moving forward!
// Rolling on all 4 wheels!

寄生式繼承 (Parasitic inheritance)

JavaScript - 隱含 Mixins

var Something = {
	cool: function() {
		this.greeting = "Hello World";
		this.count = this.count ? this.count + 1 : 1;
	}
};

Something.cool();
Something.greeting; // "Hello World"
Something.count; // 1

var Another = {
	cool: function() {
		// implicit mixin of `Something` to `Another`
		Something.cool.call( this );
	}
};

Another.cool();
Another.greeting; // "Hello World"
Another.count; // 1 (not shared state with `Something`)
  • 不拷貝定義
  • 只利用明確多型呼叫

JavaScript Mixin 類別設計

  • 問題點
    • 會有共用參考的問題

JavaScript Mixin 類別概念

By Chang Henry

JavaScript Mixin 類別概念

  • 12