大家好!
作为一名前端开发人员,在编写代码时是否经常使用JavaScript中的对象来存储数据和管理键值对,这是非常有用的。然而,最近我在项目中遇到了一些对象的安全问题,—— 对象注入攻击(方括号表示法的危险)。所以我们想知道是否有一种更安全、更高效的方式来管理实际业务中的数据。
https://github.com/eslint-community/eslint-plugin-security/blob/main/docs/the-dangers-of-square-bracket-notation.md
事实上,JavaScript 中还有一个经常被忽视的好帮手:Map。很多开发者在进行业务开发时,可能不太熟悉什么时候使用Map,什么时候使用Object。为了避免陷入陷阱,今天我们将解释两者之间的差异以及如何根据实际业务场景选择最佳工具。
Object
在JavaScript 中,对象是一种古老的数据结构,也是几乎每个开发人员都会接触的工具。它是键值对的集合,键只能是字符串或符号类型。我们经常使用对象来存储用户的基本信息,例如:
const user={ name: '张三',age: 25, email: 'zhangsan@example.com',}; 如果想获取用户名,直接使用user.name即可。然而,您可能不知道Object的原型继承机制可能会带来潜在的安全风险。
对象原型链问题
在JavaScript 中,所有对象都继承自Object.prototype。这意味着对象本身具有一些预先配置的属性和方法。问题是,如果您不小心允许用户输入诸如__proto__ 之类的属性,则对象的原型链可能会发生更改并产生意外结果。
让我们举个例子:
user['__proto__'].isAdmin=true;console.log(user.isAdmin); //true 在这段代码中,我们通过__proto__ 修改了对象的原型链,并为其添加了isAdmin 属性。此操作可能会导致应用程序认为普通用户是管理员,这可能会直接导致安全漏洞。
Map
除了熟悉的对象之外,JavaScript开发过程中还使用了更加灵活和安全的工具:——Map。 Map 是在ECMAScript 6 中引入的。与传统的Object相比,Map不仅支持各种键值对,而且避免了原型链带来的安全问题。我们通过一个例子来了解如何使用Map来安全地存储用户信息。
const userMap=new Map([ ['姓名', '张三'], ['年龄', 25], ['邮箱地址', 'zhangsan@example.com'],]);userMap.set('__proto__ ' , { isAdmin: true });console.log(userMap.get('__proto__')); //{ isAdmin: true } 从这个例子中可以看出,Map 支持数字、对象,甚至是各种类型的键。 __原型__。与Object不同,Map不依赖原型链,因此不会用__proto__等键值影响其内部结构。这意味着用户不必担心恶意更改地图原型链,从而大大降低了由此产生的安全风险。
此示例通过set 方法将__proto__ 键设置为Map,并为其分配值{ isAdmin: true }。检索此密钥将检索您保存的内容,而不会影响地图本身的结构。
Object VS Map
功能ObjectMap 键类型仅限于字符串或符号支持任何数据类型作为键原型链继承自原型链并包含属性和方法无原型链、干净的键和值提供不太健壮的键对存储,更加灵活,支持多种按键类型。安全性对原型链的篡改敏感,不会出现原型链遍历顺序相关的问题。
1. 按键类型
Object: 键值对中的键必须是字符串或符号。这意味着其他类型的数据(数字、对象等)不能直接用作键。 Map: 键可以是任何类型的数据,不仅仅是字符串,还可以是对象、数组或其他映射。这种灵活性使地图在处理复杂数据时具有显着的优势。 2. 原型链
Object:对象继承了原型链。这意味着从原型对象继承属性和方法。尽管这种机制很强大,但它可能会产生意想不到的副作用,特别是如果您不小心覆盖了对象的原型属性。 Map: Map没有原型链,保存的键值对仅与当前Map实例关联。这意味着地图更加干净、安全,避免了原型链带来的潜在风险。 3. 灵活性
由于键类型限制,Object: 对象的灵活性稍差。如果您需要更多类型的键(例如对象作为键),则对象不适合。 Map: 映射非常灵活,可以存储许多不同类型的键,这使得它们在处理非字符串类型键时特别有用。 4. 安全性
Object: 对象继承自原型链,使得原型链上的属性容易被篡改。例如,原型链上的属性可能会发生变化并影响对象的安全性。 Map: Map没有原型链的麻烦,因此不存在这些安全风险,使用起来更加安全。 5. 迭代顺序
Object: 对象的遍历顺序是不确定的并且可能会发生变化,尤其是在向对象添加或删除属性时。 Map: Map的遍历顺序是插入顺序,非常适合需要保证顺序的场景。
何时选择Map而非Object?业务需求决定!
在开发中,地图或对象的选择实际上取决于您的特定业务需求。如果您希望代码中的键类型更加灵活,我们建议您选择映射。除了支持字符串和符号作为键之外,Map 还可以使用任何类型的数据,包括对象和数字,这是Object 无法实现的。
1.灵活的按键类型
Map 的一大优势是其关键的多功能性。一些复杂的业务场景往往需要使用对象作为键来存储用户权限、缓存等场景的信息。在这种情况下,Object的键类型限制是不够的,Map提供了完美的解决方案。
2、安全性更高
另一个重要的区别是安全性。对象继承原型链。这意味着对象带有许多默认属性和方法,攻击者可以使用它们来篡改。 Map没有这么复杂的原型链,所以这些隐患不存在。这无疑使得Map 在处理用户输入的键值对时,尤其是在存储一些敏感数据时成为更安全的选择。
3.检查键值对的顺序
在某些业务场景中,维护键值对的顺序非常重要。例如,在订单处理或审批流程中,一系列操作步骤直接影响系统的业务逻辑。在Object中,键的顺序不是固定的,可以随时改变。这在订单敏感的业务场景中是无法控制的。相比之下,Maps 可以保证键值对按照插入顺序存储和遍历。这在需要有序存储数据的场景下尤其重要。
部分
一般来说,如果您有以下需求:
如果需要不同的数据类型作为键,并且需要保证数据存储的顺序不被改变,那么映射肯定比对象更好地防止潜在的攻击。
实例讲解:用Map处理复杂业务场景
Map在JavaScript中应用广泛,特别是对于复杂数据、动态键值对以及必须保证键值对顺序的场景。接下来我们结合一些典型的业务场景来详细了解一下Map的应用。
1. 存储复杂数据
在某些业务场景中,需要将对象的属性存储为键值对,值可能是简单数据,也可能是嵌套对象。例如,在处理用户信息时,地址通常是嵌套的对象结构,而映射提供了存储和管理此数据的灵活性。
const personMap=new Map();personMap.set('姓名', '爱丽丝');personMap.set('年龄', 30);personMap.set('地址', { city: '仙境', Country: '幻想曲' }); //获取数据console.log(personMap.get('name')); //输出: Aliceconsole.log(personMap.get('address').city); //本例中输出:映射允许您存储许多不同类型的数据并提供对嵌套对象的轻松访问,使它们特别适合处理复杂的数据结构,例如用户信息。
2. 遍历键值对
当需要遍历键值对集合时,Maps提供了一种非常便捷的方式来执行高效的遍历操作,同时保证顺序。例如,在库存管理中,您可能需要记录产品及其数量,并一一展示。
const FruitMap=new Map([ ['apple', 3], ['banana', 5], ['orange', 2]]); //使用forEach 遍历键值对FruitMap .forEach((value) , key)={ console.log(`${key}数量: ${value}`);});//输出://苹果数量: 3//香蕉数量: 5 //橙子数量: 2对于顺序展示的数据场景,比如插入商品库存或者订单明细,Map的遍历能力可以帮助保证数据顺序的一致性。
3. 处理动态键
有些业务场景需要动态生成键值对,例如处理动态生成的ID和对象。在这种情况下,Map 是最好的选择,因为它允许您使用对象和其他复杂类型作为键,而Object 则不能。
const DynamicMap=new Map();const key1={ name: 'KeyOne' };const key2={ name: 'KeyTwo' };dynamicMap.set(key1, '值一');dynamicMap.set(key2, '值二' );console.log(dynamicMap.get(key1)); //输出: Value Oneconsole.log(dynamicMap.get(key2)); //输出: Value 2 这种处理动态生成的用户会话的方法非常适合为元素缓存和其他场景处理不同的键提供了灵活性。
4. 检查key是否存在
Map 提供了一种便捷的方式来检查key 是否存在,在某些业务场景中特别有用。例如,汽车信息管理系统可以通过地图快速检查用户输入的信息是否完整或者某些属性是否已经存在。
const carMap=new Map([ ['make', 'Toyota'], ['model', 'Camry'], ['year', 2020]]) //检查密钥是否存在console. ('model')); //输出: trueconsole.log(carMap.has('color')); //输出: false has() 方法表明存在适合检查的键。确实如此。形成数据完整性场景或是否定义配置项。
部分
映射提供了一种灵活的方式来管理键值对,可以处理多种类型的数据,保证排序,并提供优于对象的安全性和性能优势。无论您是要存储复杂数据、迭代键值对,还是动态生成和检查键值对,映射都是极其强大的工具。
我们希望这些示例可以帮助您更好地了解如何在实际业务中应用地图。如果您在项目中遇到相关问题,请尝试使用Map优化您的代码。
结束
在前端开发中,地图和对象有不同的适用场景。在处理大量复杂数据并避免原型链问题时,地图往往是更安全、更灵活的选择。对于一些使用固定键类型的简单场景,Object 更简单易用。作为前端开发者,你需要根据项目的需求合理选择合适的数据结构,这样才能写出高效、优雅的代码。
在实际开发中,我不知道自己更喜欢哪一个。您在使用地图或物体时遇到过什么陷阱或经验吗?请在评论部分留言分享您的经验和故事。您的留言或许可以帮助到其他朋友~
版权声明:本文转载于网络,版权归作者所有。如有侵权,请联系本站编辑删除。