Javascript 组合如何扩展一个对象
Javascript composition how to extend an object
我正在尝试采用新的 ES6 功能来更好地组合没有 class 继承的对象。但是,我不明白如何扩展和反对或覆盖函数。下面的代码使用 Object.assign().
的组合模式
问题:如何在不复制整个 TextCell 对象的情况下创建第二个对象工厂 UnderlinedCell。它的行为类似于 TextCell,只是 getHeight returns getHeight + 1 和 draw 方法添加了一条带有破折号“-”的线。
我为什么要这样做?:我正在努力了解本视频中解释的构图概念 Composition over Inheritance from Johansson
下面是对象工厂 TextCell 的代码。
const getHeight = (state) => ({
getHeight: () => state.text.length
})
const draw = (state) => ({
draw: () => state.text.join(" | ")
})
const TextCell = (text) => {
let state = {
text: text.split("\n")
}
return Object.assign(getHeight(state), draw(state))
}
console.log("The height of the cell is %s", TextCell("foo\nbar").getHeight())
console.log(TextCell("foo\nbar").draw())
首先,state
是私有的。这意味着我们只能使用 TextCell
的公开 public 方法,即 getHeight
和 draw
.
现在,UnderlineCell
是一个函数,它组合了 TextCell
并且 扩展了 TextCell
的实现。 例如
const getHeight = (state) => ({
getHeight: () => state.text.length
})
const draw = (state) => ({
draw: () => state.text.join(" | ")
})
const TextCell = (text) => {
const state = {
text: text.split("\n")
}
return Object.assign(getHeight(state), draw(state))
}
const UnderlineCell = (text) => {
const textCell = TextCell(text);
const getHeight = () => textCell.getHeight() + 1;
const line = '\n------\n';
const draw = () => textCell.draw().replace(' | ', line) + line;
return {...textCell, getHeight, draw};
}
const uCell = UnderlineCell('hello\nthis');
console.log(uCell.draw());
我仍然不明白为什么在给定的代码中使用闭包的复杂性而不是使用常规的 this
,因为这也很好,而且给定的代码看起来不那么可维护,最糟糕的是它隐藏了原始的 state
对象,这阻止了以后扩展对象。您可以这样重写它并实现您的要求:
const getHeight = () => ({
getHeight() {
return this.text.length
}
})
const draw = () => ({
draw() {
return this.text.join(" | ")
}
})
const TextCell = (text) => {
let state = {
text: text.split("\n")
}
return Object.assign(state, getHeight(), draw())
}
const TextCell2 = (text) => {
let state = TextCell(text)
var originalGetHeight = state.getHeight;
return Object.assign(state, {
getHeight() {
// use the original original getHeight and append + 1
return originalGetHeight.call(this) + '1'
},
draw() {
return this.text.join(" - ")
}
});
}
console.log("The height of the cell is %s", TextCell("foo\nbar").getHeight())
console.log(TextCell("foo\nbar").draw())
console.log("The height of the cell is %s", TextCell2("foo\nbar").getHeight())
console.log(TextCell2("foo\nbar").draw())
但是如果你想继续使用隐藏 state
对象的方法,那么你需要复制 TextCell
中的代码,因为 state
对象是在工厂方法中创建的只能在那里和由 getHeight(state)
和 draw(state)
.
创建的闭包中访问
我正在尝试采用新的 ES6 功能来更好地组合没有 class 继承的对象。但是,我不明白如何扩展和反对或覆盖函数。下面的代码使用 Object.assign().
的组合模式问题:如何在不复制整个 TextCell 对象的情况下创建第二个对象工厂 UnderlinedCell。它的行为类似于 TextCell,只是 getHeight returns getHeight + 1 和 draw 方法添加了一条带有破折号“-”的线。
我为什么要这样做?:我正在努力了解本视频中解释的构图概念 Composition over Inheritance from Johansson
下面是对象工厂 TextCell 的代码。
const getHeight = (state) => ({
getHeight: () => state.text.length
})
const draw = (state) => ({
draw: () => state.text.join(" | ")
})
const TextCell = (text) => {
let state = {
text: text.split("\n")
}
return Object.assign(getHeight(state), draw(state))
}
console.log("The height of the cell is %s", TextCell("foo\nbar").getHeight())
console.log(TextCell("foo\nbar").draw())
首先,state
是私有的。这意味着我们只能使用 TextCell
的公开 public 方法,即 getHeight
和 draw
.
现在,UnderlineCell
是一个函数,它组合了 TextCell
并且 扩展了 TextCell
的实现。 例如
const getHeight = (state) => ({
getHeight: () => state.text.length
})
const draw = (state) => ({
draw: () => state.text.join(" | ")
})
const TextCell = (text) => {
const state = {
text: text.split("\n")
}
return Object.assign(getHeight(state), draw(state))
}
const UnderlineCell = (text) => {
const textCell = TextCell(text);
const getHeight = () => textCell.getHeight() + 1;
const line = '\n------\n';
const draw = () => textCell.draw().replace(' | ', line) + line;
return {...textCell, getHeight, draw};
}
const uCell = UnderlineCell('hello\nthis');
console.log(uCell.draw());
我仍然不明白为什么在给定的代码中使用闭包的复杂性而不是使用常规的 this
,因为这也很好,而且给定的代码看起来不那么可维护,最糟糕的是它隐藏了原始的 state
对象,这阻止了以后扩展对象。您可以这样重写它并实现您的要求:
const getHeight = () => ({
getHeight() {
return this.text.length
}
})
const draw = () => ({
draw() {
return this.text.join(" | ")
}
})
const TextCell = (text) => {
let state = {
text: text.split("\n")
}
return Object.assign(state, getHeight(), draw())
}
const TextCell2 = (text) => {
let state = TextCell(text)
var originalGetHeight = state.getHeight;
return Object.assign(state, {
getHeight() {
// use the original original getHeight and append + 1
return originalGetHeight.call(this) + '1'
},
draw() {
return this.text.join(" - ")
}
});
}
console.log("The height of the cell is %s", TextCell("foo\nbar").getHeight())
console.log(TextCell("foo\nbar").draw())
console.log("The height of the cell is %s", TextCell2("foo\nbar").getHeight())
console.log(TextCell2("foo\nbar").draw())
但是如果你想继续使用隐藏 state
对象的方法,那么你需要复制 TextCell
中的代码,因为 state
对象是在工厂方法中创建的只能在那里和由 getHeight(state)
和 draw(state)
.