0

I meet a problem when using javascript to implement interface and its inheritance.

The contents and idea are from the book Learning JavaScript Design Pattern. I tried using its interface code, and hope the way I using can make it act much like C#. The code I used is as following:

var Interface = function(name, methods) {
  if (arguments.length != 2) {
    throw new Error("Interface constructor called with " + arguments.length +
      "arguments, but expected exactly 2.");
  }

  this.name = name;
  this.methods = [];
  for (var i = 0, len = methods.length; i < len; i++) {
    if (typeof methods[i] !== 'string') {
      throw new Error("Interface constructor expects method names to be " +
        "passed in as a string.");
    }
    this.methods.push(methods[i]);
  }
};


function extend(subClass, superClass) {
  var F = function() {};
  F.prototype = superClass.prototype;
  subClass.prototype = new F();
  console.log(subClass.name);
  console.info(subClass);
  subClass.prototype.constructor = subClass;

  subClass.superclass = superClass.prototype;
  if (superClass.prototype.constructor == Object.prototype.constructor) {
    superClass.prototype.constructor = superClass;
  }
}

var Composite = new Interface('Composite', ['add', 'remove', 'getChild']);

console.log(Composite.name);
console.info(Composite);
console.log(Composite.__proto__.constructor.name);

var testinterface = function(name ,methods) {
  Composite.call(this, name, methods);
};

extend(testinterface, Composite);

But when executing the codes, the error message showing: Cannot read property 'constructor' of undefined.
Something indeed is wrong when I declare function testinterface, but I don't know why, since in the function body of extend, the console.log output explain that the testinterface is still a function declaration, not have any prototype, even constructor.
Is there any way to correct that, and why is the codes not working. Any instruction is highly appreciated, thanks a lot.

abramhum
  • 443
  • 2
  • 8
  • 20
  • "*I hope to have it act much like C#*" - don't. Learn JavaScript, embrace its patterns. Don't do any metaprogramming to make it look like another language. – Bergi Nov 07 '19 at 02:33
  • How old is this book? That `extend` method looks like it is from ES3. And is this the complete code of `Interface`? It seems to be missing callable methods, which would give away how it is supposed to be used. All I can tell from here: it constructs an object with a `.name` and `.methods` array, that's it. `console.info(Composite);` should've shown you. On the other hand, `extend` expects a *class* (a constructor function) as its argument. Not one of these `Interface` instances. – Bergi Nov 07 '19 at 02:39
  • How could I modify these codes to make it work? It looks like Composite not have any prototype, but, since I have use new, why it not have any prototype. And why constructor not be created for it automatically. I have read many materials about javascript, but still not clear for these details – abramhum Nov 07 '19 at 04:59
  • There is an `Interface.prototype` which did allow you to use `new Interface`. But there's no reason why `composite` would have one. – Bergi Nov 07 '19 at 11:52
  • 1
    I don't even know what you want to make work, as I know neither C# nor have read the book about their `Interface` implementation. The book should have more details about how to use these "interface" objects - notice that JavaScript does not natively have any notion of interfaces. – Bergi Nov 07 '19 at 11:54

2 Answers2

0

I see a post about polymorphism, https://medium.com/yld-blog/program-like-proteus-a-beginners-guide-to-polymorphism-in-javascript-867bea7c8be2 . I modify some codes from this web page, the codes can run, and its code snippet is as following:

function identical(a, b) {
  return a === b;
}

class Protocol {
  // accept a list of method-names
  constructor(...methods) {
    this.implementations = {};
    // and create a method for each
    methods.forEach(method => {
      // that will dispatch to an implementation stored on the type of the first argument
      this[method] = (type, ...args) => {
        // with the convention that an object's type is given by its constructor
        return this.implementations[type.constructor][method](type, ...args);
      }
    });
  }
  // register implementations for a type
  extendTo(typeConstructor, implementation) {
    const typed = this.implementations[typeConstructor] = {};
    Object.keys(implementation)
      .forEach(method => {
        typed[method] = implementation[method];
      });
  }
}

const Ramda = new Protocol('equals', 'map', 'add'); /* and the rest! */
Ramda.extendTo(String, {
  equals: (a, b) => a === b,
  map: (a, f) => a.split('').map(f),
  add: (a, b) => a + b
})

Ramda.extendTo(Date, {
  equals: (a, b) => identical(a.valueOf(), b.valueOf()),
  add: (a, b) => new Date(Number(a) + Number(b))
  // map not implemented
})

console.log(Ramda.equals('hello', 'world')); // false
console.log(Ramda.equals(new Date(1), new Date(1))); // true

I think, if I can combine these concepts, the javescript interface may be implemented.

abramhum
  • 443
  • 2
  • 8
  • 20
0

I see a post, Inheritance with Lo-Dash, and also another is https://github.com/lodash/lodash/issues/663 .

These two posts help to clarify the notion of inheritance and can used to implement interface, thanks.

abramhum
  • 443
  • 2
  • 8
  • 20