跳至主要內容

2692. 使对象不可变 🔒


2692. 使对象不可变 🔒open in new window

🟠   🔗 力扣open in new window LeetCodeopen in new window

题目

Write a function that takes an object obj and returns a new immutable version of this object.

An **immutable **object is an object that can't be altered and will throw an error if any attempt is made to alter it.

There are three types of error messages that can be produced from this new object.

  • Attempting to modify a key on the object will result in this error message: Error Modifying: ${key}.
  • Attempting to modify an index on an array will result in this error message: Error Modifying Index: ${index}.
  • Attempting to call a method that mutates an array will result in this error message: Error Calling Method: ${methodName}. You may assume the only methods that can mutate an array are ['pop', 'push', 'shift', 'unshift', 'splice', 'sort', 'reverse'].

obj is a valid JSON object or array, meaning it is the output of JSON.parse().

Note that a string literal should be thrown, not an Error.

Example 1:

Input:

obj = { "x": 5 }

fn = (obj) => {
  obj.x = 5;
  return obj.x;
}

Output:

{"value": null, "error": "Error Modifying: x"}

Explanation: Attempting to modify a key on an object resuts in a thrown error. Note that it doesn't matter that the value was set to the same value as it was before.

Example 2:

Input:

obj = [1, 2, 3]

fn = (arr) => {
  arr[1] = {};
  return arr[2];
}

Output:

{"value": null, "error": "Error Modifying Index: 1"}

Explanation: Attempting to modify an array results in a thrown error.

Example 3:

Input:

obj = { "arr": [1, 2, 3] }

fn = (obj) => {
  obj.arr.push(4);
  return 42;
}

Output:

{ "value": null, "error": "Error Calling Method: push"}

Explanation: Calling a method that can result in a mutation results in a thrown error.

Example 4:

Input:

obj = { "x": 2, "y": 2 }

fn = (obj) => {
  return Object.keys(obj);
}

Output:

{"value": ["x", "y"], "error": null}

Explanation: No mutations were attempted so the function returns as normal.

Constraints:

  • obj is a valid JSON object or array
  • 2 <= JSON.stringify(obj).length <= 10^5

题目大意

请你编写一个函数,该函数接收一个对象 obj ,并返回该对象的一个新的 不可变 版本。

不可变 对象是指不能被修改的对象,如果试图修改它,则会抛出错误。

此新对象可能产生三种类型的错误消息。

  • 如果试图修改对象的键,则会产生以下错误消息: Error Modifying: ${key}
  • 如果试图修改数组的索引,则会产生以下错误消息: Error Modifying Index: ${index}
  • 如果试图调用会改变数组的方法,则会产生以下错误消息: Error Calling Method: ${methodName} 。你可以假设只有以下方法能够改变数组: ['pop', 'push', 'shift', 'unshift', 'splice', 'sort', 'reverse']

obj 是一个有效的 JSON 对象或数组,也就是说,它是 JSON.parse() 的输出结果。

请注意,应该抛出字符串字面量,而不是 Error 对象。

提示:

  • 2 <= JSON.stringify(obj).length <= 10^5

解题思路

要创建一个不可变的对象,这意味着该对象及其嵌套对象在任何情况下都不能被修改,如果尝试修改对象的键、数组的索引或调用改变数组状态的方法,应该抛出相应的错误消息。

利用 JavaScript 的 Proxy 特性,可以在对象或数组的访问和修改操作上进行拦截,从而实现不可变性。

  1. 使用 Proxy 进行拦截

    • 为对象和数组分别定义不同的处理器(handler):
      • 对象处理器:拦截键的修改,抛出 "Error Modifying: ${key}"
      • 数组处理器:拦截索引的修改,抛出 "Error Modifying Index: ${index}",同时为改变数组的方法(如 pop, push 等)提供代理,抛出 "Error Calling Method: ${methodName}"
  2. 递归处理嵌套对象

    • 对输入对象进行递归遍历,如果发现一个属性是对象或数组,就递归调用同样的处理逻辑,将其转换为不可变的代理。
  3. 返回结果

    • 调用递归函数处理输入的 obj 并返回最终的不可变对象。

复杂度分析

  • 时间复杂度O(n),其中 n 是对象的总属性数量。需要递归遍历每个嵌套的对象。
  • 空间复杂度O(n),需要为每个对象创建代理,空间复杂度与输入的对象大小成正比。

代码

/**
 * @param {Object|Array} obj
 * @return {Object|Array}
 */
var makeImmutable = function (obj) {
	const mutableMethods = [
		'pop',
		'push',
		'shift',
		'unshift',
		'splice',
		'sort',
		'reverse'
	];

	const objectHandler = {
		set: (target, key) => {
			throw `Error Modifying: ${String(key)}`;
		}
	};

	const arrayHandler = {
		set: (target, key) => {
			if (typeof key === 'symbol' || isNaN(key)) {
				throw `Error Modifying: ${String(key)}`;
			}
			throw `Error Modifying Index: ${String(key)}`;
		}
	};

	const functionHandler = {
		apply: (target) => {
			throw `Error Calling Method: ${String(target.name)}`;
		}
	};

	const dfs = (obj) => {
		if (Array.isArray(obj)) {
			// 处理数组
			const proxyArray = new Proxy(obj, arrayHandler);
			mutableMethods.forEach((method) => {
				proxyArray[method] = new Proxy(function (...args) {
					return obj[method](...args);
				}, functionHandler);
			});
			return proxyArray;
		} else if (typeof obj === 'object' && obj !== null) {
			// 处理对象
			const proxyObject = new Proxy(obj, objectHandler);
			for (const key in obj) {
				if (typeof obj[key] === 'object') {
					proxyObject[key] = dfs(obj[key]);
				}
			}
			return proxyObject;
		}
		// 返回基本类型
		return obj;
	};

	// 调用递归函数
	return dfs(obj);
};

相关题目

题号标题题解标签难度
2690无穷方法对象 🔒open in new window[✓]
2691不可变辅助工具 🔒open in new window[✓]