前几天在 SF 回答了这个问题:如何使用proxy,如何在内部拦截get方法,然后翻了翻以前写的博客:使用 Proxy 添加魔术属性/方法,发现上次写完代理对象就停笔了,所以今天补全一下:创建有魔术属性/方法的类。这样就比较完整了。
JavaScript 构造函数的特点
ES6 增加了 class
关键字,梳理了面向对象的语法,现在我们可以这样定义一个类:
class Person {
constructor(name) {
this.name = name;
}
hello() {
return `Hello, my name is ${this.name}.`;
}
}
如果你有其它面向对象语言的经验,应该很容易理解这段代码。
不过 JS 的构造函数特别,它支持 return
一个其它对象,作为 new SomeClass()
的结果。(如果不 return
或者 return
一个空对象,那么 new SomeClass()
得到的就是 SomeClass
的实例。)
也就是说,原则上,我们可以这么做:
class Person {
constructor(name) {
this.name = name;
return {name: 'Meathill'};
}
}
const person = new Person('张三');
console.log(person.name); // 'Meathill'
结合 Proxy
理解了上一节的内容,我们就很容易得到这样一个类:
class Person {
constructor(name) {
this.name = name;
return new Proxy(this, {
get(target, property) {
if (property in target) {
return target[property];
}
console.warn('Sorry, I can't do that.');
}
}
}
}
在这个类的构造函数里,我返回了一个 Proxy
实例,代理了对真正 Person
实例的访问。当访问的属性/方法在实例上时,就返回需要的属性/方法,否则的话,输出警告。
实际上,Proxy 的 get
和 set
的功能远不止如此,上面的代码只是一些演示。
用途
魔法属性/方法主要有以下用途:
- 对象 a 要使用一部分对象 b 的功能,但是又不方便直接用原型链。比如上一篇文章的场景,我提供类 VElement 作为接口,实际完成工作的是另一个沙箱中的 Element。
- 不知道会怎么访问对象,希望所有访问都照顾到。
- 希望捕获到对对象的修改,也就是 Vue 3.0 的核心修改。
Vue 3.0
Vue 1.x & 2.x 期间,都在使用 ES5 的 Object.defineProperty
拦截对对象的修改,实现响应式。这样的做法看起来很神奇,给 Vue 带来了巨大的成功。但是这样做也有坏处:
- 声明实例时需要很多预处理工作,而且数据量越大处理的时间越久
- 不支持某些数组操作
- 不支持其它数据类型,比如 Set、Map
- 不支持后续的数据观察
使用 Proxy 之后,以上问题全部都迎刃而解,甚至,因为 Proxy 是原生 API,性能表现更好,取得了内存减半、速度加倍的效果。
想了解更多 Vue 3.0 的新特性,可以去看我在 SF 的分享:迎接 Vue 3.0。(注:这是免费广告,我不会从新购用户取得收益。)
参考文章:
Constructor, operator “new”(构造函数和操作符 “new”)
欢迎吐槽,共同进步