插入Object类型的节点
Object
数据类型,在js中是一个极其特殊的存在。在js中存在着一些分歧,那就是JS到底是面向对象还是基于对象
。
与Array
类型一样,Object
也是一个复杂引用类型,我们可以再次回忆一下,创建一个对象有几种方法。
字面量声明
javascriptvar obj1 = { name: 'shadow' }
通过new关键字
javascriptvar obj2 = new Object({ name: 'shadow' })
通过ES6的create方法
javascriptvar obj3 = Object.create({ name: 'shadow' })
通过工厂函数
javascriptfunction createObject (o) { function F () {} F.prototype = o retunr new F() } var obj4 = createObject({ name: 'shadow' })
ES6中的特殊对象 Set
javascriptvar obj5 = new Set()
ES6的特殊对象 Map
javascriptvar obj6 = new Map() obj6.set('a', 1)
我们可以看到的,在这里出现了很多新的语句,比如在第四个例子中的赋值语句
和return
语句,在第六个例子中出现的map.set
函数表达式。
从代码的实现上看,我们就可以明显的看到里面多了很多前面没有用到的语句,下面是实现的代码的实例。
字面量创建,利用
objectExpress
对象表达式和objectProperty
属性表达式javascript/** * @NOTE 字面量声明对象 * * @example * var obj1 = { name: 'shadow' } */ const obj1Node = t.objectExpression([ t.objectProperty(t.identifier('name'), t.stringLiteral('shadow')) ]) const obj1 = t.variableDeclaration('const', [ t.variableDeclarator(t.identifier('obj1'), obj1Node) ])
只需要满足AST对于对象类型的解析规则,就可以随意添加节点类型。对于一个普通对象,对象的KEY值类型必须是字符串,所以对应在
obejctProperty
里既可以使用identifier
直接量声明KEY,也可以使用stringLiteral
声明KEY。对于VALUE部分,就可以是任意类型,我现在添加的就是一个字符串节点,用
stringLiteral
声明了一个值为"shadow"的节点。new关键字创建,这种方法在创建Array时已经使用过了,通过AST我们可以很清晰将
new
关键字的内部机制了解清楚javascript/** * @NOTE new关键字创建对象 * @example * var obj2 = new Object({ name: 'shadow' }) */ const obj2CallNode = t.newExpression(t.identifier('Object'), [ t.objectExpression([ t.objectProperty(t.identifier('name'), t.stringLiteral('shadow')) ]) ]) const obj2 = t.variableDeclaration('const', [ t.variableDeclarator(t.identifier('obj2'), obj2CallNode) ])
可以依据代码中出现的语句顺序,依次解析语法结构,手动将其映射为AST语法。
obj2CallNode
其实就是new Obejct({ name: 'shadow' })
部分的重构,第一我们需要使用newExpression
new表达式,代码中函数有入参,入参类型是对象类型,所以还是可以继续按照上面方式,创建一个Object类型的节点。var obj2 = new Object({ name: 'shadow' })
,一旦源代码出现了声明语句,就必须使用variableDeclaration
来声明。使用
Object.create
函数构建,这里涉及到函数调用,所以重构的语句中必须出现callExpression
表达式javascript/** * @NOTE 通过Object.create 方法 * @example * var obj3 = Object.create({ name; 'shadow' }) */ const obj3CallNode = t.callExpression( t.memberExpression(t.identifier('Object'), t.identifier('create')), [t.objectExpression([ t.objectProperty(t.identifier('name'), t.stringLiteral('shadow')) ])] ) const obj3 = t.variableDeclaration('const', [ t.variableDeclarator(t.identifier('obj3'), obj3CallNode) ])
使用工厂函数构建,这里遇到一个新的声明语句,函数的声明
functionDelcaration
,除此之外还有块语句blockStatemtn
以及returnStatement
return语句javascript/** * @NOTE 使用工厂函数创建对象 * @example * function createObject (o) { * function F() {} * F.prototype = o * return new F() * } * var obj4 = createObject({ name: 'shadow' }) */ const obj4Func = t.functionDeclaration( t.identifier('createObject'), [t.identifier('o')], t.blockStatement([ t.functionDeclaration(t.identifier('F'), [], t.blockStatement([])), t.expressionStatement(t.assignmentExpression('=', t.memberExpression(t.identifier('F'), t.identifier('prototype')), t.identifier('o'))), t.returnStatement(t.newExpression(t.identifier('F'), [])) ]) ) const obj4Call = t.callExpression(t.identifier('createObject'), [ t.objectExpression([ t.objectProperty(t.identifier('name'), t.stringLiteral('shadow')) ]) ]) const obj4 = t.variableDeclaration('const', [ t.variableDeclarator(t.identifier('obj4'), obj4Call) ])
memberExpression
成员表达式是一个比较熟悉的,在Array那部分就已经接触过了,形如obj.name
或者是arr[0]
出现了.
号都需要使用成员表达式。returnStatement
return语句,我们经常会遇到的场景,返回一个方法、一个对象、一个Boolean值或者是数组,这个场景遇到的就是返回一个函数表达式。functionDeclaration
函数声明,我们对一个普通函数做结构操作function foo (...props) {}
并不会报错,其原因就是函数有一个隐藏类数组对象arguments
,与之对应,functionDeclaration
第一个入参作用是给函数命名,第二个就是其入参的位置。随后就是函数体部分的抽象,
{}
遇到这种符号,其实就是块语句,那就必须使用blockStatement
函数来抽象,这个函数的入参是数组类型,里面就是具体的源代码的抽象。在上面例子中,我们先抽象了一个空函数F
。然后遇到了赋值语句assignmentExpression
。最后,就是一句赋值语句
var obj4 = createObject({ name: 'shadow' })
。前面说过了,遇到了函数执行就必须使用callExpression
表达式,凡是有声明语句var obj4
就必须使用variableDeclaration
。创建Set对象,继续使用
newExpression
表达式javascript/** * @NOTE 使用Set创建对象 * @NOTE 其实就是使用new 关键字 var s = new Set() */ const obj5New = t.newExpression(t.identifier('Set'), [ t.arrayExpression([ t.numericLiteral(1), t.numericLiteral(2) ]) ]) const obj5 = t.variableDeclaration('const', [ t.variableDeclarator(t.identifier('obj5'), obj5New) ])
这一段就没必要说些什么了吧,出现的api都是一些熟面孔,只需要按照自己的需求设值就好了。
创建Map对象,继续使用
newExpression
表达式,出现了一个新面孔expressionStatement
表达式语句javascript/** * @NOTE 使用Map创建对象 * @NOTE 其实质就是使用new 关键字 var m = new Map() * @example * var m = new Map() * m.set('a', 1) */ const obj6New = t.newExpression(t.identifier('Map'), []) const obj6 = t.variableDeclaration('const', [ t.variableDeclarator(t.identifier('m'), obj6New) ]) const obj6Assign = t.expressionStatement(t.callExpression(t.memberExpression(t.identifier('m'), t.identifier('set')), [ t.stringLiteral('a'), t.numericLiteral(1) ]))
这里又出现了一个面孔,
expressionStatement
表达式语句。出现的原因就是m.set('a', 1)
,它是一个语句statement
,同时它又有调用函数的操作,所以它也有expression
表达式属性。
总结:
Object
在js数据类型中属于复杂类型,它的创建方式多种多样- 新认识了一些新的API,用于配合使用创建符合要求的节点类型,遇到新的没碰到过的语句,完全可以根据语句的语义分段构建AST语句
源码地址: