工作中遇到一个场景,是给不同的按钮挂载不同的跳转地址,在vue页面实现的。实际实现的就是给一个前端定义好的对象添加后台返回的数据,这里用一个实例代替业务代码来展现 一下这个场景:
首先页面按钮代码与触发方法:
<template>
<div>
<button v-for="(path, id) in paths" :key="id" @click="handleButtonClick(id)">
{{ id }}
</button>
</div>
</template>
原业务代码这里的按钮是一个个手写出来,这里用v-for循环一个意思。主要为了实现,多个按钮绑定对应不同的地址,然后实现点击跳转,下面就是这个点击操作触发的方法:
<script>
export default {
data() {
return {
paths: {}, // 初始化为空对象
userId: 'your-user-id', // 用户ID
rowId: 'your-row-id' // 行ID
};
},
methods: {
async fetchButtonPaths() {
try {
const res = await thingsToDo(this.userId, this.rowId);
if (res.data.msg === 'success') {
const buttonPaths = res.data.data.buttonPaths;
buttonPaths.forEach(path => {
this.paths[path.id] = path.path; // 动态添加属性
});
} else {
console.error('获取按钮路径失败:', res.data.msg);
}
} catch (error) {
console.error('请求错误:', error);
}
},
handleButtonClick(buttonId) {
const path = this.paths[buttonId];
if (path) {
this.$router.push(path);
} else {
console.error('未找到按钮路径:', buttonId);
}
}
},
created() {
this.fetchButtonPaths(); // 在组件创建时获取按钮路径
}
};
</script>
上述写法首先定义了需要接收返回数据的集对象,paths:{}是核心对象,那两个id不重要。
下面两个方法fetchButtonPaths代表了异步请求后台的操作,其中
const buttonPaths = res.data.data.buttonPaths;
buttonPaths.forEach(path => {
this.paths[path.id] = path.path; // 动态添加属性
});
这里是核心赋值代码,这里实现了 JavaScript中动态属性的赋值。其中后台返回的值res的内容结构格式如下:
{
"msg": "success",
"data": {
"buttonPaths": [
{ "id": "button1", "path": "/exam/applys/confirmnotpayexaminee1" },
{ "id": "button2", "path": "/exam/applys/confirmnotpayexaminee2" },
{ "id": "button3", "path": "/exam/applys/confirmnotpayexaminee3" }
]
}
}
上述操作后,在data属性中定义的paths:{}对象就有了内容, 拆开来看这里的操作实现了下面的转变:
const paths = {}; // 初始化为空对象
const path = { id: "button1", path: "/exam/applys/confirmnotpayexaminee1" };
// 动态添加属性
paths[path.id] = path.path;
console.log(paths); // 输出: { button1: "/exam/applys/confirmnotpayexaminee1" }
然后使用点击方法便可根据id的值获取到对应的跳转地址实现跳转,功能到这就完成了, JavaScript 动态属性赋值一般也是这么操作的,但是我有了一个疑问:
最初在data中创建的paths:{}集合是空的,里面没有事先写好的属性名,但是在动态赋值的时候,
paths[path.id] = path.path;却可以正确的添加id:path格式的键值对,这是怎么实现的,按照传统后端思维,paths集合中获取没有的key进行操作,一定会报错的。
然后我便查阅了一些资料,在这同时做个小小的原理总结:
首先说结论,
- JavaScript动态属性赋值:在 JavaScript 中,对象的属性可以动态添加。即使对象初始为空,你也可以随时为其添加新的属性。
this.paths[path.id] = path.path
:这行代码实际上是将path.path
的值赋给this.paths
对象中名为path.id
的属性。如果该属性不存在,JavaScript 会自动创建它。
依据上述案例解释下流程详细步骤:
-
初始化对象:
const paths = {};
初始化一个空对象paths
。
-
路径对象:
const path = { id: "button1", path: "/exam/applys/confirmnotpayexaminee1" };
定义一个路径对象path
,包含id
和path
属性。
-
动态添加属性:
paths[path.id] = path.path;
这行代码做了以下几件事:- 将
path.path
的值(即"/exam/applys/confirmnotpayexaminee1"
)赋值给paths
对象的"button1"
属性。 - 如果
paths
对象中不存在"button1"
属性,JavaScript 会自动创建这个属性。 paths
对象中查找名为"button1"
的属性。path.id
是字符串"button1"
。
- 将
上述就是大面上的解释,但是我觉得还不够,本着希望从源码中得到解答的精神,继续查阅资料,终于触碰到了我的知识壁垒,下面我就做个简单的原理概述,结束这次的菜狗验证:
JavaScript 引擎在处理对象属性赋值时,会执行以下步骤:
-
检查对象是否存在:
- 确认对象是否存在。如果对象不存在,会抛出错误。
-
查找属性:
- 检查对象中是否已经存在指定的属性。
-
属性不存在时创建属性:
- 如果属性不存在,引擎会创建一个新的属性,并将其值设置为指定的值。
伪代码实现
为了更好地理解这一过程,我们可以用伪代码来模拟 JavaScript 引擎的行为:
function assignProperty(obj, key, value) {
// 检查对象是否存在
if (typeof obj !== 'object' || obj === null) {
throw new TypeError('Cannot set property of non-object');
}
// 检查对象中是否已经存在指定的属性
if (!(key in obj)) {
// 属性不存在,创建新的属性
obj[key] = value;
} else {
// 属性已存在,更新属性值
obj[key] = value;
}
}
// 测试
const paths = {};
const path = { id: "button1", path: "/examn/applys/confirmnotpayexaminee1" };
assignProperty(paths, path.id, path.path);
console.log(paths); // 输出: { button1: "/exam/applys/confirmnotpayexaminee1" }
实际的 JavaScript 引擎实现
实际的 JavaScript 引擎(如谷歌的 V8)的实现要复杂得多,涉及到许多优化技术,如内联缓存、隐藏类等。以下是一个简化的 V8 引擎的实现步骤:
-
对象表示:
- 在 V8 中,对象通常由一个哈希表或数组来表示,每个属性对应一个键值对。
-
属性查找:
- 使用哈希表查找属性。如果属性存在,直接返回属性值。
- 如果属性不存在,继续下一步。
-
属性创建:
- 创建一个新的属性,并将其值设置为指定的值。
- 更新对象的内部结构(如哈希表或数组)。
V8 引擎的内部结构
V8 引擎使用了一些高级的数据结构和优化技术来提高性能。以下是简化的内部结构:
-
哈希表:
- 使用哈希表来存储对象的属性,以便快速查找和插入。
-
隐藏类:
- 使用隐藏类来优化属性访问。隐藏类是一种内部表示,用于描述对象的结构。
-
内联缓存:
- 使用内联缓存来加速属性访问和修改操作。
示例代码
为了更直观地理解,我们可以用一个简单的 JavaScript 对象来模拟这一过程:
class SimpleObject {
constructor() {
this.properties = {};
}
hasProperty(key) {
return key in this.properties;
}
getProperty(key) {
return this.properties[key];
}
setProperty(key, value) {
this.properties[key] = value;
}
}
// 测试
const paths = new SimpleObject();
const path = { id: "button1", path: "/exam/applys/confirmnotpayexaminee1" };
if (!paths.hasProperty(path.id)) {
paths.setProperty(path.id, path.path);
} else {
paths.setProperty(path.id, path.path);
}
console.log(paths.getProperty(path.id)); // 输出: /exam/applys/confirmnotpayexaminee1
总结
- 动态属性赋值:在 JavaScript 中,对象的属性可以动态添加。如果属性不存在,JavaScript 引擎会自动创建这个属性。
- 内部实现:JavaScript 引擎(如 V8)使用哈希表、隐藏类和内联缓存等技术来优化属性的查找和创建过程。