前言
原子操作这是Java多线程编程的老生常谈了。所谓原子操作是指不会被线程调度机制打断的操作;这种操作一旦开始,就一直运行到结束,中间不会有任何 context switch (切换到另一个线程)。
当然JS是单线程的,所以不存在线程打断这么一说,我只是从Java中借引了这么一个概念。如果一段JS代码在执行过程中没有未知操作被引入,那么这段代码就是100%可控和安全的,这就是原子操作。反之非原子操作可能会因为外界操作的引入导致代码变得难以控制而产生隐晦的bug。
JavaScript中可以通过Object.create(null)
来创建原子,这是非常自然而又易于理解的方式。不过也有一些其它的方法来实现相同的效果,虽然在概念上有所不同,但是它们创建的一样是“原子对象”。
创建原子
使用Object.create()
// 方法1atom = Object.create(null)
使用Object.setPrototypeOf()
// 方法2atom = Object.setPrototypeOf(new Object, null)// ORatom = Object.setPrototypeOf({}, null)
重置构造器的原型属性
// 方法3function MyObject() { // ...}Object.setPrototypeOf(MyObject.prototype, null);atom = new MyObject;
重置类的原型
注:“非派生类(没有extends声明的类)”,与将一个普通函数用作构造器时的特性基本一致。
class MyClass { // ...}Object.setPrototypeOf(MyClass.prototype, null);atom = new MyClass;
使用派生自null值的类
JavaScript在处理extends null时会将MyClass.prototype
的原型置为null,因此这个类构建的实例自然就是atom。但是,派生自null值的类无法直接构建,因此需要声明自己的构造方法(以该方法创建和返回的对象作为this)。
// 方法4class MyClass extends null { constructor() { return Object.create(new.target.prototype); }}atom = new MyClass;
上例在实现构造方法constructor()
时是直接引用new.target.prototype
来作为原型的,这样也就可以在new运算时引用到MyClass子类的原型。例如:
// 方法5class MyClassEx extends MyClass { get description() { return 'class MyClassEx'; }}atom = new MyClassEx;console.log(atom.description); // class MyClassEx
使用一般函数并直接返回原子
下面的代码是兼容构造器、原型继承和函数调用等方式的。
// 方法6// (当作为函数调用时,new.target为undefined值)function MyAtom() { return Object.create(new.target && new.target.prototype || null);}// 示例1atom = new MyAtom;// 示例2atom = MyAtom();
使用类来创建原子的一个特例
在上述方法4中,由于声明了extends null,因此类MyClass必须拥有一个自己的构造方法。但事实上在JavaScript中,
新闻热点
疑难解答
图片精选