Skip to content

插入Array类型的节点

在js中,数组类型是一个很特殊的类型,创建的方式并不是只有一种字面量的方法。除此之外,其他类型都可以使用构造函数创建。

思考一下,在js中创建数组有几种方法:

  1. 使用Array的构造函数,这里使用了new关键字,它很特殊,在babel中它有一个单独的节点类型newExpression

    javascript
    var arr = new Array(1, 2, 3)
  2. 使用字面量,字面量就很简单了,在babel中可以直接使用arrayExpression表达式。

    javascript
    var arr = [1, 2, 3]
  3. 使用Array.from创建数组,下面两个都是由函数生成,需要用到memberExpression成员表达式和objectExpression对象表达式。

    javascript
    var arr = Array.from({ 0: 1, 1: 2, length: 2 })
  4. 使用Array.of创建数组

    javascript
    var a1 = Array.of({ name: 1 }, { name: 2 })

所以在babel中,也有对应的表达式,与上面创建的方法一一对应。

第一种使用构造函数中,使用了new来实例化,对应了newExpressionnew值表达式。

下面是对应的实现的代码:

  1. new 关键字

    javascript
      /**
       * @NOTE 使用new表达式构建数组
       */
      const newArr3 = t.newExpression(t.identifier('Array'), [ t.numericLiteral(1),       		t.numericLiteral(2) ])
      const arr3 = t.variableDeclaration('const', [
        t.variableDeclarator(t.identifier('arr3'), newArr3)
      ])

可以使用由newExpressionnew表达式声明一个Array实例,其效果就是new Array(1, 2)。到这里已经完成了等号右边的内容,还有左边的内容可以继续使用variableDeclaration声明去创建。

  1. 字面量创建

    javascript
      /**
       * @NOTE 这里使用的字面量表达式创建了一个变量arr2
       */
      const arr2 = t.variableDeclaration('const', [
        t.variableDeclarator(t.identifier('arr2'), t.arrayExpression([ 			    t.numericLiteral(1), t.numericLiteral(2) ]))
      ])

    在这里又回到了常规的套路,可以直接使用arrayExpression表达式,在内部添加数组元素就可以。我的这个例子是数组的类型就是Number类型,其他类型都可以使用对应的函数创建。

  2. Array.from 函数

    javascript
      /**
       * @NOTE 使用Array.from实现创建数组
       * 
       * @example Array.from({ 0: 1, 1: 2, length: 2 })
       */
      const arr4ObjValue = t.objectExpression([
        t.objectProperty(t.numericLiteral(0), t.numericLiteral(1)),
        t.objectProperty(t.numericLiteral(1), t.numericLiteral(2)),
        t.objectProperty(t.identifier('length'), t.numericLiteral(2)),
      ])
      const arr4 = t.variableDeclaration('const', [
        t.variableDeclarator(t.identifier('arr4'), t.callExpression(
          t.memberExpression(t.identifier('Array'), t.identifier('from')),
          [arr4ObjValue]
        ))
      ])

    这个就很特殊了,它是由一个函数创建的,需要使用提供的callExpresion函数表达式,与此同时Array.from期待一个对象作为入参,所以我们还需要准备一个对象节点,作为callExpress的入参。

    可以使用objectExpress对象表达式创建一个函数节点,用objectProperty给这个函数对象添加属性。用这种声明式方法去创建需要的节点是极其简单的,只需要提前梳理一下types提供的方法就好啦。

  3. Array.of

    javascript
      /**
       * @NOTE 使用Array.of 创建数组
       * 
       * @example Array.of(1, 2, 3)
       */
      const arr5 = t.variableDeclaration('const', [
        t.variableDeclarator(t.identifier('arr5'), t.callExpression(
          t.memberExpression(t.identifier('Array'), t.identifier('of')),
          [t.numericLiteral(1), t.numericLiteral(2)]
        ))
      ])

    Array.from同理,Array.of也是由函数驱动,所以我们同样需要callExpressioncall表达式来创建右值部分。所不同的地方只有入参部分不一致,Array.of期待由数组元素组成的入参。

如何在acorn中实现对应需求

babel 内实现的需求代码直接搬到acorn中执行时,会有一个明显的报错,如果你还有印象的话,就是我们之前说过的acorn中都是直接量Liternal没有numericLiternal或者是其他的什么节点。

那么有没有什么一些方法可以做到?

最简单的方法,其实可以直接利用acorn.parse将一段源代码解析成为AST,然后把正常解析的AST对象,重新放回目标AST树。

第一步,先生成目标组织树

javascript
const workflow = async () => {
  const source = await fs.readFile(filepath)
  const ast = acorn.parse(source, { sourceType: 'module', ecmaVersion: 2021 })
  
}

第二步,将Array.fromArray.of这两种语句以源代码字符串,解析为正常AST

javascript
  /**
   * @NOTE 使用new关键字实例
   */
  const arr3Node = t.newExpression(t.identifier('Array'), [t.identifier('1'), t.identifier('2')])
  const arr3 = t.variableDeclaration('const', [
    t.variableDeclarator(t.identifier('arr3'), arr3Node)
  ])

  /**
   * @NOTE 使用Array.from 构建数组
   */
  const arr4Ast = acorn.parse(`const arr4 = Array.from({ 0: 1, length: 2 })`)
  const arr4 = arr4Ast.body[0]

  /**
   * @NOTE 使用Array.of 构建数组
   */
  const arr5Ast = acorn.parse(`const arr5 = Array.of(1, 2, 3)`)
  const arr5 = arr5Ast.body[0]

  ast.body.push(arr2, arr3, arr4, arr5)

在这里用这种取巧的方法已经不是我之前本意了,我们的目的就是为了通过使用babel中提供的方法来认识AST中的节点类型。

但是这里也给我们一点其法,所有问题并不是只有一种解法,为了达到目标去探索实现的方法,也是一种学习的过程。

总结:

  1. 数组类型是一个极其特殊的对象类型,它被允许以多种方式生成,所以会认识多个由types提供的方法,生成节点
  2. 在js中,每一种类型都有对应的构造函数,同理,可以创建其他数据类型的ast节点

源码地址:

  1. babel创建数组类型: https://github.com/stack-wuh/mini-cli/blob/main/core/create-array-node/babel.js
  2. acorn创建数组类型:https://github.com/stack-wuh/mini-cli/blob/main/core/create-array-node/acorn.js

MIT License.