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

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...