javascript数组转字符串-用JS解释JS!解释 AST 及其应用

什么是 AST?

1AST: 抽象语法树 - 比喻句子树

当我们查看当今主流项目中的 devDependencies 时,我们会发现各种模块工具。总结一下:JavaScript 转译、CSS 预处理器、elint、pretiier 等。我们不在生产中使用这些模块,但它们在我们的开发过程中发挥着重要作用,上述所有工具都是基于 AST 构建的。

2AST 工作流程

3AST 树预览

AST 辅助开发工具:

二、从简单的要求入手

代码压缩的伪要求:用引用简化平方函数参数,并将变量从 num 转换为 n:

解决方案 1:使用位置暴力转换

const sourceText = `function square(num) {  return num * num;}`;

sourceText.replace(/num/g, 'n');

上述操作相当剧烈,容易引起bug,无法投入使用。如果字符串“num”存在,它也将被转换:

javascript数组转字符串_字符数组a复制到字符数组b_字符串数组转int数组

// 转换前function square(num) {  return num * num;}console.log('param 2 result num is ' + square(2));
// 转换后function square(n) { return n * n;}console.log('param 2 result n is ' + square(2));

解决方案 2:使用 babel 进行 AST 操作

module.exports = () => {  return {    visitor: {      // 定义 visitor, 遍历 Identifier      Identifier(path) {        if (path.node.name === 'num') {          path.node.name = 'n'; // 转换变量名        }      }    }  }};

通过定义标识符访问者,将遍历标识符(变量),如果标识符名称为“num”,则对其进行转换。上面的代码解决了将 num 转换为字符串的问题,但也存在潜在的问题,例如在代码如下时导致错误:

// 转换前function square(num) {  return num * num;}console.log('global num is ' + window.num);
// 转换后function square(n) { return n * n;}console.log('global num is ' + window.n); // 出错了

因为 window.num 也会与上面的访问者迭代器匹配并转换,所以转换后的代码是 window.n,这会导致错误。分析要求是“简化平方函数参数和引用,将变量从 num 转换为 n”,提取的三个关键字是“平方函数、参数、引用”,进一步优化了代码。

解决方案 2 升级:查找引用关系

module.exports = () => {  return {    visitor: {      Identifier(path,) {        // 三个前置判断        if (path.node.name !== 'num') { // 变量需要为 num          return;        }        if (path.parent.type !== 'FunctionDeclaration') { // 父级需要为函数          return;        }        if (path.parent.id.name !== 'square') { // 函数名需要为 square          return;        }        const referencePaths = path.scope.bindings['num'].referencePaths; // 找到对应的引用        referencePaths.forEach(path => path.node.name = 'n'); // 修改引用值        path.node.name = 'n'; // 修改自身的值      },    }  }};

上面的代码将该过程描述为:

转换结果:

// 转换前function square(num) {  return num * num;}console.log('global num is ' + window.num);
// 转换后function square(n) { return n * n;}console.log('global num is ' + window.num);

在面向业务的AST运营中,要具体化“人”的判断,进行合理的改造。

三巴贝林

1接口概述

// 三剑客const parser = require('@babel/parser').parse;const traverse = require('@babel/traverse').default;const generate = require('@babel/generator').default;
// 配套包const types = require('@babel/types');
// 模板包const template = require('@babel/template').default;

javascript数组转字符串_字符串数组转int数组_字符数组a复制到字符数组b

2@babel/解析器

通过 babel/parser(简单的图像)将源代码转换为 AST。

const ast = parser(rawSource, {  sourceType: 'module',  plugins: [    "jsx",  ],});

3@babel/遍历

在 AST 开发的核心,超过 95% 的代码是通过 @babel/遍历编写的。

const ast = parse(`function square(num) {  return num * num;}`);
traverse(ast, { // 进行 ast 转换 Identifier(path) { // 遍历变量的visitor // ... }, // 其他的visitor遍历器 } )

访问者的第一个参数是 path,path 不直接等于节点,path 的属性和重要路径组成如下:

4@babel/发电机

通过@babel/生成器,AST已经操作生成相应的源代码,简单直观。

const output = generate(ast, { /* options */ });

5@babel/类型

@babel/类型用于创建 AST 节点并确定 AST 节点,这些节点在实际开发中经常使用。

// is开头的用于判断节点types.isObjectProperty(node);types.isObjectMethod(node);
// 创建 null 节点const nullNode = types.nullLiteral();// 创建 square 变量节点const squareNode = types.identifier('square');

6@babel/模板

@bable/类型可以创建 AST 节点,但过于冗长,您可以通过 @babel/模板快速创建整个 AST 节点段。下面比较了获取importReactfrom'react'ast节点的两种形式:

// @babel/types// 创建节点需要查找对应的 API,传参需要匹配方法const types = require('@babel/types');const ast = types.importDeclaration(  [ types.importDefaultSpecifier(types.identifier('React')) ],   types.stringLiteral('react'));
// path.replaceWith(ast) // 节点替换

// 使用 @babel/template// 创建节点输入源代码即可,清晰易懂const template = require('@babel/template').default;const ast = template.ast(`import React from 'react'`);
// path.replaceWith(ast) // 节点替换

7. 定义通用的通天塔插件

定义一个通用的 babelplugin 将有助于与 Webpack 的集成javascript数组转字符串,例如:

// 定义插件const { declare } = require('@babel/helper-plugin-utils');
module.exports = declare((api, options) => { return { name: 'your-plugin', // 定义插件名 visitor: { // 编写业务 visitor Identifier(path,) { // ... }, } }});

// 配置 babel.config.jsmodule.exports = {    presets: [        require('@babel/preset-env'), // 可配合通用的 present    ],    plugins: [        require('your-plugin'),        // require('./your-plugin') 也可以为相对目录    ]};

在 Babelplugin 的开发中,可以说你是在编写 asttransformcallbackjavascript数组转字符串,没有直接接触 “@babel/parser, @babel/traverse, @babel/generator” 等模块,这些模块在 Babel 中调用。

当它是

需要使用 @babel/types 能力,建议直接使用 @babel/core,从源代码 [1] 可以看出,@babel/core 直接揭示了上面的 babel 模块。

const core = require('@babel/core');const types = core.types; // const types = require('@babel/types');

四 ESLintinAST

掌握了 AST 的核心原则后,自定义 ESlint 规则变得更加容易了,可以直接进入代码:

// eslint-plugin-my-eslint-pluginmodule.exports.rules = {   "var-length": context => ({ // 定义 var-length 规则,对变量长度进行检测    VariableDeclarator: (node) => {       if (node.id.name.length <= 1){         context.report(node, '变量名长度需要大于1');      }    }  })};

// .eslintrc.jsmodule.exports = {  root: true,  parserOptions: { ecmaVersion: 6 },  plugins: [   "my-eslint-plugin"  ],  rules: {    "my-eslint-plugin/var-length": "warn"   }};

体验功效

IDE 正确提示:

执行警告::的 eslint 命令

字符数组a复制到字符数组b_字符串数组转int数组_javascript数组转字符串

有关 ESLintAPI 的更多信息,请参阅官方文档 [2]。

5. 获得您需要的 JSX 解释权

第一次接触 JSX 句型主要是在学习 React 的时候,React 将 JSX 的功能推广给了中信 [3]。但是JSX不等于React,也不是由React创建的。

// 使用 react 编写的源码const name = 'John';const element = <div>Hello, {name}</div>;

// 通过 @babel/preset-react 转换后的代码const name = 'John';const element = React.createElement("div", null, "Hello, ", name);

JSX,作为一个标签句子,既不是字符串也不是HTML,而是一个JavaScript句子扩展,描述了UI应该如何与之交互的本质。JSX让人联想到模板语言,它也具有JavaScript的所有功能。让我们自己写一个 babelplugin 来获得对 JSX 的必要解释。

1JSXBabel插件我们知道HTML是描述网页的语言,

axml或vxml是描述小程序页面的语言,不同的容器是不兼容的。但相似之处在于它们都是基于 JavaScript 堆栈的,那么它们可以通过定义一组 JSX 规范在来世创建吗?

2 个进球

export default (  <view>    hello <text style={{ fontWeight: 'bold' }}>world</text>  </view>);

<div>  hello <span style="font-weight: bold;">world</span></div>

<view>  hello <text style="font-weight: bold;">world</text></view>

目前关注的是 AST 只能用作 JavaScript 的转换,那么如何转换 HTML 和 axml 等文本标记语言呢?不妨改变思路:将上面的JSX代码转换为JS代码,在网页端和小程序上提供组件消费。这是 AST 开发的设计思路,AST 工具只做代码编译,具体消耗由上层操作,@babel/preset-react 和 react 就是这种模式。

// jsx 源码module.exports = function () {  return (    <view      visible      onTap={e => console.log('clicked')}    >ABC<button>login</button></view>  );};
// 目标:转后为更通用的 JavaScript 代码module.exports = function () { return { "type": "view", "visible": true, "children": [ "ABC", { "type": "button", "children": [ "login1" ] } ] };};

一旦我们有了明确的目标,我们需要做的是:

1. 将 jsx 标签转换为 Object,标签名称是 type 属性,如转换为 {type:'view'}

字符串数组转int数组_字符数组a复制到字符数组b_javascript数组转字符串

2. 标签上的属性被转换为对象的属性,例如 {}}/>转换为 {type:'view', onTap:e=>{}}

3. 将 JSX 中的子元素移植到 child 属性,这是一个字段,例如 {type:'view', style, children:[...]}

4.面对子元素,重复上述3个步骤的工作。

下面是实现的示例代码:

const { declare } = require('@babel/helper-plugin-utils');const jsx = require('@babel/plugin-syntax-jsx').default;const core = require('@babel/core');const t = core.types;
/* 遍历 JSX 标签,约定 node 为 JSXElement,如 node = console.log('clicked')} visible>ABC*/const handleJSXElement = (node) => { const tag = node.openingElement; const type = tag.name.name; // 获得表情名为 View const propertyes = []; // 储存对象的属性 propertyes.push( // 获得属性 type = 'ABC' t.objectProperty( t.identifier('type'), t.stringLiteral(type) ) ); const attributes = tag.attributes || []; // 标签上的属性 attributes.forEach(jsxAttr => { // 遍历标签上的属性 switch (jsxAttr.type) { case 'JSXAttribute': { // 处理 JSX 属性 const key = t.identifier(jsxAttr.name.name); // 得到属性 onTap、visible const convertAttributeValue = (node) => { if (t.isJSXExpressionContainer(node)) { // 属性的值为表达式(如函数) return node.expression; // 返回表达式 } // 空值转化为 true, 如将 转化为 { type: 'view', visible: true } if (node === null) { return t.booleanLiteral(true); } return node; } const value = convertAttributeValue(jsxAttr.value); propertyes.push( // 获得 { type: 'view', onTap: e => console.log('clicked'), visible: true } t.objectProperty(key, value) ); break; } } }); const children = node.children.map((e) => { switch(e.type) { case 'JSXElement': { return handleJSXElement(e); // 如果子元素有 JSX,便利 handleJSXElement 自身 } case 'JSXText': { return t.stringLiteral(e.value); // 将字符串转化为字符 } } return e; }); propertyes.push( // 将 JSX 内的子元素转化为对象的 children 属性 t.objectProperty(t.identifier('children'), t.arrayExpression(children)) ); const objectNode = t.objectExpression(propertyes); // 转化为 Object Node /* 最终转化为 { "type": "view", "visible": true, "children": [ "ABC", { "type": "button", "children": [ "login" ] } ] } */ return objectNode;}
module.exports = declare((api, options) => { return { inherits: jsx, // 继承 Babel 提供的 jsx 解析基础 visitor: { JSXElement(path) { // 遍历 JSX 标签,如: // 将 JSX 标签转化为 Object path.replaceWith(handleJSXElement(path.node)); }, } }});

六、小结

我们介绍了AST和AST的工作模式,并体验了AST实现的令人震惊的能力。如今,AST 有哪些业务场景?当用户:

AST 将是你强大的装备。

注意:本文中演示的代码片段和测试方法可供感兴趣的读者使用。

紧急招聘

作者在阿里云-人工智能实验室-应用开发部工作。目前,我部门已积累近20W开发者和企业用户,为数亿台设备提供连接服务。目前,团队正在紧急招聘大型后端(后端、iOS、Android 等)、Java 开发、数据算法等全方位的工程师。方向为联通DevOps平台、移动中间件、无服务器、低代码平台、小程序云、云渲染应用平台、新零售/教育行业数字化转型等,详情请看:changwen.tcw@alibaba-inc.com

引用

[1]#L10-L14

[2]

[3]

技术公开课程

“反应与实践简介”

React 是一个用于构建用户界面的 JavaScript 库。本课程共 54 课时,带您全面深入地学习 React 的基础知识,并通过案例掌握相关应用。

用于校准非零正整数实例的 JavaScript 正则表达式

更新时间:2016年12月13日09:23:32 作者:冷战

本文分享 JavaScript 正则表达式 (^[1-9]d*$ 或 ^([1-9][0-9]*){1,3}$ 或 ^+?[1-9][ 0 -9]*$) 校准非零正整数的示例代码,代码简单易懂,有需要的同学可以阅读

话不多说javascript正则表达式javascript正则表达式,请看示例代码




  
  
    function validation() {
      var val = document.getElementById("txtNumber").value;
      var regu = /^[1-9]d*$/;
      //var regu = /^([1-9][0-9]*){1,3}$/; 亲测可用
      //var regu = /^+?[1-9][0-9]*$/; 亲测可用
      if (val != "") {
        if (!regu.test(val)) {
          document.getElementById("labResult").style.color = "red";
          document.getElementById("labResult").innerHTML = "验证失败!";
        } else {
          document.getElementById("labResult").style.color = "green";
          document.getElementById("labResult").innerHTML = "验证成功!";
        }
      }
    }
  


  
  
  验证结果:

以上就是本文的全部内容。 希望本文的内容能够给您的学习或者工作带来一些帮助。 同时也希望大家能够支持脚本之家!