Why is object[key] not equal to key, if key is an object, in JavaScript?

后端 未结 3 1087
我在风中等你
我在风中等你 2020-12-09 08:52
var a = new Object;
var b = new Object;
var c = new Object;

c[a] = a;
c[b] = b;

console.log(c[a] === a);

I tested the code above and get fa

相关标签:
3条回答
  • 2020-12-09 09:03

    The problem here has to do with how an Object's keys are set. From MDN:

    Parameters

    nameValuePair1, nameValuePair2, ... nameValuePairN

    • Pairs of names (strings) and values (any value) where the name is separated from the value by a colon.

    value

    • Any value.

    An object's values can be accessed (via the appropriate key) in three ways:

    var o = {};
    var key = "fun";
    
    // method 1:
    o[key]    = "the key will be equal to `key.toString()"
    // method 2:
    o.key     = "the key will be equal to 'key'"
    // method 3:
    o["key2"] = "the key will be equal to `key2`"
    /*
    {
        "fun" : "the key will be...",    // method 1
        "key" : "the key will be...",    // method 2
        "key2": "the key will be..."     // method 3
    }
    */
    

    When using bracket notation, you need to mind the gap...between the brackets! Objects set their keys and values using the toString method, unless they're passed a string (then there's no point in toString). When using the dot notation, they use .key as the key.

    Let's look at your case:

    var a = {}
      , b = {}
      , c = {}
      ;
    
    c[a] = a;
    // `a` is not a string, and we're using brackets, so the key
    // will be equal to `key.toString()`:
    // a.toString() === "[object Object]"
    // Try the following in your console: `{}.toString()`
    // Note how this is different from console.log({}), since
    // the console exposes your object (that's why the dev console is useful)
    // c is now: `{ "[object Object]" : a }`
    
    c[b] = b;
    // b is also an object, so `b.toString()` is the same as `a.toString()`
    // that means c is now `{ "[object Object]" : b }`
    
    assert c[a] === a
    // a.toString() == b.toString() == "[object Object]"
    // and we just noted that c was `{ "[object Object]" : b }`
    // so of course this is false
    assert c[b] === b
    // true because c[b] == b;
    assert c["[object Object]"] === b;
    // also true
    assert c.b === b
    // false, since `c` has no "b" key (c.b is `undefined`)
    
    0 讨论(0)
  • 2020-12-09 09:05

    I experimented with it a lit bit and @royhowie might be right. As you can see in this implementation I switched the order of the assignments and then c[a] == a gave a true.

    var a = new Object;
    var b = new Object;
    var c = new Object;
    
    //I switched the following lines of code
    c[b]=b; 
    c[a]=a;
    
    console.log(c[a]===a);
    

    Output: true

    0 讨论(0)
  • 2020-12-09 09:14

    Object is not a valid key for JavaScript Object, only strings are

    So, when you do this:

    c[a] = a;
    c[b] = b;
    

    The compiler can not use the a or b as a key for c as in c[a] or c[b].

    However, it does not fail, because JavaScript can work around this problem. First it figures out that

    1. The variable is an object and
    2. The variable has toString -function

    Thus, the JavaScript compiler will call toString() of each of those variables. And by default, the Object.prototype.toString it will return "[object Object]" -string, because the implementation is the default implementation which does that and that value becomes the new key

    c["[object Object]"] = a;
    c["[object Object]"] = b; // overrides the previous
    

    Which is not what you wanted. The problem is that toString will by default return always the same value, so assigments will always go to the same key.

    To demonstrate that toString is actually the problem, you can actually do a horrible cheat to make each object return unique string

    // don't do this!!! 
    (function() {
      var id=1;
      Object.prototype.toString = function() {
       if(!this._id) this._id = id++;
       return "Object"+this._id;
      }
    }());
    

    After that key of c[a] will be c["Object1"] and c[b] will be c["Object2"] and so on... and the c[a] == a and c[b] == b are working as expected, but in real life this is not a good solution.

    An acceptable way to solve this would be either use some other key, perhaps an ID assigned to the object like c[a.id] = a or to use ES6 Map Object, where any value, including Objects, can be used as a key.

    https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map

    The Map object is a simple key/value map. Any value (both objects and primitive values) may be used as either a key or a value.

    0 讨论(0)
提交回复
热议问题