Skip to content

Commit

Permalink
✨ ES6 class inheritance improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
skerit committed Apr 26, 2024
1 parent 6b2dbc3 commit 44827a1
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 14 deletions.
60 changes: 46 additions & 14 deletions lib/function_inheritance.js
Original file line number Diff line number Diff line change
Expand Up @@ -527,12 +527,46 @@ const defClassMethodForProto = function defClassMethodForProto(fnc) {
return defClassMethod(fnc, 'this.prototype');
};

/**
* Make sure the namespace is configured if it's available
*
* @author Jelle De Loecker <[email protected]>
* @since 0.9.3
* @version 0.9.3
*
* @param {Function} new_constructor
* @param {Function} parent_constructor
* @param {string} namespace
*/
const ensureNamespace = function ensureNamespace(new_constructor, parent_constructor, namespace) {

if (!new_constructor) {
return namespace;
}

if (new_constructor.namespace) {
return namespace;
}

if (!namespace && typeof parent_constructor == 'function') {
namespace = parent_constructor.namespace;
}

if (!namespace) {
namespace = '';
}

Blast.defineValue(new_constructor, 'namespace', namespace == '@' ? '' : namespace);

return namespace;
};

/**
* Make one class inherit from the other
*
* @author Jelle De Loecker <[email protected]>
* @since 0.1.3
* @version 0.8.18
* @version 0.9.3
*
* @param {string|Function|Array} _parent Parent class to inherit from
* @param {string} _namespace Namespace to store class in
Expand Down Expand Up @@ -602,18 +636,7 @@ Blast.defineStatic('Function', function inherits(_parent, _namespace, _newConstr
}

// Set the namespace on the constructor if it's given
if (!newConstructor.namespace) {

if (!namespace && typeof parentConstructor == 'function') {
namespace = parentConstructor.namespace;
}

if (!namespace) {
namespace = '';
}

Blast.defineValue(newConstructor, 'namespace', namespace == '@' ? '' : namespace);
}
namespace = ensureNamespace(newConstructor, parentConstructor, namespace);

if (_do_constitutors == null) {
_do_constitutors = true;
Expand Down Expand Up @@ -783,8 +806,8 @@ Blast.defineStatic('Function', function inherits(_parent, _namespace, _newConstr
temp = Fn.create(new_constructor_name, temp);
temp.staticChain = newConstructor.staticChain;
temp.super = newConstructor.super;
Blast.defineValue(temp, 'namespace', namespace);
newConstructor = temp;
ensureNamespace(newConstructor, parentConstructor, namespace);
}
}

Expand Down Expand Up @@ -814,9 +837,18 @@ Blast.defineStatic('Function', function inherits(_parent, _namespace, _newConstr
replacement_class = class extends parentConstructor {};
}

// Make sure all static properties become "uninherited"
replacement_class.children = null;
replacement_class.staticChain = null;
replacement_class.super = null;
replacement_class.namespace = null;
replacement_class.postInheritors = null;
replacement_class.constitutors = null;

Object.defineProperty(replacement_class, 'name', {value: new_constructor_name});
fallback_constructor = newConstructor;
newConstructor = replacement_class;
ensureNamespace(newConstructor, parentConstructor, namespace);
}
}

Expand Down
26 changes: 26 additions & 0 deletions test/function_inheritance.js
Original file line number Diff line number Diff line change
Expand Up @@ -234,17 +234,30 @@ describe('Inheritance', function() {
this.did_test++;
}
}

assert.strictEqual(Kak.children, undefined, 'A vanilla ES6 class should not have a static `children` property');

let kak = new Kak();
assert.strictEqual(kak.seen_kak, true);

const Unkaked = Function.inherits(Kak, function Unkaked() {
this.seen_unkaked = true;
});

let counter = 0;

Unkaked.postInherit(function test() {
this.counter = ++counter;
});

assert.strictEqual(Kak.children.length, 1, 'After inheriting, the parent should have a `children` property');
assert.notStrictEqual(Kak.children, Unkaked.children, 'The parent & child class are sharing the same `children` property!');
assert.strictEqual(Unkaked.counter, 1);

let unkaked = new Unkaked();
assert.strictEqual(unkaked.seen_unkaked, true);
assert.strictEqual(unkaked.seen_kak, true);
assert.strictEqual(Unkaked.children?.length, undefined);

const DeeperUnkaked = Function.inherits(Unkaked, function DeeperUnkaked() {
DeeperUnkaked.super.call(this);
Expand All @@ -255,6 +268,8 @@ describe('Inheritance', function() {
assert.strictEqual(deeper.seen_unkaked, true);
assert.strictEqual(deeper.seen_kak, true);
assert.strictEqual(deeper.is_deep, true);
assert.strictEqual(Unkaked.children?.length, 1);
assert.strictEqual(DeeperUnkaked.counter, 2);

class DeepKak extends Kak {
constructor() {
Expand All @@ -272,6 +287,17 @@ describe('Inheritance', function() {
assert.strictEqual(unkaked_child_by_name.seen_unkaked, true);
assert.strictEqual(unkaked_child_by_name.seen_kak, true);
assert.strictEqual(unkaked_child_by_name.is_deep, true);
assert.strictEqual(UnkakedChildByName.counter, 3);

const NestedUnkaked = Function.inherits('Unkaked', 'Nested.Unkaked', 'NestedUnkaked');
assert.strictEqual(NestedUnkaked.namespace, 'Nested.Unkaked');
assert.strictEqual(DeeperUnkaked.counter, 2);
assert.strictEqual(UnkakedChildByName.counter, 3);
assert.strictEqual(NestedUnkaked.counter, 4);

const DeepNested = Function.inherits('Nested.Unkaked.NestedUnkaked', 'DeepNested');
assert.strictEqual(Blast.Classes.Nested.Unkaked.DeepNested, DeepNested);
assert.strictEqual(DeepNested.counter, 5);

});
});
Expand Down

0 comments on commit 44827a1

Please sign in to comment.