Progo
Progo

Reputation: 3490

Pure JavaScript each function not working

In pure JavaScript, I am trying to make the jQuery.each function. So far I have just copied parts from the query source code.

Here is what I have so far:

var class2type = {
    "[object Boolean]": "boolean",
    "[object Number]": "number",
    "[object String]": "string",
    "[object Function]": "function",
    "[object Array]": "array",
    "[object Date]": "date",
    "[object RegExp]": "regexp",
    "[object Object]": "object",
    "[object Error]": "error"
},
core_toString = class2type.toString;
function type(obj) {
    if (obj == null) {
        return String(obj);
    }
    return typeof obj === "object" || typeof obj === "function" ? class2type[core_toString.call(obj)] || "object" : typeof obj;
}
function isWindow(obj) {
    return obj != null && obj == obj.window;
}
function isArraylike(obj) {
    var length = obj.length,
        type = type(obj);

    if (isWindow(obj)) {
        return false;
    }

    if (obj.nodeType === 1 && length) {
        return true;
    }

    return type === "array" || type !== "function" && (length === 0 || typeof length === "number" && length > 0 && (length - 1) in obj);
}

function each( obj, callback, args ) {
    var value,
        i = 0,
        length = obj.length,
        isArray = isArraylike( obj );
    if ( args ) {
        if ( isArray ) {
            for ( ; i < length; i++ ) {
                value = callback.apply( obj[ i ], args );

                if ( value === false ) {
                    break;
                }
            }
        } else {
            for ( i in obj ) {
                value = callback.apply( obj[ i ], args );

                if ( value === false ) {
                    break;
                }
            }
        }
    } else {
        if ( isArray ) {
            for ( ; i < length; i++ ) {
                value = callback.call( obj[ i ], i, obj[ i ] );

                if ( value === false ) {
                    break;
                }
            }
        } else {
            for ( i in obj ) {
                value = callback.call( obj[ i ], i, obj[ i ] );

                if ( value === false ) {
                    break;
                }
            }
        }
    }
    return obj;
}

It should work fine, but when I I try to run the following code:

each([1, 2], function( index, value ) {
  alert( index + ": " + value );
});

I get the following error: TypeError: 'undefined' is not a function (evaluating 'type(obj)') This refers to here:

23| function isArraylike(obj) {
24|     var length = obj.length,
25|     type = type(obj);

Why won't this code work? I just used parts directly from jQuery's source code.

Thank you.

Upvotes: 1

Views: 1240

Answers (1)

Tibos
Tibos

Reputation: 27823

The problem is one of variable hoisting and shadowing. You have a type function outside of the current scope and you expect that in the statement on line 25 it is the one used as a function and then the result is passed to the local variable with the same name:

function type () {};

function isArraylike(){
  var type = type(1);
};

In fact, what the code looks like due to variable hoisting is:

function type() {};

function isArraylike(){
  var type; // type is undefined here
  type = type(1);
};

So you can see that throughout the isArraylike function, type will always be a variable and it will never reference the function from the outer scope. The fix is simple: use another name either for the function or the variable.

Upvotes: 3

Related Questions