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

使用Rollup、Buble等工具实现棋盘表示的自引用类实例枚举

Yes, you absolutely can create this enum-like Refs collection with all the features you need!

Here's a step-by-step implementation that meets your requirements: encapsulation of Ref instances, type-safe traversal, a working move method, and compatibility with your toolchain (ES2015+Flow, Rollup, Buble, flow-remove-types).

Core Implementation (with Flow Types)

First, we'll create a modular setup where Ref instances are restricted to our predefined Refs collection, ensuring immutability and type safety.

// src/refs.js
const CONSTRUCTOR_TOKEN = Symbol('RefConstructorToken');
const coordinateMap: Map<string, Ref> = new Map();

// Define valid movement directions
type Direction = 'up' | 'down' | 'left' | 'right';

// Helper type for coordinates
type RefCoordinates = {| x: number, y: number |};

class Ref {
  x: number;
  y: number;
  id: string;

  // Restrict instantiation to our internal factory function
  constructor(token: symbol, id: string, x: number, y: number) {
    if (token !== CONSTRUCTOR_TOKEN) {
      throw new Error('Ref instances can only be created via the Refs collection');
    }
    this.id = id;
    this.x = x;
    this.y = y;
  }

  // Move logic: returns matching Ref or null if out of bounds
  move(direction: Direction): ?Ref {
    const newCoords = this._calculateNewCoords(direction);
    // Use coordinate map for O(1) lookups
    return coordinateMap.get(`${newCoords.x},${newCoords.y}`) || null;
  }

  // Internal helper to compute new coordinates
  _calculateNewCoords(direction: Direction): RefCoordinates {
    switch (direction) {
      case 'up': return { x: this.x, y: this.y - 1 };
      case 'down': return { x: this.x, y: this.y + 1 };
      case 'left': return { x: this.x - 1, y: this.y };
      case 'right': return { x: this.x + 1, y: this.y };
      default: throw new Error(`Invalid direction: ${direction}`);
    }
  }
}

// Create our fixed collection of Ref instances (tic-tac-toe example)
const Refs = {};

// Factory function to create and register Refs
const createRef = (id: string, x: number, y: number): Ref => {
  const ref = new Ref(CONSTRUCTOR_TOKEN, id, x, y);
  Refs[id] = ref;
  coordinateMap.set(`${x},${y}`, ref);
  Object.freeze(ref); // Make individual instances immutable
  return ref;
};

// Populate the Refs collection
Refs.TOP_LEFT = createRef('TOP_LEFT', 0, 0);
Refs.TOP_CENTER = createRef('TOP_CENTER', 1, 0);
Refs.TOP_RIGHT = createRef('TOP_RIGHT', 2, 0);
Refs.MIDDLE_LEFT = createRef('MIDDLE_LEFT', 0, 1);
Refs.CENTER = createRef('CENTER', 1, 1);
Refs.MIDDLE_RIGHT = createRef('MIDDLE_RIGHT', 2, 1);
Refs.BOTTOM_LEFT = createRef('BOTTOM_LEFT', 0, 2);
Refs.BOTTOM_CENTER = createRef('BOTTOM_CENTER', 1, 2);
Refs.BOTTOM_RIGHT = createRef('BOTTOM_RIGHT', 2, 2);

// Add traversal helpers
Refs.all = Object.freeze(Object.values(Refs)); // Immutable array of all Refs

// Freeze the entire collection to prevent modifications
Object.freeze(Refs);

// Export only what's needed externally
export type { Ref };
export default Refs;

Key Features Explained

  1. Encapsulation & Immutability:

    • The CONSTRUCTOR_TOKEN ensures no one can create new Ref instances outside our factory function.
    • All Ref instances and the Refs object are frozen to prevent accidental modifications.
  2. Working move Method:

    • Computes new coordinates based on direction.
    • Uses a coordinateMap for fast lookups of existing Ref instances (O(1) time instead of looping through all instances).
    • Returns null if the new coordinates are outside our predefined board.
  3. Type Safety:

    • Flow enforces valid Direction values, ensuring no invalid movement commands.
    • The Ref type guarantees variables are only assigned valid instances from the Refs collection.
  4. Traversal:

    • Refs.all provides an immutable array of all Ref instances, making it easy to loop through the entire board:
      for (const ref of Refs.all) {
        console.log(`Ref ${ref.id} at (${ref.x}, ${ref.y})`);
      }
      

Toolchain Setup (Rollup + Buble + flow-remove-types)

To compile this code into a production-ready bundle:

Step 1: Install Dependencies

npm install rollup rollup-plugin-buble rollup-plugin-flow-remove-types --save-dev

Step 2: Create Rollup Config (rollup.config.js)

import flowRemoveTypes from 'rollup-plugin-flow-remove-types';
import buble from 'rollup-plugin-buble';

export default {
  input: 'src/refs.js',
  output: {
    file: 'dist/refs.js',
    format: 'esm', // Choose 'cjs' for CommonJS or 'umd' for universal support
    sourcemap: true,
  },
  plugins: [
    // Strip Flow type annotations first
    flowRemoveTypes(),
    // Transpile ES2015+ features to compatible code
    buble({
      transforms: {
        classes: true,
        objectRestSpread: true,
      },
      targets: {
        chrome: 60,
        firefox: 55,
        safari: 11,
        edge: 16,
      },
    }),
  ],
};

Step 3: Add Build Scripts to package.json

{
  "scripts": {
    "build": "rollup -c",
    "watch": "rollup -c -w"
  }
}

Run npm run build to generate your compiled bundle, or npm run watch for development mode.

Usage Example

Import and use the Refs collection in your application:

// src/app.js
import Refs from './refs';
import type { Ref } from './refs';

function logMovement(ref: Ref, direction: Direction) {
  const targetRef = ref.move(direction);
  if (targetRef) {
    console.log(`Moved from ${ref.id} to ${targetRef.id}`);
  } else {
    console.log(`Cannot move ${direction} from ${ref.id} (out of bounds)`);
  }
}

// Test valid movement
logMovement(Refs.CENTER, 'right'); // Output: "Moved from CENTER to MIDDLE_RIGHT"

// Test out-of-bounds movement
logMovement(Refs.TOP_LEFT, 'up'); // Output: "Cannot move up from TOP_LEFT (out of bounds)"

Optional Improvements

  • Pre-Cached Adjacents: For performance, precompute adjacent refs during initialization and store them as properties on each Ref.
  • Coordinate Lookup: Add a Refs.getByCoordinates(x, y) method to directly retrieve a ref by its position.
  • Board Size Flexibility: Parameterize the board creation to support different sizes (e.g., 8x8 chessboard) instead of hardcoding tic-tac-toe.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 08:11:08