Skip to content

Instantly share code, notes, and snippets.

@benpriebe
Last active July 13, 2023 21:29
Show Gist options
  • Star 15 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save benpriebe/55b7e950b5e9d056b47e to your computer and use it in GitHub Desktop.
Save benpriebe/55b7e950b5e9d056b47e to your computer and use it in GitHub Desktop.
Douglas Crockford - Create Object Recipe (2014)

Douglas Crockford showed a slide showing how he creates JavaScript objects in 2014.

He no longer uses Object.create(), avoids 'this' and doesn't even care about memory reduction by using prototypes.

https://www.youtube.com/watch?v=bo36MrBfTk4 (skip ahead to 35 mins for relevant section)

Here is the pattern described on the slide:

function constructor(spec) {
	var that = otherConstructor(spec),
	member,
	method = function () {
		// spec, member, method
	};
	that.method = method;
	return that;
}

Here is my interpretation of this pattern:

function Person(spec) {
	var person = spec;
	
	// methods
	person.getDisplayName = getDisplayName; 
	
	return person;

	function getDisplayName() {
		return person.firstName + " " + person.lastName;
	}
}
function Employee(spec) {
	
	var employee = Person(spec);

	// members
	employee.employeeId = spec.employeeId;
	employee.hourlyRate = spec.hourlyRate;
	
	// methods
	employee.calculatePay = calculatePay;

	return employee;

	// implementations
	function calculatePay(hoursWorked) {
		return employee.hourlyRate * hoursWorked;
	}
}
var ben = Employee({ 
		firstName: 'Ben', 
		lastName: 'Priebe', 
		hourlyRate: 120,
		employeeId: 1
});

console.log(ben.getDisplayName());
console.log(ben.calculatePay(38));
@gerrod
Copy link

gerrod commented Mar 20, 2015

I like it. So simple.

@pmw57
Copy link

pmw57 commented Oct 13, 2017

There is a problem with using a capital first letter for function names, because jslint requires those constructor functions to be used with the new keyword.

If using lowercase for the function name, such as:

function person(spec) {
    var person = spec; // problem
    ...
}
var ben = person({...});

You then have a problem where the person variable cannot be correctly used inside of the function as the spec.

I've asked about this on the JSLint discussion group too at https://plus.google.com/+PaulWilkinsNZ/posts/AP5K1kh55KG

@pmw57
Copy link

pmw57 commented Oct 13, 2017

The JSLint community has come back to me with some excellent feedback.

Crockford's preferred style has advanced since JSConfUY 2014. In his TDC 2014 talk he provides a more advanced constructor pattern, that we can update with his recently preferred style of separating assignment statements.

function constructor(spec) {
    let {member} = spec;
    let {other} = other_constructor(spec);

    function privateMethod() {
        // member, other, privateMethod, and spec are all accessible here
        // can only be called from within constructor
        return member;
    }
    function publicMethod() {
        // member, other, privateMethod, publicMethod, and spec are all accessible here
        return privateMethod();
    }
    return Object.freeze({
        publicMethod,
        other
    });
}

Using the prefix create is ill-advised, as it's too easily confused with Object.create(), so "make" is the preferred term to use instead.

The main improvements in Crockford's preferred style are:

  • using separate let assignment statements, preventing maintenance problems
  • no assigning to a that variable, instead using local variables
  • returning a frozen object
function makePerson(spec) {
    let {firstName, lastName} = spec;

    function getDisplayName() {
        return firstName + " " + lastName;
    }

    return Object.freeze({
        getDisplayName
    });
}

function makeEmployee(spec) {
    let {employeeId, hourlyRate} = spec;
    let {getDisplayName} = makePerson(spec);

    function calculatePay(hoursWorked) {
        return hourlyRate * hoursWorked;
    }

    return Object.freeze({
        getDisplayName,
        calculatePay
    });
}

const ben = makeEmployee({
    firstName: "Ben",
    lastName: "Priebe",
    hourlyRate: 120,
    employeeId: 1
});

console.log(ben.getDisplayName());
console.log(ben.calculatePay(38));

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment