工作中遇到一个场景,是给不同的按钮挂载不同的跳转地址,在vue页面实现的。实际实现的就是给一个前端定义好的对象添加后台返回的数据,这里用一个实例代替业务代码来展现 一下这个场景:
首先页面按钮代码与触发方法:
原业务代码这里的按钮是一个个手写出来,这里用v-for循环一个意思。主要为了实现,多个按钮绑定对应不同的地址,然后实现点击跳转,下面就是这个点击操作触发的方法:
上述写法首先定义了需要接收返回数据的集对象,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进行操作,一定会报错的。
然后我便查阅了一些资料,在这同时做个小小的原理总结:
首先说结论,
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 引擎(如谷歌的 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