jashkenas / underscore

JavaScript's utility _ belt
https://underscorejs.org
MIT License
27.33k stars 5.53k forks source link

Bug about _.keys()? #2799

Closed rookieLink closed 4 years ago

rookieLink commented 5 years ago

I read underscore's code about _.keys() if I want to invoke the code in IE7, there will be: ` function A (){} A.prototype.toString = 'test';

var a = new A(); a.toString = 'test'; _.keys(a); //there should be [] rather than ['toString']? ` right? I need some help

HelloAny commented 5 years ago
 _.keys = function(obj) {
    if (!_.isObject(obj)) return [];
    if (nativeKeys) return nativeKeys(obj);
    var keys = [];
    for (var key in obj) if (has(obj, key)) keys.push(key);
    // Ahem, IE < 9.
    if (hasEnumBug) collectNonEnumProps(obj, keys);
    return keys;
  };
_.isObject = function(obj) {
    var type = typeof obj;
    return type === 'function' || type === 'object' && !!obj;
  };

Here you can verify the function

jgonggrijp commented 4 years ago

@rookieLink If you do just this,

function A (){}
A.prototype.toString = 'original value';

var a = new A();

Then a.toString is only an inherited property of a and _.keys(a) will be []. The internal structure of a looks like this ([[Prototype]] is an abstract property of each object that you cannot access directly):

{
    [[Prototype]]: {
        constructor: A,
        toString: 'original value'
    }
}

If you now also do this:

a.toString = 'new value';

Then you create an extra own property on a that shadows the toString property of the prototype:

{
    toString: 'new value',
    [[Prototype]]: {
        constructor: A,
        toString: 'original value'
    }
}

So it is important to understand that when you do

A.prototype.toString = 'original value';

this just sets a “default value” for the toString property of instances of A in case they don’t have an own property with that name.

Hope this answers your question. I will close this issue now, but feel free to comment again.