首页 > 解决方案 > 返回现有对象的函数设置属性的函数。JS

问题描述

我希望能够在对象上生成动态属性,我尝试通过创建一个接受输入对象的函数然后返回一个接受参数的函数来做到这一点。该参数用于设置动态属性。

我的问题是,一旦创建了函数,我似乎并没有每次都得到一个新对象,而是该函数在先前分配的对象上设置属性。

我尝试重新分配对象但无济于事,我测试了可行的替代方案(不太理想的代码),但我想知道为什么我的初始解决方案不起作用。

/* Returns a function which will assign a 'required' property to all objects within the given object */
const generateSchemaField = obj => {
	obj = Object.assign({}, obj);
	return function(required = false) {
		Object.keys(obj).forEach(key => {
			Object.assign(obj[key], {
				required,
			});
		});
		return obj;
	};
};

/* The way the above function would be invoked*/
const userEmailUsingGenerateSchemaField = generateSchemaField({
	user_email: {
		type: 'string',
		description: 'A user email',
	},
});


/* The below function does not encounter the same problem */
const userEmailNotUsingGenerateSchemaField = function(required = false) {
	let obj = {
		user_email: {
			type: 'string',
			description: 'A user email',
		},
	};
	Object.keys(obj).forEach(key => {
		Object.assign(obj[key], {
			required,
		});
	});
	return obj;
}; 

let firstResultUsing = userEmailUsingGenerateSchemaField();
let secondResultUsing = userEmailUsingGenerateSchemaField(true);

console.log(firstResultUsing);
console.log(secondResultUsing);

预期产出

{
  user_email: { type: 'string', description: 'A user email', required: false }
}
{
  user_email: { type: 'string', description: 'A user email', required: true }
}

实际的

{
  user_email: { type: 'string', description: 'A user email', required: true }
}
{
  user_email: { type: 'string', description: 'A user email', required: true }
}

标签: javascriptnode.js

解决方案


短篇故事

这是引用同一对象的简单问题。

为了证明这一点,比较两个对象

console.log(firstResultUsing === secondResultUsing)

你会看到它打印出来true证明它们都引用同一个对象。

向下滚动以获得解决方案!

很长的故事

在这一行:

const userEmailUsingGenerateSchemaField = generateSchemaField({
  user_email: {
    type: 'string',
    description: 'A user email',
  },
})

这里发生的是你的generateSchemaField函数正在返回一个函数,该函数有一个闭包obj它只是

{
  user_email: {
    type: 'string',
    description: 'A user email',
  },
}

现在在这一行:

const firstResultUsing = userEmailUsingGenerateSchemaField()

该函数被评估并返回修改后的对象

{
  user_email: {
    type: 'string',
    description: 'A user email',
    required: false
  },
}

记住返回的对象仍然具有相同的引用obj

现在再次在线:

const secondResultUsing = userEmailUsingGenerateSchemaField(true)

这里发生的是相同的引用obj对象被修改并使用属性更新required: true

这就是为什么当你们console.log都在展示的时候,required: true因为他们都引用了同一个对象。

解决方案

const generateSchemaField = obj => {
  return function(required = false) {
    const objClone = JSON.parse(JSON.stringify(obj));
    Object.keys(objClone).forEach(key => {
      Object.assign(objClone[key], {
        required,
      });
    });
    return objClone;
  };
};

让我们分解一下。我删除obj = Object.assign({}, obj);了,因为它没有任何好处。这似乎是一条多余的线。

接下来,我对obj. 记住Object.assign不会起作用,因为它只是创建了一个浅拷贝/克隆,在这里它不会起作用,因为键email_id包含对对象的引用。请注意,使用深度克隆JSON.parse(JSON.stringify(obj))仅适用于具有 JSON 安全值的对象(无函数或undefined等...)。

然后,我正在操作这个克隆的对象并返回它。现在不存在操纵相同引用对象的威胁。

让我知道这是否有帮助,或者您需要更好的解释。


推荐阅读