2
\$\begingroup\$

I have a small library for utility functions for ES5 mainly aimed at OO code organization.

Are there any recommendations for the API or for the code quality / organization?

Note the library has a few tricks in it for aggressive minification.

Code inlined

/*
    pd(obj) -> propertyDescriptorsOfObject {
        bindAll: function that binds all the methods of an object to the object,
        extend: function that extends the first argument with the rest
        Name: returns a namespace(anyKey) -> uniqueObject function
    }

    pd requires ES5. Uses the shimmable subset of ES5.
*/
(function (Object, slice) {
    "use strict";

    extend(pd, {
        bindAll: bindAll,
        extend: extend,
        Name: Name
    });

    typeof window !== "undefined" ? window.pd = pd : module.exports = pd;

    /*
        pd will return all the own propertydescriptors of the object

        @param Object object - object to get pds from.

        @return Object - A hash of key/propertyDescriptors
    */    
    function pd(obj, retObj) {
        retObj = {};
        Object.getOwnPropertyNames(obj).forEach(function(key) {
            var pd = Object.getOwnPropertyDescriptor(obj, key);
            retObj[key] = pd;
        });
        return retObj;
    }

    /*
        Extend will extend the firat parameter with any other parameters 
        passed in. Only the own property names will be extended into
        the object

        @param Object target - target to be extended
        @arguments Array [target, ...] - the rest of the objects passed
            in will extended into the target

        @return Object - the target
    */
    function extend(target) {
        slice.call(arguments, 1).forEach(function(source) {
            Object.defineProperties(target, pd(source));
        });
        return target;
    }

    /*
        defines a namespace object. This hides a "privates" object on object 
        under the "key" namespace

        @param Object object - object to hide a privates object on
        @param Object key - key to hide it under

        @author Gozala : https://gist.github.com/1269991

        @return Object privates
    */
    function namespace(object, key) {
        var privates = Object.create(object),
            valueOf = object.valueOf;

        Object.defineProperty(object, "valueOf", {
            value: function(value) {
                return value !== key ? valueOf.apply(this, arguments) : privates;
            },
            writable: true
        });

        return privates;
    }

    /*
        Constructs a Name function, when given an object it will return a
        privates object. 

        @author Gozala : https://gist.github.com/1269991

        @return Function name
    */
    function Name(key) {
        key = {};
        return name;

        function name(object) {
            var privates = object.valueOf(key);
            return privates !== object ? privates : namespace(object, key)
        }
    }

    /*
        bindAll binds all methods to have their context set to the object

        @param Object obj - the object to bind methods on
        @param Array whitelist - optional whitelist of methods to bind

        @return Object - the bound object
    */
    function bindAll(obj, whitelist) {
        (whitelist || Object.keys(obj).filter(getMethods)).forEach(bindMethods);

        return obj;

        function getMethods(key) {
            return obj[key].call;
        }

        function bindMethods(name) {
            obj[name] = obj[name].bind(obj)
        }
    }   
})(Object, [].slice);
\$\endgroup\$
1
\$\begingroup\$

There isn't anything majorly wrong, so these will be mostly minor things.

getMethods doesn't really get methods, isMethod (or isBindable) is a more descriptive name for it.

You'd also want to check that .call is in fact the .call inherited from Function prototype, or since you are doing the filtering in preparation for .bind, just check that obj[key].bind === Function.prototype.bind or similar. Also check for truthiness, so you don't try to access null.bind:

return obj[key] && obj[key].bind ===

If Name is a normal function, it shouldn't be capitalized and be a named like a noun, this is convention for functions that are meant to be invoked as constructors using new. Something like makeName or makeNamespace, a verb that implies a factory, would be a better fit.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ isMethod and .bind === Function.prototype.bind are indeed issues, I've handled those. As for the pd.Name name, not sure how to handle that. I don't like makeName it'll be either pd.name or pd.Name \$\endgroup\$ – Raynos Jan 21 '12 at 15:03

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.