來細拆 fabric-history 裏面有哪些東西吧
將整個擴展包裹在一個立即執行函數中,可以避免污染全局命名空間。
fabric.Canvas.prototype.initialize = (function (originalFn) {
return function (...args) {
originalFn.call(this, ...args);
this._historyInit();
return this;
};
})(fabric.Canvas.prototype.initialize);
IIFE(Immediately Invoked Function Expression)是指立即執行的函數表達式。它是一種在 JavaScript 中常用的設計模式,用於創建一個立即執行的匿名函數,並且不會污染全局命名空間。
IIFE 的用途:
我們看到了很多有下底線為開頭的命名,這代表的是...?
在 JavaScript 中,前綴 _ 通常用來表示方法或屬性是私有的,但這只是約定俗成的命名規則,並沒有語法上的強制性。實際上,JavaScript 並沒有真正的私有方法或屬性,除非使用 ES6 的 class 語法和 # 前綴來定義私有字段。
命名約定: 使用 _ 前綴表示這個方法是內部使用的,不應該在外部直接調用。這是一種約定,開發者應該遵守這個約定來避免誤用。
實際情況: 雖然 _historyInit
被命名為私有方法,但它仍然可以在外部訪問和調用。這只是通過命名來提示其他開發者這個方法是內部使用的。
所以說這裡的_historyInit
其實是假私有
fabric.Canvas.prototype._historyInit = function () {
this.historyUndo = [];
this.historyRedo = [];
this.extraProps = ['selectable', 'editable'];
this.historyNextState = this._historyNext();
this.on(this._historyEvents());
};
如果你想要真正的私有方法,可以使用 ES6 的 class 語法和 # 前綴:
class Canvas {
#historyProcessing = false;
#historySaveAction() {
if (!this.#historyProcessing) {
// 執行保存操作
console.log('History action saved');
}
}
onHistory() {
this.#historyProcessing = false;
this.#historySaveAction();
}
offHistory() {
this.#historyProcessing = true;
}
}
// 使用示例
const canvas = new Canvas();
canvas.onHistory(); // 會執行保存操作
canvas.offHistory(); // 禁用歷史功能
// 而 #historySaveAction 不能在這邊被拿出來用,只能在 canvas 內部被使用
同場加應,也可以:
(這是虛擬的例子,以展示概念)
const _history = Symbol('history'); fabric.Canvas.prototype[_history] = [];
在 JavaScript 中,Symbol
可以用來模擬私有屬性。雖然 Symbol
屬性不是完全私有的,但它們不會出現在普通的對象屬性枚舉中(例如 for...in
迴圈或 Object.keys
方法),因此可以用來實現更隱蔽的屬性。
Symbol
作為私有屬性創建一個 Symbol
:
首先,創建一個唯一的 Symbol
,這個 Symbol
將用作對象的私有屬性鍵。
const _privateProperty = Symbol('privateProperty');
在對象中使用 Symbol
屬性:
使用這個 Symbol
作為對象屬性鍵,並賦予它值。
class MyClass {
constructor(value) {
this[_privateProperty] = value;
}
getPrivateProperty() {
return this[_privateProperty];
}
}
const instance = new MyClass('secret');
console.log(instance.getPrivateProperty()); // 'secret'
console.log(instance._privateProperty); // undefined
屬性不可枚舉:
使用 Symbol
作為屬性鍵的屬性不會出現在普通的屬性枚舉中。
for (let key in instance) {
console.log(key); // 不會輸出 _privateProperty
}
console.log(Object.keys(instance)); // []
console.log(Object.getOwnPropertyNames(instance)); // []
訪問 Symbol
屬性:
雖然 Symbol
屬性不會出現在普通的屬性枚舉中,但仍然可以通過 Object.getOwnPropertySymbols
方法來訪問。
const symbols = Object.getOwnPropertySymbols(instance);
console.log(symbols); // [Symbol(privateProperty)]
console.log(instance[symbols[0]]); // 'secret'
使用 Symbol
作為私有屬性可以增加屬性的隱蔽性,避免與其他屬性發生衝突,並且不會出現在普通的屬性枚舉中。雖然這種方法並不能完全防止屬性被訪問,但它提供了一種更隱蔽的方式來管理對象的內部狀態。
(這是虛擬的例子,以展示擴展)
const originalRender = fabric.Canvas.prototype.renderAll; fabric.Canvas.prototype.renderAll = function() {
originalRender.call(this);
this.historySaveAction();
};
const originalRender = fabric.Canvas.prototype.renderAll;
fabric.Canvas.prototype.renderAll = function() {
// 調用原有的 renderAll 方法
originalRender.call(this);
// 添加新的功能
this.historySaveAction();
};
提供啟用和禁用歷史功能的方法,並在啟用歷史功能時執行保存操作。
fabric.Canvas.prototype.onHistory = function() {
this.historyProcessing = false;
this._historySaveAction();
};`
fabric.Canvas.prototype.offHistory = function () {
this.historyProcessing = true;
};
fabric.Canvas.prototype._historySaveAction = function (e) {
// 檢查 historyProcessing 屬性,只有在歷史功能啟用時才執行保存操作。
if (this.historyProcessing) return;
//...其他執行紀錄的動作
};
通過這些技術,我們可以設計一個既強大又不影響原有 Fabric.js 代碼的擴展。這種方法保持了代碼的模塊化和可維護性,同時最小化了對現有系統的影響。