You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Esprima统计JavaScript内置对象出现次数的实现需求

解决JavaScript内置对象统计的正确姿势:用AST代替正则

Hey there! I totally get why regex and raw string checks didn't work out for you—JavaScript code is full of edge cases like variable shadowing, nested scopes, and dynamic property access that regex can't possibly parse accurately. Let's switch gears to using an Abstract Syntax Tree (AST) approach, which will give you reliable, precise counts of intrinsic usage.

Why Regex Fails

Think about it: if someone writes const Object = {}; in their code, regex would count that as an "Object" intrinsic reference, but it's actually a local variable shadowing the global intrinsic. Or dynamic access like window['Array']—regex might miss that entirely. AST parsing solves these problems by understanding the actual structure and scope of the code.

Step-by-Step Implementation

We'll use three tools here:

  • acorn: A fast, lightweight JS parser to generate the AST
  • escope: A scope analyzer to tell us if an identifier refers to the global intrinsic or a local variable
  • acorn-walk: To traverse the AST nodes

1. Install Dependencies

First, install the required packages:

npm install acorn acorn-walk escope

2. Full Script Code

Create a script (let's call it intrinsic-counter.js) with this code:

const fs = require('fs');
const acorn = require('acorn');
const walk = require('acorn-walk');
const escope = require('escope');

// Define the list of intrinsics you want to track (expand this as needed)
const TARGET_INTRINSICS = new Set([
  'Object', 'Array', 'String', 'Number', 'Boolean', 'Function',
  'Date', 'RegExp', 'Map', 'Set', 'Promise', 'JSON', 'Math',
  'Symbol', 'WeakMap', 'WeakSet', 'Intl'
]);

// Read the target JS file
const jsCode = fs.readFileSync('./Main.js', 'utf8');

// Parse code into AST (enable latest JS features)
const ast = acorn.parse(jsCode, { ecmaVersion: 'latest' });

// Analyze scopes to distinguish global vs local references
const scopeManager = escope.analyze(ast);

// Initialize count map
const intrinsicCounts = {};
TARGET_INTRINSICS.forEach(name => intrinsicCounts[name] = 0);

// Traverse the AST to count valid intrinsic references
walk.simple(ast, {
  // Handle direct identifier references (e.g., `Object.create()`)
  Identifier(node) {
    const name = node.name;
    if (!TARGET_INTRINSICS.has(name)) return;

    // Check if this identifier refers to the global intrinsic
    const reference = scopeManager.getReference(node);
    if (!reference) return;

    // If resolved is null, it's an unresolved global (matches our intrinsics)
    // Or if resolved scope is global, it's the actual intrinsic
    if (reference.resolved === null || (reference.resolved?.scope.type === 'global')) {
      intrinsicCounts[name]++;
    }
  },

  // Handle dynamic property access (e.g., `window['Array']`)
  MemberExpression(node) {
    // Check if the object is the global `window` or `globalThis`
    const isGlobalObject = node.object.type === 'Identifier' && 
                          (node.object.name === 'window' || node.object.name === 'globalThis');
    
    if (!isGlobalObject) return;

    // Get the property name (handles both dot notation and bracket notation)
    let propName;
    if (node.property.type === 'Identifier') {
      propName = node.property.name;
    } else if (node.property.type === 'Literal' && typeof node.property.value === 'string') {
      propName = node.property.value;
    }

    if (propName && TARGET_INTRINSICS.has(propName)) {
      intrinsicCounts[propName]++;
    }
  }
});

// Print the final counts
console.log('=== Intrinsic Usage Counts ===');
Object.entries(intrinsicCounts)
  .filter(([_, count]) => count > 0)
  .sort((a, b) => b[1] - a[1])
  .forEach(([name, count]) => {
    console.log(`${name}: ${count} times`);
  });

3. How It Works

  • AST Parsing: acorn converts your JS code into a structured AST, which represents every part of the code's syntax and logic.
  • Scope Analysis: escope tracks variable scopes, so we can tell if an Object reference is the global intrinsic or a local variable that shadows it.
  • Node Traversal: We walk through two key node types:
    • Identifier: Catches direct uses like Array.push().
    • MemberExpression: Catches dynamic uses like window.JSON.parse().
  • Counting: We only increment counts for references that are confirmed to be the global intrinsics, not local shadows.

Customization Tips

  • Expand Intrinsic List: Add more intrinsics to TARGET_INTRINSICS based on what you want to track (e.g., Intl, Reflect).
  • Handle JSX/TypeScript: If your users submit TS/JSX code, switch to @babel/parser instead of acorn—it supports these syntaxes out of the box.
  • Filter False Positives: For even more precision, you could add checks to exclude references in comments or string literals (the current script ignores these since AST doesn't include comment nodes by default; you can enable comment parsing in acorn if needed).

内容的提问来源于stack exchange,提问作者CryoLogic

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 04:15:17