Search code examples
javascripthtmlmootoolsprototype

why object prototype shows up as html attribute?


I did this (using version mootools-core-1.4.5.js)

Object.prototype.walkRecursive = function(callable, bindable) {
var p, val, retVal, _bind;

_bind = (typeof bindable === 'object') ? bindable : this;
for (p in this) {
    if (this.hasOwnProperty(p)) {
        if (!(this[p] instanceof Object)) {
            if (typeof callable === 'function')
            {
                retVal = callable.call(_bind, p, this[p]);
                if (typeof retVal !== 'undefined')
                {
                    this[p] = retVal;
                }
            }
            else {
                throw "Podaj funkcje jako drugi argument";
            }
        } else if (this[p] instanceof Object) {
            this[p].walkRecursive(callable, bindable);
        }
    }
}
return this;
};

Then

    var navigationEl = new Element('div', {
        'class' : 'wcag-navigation'
    }).inject(this._element);

And when I check how this._wcagButton.pausePlay HTML element looks like in Chrome Developer Tools looks like, I got:

strange html attribute

Same thing happens when I use Mootools' implement:

 Object.implement({walkRecursive :function(callable, bindable) {....}.protect()});

How come? Please help, how to cope with this issue

Example of use

var data = [
{
    id: 0, 
    name: 'Template 0', 
    subComponents: [
        {id: 1, name: 'Template 1', subItems:[
            {id: 2, name: 'Template 2', subComponents:[
                {id: 3, name: 'Template 3'}
            ], 
             subItems: [
                 {id: 4, name: 'Template 4',},
                 {id: '42'}
             ]
            }
        ]}
    ]}
];

var bindMe = {
  nothing:'special'
}

data.walkRecursive(function(key,value){
if(key === 'name')
{
    return value+' '+this.nothing;
}
},bindMe)

Solution

  • er. Although you can extend Object, it is still strongly considered a bad practice to do so since in JavaScript, everything inherits from that Type. To see why you should not be doing that - http://erik.eae.net/archives/2005/06/06/22.13.54/ or google extending object prototype is verboten - plenty of answers on the subject. As you can see yourself, what you added is enumerable.

    If you only care about modern JavaScript, you can use defineProperty to set it as enumerable: false.

    In order to be able to work with Objects easier in MooTools, there used to be a thing called Hash - http://mootools.net/docs/core125/core/Native/Hash - which is now available in compat MooTools builds - otherwise, it has been deprecated for 1.4.x - see http://mootools.net/docs/core/Types/Object#Deprecated-Functions:Hash

    You can just use Object to host methods and call them on objects, this is what http://mootools.net/docs/core/Types/Object does.

    Here is your code converted: http://jsfiddle.net/ZQ4Yh/3/

    Object.extend({
        walkRecursive: function (object, callable, bindable) {
            var p, val, retVal, _bind;
    
            // this next bit is unsafe, 'object' also returned for Date or Array constructor
            _bind = (typeof bindable === 'object') ? bindable : object;
            for (p in object) {
                if (object.hasOwnProperty(p)) {
                    if (!(object[p] instanceof Object)) {
                        if (typeof callable === 'function') {
                            retVal = callable.call(_bind, p, object[p]);
                            if (typeof retVal !== 'undefined') {
                                object[p] = retVal;
                            }
                        } else {
                            throw "Podaj funkcje jako drugi argument";
                        }
                        //also unsafe instanceof. 
                    } else if (object[p] instanceof Object) {
                        // shouldn't you use _bind, not bindable?
                        Object.walkRecursive(object[p], callable, bindable);
                    }
                }
            }
            return this;
        }
    });
    
    
    var data = [{
        id: 0,
        name: 'Template 0',
        subComponents: [{
            id: 1,
            name: 'Template 1',
            subItems: [{
                id: 2,
                name: 'Template 2',
                subComponents: [{
                    id: 3,
                    name: 'Template 3'
                }],
                subItems: [{
                    id: 4,
                    name: 'Template 4',
                }, {
                    id: '42'
                }]
            }]
        }]
    }];
    
    var bindMe = {
        nothing: 'special'
    };
    
    Object.walkRecursive(data, function(key, value){
        if (key === 'name'){
            return value + ' ' + this.nothing;
        }
    }, bindMe);
    
    console.log(data);
    
    // unsafe because.
    console.log(new Date() instanceof Object, [] instanceof Object, function(){} instanceof Object);
    
    // eg safer isObject
    var isObject = function(what){
        return {}.toString.call(what).slice(8, -1) === 'Object';
    };
    
    console.log(isObject(new Date()), isObject([]), isObject(function(){}), isObject({}));
    

    this includes some examples of the unsafe checks and how to do them differently to ensure correct logic.

    in any case--in case you were wondering--a DOM element also returns an Element Object, hence you can see what you've added.

    some people say it's OK to use this if you iterate via hasOwnProperty but it is not. eg a dictionary object:

    var obj = Object.create(null);
    obj.name = 'template 0';
    
    for (var k in obj){
        console.log(obj.hasOwnProperty[k]); // throws, no method hasOwnProperty
        // if you omit hasOwnProperty here, you break jslint and jshint, confusing... 
    }
    

    of course, you can fix it by using:

    var obj = Object.create(null);
    obj.name = 'template 0';
    
    var hasOwnProperty = {}.hasOwnProperty;
    
    for (var k in obj){
        console.log(k, hasOwnProperty.call(obj, k));
    }
    

    etc etc - but it makes your code base way too defensive just because of a style choice and a simple convenience that you really don't need.