网站首页 > 技术文章 正文
在JavaScript中,类(Class)作为ES6引入的重要特性之一,为我们提供了更加面向对象的编程范式。然而,与一些其他编程语言(如Java或C#)不同,ES6中的类并没有直接支持私有属性或方法。尽管如此,随着JavaScript语言的不断演进,我们仍然可以通过一些技巧和模式来实现类似私有属性的功能。
Proxy
Proxy 对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)。MDN地址
我们可以先给私有变量一个特殊的命名格式,例如前面加一个下划线 _,当Proxy劫持对象后,根据用户访问的属性名的格式来判断用户即将访问的属性是否是私有属性:
class Stu {
constructor(name, age, sexy) {
this._name = name;
this._age = age;
this.sexy = sexy
}
get info() {
return `该学生姓名:${this._name},年龄:${this._age}`;
}
}
// 定义一个处理器
const handler = {
get (target, key) {
if (key[0] === '_') {
throw new Error('禁止访问!');
}
return target[key];
},
set (target, key, value) {
if (key[0] === '_') {
throw new Error('禁止访问!');
}
target[key] = value;
},
ownKeys(target, prop) {
return Object.keys(target).filter(key => key[0] !== '_')
},
}
const zs = new Proxy(new Stu('张三', 18, '男'), handler);
console.log(zs.info); // 该学生姓名:张三,年龄:18
console.log(Object.keys(zs)) // [ 'sexy' ]
zs._age = 20; // 错误:禁止访问!
通过以上代码就实现了通过Proxy去控制对象内属性的访问权限,当用户尝试访问以 _ 开头的属性时就会抛出禁止访问的错误,当然,也可以在get和set中进行其他的处理,例如不抛出错误,而是返回 undefined 。当定义了 ownKeys 时,使用 Object.keys() 时就会过滤掉目标对象中下划线开头的属性再返回。
Symbol
Symbol 是ES6中新增加的一种数据类型,用于创建一个唯一的值,那么怎么通过这个特性来创建私有变量呢?例如如下代码:
const nameSymbol = Symbol('name');
const ageSymbol = Symbol('age');
class Stu {
constructor(name, age) {
this[nameSymbol] = name;
this[ageSymbol] = age;
}
get info() {
return `该学生姓名:${this[nameSymbol]},年龄:${this[ageSymbol]}`;
}
}
const zs = new Stu('张三', 18);
console.log(zs.info); // 该学生姓名:张三,年龄:18
console.log(Object.keys(zs)) // []
console.log(zs.age); // undefined
在如上代码中,我们没有直接使用 name 和 age 来作为属性名,而是使用了Symbol生成了唯一的名字,所以在外部是拿不到属性名的,相比于proxy方式更加简单,但是,有一个api叫做 Object.getOwnPropertySymbols ,通过这个api可以获取到对象中所有Symbol属性:
const allSymbol = Object.getOwnPropertySymbols(zs);
console.log(allSymbol); // [ Symbol(name), Symbol(age) ]
console.log(zs[allSymbol[0]]); // 张三
console.log(zs[allSymbol[1]]); // 18
那么有没有更加完善的方法呢?
WeakMap
在MDN私有属性一节中,提到了在 # 这个语法出现之前,可以通过 WeakMap 来创建私有变量。WeakMap与Map的区别就是 只能用对象作为 key,对象销毁,这个键值对就销毁。 所以使用 WeakMap 可以避免所有的实例对象都共有同一个Map的问题,代码实现如下:
const wmName = new WeakMap();
const wmAge = new WeakMap();
const wmSet = function (receiver, key, value) {
key.set(receiver, value);
};
const wmGet = function (receiver, key) {
return key.get(receiver);
};
class Stu {
constructor(name, age) {
wmName.set(this, undefined);
wmAge.set(this, undefined);
wmSet(this, wmName, name);
wmSet(this, wmAge, age);
}
get info() {
return `该学生姓名:${wmGet(this, wmName)},年龄:${wmGet(this, wmAge)}`;
}
}
const zs = new Stu("张三", 18);
console.log(zs.info); // 该学生姓名:张三,年龄:18
console.log(Object.keys(zs)); // []
console.log(zs.age); // undefined
我们针对每一个私有变量,定义了一个 WeakMap ,在对象内部通过 wmGet 和 wmSet 两个函数来进行私有变量的读写,而在对象外部则无法访问到私有变量。这种办法相对于前两种办法更加完善,在js引擎未完全支持前,babel对 # 语法进行转换时就会转为 WeakMap 的实现方式,可以理解为 # 是 WeakMap 的语法糖。
#
在最新的ES标准中,可以通过 # 的方式来标识私有属性和方法。例如:
class Stu {
#name;
#age;
constructor(name, age) {
this.#name = name;
this.#age = age;
this.sexy = '男';
}
get info() {
return `该学生姓名:${this.#name},年龄:${this.#age}`;
}
}
const zs = new Stu('张三', 18);
console.log(zs.info); // 该学生姓名:张三,年龄:18
console.log(Object.keys(zs)) // [ 'sexy' ]
console.log(zs.age); // undefined
虽然使用 # 可以很方便的创建私有变量,但是需要注意该语法的浏览器兼容性,大多是2020年之前的浏览器版本都没有提供相应的支持。
至于为什么要用 # ,TC39 委员会解释道,他们也是做了深思熟虑最终选择了 # 符号,而没有使用 private 关键字。其中还讨论了把 private 和 # 符号一起使用的方案。并且还打算预留了一个 @ 关键字作为 protected 属性 。
文章转自:https://juejin.cn/post/7372108850269487116
猜你喜欢
- 2024-10-04 Vue选项:computed计算属性(vue3.0 计算属性)
- 2024-10-04 Vue 中的计算属性(vue中的计算属性)
- 2024-10-04 Js基础7:表单元素属性(js中表单)
- 2024-10-04 Jquery属性——学习笔记(一)(如何使用jquery设置一个属性值)
- 2024-10-04 前端入门——在网页中使用JavaScript
- 2024-10-04 前端-JavaScript中的class(javascript class)
- 2024-10-04 如何获取JavaScript中某个函数可接受的参数数量
- 2024-10-04 关于js设置对象attr属性的问题(js对象属性值)
- 2024-10-04 react三大核心属性(react的核心)
- 2024-10-04 JS 对象基本用法(js对象的操作方法)
- 10-02基于深度学习的铸件缺陷检测_如何控制和检测铸件缺陷?有缺陷铸件如何处置?
- 10-02Linux Mint 22.1 Cinnamon Edition 搭建深度学习环境
- 10-02AWD-LSTM语言模型是如何实现的_lstm语言模型
- 10-02NVIDIA Jetson Nano 2GB 系列文章(53):TAO模型训练工具简介
- 10-02使用ONNX和Torchscript加快推理速度的测试
- 10-02tensorflow GPU环境安装踩坑日记_tensorflow配置gpu环境
- 10-02Keye-VL-1.5-8B 快手 Keye-VL— 腾讯云两卡 32GB GPU保姆级部署指南
- 10-02Gateway_gateways
- 最近发表
-
- 基于深度学习的铸件缺陷检测_如何控制和检测铸件缺陷?有缺陷铸件如何处置?
- Linux Mint 22.1 Cinnamon Edition 搭建深度学习环境
- AWD-LSTM语言模型是如何实现的_lstm语言模型
- NVIDIA Jetson Nano 2GB 系列文章(53):TAO模型训练工具简介
- 使用ONNX和Torchscript加快推理速度的测试
- tensorflow GPU环境安装踩坑日记_tensorflow配置gpu环境
- Keye-VL-1.5-8B 快手 Keye-VL— 腾讯云两卡 32GB GPU保姆级部署指南
- Gateway_gateways
- Coze开源本地部署教程_开源canopen
- 扣子开源本地部署教程 丨Coze智能体小白喂饭级指南
- 标签列表
-
- cmd/c (90)
- c++中::是什么意思 (84)
- 标签用于 (71)
- 主键只能有一个吗 (77)
- c#console.writeline不显示 (95)
- pythoncase语句 (88)
- es6includes (74)
- sqlset (76)
- apt-getinstall-y (100)
- node_modules怎么生成 (87)
- chromepost (71)
- flexdirection (73)
- c++int转char (80)
- mysqlany_value (79)
- static函数和普通函数 (84)
- el-date-picker开始日期早于结束日期 (76)
- js判断是否是json字符串 (75)
- c语言min函数头文件 (77)
- asynccallback (87)
- localstorage.removeitem (74)
- vector线程安全吗 (70)
- java (73)
- js数组插入 (83)
- mac安装java (72)
- 无效的列索引 (74)