为什么会出现class
其实,学过 java
的小伙伴一定对 class
熟悉不过了。那为什么 JS
里面还要引入 class
呢?
在 es6 之前,虽然 JS 和 Java 同样都是 OOP (面向对象)语言,但是在 JS 中,只有对象而没有类的概念。
es6 中 class 的出现拉近了 JS 和传统 OOP 语言的距离。但是,它仅仅是一个语法糖罢了,不能实现传统 OOP 语言一样的功能。在其中,比较大的一个痛点就是私有属性问题。
何为私有属性?
私有属性是面向对象编程(OOP)中非常常见的一个特性,一般满足以下的特点:
1: 能被class类内部的不同方法访问,但是不能在类外部被访问
2: 子类步能继承父类的私有属性
在 Java 中,可以使用 private
实现私有变量,但是可惜的是, JS 中并没有该功能。
私有属性提案
在2015年6月,ES6正式成为标准,为了纪念这个时刻,这个标准又被称为ES2015。
javaScript 中的 class 从备胎中转正,但是没有解决私有属性的问题,产生了一个提案------在属性名前加上 # ,就表示是一个私有属性。
class Foo {
#a; // 定义私有属性
constructor(a, b) {
this.#a = a;
this.b = b
}
}
备注: 上述代码私有属性的声明,需要先经过Babel等编译器编译后才能正常使用
至于为什么不用 private
关键字呢?参考大佬说的就是有一大原因是向 Python 靠拢,毕竟从 es6 以来, JS 一直向着 Python 发展。
如何设计实现私有属性呢?
上文我们介绍了class类 出现原因,以及它没有解决私有属性这个问题,那么如何自己设计一下呢?带着好奇心来探讨一下吧
class Student {
constructor(name, age) {
this._name = name;
this._age = age;
}
get userInfo() {
return '姓名:' + this._name + ',年龄:' + this._age;
}
}
const handler = {
get: function (target, key) {
if (key[0] === '_') { // 访问私有属性,返回一个 error
throw new Error('Attempt to access private property');
} else if (key === 'toJSON') {
const obj = {};
for (const key in target) { // 只返回公共属性
if (key[0] !== '_') {
obj[key] = target[key];
}
}
return () => obj;
}
return target[key]; // 访问公共属性,默认返回
},
set: function (target, key, value) {
if (key[0] === '_') {
throw new Error('Attempt to access private property');
}
target[key] = value;
},
// 解决私有属性能遍历问题,通过访问属性对应的属性描述符,然后设置 enumerable 为 false
getOwnPropertyDescriptor(target, key) {
const desc = Object.getOwnPropertyDescriptor(target, key);
if (key[0] === '_') {
desc.enumerable = false;
}
return desc;
}
}
const stu = new Proxy(new Student('Chocolate', 21), handler);
console.log(stu.userInfo); // 姓名:Chocolate,年龄:21
console.log(stu instanceof Student); // true
console.log(JSON.stringify(stu)); // "{}"
for (const key in stu) { // No output 不能遍历私有属性
console.log(key);
}
stu._name = 'Lionkk'; // Error: Attempt to access private property
新式做法
就发展趋势来看, TS 已经成为前端必备的技能之一,TypeScript
的 private 很好解决了私有属性这个问题,后续学习了 ts
之后再补充吧。
附:TypeScript 中的处理方式
TypeScript 是 JavaScript 的一个超集,它会编译为原生 JavaScript 用在生产环境。允许指定私有的、公共的或受保护的属性是 TypeScript 的特性之一。
class Student {
private name;
private age;
constructor(name, age) {
this.name = name;
this.age = age;
}
get userInfo() {
return '姓名:' + this.name + ',年龄:' + this.age;
}
}
const stu = new Student('Chocolate', 21);
console.log(stu.userInfo); // 姓名:Chocolate,年龄:21
使用 TypeScript 需要注意的重要一点是,它只有在 编译 时才获知这些类型,而私有、公共修饰符在编译时才有效果。
如果你尝试访问 stu.name
,你会发现,居然是可以的。
只不过 TypeScript 会在编译时给你报出一个错误,但不会停止它的编译。
// 编译时错误:属性 ‘name’ 是私有的,只能在 ‘Student ’ 类中访问。
console.log(stu.name); // 'Chocolate'
TypeScript 不会自作聪明,不会做任何的事情来尝试阻止代码在运行时访问私有属性。我只把它列在这里,也是让大家意识到它并不能直接解决问题。
另外,TypeScript 的 class 私有变量最终编译也是通过 WeakMap
来实现的,来自评论区小伙伴们的解答~