如何解决JS 中的组合 只创建一个对象并赋值给它不要使用箭头函数,而是使用this混合方法
我正在学习 JS 中的组合概念。下面是我的演示代码。
moveBy
函数将值正确分配给 x
和 y
。
但是,setFillColor
函数不会将传递的值分配给 fillColor
。
const withMoveBy = (shape) => ({
moveBy: (diffX,diffY) => {
shape.dimensions.x += diffX;
shape.dimensions.y += diffY;
},});
const withSetFillColor = (shape) => ({
setFillColor: (color) => {
console.log(shape.fillColor); // 1
shape.fillColor = color;
shape.dimensions.fillColor = color;
console.log(shape.fillColor); // 2
},});
const shapeRectangle = (dimensions) => ({
type: 'rectangle',fillColor: 'white',dimensions,});
const shapeCircle = (dimensions) => ({
type: 'circle',});
const createShape = (type,dimensions) => {
let shape = null;
switch (type) {
case 'rectangle': {
shape = shapeRectangle(dimensions);
break;
}
case 'circle': {
shape = shapeCircle(dimensions);
break;
}
}
if (shape) {
shape = {
...shape,...withSetFillColor(shape),...withMoveBy(shape),};
}
return shape;
};
let r = createShape('rectangle',{
x: 1,y: 1,width: 10,height: 10,});
let c = createShape('circle',{ x: 10,y: 10,diameter: 10 });
r.moveBy(2,3);
c.moveBy(1,2);
r.setFillColor('red');
c.setFillColor('blue');
console.log(r);
console.log(c);
输出:
标记为 // 1
的行在矩形和圆形的情况下打印 white
。
标记为 // 2
的线为矩形打印 red
,为圆形打印 blue
。
最终输出为:
{
"type": "rectangle","fillColor": "white","dimensions": {
"x": 3,"y": 4,"width": 10,"height": 10,"fillColor": "red"
}
}
{
"type": "circle","dimensions": {
"x": 11,"y": 12,"diameter": 10,"fillColor": "blue"
}
}
作为对象属性的fillColor
仍然是white
。
但是,dimensions
中的那个取了正确的值。
解决方法
问题源于 createShape
中的这个作业 - 我的注释:
// creating the "new object"
shape = {
...shape,// shallow copying of the "old object"
...withSetFillColor(shape),...withMoveBy(shape),};
在这里,您创建一个新对象,它由:
- 现有
...shape
的浅拷贝属性(类型、填充颜色、尺寸,这是一个对象) -
setFillColor
,一个绑定到shape
(旧对象)的闭包 -
moveBy
,一个绑定到shape
(旧对象)的闭包
执行此语句后,您创建了两个形状:
- 方法操作的“旧对象”
- 返回的“新对象”
在从旧对象复制的属性中,只有维度是非原始值,因此在实例之间共享。
然后,当你打电话时:
r.moveBy(2,3);
它改变了 oldShape.dimensions
,但它与 newShape.dimensions
是同一个对象,所以它在输出中可见。
但是,这个调用:
r.setFillColor('red');
修改了您没有看到的 fillColor
的 oldShape
属性。它还写入 oldShape.dimensions.fillColor
,它再次在对象之间共享,因此更改在两者中都是可见的。
让我通过重写您的代码来说明问题。我删除了一些细节,只关注这个问题。在代码中添加了注释和日志记录以更清楚地显示发生了什么:
const withSetFillColor = (shape) => ({
setFillColor: (color) => {
console.log(`now changing shape with id [${shape.id}]`);
shape.fillColor = color;
shape.dimensions.fillColor = color;
},});
const shapeRectangle = (dimensions) => ({
id: 1,//add an ID of the created object for illustrative purpose
type: 'rectangle',fillColor: 'white',dimensions,});
const createShape = (type,dimensions) => {
//variable is now named 1 to showcase what happens
let shape1 = null;
switch (type) {
case 'rectangle': {
shape1 = shapeRectangle(dimensions);
break;
}
}
//this is effectively what happens when you clone and reassign an object:
//a *second one* is created but the first one persists
let shape2 = null;
if (shape1) {
shape2 = {
...shape1,...withSetFillColor(shape1),id: 2,//make it a different ID for illustrative purpose
};
}
console.log(`Created shape1 and shape2 and they are the same: ${shape1 === shape2}`);
console.log(`The dimensions object is the same: ${shape1.dimensions === shape2.dimensions}`);
return shape2;
};
let r = createShape('rectangle',{
x: 1,y: 1,width: 10,height: 10,});
r.setFillColor('red');
console.log(r);
您创建和操作两个不同的对象。这就是为什么代码为对象分配了一个属性但它看起来好像没有改变的原因。
有几种方法可以解决这个问题。
只创建一个对象并赋值给它
如果您使用 Object.assign()
,您可以直接更改一个对象而不是两个相互竞争的对象。因此,将对象传递给 withX()
函数将按预期工作。
const withMoveBy = (shape) => ({
moveBy: (diffX,diffY) => {
shape.dimensions.x += diffX;
shape.dimensions.y += diffY;
},});
const withSetFillColor = (shape) => ({
setFillColor: (color) => {
shape.fillColor = color;
shape.dimensions.fillColor = color;
},});
const shapeRectangle = (dimensions) => ({
type: 'rectangle',});
const shapeCircle = (dimensions) => ({
type: 'circle',dimensions) => {
let shape = null;
switch (type) {
case 'rectangle': {
shape = shapeRectangle(dimensions);
break;
}
case 'circle': {
shape = shapeCircle(dimensions);
break;
}
}
if (shape) {
//use Object assign to only manipulate one `shape` object
Object.assign(
shape,withSetFillColor(shape),withMoveBy(shape)
);
}
return shape;
};
let r = createShape('rectangle',});
let c = createShape('circle',{ x: 10,y: 10,diameter: 10 });
r.moveBy(2,3);
c.moveBy(1,2);
r.setFillColor('red');
c.setFillColor('blue');
console.log(r);
console.log(c);
不要使用箭头函数,而是使用this
或者,使用常规函数或 the shorthand method definition syntax 可让您使用 this
。然后,您可以将这些方法添加到您的对象中,并使用 this
来引用该对象,而不必将其传入。
const withMoveBy = { //no need for a function to produce the object
moveBy(diffX,diffY) { //shorthand method syntax
this.dimensions.x += diffX;
this.dimensions.y += diffY;
},};
const withSetFillColor = { //no need for a function to produce the object
setFillColor(color) { //shorthand method syntax
this.fillColor = color;
this.dimensions.fillColor = color;
},};
const shapeRectangle = (dimensions) => ({
type: 'rectangle',dimensions) => {
let shape = null;
switch (type) {
case 'rectangle': {
shape = shapeRectangle(dimensions);
break;
}
case 'circle': {
shape = shapeCircle(dimensions);
break;
}
}
if (shape) {
shape = {
...shape,...withSetFillColor,...withMoveBy,};
}
return shape;
};
let r = createShape('rectangle',2);
r.setFillColor('red');
c.setFillColor('blue');
console.log(r);
console.log(c);
混合方法
这更多是对正在发生的事情的解释,而不是实际的新方法。
以上两种方法都有效,但显示的是同一枚硬币的两面。将对象组合在一起称为mixin*。 Mixin 与对象组合相似,因为您可以从更简单的对象构建更复杂的对象,但由于您是通过串联来构建的,因此它也有自己的单独类别。
传统上,您会使用 Object.assign(obj,mixinA,mixinB)
将内容添加到 obj
。这使它类似于第一种方法。但是,mixinA
和 mixinB
将是第二种方法中的实际对象。
使用类语法,有一种有趣的替代方法可以向类中添加 mixin。我在这里添加它只是为了展示它 - 不使用类而使用常规对象是完全可以的。
const withMoveBy = Base => class extends Base { //mixin
moveBy(diffX,diffY) {
this.dimensions.x += diffX;
this.dimensions.y += diffY;
}
};
const withSetFillColor = Base => class extends Base { //mixin
setFillColor(color) {
this.fillColor = color;
this.dimensions.fillColor = color;
}
};
class Shape {
constructor({type,fillColor,dimensions}) {
this.type = type;
this.fillColor = fillColor;
this.dimensions = dimensions;
}
}
const shapeRectangle = (dimensions) => ({
type: 'rectangle',dimensions) => {
let shapeArgs = null;
switch (type) {
case 'rectangle': {
shapeArgs = shapeRectangle(dimensions);
break;
}
case 'circle': {
shapeArgs = shapeCircle(dimensions);
break;
}
}
let shape = null;
if (shapeArgs) {
//add mixins to the Shape class
const mixedInConstructor = withMoveBy(withSetFillColor(Shape));
//create the enhanced class
shape = new mixedInConstructor(shapeArgs);
}
return shape;
};
let r = createShape('rectangle',2);
r.setFillColor('red');
c.setFillColor('blue');
console.log(r);
console.log(c);
* 是的,标题是双关语。你现在可以笑了。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。