inherits-ex

Browser-friendly enhanced inheritance fully compatible with standard node.js and coffee-script

Usage no npm install needed!

<script type="module">
  import inheritsEx from 'https://cdn.skypack.dev/inherits-ex';
</script>

README

Inherits-Ex npm

Join the chat at https://gitter.im/snowyu/inherits-ex.js

Build Status Code Climate Test Coverage downloads license

Browser-friendly enhanced inheritance fully compatible with standard node.js inherits with dynamic inheritance or creation.

This package modifies and enhances the standard inherits from node.js util module in node environment. It also has a shim for old browsers with no Object.create support.

Differs from the standard implementation is:

  • coffee-script supports
  • static inheritance
  • multi-inheritances(inheritance chain) supports
  • inherits at anytime.
    • you can not declare method/property before inherits in the standard way for it will replace the prototype object.
  • duplication inheritance check
  • Es6 Class supports
  • more helper functions
    • isInheritedFrom(ctor, superCtor|superCtorName) Check the ctor whether inherited from superCtor
    • mixin(ctor, superCtor|superCtor[]) Mixin the methods and properties of the SuperCtor:
      • Clone(Copy) all superCtor's properties(methods) to ctor.
    • isMixinedFrom(ctor, superCtor|superCtorName) Whether mixined from superCtor
    • createCtor(name, args, body) Create Ctor(Class) dynamically
    • createObject(ctor, args...) Create Object instance dynamically
    • createFunction(name, [args,] body[, scope[, values]]) Create Function dynamically

The standard inherits implementation is in inherits-ex/lib/inheritsDirectly, of cause it's the coffee-script supports and browser-friendly.

API

inherits(ctor, superCtor|superCtor[], staticInherit = true)

  • staticInherit (boolean): whether static inheritance,defaults to true.
const inherits = require('inherits-ex/lib/inherits')

The enhanced inherits implementation.

  • coffee-script supports
  • multi-inheritances(inheritance chain) supports
  • inherits at anytime.
    • you can not declare method/property before inherits in the standard way for it will replace the prototype object.
  • duplication inheritance check
  • add the super_ property(the super ctor) to the ctor.
  • add the __super__ property(the super's prototype) to the ctor for the coffeeScirpt super keyword.
  • add the Class property(point to the current class) to the object's prototype.
    • just be care: the ctor may not be the current class.

Known Issues

The default constructor(empty) chain failed for ES6 Class can not call constructor directly if no Reflect.construct(target, args, newTarget) native supports

const inherits = require('inherits-ex/lib/inherits')

// Or use function class instead of ES6 class:
// function Root() {this.initialize.apply(this, arguments)}
// Root.prototype.initialize = function initialize() {console.log('Root.init')}
class Root {
  constructor() {
    this.initialize.apply(this, arguments)
  }
  initialize() {
    console.log('Root.init')
  }
}

class A {
  /*
  // Workaround: must add the constructor in the derived class if you use inherits
  // Or use function Root instead of ES6 class
  constructor() {
    this.initialize.apply(this, arguments)
  }
  // */

  initialize() {
    const ParentPrototype = this.Class.__super__
    ParentPrototype.init.apply(this, arguments)
    console.log('A.init')
  }
}

inherits(A, Root)

const obj = new A() // Bug: The initialize method can not be executed.

usage


# Coffee@1
assert = require('assert')
inherits = require('inherits-ex/lib/inherits')
isInheritedFrom = require('inherits-ex/lib/isInheritedFrom')
log = console.log.bind console

class Root
  m: -> log('root')

class A
  inherits A, Root
  m: ->
    log('A')
    super

class B
  inherits B, Root
  m: ->
    log('B')
    super

class MyClass
  # MyClass -> A -> Root
  inherits MyClass, B
  # MyClass -> A -> B -> Root
  inherits MyClass, A

assert.notOk inherits(A, Root) #duplication inheritances prohibited.
assert.ok isInheritedFrom(MyClass, A)
assert.ok isInheritedFrom(MyClass, Root)
assert.ok isInheritedFrom(MyClass, B)

and the following codes do same thing:


class Root
  @static: 1
  m: -> log('root')

class A
  m: ->
    log('A')
    super

class B
  m: ->
    log('B')
    super

class MyClass
  # create inheritances chain:
  # MyClass -> A -> B -> Root
  inherits MyClass, [A, B, Root]

assert.ok isInheritedFrom(MyClass, A)
assert.ok isInheritedFrom(MyClass, Root)
assert.ok isInheritedFrom(MyClass, B)
assert.equal MyClass.static, 1

inheritsDirectly(ctor, superCtor, staticInherit = true)

  • staticInherit (boolean): whether static inheritance,defaults to true.
  var inheritsDirectly = require('inherits-ex/lib/inheritsDirectly')

The standard inherits implementation in node.js environment with coffee-script supports and browser-friendly.

isInheritedFrom(ctor, superCtor|superCtorName, raiseError=false)

  var isInheritedFrom = require('inherits-ex/lib/isInheritedFrom')

return the superCtor's son if ctor is inherited from superCtor. else return false.

it will use the ctor.name to check whether inherited from superCtorName.

mixin(ctor, superCtor|superCtor[], options:{ filter: number|function})

Mixin the methods and properties of the SuperCtor: Clone(Copy) all superCtor's properties(methods) to ctor.

  • options:
    • filter: defaults to 0.
      • 0: copy all properties(methods)
      • 1: raise error if found a method using super
      • 2: skip these methods which using super
      • string[]: only name in the array of string will be copied.
      • function(name, value){return value} the callback function of filter.
        • name: the property name
        • value: the property value.
  var mixin = require('inherits-ex/lib/mixin')

mixin all superCtors to ctor.

  • duplication mixin or inheritance check
  • NOTE::the methods in mixins using super() will jump to the old class(not stay on the class).
  • The mixined properties(methods) are cloned(copied) from superCtors(includes the static members)
  • The all mixined properties(methods) are the first parent's ctor(MixinCtor_)
    • eg, ctor -> MixinCtor_ -> original parents
## Coffee@2.x
mCallOrder = []
class Root

class C extends Root
  m: ->
    mCallOrder.push 'C'
    super

class A
  m: ->
    mCallOrder.push 'A'

class A1 extends A
  m: ->
    mCallOrder.push 'A1'
    super

class B
  inherits B, Root

class B1 extends B
  m: ->
    mCallOrder.push 'B1'
    super

mixin(B1, [A1, C]).should.be.equal true, 'mixin'
o = new B1()
o.m("a", 12) # call chain: B1::m -> C::m
A::m.should.have.been.calledOnce
A::m.should.have.been.calledWith "a", 12
mCallOrder.should.be.deep.equal ['B1', 'C']

The inheritance chain: B1 -> MixinCtor_ -> B -> Root All mixins will be added to MixinCtor_.

isMixinedFrom(ctor, superCtor|superCtorName)

check the ctor whether is mixined from superCtor.

  var isMixinedFrom = require('inherits-ex/lib/isMixinedFrom')

createCtor(name, args, body)

Create a constructor(class) dynamically.

  • name(string): the class name
  • args(any[]): the optional constructor's args.
  • body(string): the optional constructor function body.
  const createClass = require('inherits-ex/lib/createCtor')
  const MyClass = createClass('MyClass', ['a', 'b'], 'this.sum = a + b');
  var my = new MyClass(1, 2);
  console.log(my.sum);

createObject(ctor, args...)

The helper function to create the object dynamically and arguments provided individually.

  var createObject = require('inherits-ex/lib/createObject')
  class MyClass {
    constructor(a,b) {
      this.sum = a + b;
    }
  }
  var o = createObject(MyClass, 1, 2)
  console.log(o.sum)

NOTE: It will call the parent constructor if the class is the Empty constructor.

var inherits        = require('inherits-ex/lib/inherits')
var createObject    = require('inherits-ex/lib/createObject')

class Root {
  constructor() {
    this.init = 'root'
  }
}

class MyClass {
}

inherits(MyClass, Root)

var obj = createObject(MyClass)
assert.equal(obj.init, 'root')

Usage:


class RefObject
  constructor: -> @initialize.apply @, arguments
class MyObject
  inherits MyObject, RefObject
  initialize: (@a,@b)->
    super

obj = createObject(MyObject, "a", "b")
# obj = new MyObject("a", "b") # it will have no property a and b.
assert.equal obj.a "a"
assert.equal obj.b "b"

createObjectWith(ctor, [args...])

The helper function to create the object dynamically. provides the arguments as an array (or an array-like object).

var createObjectWith = require('inherits-ex/lib/createObjectWith')
var obj = createObjectWith(MyObject, ['a', 'b'])

NOTE: It will call the parent constructor if the class is the Empty constructor.

createFunction(name, [args,] body[, scope[, values]])

  • arguments:
    • name (String): the function name
    • args (Array): the function argument list. it's optional.
    • body (String): the function body.
    • ``scope` (Object|Array): the optional function scope.
      • ignore the values if it's an object.
      • the value is required if it's an array. It's the key's name list
    • value (Array): the optional function scope's value list. only for the scope is the Array.

The helper function to create the function dynamically.

  var createFunction = require('inherits-ex/lib/createFunction')

Usage:


class RefObject
  constructor: -> @initialize.apply @, arguments

class MyObject
  inherits MyObject, RefObject
  initialize: (@a,@b)->
    super

obj = createObject(MyObject, "a", "b")
#obj = new MyObject("a", "b") # it will have no property a and b.
assert.equal obj.a "a"
assert.equal obj.b "b"