JavaScript中创建原子的方法总结


Posted in Javascript onAugust 26, 2018

前言

原子操作这是Java多线程编程的老生常谈了。所谓原子操作是指不会被线程调度机制打断的操作;这种操作一旦开始,就一直运行到结束,中间不会有任何 context switch (切换到另一个线程)。

当然JS是单线程的,所以不存在线程打断这么一说,我只是从Java中借引了这么一个概念。如果一段JS代码在执行过程中没有未知操作被引入,那么这段代码就是100%可控和安全的,这就是原子操作。反之非原子操作可能会因为外界操作的引入导致代码变得难以控制而产生隐晦的bug。

JavaScript中可以通过Object.create(null)来创建原子,这是非常自然而又易于理解的方式。不过也有一些其它的方法来实现相同的效果,虽然在概念上有所不同,但是它们创建的一样是“原子对象”。

创建原子

使用Object.create()

// 方法1
atom = Object.create(null)

使用Object.setPrototypeOf()

// 方法2
atom = Object.setPrototypeOf(new Object, null)

// OR
atom = Object.setPrototypeOf({}, null)

重置构造器的原型属性

// 方法3
function 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)。

// 方法4
class MyClass extends null {
 constructor() {
 return Object.create(new.target.prototype);
 }
}
atom = new MyClass;

上例在实现构造方法constructor()时是直接引用new.target.prototype来作为原型的,这样也就可以在new运算时引用到MyClass子类的原型。例如:

// 方法5
class 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);
}

// 示例1
atom = new MyAtom;
// 示例2
atom = MyAtom();

使用类来创建原子的一个特例

在上述方法4中,由于声明了extends null,因此类MyClass必须拥有一个自己的构造方法。但事实上在JavaScript中,extends null所表达的含义是:

  • 因为有extends声明,所以默认的constructor()将总是调用父类super()来创建实例(亦即是所谓“this引用总是由祖先类创建的”);但是,
  • 由于extends null意味着父类为null,因此“调用父类super() ”失败。

这是类MyClass不能使用默认的constructor() ——而“通常”必须由用户代码来实现构造方法的原因。然而JavaScript只是在静态语法分析时才通过extends null来识别父类,真正在运行期时,它是通过方法的内部槽([[HomeObject]])来动态查找super的。——由于该内部槽指向类MyClass(或类的原型属性MyClass.prototype),因此所谓的super其实就是如下的运算值:

// for static class methods
_super = Object.getPrototypeOf(MyClass)

// for instance methods
_super = Object.getPrototypeOf(MyClass.prototype)

既然如此,我们就可以通过如下的代码来声明一个“可以创建原子”的类。例如:

// 方法7
Atom = Object.setPrototypeOf(class extends null {}, Object)
atom = new Atom;

在这个方法中,实际上Atom指向类表达式,并且重置了它的原型:

// (如下等价于方法7的第一行代码)
Atom = class extends null {};
Object.setPrototypeOf(Atom.prototype, null);
Object.setPrototypeOf(Atom, Object);

其中extends null决定了Atom.prototype的原型指向一个null值,而setPrototypeOf(...)决定了当new Atom()时,对象实际上是由Object() ——这个super来创建的。因此,当new Atom时,实际发生的操作是:

// (如下等价于方法7的第二行代码)
_this = new Object(); // call super()
Object.setPrototypeOf(_this, Atom.prototype); // prototype is null
atom = _this;

所以方法7是一种能够“利用JavaScript原生构造器”来创建原子的技巧。比如最简单的获得一个Arguments()构造器的方法其实是这样:

let Arguments = Object.setPrototypeOf(class extends null {}, Array);

这样得到的对象将与JavaScript内置在函数调用中创建的argument完全一致:

// (JavaScript的arguments对象也是原子)
let arguments = new Arguments(1,2,3); // more paraments

不过在ES6中arguments也实现了迭代器界面,因此需要一行额外的代码来处理之:

// for ES6
Arguments.prototype[Symbol.iterator] = [][Symbol.iterator];

其它

1. 关于映射类

在Metameta( @aimingoo/metameta  (本地下载))中,可以使用Meta.from()来得到一个映射类,这与上面的方法7是相同的方式:

// 方法8(in metameta)
MyAtomObject = Meta.from(Object);
atom = new MyAtomObject;

有趣的是,这样得到的“MyAtomObject类”(在Metameta中称为Objext类)将继承所有来自Object.xxx的类方法,例如Object.keys()等。这些方法在元系统中也是可以直接使用的。例如:

// (in metameta)
Objext = Meta.from(Object);
Objext.keys(new Objext);

2. 关于extends new.target

上面在方法4中提到extends null相当于将MyClass.prototype的原型设为null,——在方法7中也使用了这一技巧——因此事实上在Metameta( @aimingoo/metameta  (本地下载))中实现MetaMeta()时,采用的extends new.target也就相当于:

// class MyClass extends new.target ...
Object.setPrototypeOf(MyClass.prototype, new.target)

这一技巧用在类的constructor()方法中,返回一个新的类(类声明的表达式)。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对三水点靠木的支持。

Javascript 相关文章推荐
jQuery使用数组编写图片无缝向左滚动
Dec 11 Javascript
jquery 实现两级导航菜单附效果图
Mar 07 Javascript
IE6-8中Date不支持toISOString的修复方法
May 04 Javascript
js鼠标滑过图片震动特效的方法
Feb 17 Javascript
每天一篇javascript学习小结(属性定义方法)
Nov 19 Javascript
jQuery获取父元素节点、子元素节点及兄弟元素节点的方法
Apr 14 Javascript
JS实现头条新闻的经典轮播图效果示例
Jan 30 Javascript
vue实现后台管理权限系统及顶栏三级菜单显示功能
Jun 19 Javascript
小程序实现搜索框功能
Mar 26 Javascript
vue路由守卫及路由守卫无限循环问题详析
Sep 05 Javascript
windows下create-react-app 升级至3.3.1版本踩坑记
Feb 17 Javascript
在VUE中使用lodash的debounce和throttle操作
Nov 09 Javascript
解决vue.js 数据渲染成功仍报错的问题
Aug 25 #Javascript
解决Vue.js由于延时显示了{{message}}引用界面的问题
Aug 25 #Javascript
vue中各选项及钩子函数执行顺序详解
Aug 25 #Javascript
vue实现在一个方法执行完后执行另一个方法的示例
Aug 25 #Javascript
JS Object.preventExtensions(),Object.seal()与Object.freeze()用法实例分析
Aug 25 #Javascript
对Vue beforeRouteEnter 的next执行时机详解
Aug 25 #Javascript
vue 监听键盘回车事件详解 @keyup.enter || @keyup.enter.native
Aug 25 #Javascript
You might like
奇怪的PHP引用效率问题分析
2012/03/23 PHP
php fseek函数读取大文件两种方法
2016/10/12 PHP
php实现快速对二维数组某一列进行组装的方法小结
2019/12/04 PHP
js下用eval生成JSON对象
2010/09/17 Javascript
JQuery对class属性的操作实现按钮开关效果
2013/10/11 Javascript
jquery Ajax 实现加载数据前动画效果的示例代码
2014/02/07 Javascript
Jquery修改页面标题title其它JS失效的解决方法
2014/10/31 Javascript
js实现兼容IE和FF的上下层的移动
2015/05/04 Javascript
Nodejs下DNS缓存问题浅析
2016/11/16 NodeJs
详解如何让Express支持async/await
2017/10/09 Javascript
Vue cli+mui 区域滚动的实例代码
2018/01/25 Javascript
微信小程序url传参写变量的方法
2018/08/09 Javascript
Layui组件Table绑定行点击事件和获取行数据的方法
2018/08/19 Javascript
React Native中Mobx的使用方法详解
2018/12/04 Javascript
使用puppeteer爬取网站并抓出404无效链接
2018/12/20 Javascript
微信小程序外卖选购页实现切换分类与数量加减功能案例
2019/01/15 Javascript
vue 项目 iOS WKWebView 加载
2019/04/17 Javascript
微信小程序实现多选框全选与反全选及购物车中删除选中的商品功能
2019/12/17 Javascript
[45:14]Optic vs VP 2018国际邀请赛淘汰赛BO3 第二场 8.24
2018/08/25 DOTA
Python中的面向对象编程详解(下)
2015/04/13 Python
Python实现扫描局域网活动ip(扫描在线电脑)
2015/04/28 Python
python实现嵌套列表平铺的两种方法
2018/11/08 Python
python创建属于自己的单词词库 便于背单词
2019/07/30 Python
Django实现跨域的2种方法
2019/07/31 Python
Python安装与卸载流程详细步骤(图解)
2020/02/20 Python
Python使用configparser读取ini配置文件
2020/05/25 Python
Django如何实现密码错误报错提醒
2020/09/04 Python
详解Anaconda安装tensorflow报错问题解决方法
2020/11/01 Python
“型”走纽约上东区:Sam Edelman
2017/04/02 全球购物
幼儿园安全检查制度
2014/01/30 职场文书
英语教师岗位职责
2014/03/16 职场文书
第二批党的群众路线教育实践活动总结报告
2014/10/30 职场文书
运动会广播稿100字
2015/08/19 职场文书
新入职员工工作总结
2015/10/15 职场文书
2016年母亲节广告语
2016/01/28 职场文书
MongoDB使用profile分析慢查询的步骤
2021/04/30 MongoDB