JavaScript的元系统

栏目: JavaScript · 发布时间: 6年前

内容简介:本文是对在第四届FEDay中分享的《无类继承:JavaScript面向对象的根基》的进一步讨论。也是对开源项目ECMAScript中只有两处提及到“Meta”这个概念,一处是说明ECMAScript的规范类型(所以ECMAScript中是没有所谓“元系统(

本文是对在第四届FEDay中分享的《无类继承:JavaScript面向对象的根基》的进一步讨论。也是对开源项目 @aimingoo/metameta 的解析。

ECMAScript中只有两处提及到“Meta”这个概念,一处是说明ECMAScript的规范类型( a specification type )是用于描述和实现语言类型( language types )的元值( meta-values ),另一处则是唯一被称为“元属性( Meta Property )”的 new.target

所以ECMAScript中是没有所谓“元系统( Meta system )”或“元类型系统( Meta type system )”。我们在这里先定义一个称为“原子( Atom )”的东西,并基于此来构建起一个完整的JavaScript元系统。

原子(atom)

定义:原子是JavaScript中的对象的最小单元,它是对象但不继承自Object();以原子为原型的对象也会被称为原子对象。

JavaScript中的对象就是一个属性包( properties bag, or a collection of properties ),一个属性包为空集时,它必然是对象的最小形态。因此一个没有原型,且自有属性集为空的对象,必然是一个原子。

原子可以用ES5兼容的语法创建出来:

var atom = Object.create(null);

也可以通过将一般对象的原型置为null来得到一个原子:

var atom = Object.setPrototypeOf(new Object, null);

并且,在ECMAScript中有四个内建/原生对象是原子的:

function isAtom(x) {
    switch (typeof x) {
        case 'object':
        case 'function': return !(x instanceof Object);
    }
    return false;
}

// modules in es6
import * as namespace from './your-module.js';

// arguments from function
var args = Function('return arguments')();

console.log(isAtom(null));
console.log(isAtom(Object.prototype));
console.log(isAtom(namespace));
console.log(isAtom(args));

在同一个运行环境中,可以并存多个原子,以及由原型指向原子的、原型继承的对象系统。所有这些原子以及衍生的对象系统都是互不相等、没有交集的。

> Object.create(null) === Object.create(null)
false

因此,JavaScript原生的、由Object()派生或创建的对象、类,在本质上也是上述“对象系统”之一。但是,

  • 作为唯一特例,Object()所属的对象系统称为“原生对象系统”,以区别于后来创建的其它原子对象系统。

并且,

  • 作为唯一特例,null值是一个原子(注:原子在ECMAScript约定的ECMAScript language types中不是对象,但在JavaScript自身的类型检查( typeof )中它是对象)。

元(meta)

定义:能产生原子(atom)的一个过程称为元(meta)。

推论:原子的构造器(Atom)与元(meta)是等义的。

由于atom对象的构造器通常记为Atom(),所以从概念上它与“元(meta)”是等义的,在实际使用中我们也并不明确地区分二者。

meta可以是一个函数,也可以是一个类,甚至也可以是一个代理对象( proxy )、箭头函数( arrow functions )或方法( methods )。——在概念定义中,我们只约定了“meta是一个过程”,并没有强调atom是它构建出来的,亦或只是它的调用结果。

在开源项目中Metameta( @aimingoo/metameta )中,meta是以ES6的语法声明的一个Atom类:

class Atom extends null {
    constructor() {
        return Object.create(new.target.prototype);
    }
}

任何情况下,我们用该meta都可以产生新的原子对象:

> isAtom(new Atom)
true

> new Atom === new Atom
false

元类型(Meta,Meta types)

定义:所有元(meta)的类型称为元类型(Meta types)

在JavaScript中,一个数据所对应的类型可以用它的构造器来标示,亦即是Meta();并且这也意味着Meta()作为构造器产生的实例是元( meta )。亦即是说,Meta()应当是一个“返回meta过程”的过程。

在ES6的语法中,可以简单地在函数中返回一个“类声明( class definitions )”来得到一个字面量风格的类。因此在Metameta中声明了MetaMeta()类来作为元类型的祖先类:

// Meta's super
class MetaMeta extends null {
  constructor(base) { // Atom() by default
    return Object.setPrototypeOf(class extends new.target {}, base);
  }
...

所以现在,我们就可以通过如下的方法来得到一个原子了:

// Atom与meta是同义的
> Atom = meta = new MetaMeta

// 创建一个原子
> atom = new Atom

// 检测
> isAtom(atom)
true

基于原子的继承性

我们之所以要用class来声明Atom和MetaMeta,是为了简单地得到面向对象的继承性。亦即是说,当我们想要派生一个新的原子对象类型的时候,可以简单地通过扩展上述的系统来得到它的构造器。例如:

class MyAtomObject extends new MetaMeta {
    get description() {
        return 'i am an atom.';
    }
}

var x = new MyAtomObject;
console.log(x.description);

在这个例子中, new MetaMeta 直接创建了一个Atom,而 MyAtomObject 则派生自该Atom,因此它的实例自然是atom。并且,基于ES6的类声明语法, MyAtomObject 也可以具有自己的存取器成员、对象方法,或者类方法。

基于元的继承性

从MetaMeta也可以基于元类型进行派生,由此我们可以实现“元类(Meta class)类型”。

定义:元类(Meta class)是一个产生类(class)的过程。

从定义上来说,简单的元类可以写成:

function SimpleMetaClass() {
    return class {};
}

当然,由于在MetaMeta中“元类型”本身就是基于类实现的——亦即是它本来就是一个“返回类”的过程,因此它只需要简单的一层概念抽象就可以实现“元类”类型了。如下:

// “元(Meta)”类型
class Meta extends MetaMeta { ... }

// “元类(MetaClass)”类型
class MetaClass extends Meta { ... }

之所以让Meta派生自MetaMeta(),主要目的是为了得到一层super声明,以确保Meta()以及它的类方法( static methods )之于它的super是词法上下文绑定的。而“元类(MetaClass)”则用于派生一层类型声明,以便让MetaClass()能拥有自己的类方法,例如 MetaClass.isClassOf()

现在,我们已经在Meta上实现了一层派生,我们也可以实现更多层的派生,以通过“类类型”的方法来得到更多的构造器——换言之,我们可以产生更多的类,它们都可以作为更多的“不同的对象系统的”祖先类。我们可以让JavaScript中出现多个完全不同的、与Object()所代表的“原生对象系统”并列的对象系统。

如前所述的——它们相互独立,没有交集。例如:

// “元类(MetaClass)”产生类
var ObjectEx = new MetaClass;

// 基于ObjectEx可以派生一个“独立的、不同的”对象系统
class MyObjectEx extends ObjectEx {};

// 可以用类似的方法来派生更多这样的对象系统
class MyObjectPlus extends new MetaClass {
    ...
};

接下来,你可以检测它们的类属关系:

> ObjectEx.isClassOf(MyObjectEx)
true

> MetaClass.isClassOf(ObjectEx)
true

或使用ECMAScript内置方法检测原子:

> (new MyObjectEx) instanceof ObjectEx
true

> (new MyObjectEx) instanceof MyObjectPlus
false

以上所述就是小编给大家介绍的《JavaScript的元系统》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Java解惑

Java解惑

布洛赫、加夫特 / 陈昊鹏 / 人民邮电出版社 / 2006-1 / 39.00元

本书特写了95个有关Java或其类库的陷阱和缺陷的谜题,其中大多数谜题都采用了短程序的方式,这些程序的行为与其看似的大相径庭。在每个谜题之后都给出了详细的解惑方案,这些解惑方案超越了对程序行为的简单解释,向读者展示了如何一劳永逸地避免底层的陷阱与缺陷。 本书趣味十足、寓教于乐,适合于具备Java知识的学习者和有编程经验的Java程序员。一起来看看 《Java解惑》 这本书的介绍吧!

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具