eval() 获取难以索引的对象

本文探讨了使用JavaScript的eval函数来简化对文档对象模型(DOM)中多个元素的动态访问和修改过程。通过实例展示了如何根据用户输入动态选择并替换图片源,避免冗长的条件判断语句。
文档对象模型(DOM)的问题之一是:有时你要获取你要求的
对象简直就是痛苦。例如,这里有一个函数询问用户要变换
哪个图象:变换哪个图象

 


  

你可以用下面这个函数:

      
function swapOne()
{
        var the_image = prompt("change parrot or cheese","");
        var the_image_object;

        if (the_image == "parrot")
        {
                the_image_object = window.document.parrot;
        }
  else {
                the_image_object = window.document.cheese;
        }

        the_image_object.src = "ant.gif";
}

连同这些image标记:

<img src="stuff3a/parrot.gif" name="parrot">
<img src="stuff3a/cheese.gif" name="cheese">

请注意象这样的几行语句:

      
the_image_object = window.document.parrot;

它把一个鹦鹉图象对象敷给了一个变量。虽然看起来有点
儿奇怪,它在语法上却毫无问题。但当你有100个而不是两个
图象时怎么办?你只好写上一大堆的 if-then-else语句,要是
能象这样就好了:

 

      
function swapTwo()
{
        var the_image = prompt("change parrot or cheese","");
        window.document.the_image.src = "ant.gif";
}

不幸的是, JavaScript将会寻找名字叫 the_image而不是你所希
望的"cheese"或者"parrot"的图象,于是你得到了错误信息:”
没听说过一个名为the_image的对象”。

 

还好,eval能够帮你得到你想要的对象。

   
function simpleSwap()
{
        var the_image = prompt("change parrot or cheese","");
        var the_image_name = "window.document." + the_image;
        var the_image_object = eval(the_image_name);
        the_image_object.src = "ant.gif";
}

如果用户在提示框里填入"parrot",在第二行里创建了一个字
符串即window.document.parrot. 然后包含了eval的第三
行意思是: "给我对象window.document.parrot" - 也就是
你要的那个图象对象。一旦你获取了这个图象对象,你可以把
它的src属性设为ant.gif. 有点害怕?用不着。其实这相当有
用,人们也经常使用它。
<think> 我们有两个问题需要解决: 1. 数组解构时,解构变量能否使用变量而非字面量? 2. 如何将从数组 `const names = [{name: 'a',...}, {name: 'b', ...}].map(n => n.name)` 获取的名称(即 `['a', 'b']`)用于解构 `Promise.allSettled` 的返回结果,而不使用字面量 `[a, b]`? 首先,关于第一个问题:数组解构时,解构变量不能直接使用变量名(变量存储的字符串)作为标识符。解构赋值左侧的变量名必须是字面量标识符,不能是动态的变量。例如: const [a, b] = [1, 2]; // 正确:a和b是字面量标识符 const varName = 'x'; const [varName] = [3]; // 错误:这样会创建一个名为varName的变量,而不是名为x的变量 但是,我们可以通过其他方式将数组中的值赋给动态属性名的变量?答案是:不能直接通过解构赋值。不过,我们可以将解构得到的数组按索引分配给一个对象,该对象的属性名由我们提供的数组(names)决定。 因此,对于第二个问题:我们有一个数组 `names`,如 `['a', 'b']`,以及一个由 `Promise.allSettled` 返回的数组(假设为 `results`,长度为2)。我们想要将 `results[0]` 赋值给一个名为 `'a'` 的变量,`results[1]` 赋值给一个名为 `'b'` 的变量。但是,由于解构赋值要求左侧是标识符,我们不能使用动态变量名。 替代方案:我们可以创建一个对象,其属性名是 `names` 中的字符串,属性值是对应的结果数组中的值。 例如: const results = await Promise.allSettled(promises); // 假设promises是一个包含多个promise的数组 const nameArray = names; // names 是 ['a', 'b', ...] const resultObj = {}; nameArray.forEach((name, index) => { resultObj[name] = results[index]; }); 然后我们就可以通过 `resultObj.a` 和 `resultObj.b` 来访问。 但是,如果我们想要直接使用变量 `a` 和 `b`(即作为单独的变量),在ES6中无法通过动态变量名实现。这是因为变量必须在代码中显式声明。 另一种方法是使用 `eval`,但强烈不推荐,因为不安全且难以维护。 所以,总结:不能使用动态变量名进行解构赋值,但可以通过构建一个对象来按名称访问结果。 示例代码: // 假设我们有 names 数组和 promises 数组 const names = [{name: 'a'}, {name: 'b'}].map(item => item.name); // ['a','b'] const promises = [promiseA, promiseB]; // 两个promise const results = await Promise.allSettled(promises); // 创建一个对象,将每个名称映射到对应的结果 const resultMap = {}; names.forEach((name, index) => { resultMap[name] = results[index]; }); // 现在我们可以通过 resultMap.a 和 resultMap.b 访问结果 或者,我们可以使用数组的 `reduce` 方法: const resultMap = names.reduce((obj, name, index) => { obj[name] = results[index]; return obj; }, {}); 如果我们想要在全局作用域或函数作用域中创建变量,可以使用 `var` 或 `let` 配合动态变量名,但注意这在严格模式下是不允许的(使用 `eval` 除外),而且也不推荐。 因此,推荐使用对象映射的方式。 另外,如果我们知道名称是固定的(比如只有两个,a和b),我们可以手动解构并赋值给变量,但问题要求不使用字面量,所以不行。而如果名称是动态的(比如从数组中来,数量不定),则只能使用对象映射。 所以,最终答案:不能直接使用变量名作为解构赋值的标识符,而是应该构建一个对象来按名称存储结果。 注意:在引用[1]中,我们看到了对象解构的基本用法,但对象解构同样要求左侧是字面量属性名或显式指定的变量名,无法动态生成变量名。[^1] 在引用[2]中,我们看到了对象属性的简写,但同样需要事先知道属性名。[^2] 在引用[3]和[4]中,提到了Promise和扩展运算符,但未涉及动态变量名的问题。[^3][^4] 因此,我们得出结论:使用对象映射是解决此类问题的最佳实践。 示例代码: const names = ['a', 'b']; // 从某个数组map得到 const promises = [fetchA(), fetchB()]; // 两个异步操作 const results = await Promise.allSettled(promises); // 构建映射对象 const resultMap = {}; names.forEach((name, i) => { resultMap[name] = results[i]; }); // 使用 console.log(resultMap.a.status); console.log(resultMap.b.status); 或者,如果我们希望结果是一个对象,其属性名就是names中的值,也可以使用`Object.fromEntries`(ES2019): const resultMap = Object.fromEntries( names.map((name, index) => [name, results[index]]) ); 这样更简洁。 因此,根据环境支持情况,可以选择使用 `Object.fromEntries` 或 `reduce` 或循环。 注意:`Promise.allSettled` 返回的数组顺序与传入的promises顺序一致,所以我们可以放心按索引对应。[之前的问题已确认] 所以,最终代码: const names = [{name: 'a'}, {name: 'b'}].map(item => item.name); const promises = names.map(name => someAsyncOperation(name)); // 等待所有promise完成 const results = await Promise.allSettled(promises); // 创建映射对象 const resultObj = Object.fromEntries( names.map((name, index) => [name, results[index]]) ); // 使用 const { a, b } = resultObj; // 现在a和b是变量,但注意这里还是通过对象解构,但属性名固定。如果names是动态的,我们通常不会解构出所有变量,而是直接使用resultObj来访问。 注意:上面的解构 `const { a, b } = resultObj;` 要求我们知道对象有a和b属性,如果names是动态的,且我们不知道具体有哪些属性,那么我们就不能这样解构。所以通常我们直接使用 `resultObj.a` 或 `resultObj[name]` 来访问。 因此,如果names是动态的,我们无法在代码中写出解构所有变量的语句(因为变量名不确定),所以直接使用对象访问。 总结:我们无法使用动态变量名进行解构,但可以通过构建对象来按名称访问结果,这是推荐的方式。 </think> ### 数组解构动态变量名解析 #### 1. **数组解构能否使用变量而非字面量?** **不能直接使用变量名作为解构标识符**。数组解构要求左侧使用字面量标识符(如 `[a, b]`),无法直接使用变量存储的字符串(如 `['name1', 'name2']`)作为变量名。原因: - 解构语法是编译时确定的静态结构 - 变量名是动态值,无法在解构时被识别为标识符 - 尝试使用变量名会导致语法错误(如 `const [names[0]] = [...]` 无效) #### 2. **动态名称解构 Promise.allSettled 结果的方案** 通过 **对象解构 + 映射转换** 实现。以下是完整解决方案: ```javascript // 1. 准备名称数组 const data = [{name: 'a', id: 1}, {name: 'b', id: 2}]; const names = data.map(n => n.name); // ['a', 'b'] // 2. 创建Promise数组(与names顺序一致) const promises = names.map(name => fetchData(name)); // 3. 获取所有结果(保证顺序一致) const results = await Promise.allSettled(promises); // 4. 将结果数组转换为对象映射 const resultMap = Object.fromEntries( names.map((name, index) => [name, results[index]]) ); // 5. 使用对象解构获得动态名称变量 const { a, b } = resultMap; console.log(a.status, b.value); // 访问结果属性 ``` #### 关键步骤说明: 1. **名称映射**(步骤4) 通过 `Object.fromEntries` 将 `[name, result]` 键值对转换为对象: ```javascript // 转换结果示例 const resultMap = { a: {status: 'fulfilled', value: /*...*/}, b: {status: 'rejected', reason: /*...*/} } ``` 2. **对象解构**(步骤5) 利用对象属性名动态匹配能力: ```javascript // 等效于: const a = resultMap.a; const b = resultMap.b; ``` 3. **动态扩展** 支持任意长度的名称数组: ```javascript // 若names = ['x', 'y', 'z'] const { x, y, z } = resultMap; ``` #### 备选方案:reduce 实现映射 ```javascript const resultMap = names.reduce((obj, name, index) => ({ ...obj, [name]: results[index] // 动态属性名 }), {}); ``` ### 总结 - ✅ **顺序保证**:`Promise.allSettled` 返回数组顺序严格匹配输入顺序 - ✅ **动态名称**:通过 `Object.fromEntries` 或 `reduce` 转换为对象映射 - ❌ **禁止方案**:避免尝试 `eval` 或动态变量名解构(违反安全规范) > 此方案符合 ES6 规范,在 Vue/React 等框架中可安全使用[^1][^3]
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值