constructor

0.0.6 • Public • Published

constructor.js

Build Status

constructor.js ~ constructor-, prototype- and super- inheritance module.

Motivation

Though there are problems with inheritance (mainly overuse and coupling), it should just work and JavaScript should support it. Yes, it already "does", but see this proposal for a better api.

tape & testling

Using tape to run tests from the node.js command line, and in order to use testling from the github service hook.

browser support

Things I've found about checking things in for testling to work

use

node:

var Constructor = require('./constructor').Constructor;

browser:

<script src='path/to/constructor.js'></script>
<script>
  var example = window.Constructor(etc);
</script>

Constructor API

Constructor(base) ~ specify a base object with 'constructor' defined as a function. If constructor is not defined, an empty function is provided. The constructor function's prototype is then set to the base object, and the function is returned. If you pass in a function, that function is returned immediately without modification. Use of the 'new' keyword when calling Constructor() is optional.

Example:

Dialog = new Constructor({
  constructor: function (contentNode) {
    this.contentNode = contentNode;
  },
  hide: function () {
    this.contentNode.hide();
  },
  reposition: function (left, top) {
    // do some repositioning
  },
  resize: function (width, height) {
    // do some resizing
  },
  show: function () {
    this.contentNode.show();
  }
});

// Use:

var a = new Dialog({ id: 'superFunHappyMockContentNode'});

Prior Art

This implementation is based on the type() method suggested by Nicholas Zakas in his post Custom types (classes) using object literals in JavaScript

Zakas' method is in turn based on a de-sugaring of Jeremy Askenas' suggested api for the class, extend, super keyword proposals for ES6.

Constructor.extend(base, child)

Specify a base object or function to inherit from, and a child object or function that will inherit from the base. The base is referenced from the child by this.__super__. In the constructor, use it as a function call initially, then as an object thereafter.

An example - hastily presented:

ConfigurableDialog = Constructor.extend(Dialog, {

  constructor: function (contentNode, state) {

    this.__super__(contentNode); // first use of __super__
    
    this.state = state;
    
    if (this.state.displayOnCreate === true && !this.state.shown) {
        this.show();
    }
  },
  
  hide: function () {
    if (this.state.shown) {
    
      this.__super__.hide(); // delegate to the __super__
      
      this.state.shown = false;
    }
  },
  
  show: function () {
    if (!this.state.shown) {
  
      this.__super__.show(); // delegate to the __super__
      
      this.state.shown = true;
    }
  }
});

Statics or Class-level properties

Statics are not inherited by the Constructor.extend() operation as those are defined on a constructor directly, not on its prototype (which provides a map for instances). Inheriting statics is not regarded as a good practice, anyway.

However, using constructor.js, you can access a staticName on a base constructor by referring to this.__super__.constructor.staticName:

Example

Request = Constructor({

});

Request.staticMethod = function (obj) {
  // do something with obj
};

Post = Constructor.extend(Request, {
  constructor: function () {
    //...
  },

accessing the method:

  accessStatic: function () {
    return this.__super__.constructor.staticMethod(this);
  },

applying the method to the current scope:

  applyStatic: function () {
    return this.__super__.constructor.staticMethod.apply(this, this);
  }
  
});

UPDATE 27 MAR 2014: Blog Post on the problem with static inheritance

Tests

  • base case tests done for now
  • extend case tests done for now
  • anti-pattern tests, inherit-statics, using-natives done for now

The only failing tests are in extend/using-natives.js, and only in IE 6, 7, & 8. See the Extending Natives? section further down.

The /base and /extend test cases show the intended usage of Constructor() and Constructor.extend().

The /base and /extend directories contain an anti-patterns.js test which shows a few clever and/or misguided uses that have surprising side-effects.

Under /extend is the inherit-statics.js tests which show how to access a static attribute from a super constructor, but goes into some detail about the example from which this test is derived. The example is taken from Programming in CoffeeScript by Mark Bates, Addison-Wesley, pp. 147-150, where the author shows that CoffeeScript does not support static inheritance through the __super__ keyword. However, the example contains a more fundamental problem with respect to static property access that is NOT specific to CoffeeScript.

UPDATE 27 MAR 2014: Blog Post on this problem fixing static inheritance

test from node.js command line:

cd ./constructor

// run suite of all tests

npm test

// run individual base tests

node test/base/anti-patterns.js
node test/base/base-patterns.js
node test/base/using-natives.js

// run individual extend tests

node test/extend/anti-patterns.js 
node test/extend/extend-patterns.js
node test/extend/using-natives.js
node test/extend/inherit-statics.js 

browser test suite:

Using browserify to bundle up the tape tests above.

$ npm run bundle

or

$ browserify ./test/suite.js -o ./browser-test/bundle.js

The html suite uses a dom-console.js shim for reporting all of tape's console.log statements into the DOM itself. This is located at https://github.com/dfkaye/constructor/blob/master/browser-test/dom-console.js

You can view the browser-test/suite.html file on rawgit

Extending Natives?

YES, you can inherit from Native functions, but there are some caveats - see the test/extend/using-natives.js file for a complete implementation of a SubArray that inherits from the native Array constructor.

And the caveats? The use of the internal [[Class]] identifier in the JS engines differs between IE 6-8 and all the others (not surprisingly) - but all have a common restriction in that special methods based on the [[Class]] will fail on any objects not identified as constructed by that [[Class]].

tl;dr

  • subclassing Array requires overwriting concat() and toString() methods.
  • IE 6-8 iterations fail on this.length on subarray instances.
  • IE 6-7 don't allow subclasses to inherit any methods in this implementation of constructor inheritance.

npm

npm install constructor

license

JSON

Readme

Keywords

none

Package Sidebar

Install

npm i constructor

Weekly Downloads

0

Version

0.0.6

License

none

Last publish

Collaborators

  • dfkaye