美文网首页
JS对象创建详解

JS对象创建详解

作者: JasonQiao | 来源:发表于2016-03-22 11:10 被阅读90次

转自http://driftcloudy.iteye.com/blog/949318

下面是JS的一个面试题

    var a = new Object;
    var b = new Object;
    var c = new Object;
    c[a] = a;
    c[b] = b;
    //输出false
    alert(c[a] === b);

    //输出true
    alert(c[a] === b);

答案已在注释中指出。此处分析原因,主要是要理解JS创建对象的过程。
JS中有两种创建对象的方式,一种是通过new运算符,还有一种是通过字面量的方式。
一、利用字面量
ECMA标准语法如下:

Ecma 262 10.1.5
ObjectLiteral :
{ }
{ PropertyNameAndValueList }
PropertyNameAndValueList :
PropertyName : AssignmentExpression
PropertyNameAndValueList , PropertyName : AssignmentExpression
PropertyName :
Identifier
String Literal
Numeric Literal

根据描述,如果创建的不是空对象,而是一个带有Name和Value的对象,那么Name可以是JS中的标识符、字符串或者数字。具体的创建过程是可以描述成:
(1)var obj = {} 就等于var obj = new Object()
The production ObjectLiteral : {} is evaluated as follows:

  1. Create a new object as if by the expression new Object().
  2. Return Result(1).

(2)var obj = { PropertyNameAndValueList }
如果是这种带了键值对的对象,首先还是调用new Object()来创建一个新的对象,然后会计算PropertyName、AssignmentExpression,利用GetValue方法获取AssignmentExpression的值,最后调用被新创建对象的[[Put]] 方法(obj的put方法是内部方法,外部无法调用),具体细节如下:
The production ObjectLiteral : { PropertyNameAndValueList } is evaluated as follows:

  1. Evaluate PropertyNameAndValueList.
  2. Return Result(1);

The production PropertyNameAndValueList : PropertyName : AssignmentExpression is evaluated as follows:

  1. Create a new object as if by the expression new Object().
  2. Evaluate PropertyName.
  3. Evaluate AssignmentExpression.
  4. Call GetValue(Result(3)).
  5. Call the [[Put]] method of Result(1) with arguments Result(2) and Result(4).
  6. Return Result(1).

这里的GetValue和[[Put]]方法都可以暂且不管,因为它们对于程序员并不可见。进一步看一下Evaluate PropertyName的过程:
The production PropertyName : Identifier is evaluated as follows:

  1. Form a string literal containing the same sequence of characters as the Identifier.
  2. Return Result(1).

The production PropertyName : StringLiteral is evaluated as follows:

  1. Return the value of the StringLiteral.

The production PropertyName : NumericLiteral is evaluated as follows:

  1. Form the value of the NumericLiteral.
  2. Return ToString(Result(1)).

可以发现,在利用字面量创建对象的时候:如果属性的name用JS中的标识符表示,那么name会被转成值相同的字符串;如果属性的name是number,那么会调用ToString来计算该number的字符串表示,这儿的ToString也是JS内部的方法。

二、利用new Object()
ECMA标准语法如下:

new Object ( [ value ] )
When the Object constructor is called with no arguments or with one argument value, the following steps are taken:

  1. If value is not supplied, go to step 8.
  2. If the type of value is not Object,go to step 5.
  3. If the value is a native ECMAScript object, do not create a new object but simply return value.
  4. If the value is a host object, then actions are taken and a result is returned in an implementation-dependent manner that may depend on the host object.
  5. If the type of value is String, return To Object(value).
  6. If the type of value is Boolean, return To Object(value).
  7. If the type of value is Number, return To Object(value).
  8. (The argument value was not supplied or its type was Null or Undefined.)Create a new native ECMAScript object.
    The [[Prototype]] property of the newly constructed object is set to the Object prototype object.
    The [[Class]] property of the newly constructed object is set to "Object".
    The newly constructed object has no [[Value]] property.Return the newly created native object

很显然,**如果是不传参数,那么会创建一个 native ECMAScript object,随后会给这个object添加一系列的内部属性 **,比如将 [[Prototype]]设置为Object prototype object(即Object.prototype),将[[Class]]设置为字符串“Object”。注意,在Object.prototype中已经包含了一些方法:

  1. toString ( )
  2. toLocaleString ( )
  3. valueOf ( )
  4. hasOwnProperty (V)
  5. isPrototypeOf (V)
  6. propertyIsEnumerable (V)

利用new Object新创建的对象不会有除了上面之外别的方法。

对象的属性访问、赋值

对JS中的对象进行属性访问同样也是两种形式,一种是利用“[ ]”运算符,还有一种是利用“.”运算符。即:
CallExpression. Identifier 或CallExpression[ Expression ]
注意就是利用“.”的时候,只能后接一个合法的Identifie。但是如果是利用“[ ]”却可以包含一个表达式进来,本文刚开始的题目中就是利用这种形式去访问obj的属性。其实CallExpression. Identifier也会按照CallExpression[ <Identifier-string>]去执行。

CallExpression[ Expression ] is evaluated as follows:

  1. Evaluate CallExpression.
  2. Call GetValue(Result(1)).
  3. Evaluate Expression.
  4. Call GetValue(Result(3)).
  5. Call ToObject(Result(2)).
  6. Call ToString(Result(4)).
  7. Return a value of type Reference whose base object is Result(5) and whose property name is Result(6).

尤其要注意第6行, 所有方括号中的Expression的值要经过ToString这一步。

对于赋值,具体的计算过程如下:

The production AssignmentExpression : LeftHandSideExpression = AssignmentExpression is evaluated as follows:

  1. Evaluate LeftHandSideExpression.
  2. Evaluate AssignmentExpression.
  3. Call GetValue(Result(2)).
  4. Call PutValue(Result(1), Result(3)).

CallExpression就是一种 LeftHandSideExpression。

题目详解

下面来拆分题目,逐行来推敲具体的执行过程,首先是三条创建对象的语句:

//创建一个native Ecmascript object  
//[[Prototype]]指向Object.prototype  
//[[Class]]设为"Object"  
var a = new Object;  
//同a  
var b = new Object;  
//同a  
var c = new Object;  

注意,一个obj里并非只有 [[Prototype]]和[[Class]]两个内部属性,具体的内部属性以及内部方法可以参考Ecma262的8.6章节。

在创建完对象后,又是两条属性赋值语句。

//c[a]会按照CallExpression[ Expression ] 的7个步骤来计算,  
//其中的GetValue较为复杂,可以参考http://www.w3help.org/zh-cn/causes/SD9028的注释2  
//注意第6步,ToString(a)会调用到ToPrimitive(a),进而调用a的[[DefaultValue]]方法,具体参考Ecma规范  
//这里最终会调用到a.toString方法,根据Object.prototype.toString的描述,会返回[object Object]  
//即最终相当于c["[object Object]"]=a;  
c[a]=a;  
//即最终相当于c["[object Object]"]=b;  
c[b]=b;  
alert(c[a]===a);  

稍微变个形

var a = {};  
var b = {};  
var c = {};  
c.a=a;  
c.b=b;  
alert(c[a]===a);  

你可能会立马想到,c.a是怎么处理的,很显然这是利用了“.”运算符访问属性,而不是“[ ]”。根据上面的描述, CallExpression. Identifier会按照CallExpression[ <Identifier-string>]去执行,那么这里的c.a就相当于c["a"]。因此最后结果肯定是true了?
真的是这样么?别忘了,在alert语句中还有c[a]的存在,如前文所述,c[a]完全就相当于c["[object Object]"],因此这段代码相当于:

var a = {};  
var b = {};  
var c = {};  
c["a"]=a;  
c["b"]=b;  
alert(c["[object Object]"]===a);  

真是相当的恶心....

再来变一变

这次c的属性直接在声明里写好了。

var a = {};  
var b = {};  
var c = {  
    a:a,  
    b:b  
};  
alert(c.a===a);  

这回是利用了字面量创建对象的方式。根据上面的描述,如果键值对的name是一个合法的JS标识符,那么name就是将该标识符变成直接字符串,简单来说,就是两边加上引号。
因此,上面这段代码相当于:

var a = {};  
var b = {};  
var c = {};  
c["a"]=a;  
c["b"]=b;  
alert(c.a===a);  

终于输出了true....

相关文章

网友评论

      本文标题:JS对象创建详解

      本文链接:https://www.haomeiwen.com/subject/qdfllttx.html