简体   繁体   中英

How to do class free inheritance in JavaScript (Crockford style)

Background

I am trying to improve my JS skills, and with that I am watching some of the big entities in the JS world. Such an entity is Douglas Crockford, and he is preaching a class free paradigm.

After watching him at Nordic js (I highly recommend) I was baffled by the way he makes objects without any taxonomy whatsoever.

Problem

I am now trying to replicate his slides into a simple example, that uses Exceptions, but I can't get it to work right.

I don't understand how my UniformityException.js file can reuse the code from Exception.js and be consistent with Crockford's ideology.

The problem here is that my specs variable in UniformityException.js only has a write method, and rightfully so.

After watching the slides many times, I can't understand how to change this code without making heavy use of Object.assign .

Code

Exception.js

"use strict";

let jsonfile = require("jsonfile");
let _ = require("underscore");

let Exception = function(args){

    let DEFAULT_PROPS = {
        exceptionName: "Exception",
        outputFolder: "./ErrorLogs/",
        fileExtension: ".txt"
    };

    DEFAULT_PROPS.message = "A generic exception occurred.";
    DEFAULT_PROPS.outputFile = DEFAULT_PROPS.exceptionName  + "_" + _.now();

    let props = Object.assign(DEFAULT_PROPS, args);

    let write = function(){
        jsonfile.writeFile(props.outputFolder + props.outputFile + props.fileExtension, props.message, error => {
            if(_.isNull(error) || _.isUndefined(error))
                console.log(error);
        });
    };

    return Object.freeze({
        write
    });
};

module.exports = Exception;

UniformityException.js

"use strict";

let Exception = require("./Exception.js");
let jsonfile = require("jsonfile");
let _ = require("underscore");

let UniformityException = function(args){

    let specs = Exception({
        exceptionName: "UniformityException",
        fileExtension: ".json"
    });

    let write = function(info){
        jsonfile.writeFile(specs.outputFolder + specs.outputFile, info, error => {
            if(_.isNull(error) || _.isUndefined(error))
                console.log(error);
        });
    };

    return Object.freeze({
        write
    });
};

module.exports = UniformityException;

index.js

"use strict";

let UniformityException = require("./Exceptions/UniformityException.js");

let myUniformException = UniformityException();

myUniformException.write({fruit: "banana"});

Questions

  1. How do I make UniformityException.js reuse the code from Exception.js in a way that fits the Crockford style?

As usual, keywords like new , this and Object.create are to be avoided.

What you need

In order to do that, you need to use the Destructuring assignment pattern . With this pattern, the following code from the slides:

function animal(spec) {
  let {body, type, name} = spec;
}

Will get converted to the following:

function animal(spec) {
    let body = spec.body;
    let type = spec.type;
    let name = spec.name;
}

This is in reality the ECMA6 version of the factory pattern, as described in this blog post constructors VS factories .

With this in mind, we can now move to the code.

Code

index.js

"use strict";

let _ = require("underscore");
let Exception = require("./Exceptions/Exception.js");
let UniformityException = require("./Exceptions/UniformityException.js");

let jsonParams = {
    outputFolder: "./ErrorLogs/",
    fileExtension: ".txt",
    message: {info: "A generic error ocurred."},
    exceptionName: "Exception",
};

let myException = Exception(jsonParams);
myException.write();

jsonParams.fileExtension  = ".json";
jsonParams.exceptionName = "UniformException";
jsonParams.outputFileName = jsonParams.exceptionName + "_" + _.now();

let myUniformException = UniformityException(jsonParams);
myUniformException.write({
    fruit: "banana"
});

Exception.js

"use strict";

let jsonfile = require("jsonfile");
let _ = require("underscore");

let Exception = function(args) {
    let {
        outputFolder,
        fileExtension,
        exceptionName,
        message,
        outputFileName,
    } = args;

    outputFileName = outputFileName|| (exceptionName  + "_" + _.now());

    let filePath = outputFolder + outputFileName + fileExtension;

    let write = function() {
        jsonfile.writeFile(filePath, message, error => {
            if (!_.isNull(error) && !_.isUndefined(error))
                console.log(error);
        });
    };

    return Object.freeze({
        filePath,
        write
    });
};

module.exports = Exception;

UniformityException.js

"use strict";

let Exception = require("./Exception.js");
let jsonfile = require("jsonfile");
let _ = require("underscore");

let UniformityException = function(args) {

    let {filePath} = Exception(args),

        write = function(info) {
            jsonfile.writeFile(filePath, info, error => {
                if (!_.isNull(error) && !_.isUndefined(error))
                    console.log(error);
            });
        };

    return Object.freeze({
        filePath,
        write
    });
};

module.exports = UniformityException;

A thing I would like to point out, is that your factory functions should all start with a lowercase letter ( function animal ), and could follow the camelCase convention and name it ' animalConstructor() '.

Typically, by convention, constructor functions which require the use of new are name with an first Uppercase letter ( function Animal ). To avoid confusion in the future you should rename your exceptions.

Additional

Another resource I also highly recommend is from The Empire of Evil:

Which is in my opinion an accurate interpretation of Crockford's factory functions.

Hope it helped !

You can only "reuse" what is retuned by Exception . Ie if you want to be able to access props directly you need to expose it. There is no way around it.

This is actually in line with the visibility model you find in other class based languangues: private members (eg props ) can only be accessed by the class itself. If a child class should have access, many languages offer a "protected" mode, but JS doesn't have something like that.


Instead of adding it to the object returned by the function, you could consider making Exception accept a function that it passes its "private" members to, just like Promise does. Not sure what Crockford says about that though.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM