Null object pattern
How to leverage JavaScript property enumeration to build an object that looks like a class instance but with the possibility to override method results (and even call the intial implementation)
by Arnaud Buchholz
May 30, 2017
CSS
.ok {
color: green;
}
.ko {
color: red;
}
JavaScript
function test (label, result, expected) {
var line = document.createElement("div");
line.appendChild(document.createTextNode(label + ": "));
var span = document.createElement("span");
span.className = (result === expected) ? "ok" : "ko";
span.appendChild(document.createTextNode(result));
span = line.appendChild(span);
return document.body.appendChild(line);
}
function A () {
/* A constructor */
}
A.prototype = {
method1: function () {
return "method1";
}
}
function B () {
A.apply(this, arguments);
}
B.prototype = Object.assign(Object.create(A.prototype), {
method2: function () {
return "method2";
}
});
var b = new B();
test("b instanceof B", b instanceof B, true);
test("b.method1()", b.method1(), "method1");
test("b.method2()", b.method2(), "method2");
document.body.appendChild(document.createElement("hr"));
function allocateNullableMethod (member) {
var result = null,
callCount = 0;
function nullMethod() {
++callCount;
if (undefined === result) {
return member.apply(this, arguments);
}
return result;
}
Object.assign(nullMethod, {
"return": function (value) {
result = value;
},
getCallCount: function () {
return callCount;
},
hasBeenCalled: function () {
return callCount !== 0;
}
});
return nullMethod;
}
function getNullFor(objectOrClass) {
var
proto,
members,
nullObject,
member;
if ("function" === typeof objectOrClass) {
proto = objectOrClass.prototype,
members = proto;
} else {
proto = Object.getPrototypeOf(objectOrClass);
members = objectOrClass;
}
nullObject = Object.create(proto);
for (var method in members) {
member = members[method];
if (typeof member === "function") {
nullObject[method] = allocateNullableMethod(member);
}
}
return nullObject;
}
var fakeB = getNullFor(B);
fakeB.method1.return("Hello World!"); // Change return value
fakeB.method2.return(); // Call initial implementation
test("fakeB...