javascriptecmascript-6es6-generator

Error using a generator function as value of a WeakMap


I'm building a linked list by my own. I tried to assign a generator as value of a key/value WeakMap in the constructor. The _iterator is a WeakMap because is a private member that I want use to make simple iterate over my data structure, but I don't want make the generator visible outside because otherwise I'll break abstraction. But I have the error SyntaxError: missing ( before formal parameters. What is the problem?

  class Node{
     constructor(value){
        this.value = value;
        this.next = null;
     }
  }

  //IIFE function that return the class. 
  const SingleLinkedList = (() => { //Here use IIFE to create a new block scope for private variables
     let _counts = new WeakMap();
     let _head = new WeakMap();
     let _tail = new WeakMap();
     let _iterator = new WeakMap();

     class SingleLinkedList {
        constructor() {
           _counts.set(this, 0);
           _head.set(this, null);
           _tail.set(this, null);
           let instance = this;//closure
           _iterator.set(this,(function* [Symbol.iterator](){
              let n=_head.get(instance);
              while(n){
                 yield n;
                 n = n.next;
              }
           }));
        }


        get counts() { //read only get accessor property
           return _counts.get(this);
        }

        isEmpty() {
           return !this.counts;  //or return !_head.get(this)
        }

        //insert a new Node (in tail) with the desired value
        push(value) {
           let n = new Node(value);
           if (this.isEmpty()) 
              _head.set(this, n);
           else {
              let tail = _tail.get(this); //tail is a pointer to the node-tail
              tail.next = n; //old tail-node will point to new node
           }

           //the following instructions are common to both the cases.
           _tail.set(this, n); //set new tail
           _counts.set(this, this.counts+1); //increment item counter

           return this; //to allow multiple push call
        }

        //Generator to return the values
        *[Symbol.iterator](){

           let n = _head.get(this); 
           while(n){
              yield n.value;
              n=n.next;
           }
        }
        //the the values of each node
        print() {
           let output = "";
           for(let el of this)
              output += `${el} `;
           console.log(output);
        }
     }
     return SingleLinkedList;
  })();

  let myLinkedList = new SingleLinkedList();
  myLinkedList.push(3);
  myLinkedList.push(5);
  myLinkedList.print();

  /*for(let x of myLinkedList)
     console.log(x);*/

Solution

  • Function expressions can't have computed names, that syntax is only allowed on object literal members or class members, so define the constructor of your SingleLinkedList like this instead:

    constructor() {
      _counts.set(this, 0);
      _head.set(this, null);
      _tail.set(this, null);
      let instance = this;//closure
      _iterator.set(this, function* (){
    // remove the computed name ---^
        let n=_head.get(instance);
        while(n){
          yield n;
          n = n.next;
        }
      });
    }
    

    Perhaps even more useful is knowing how to debug syntax errors in the future so you don't need to ask questions like this again. If you open your developer console by pressing F12, you'll see a log that looks something like this:

    SyntaxError in developer console

    Click that link and it will take you straight to the location of the error:

    SyntaxError in debugger


    Just for fun, here's my suggested rewrite using some more modern features of ECMAScript like private fields:

    class Node {
      next = null;
    
      constructor (value) {
        this.value = value;
      }
    }
    
    class SingleLinkedList {
      #size = 0;
      #head = null;
      #tail = null;
    
      // read only get accessor property
      get size () {
        return this.#size;
      }
    
      isEmpty () {
        return this.#size === 0;
      }
    
      // insert a new Node (in tail) with the desired value
      push (value) {
        const node = new Node(value);
    
        if (this.isEmpty()) {
          this.#head = node;
        } else {
          this.#tail.next = node;
        }
    
        // the following instructions are common to both the cases.
        this.#tail = node;
        this.#size++;
    
        // to allow multiple push call
        return this;
      }
    
      // generator to return the values
      *[Symbol.iterator] () {
        for (let current = this.#head; current; current = current.next) {
          yield current.value;
        }
      }
    
      // the the values of each node
      toString () {
        return [...this].join(' ');
      }
    }
    
    const myLinkedList = new SingleLinkedList();
    myLinkedList.push(3).push(5);
    console.log(myLinkedList.toString());