`
driftcloudy
  • 浏览: 132055 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

JS中的对象创建、属性访问

阅读更多

一条面试题

公司最近在招web fronter,免不了需要进行面试,于是大家在群里讨论一些题目。其中一题如下:

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

这个题目还是很有意思的,第一反应往往会心中涌起一丝疑惑,这难道不是true么(我就是的= =!)事实上还真输出了一个false...这样也许看不出什么端倪,但是如果稍微变动一下代码:

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

从结果可以明显的推断出一个事实:当运行了c[b]=b这条语句之后,原先的属性c[a]被覆盖了。也就是说,其实c[a]和c[b]指向的是同一个东西。

 

创建JS对象

现在需要深究一下,JS对象的属性究竟是怎么回事...一般而言,我们会认为JS中的对象就是一个无序的属性集,这个属性集中的每个属性都由键值对构成,就好比一种散列表类似的数据结构。对象里属性的值可以用来存放JS中原型的值、对象的引用、函数的引用。这在Ecmascript-262标准中可以找到理论依据:

Ecmascript-262 3rd
An object is a member of the type Object. It is an unordered collection of properties each of which
contains a primitive value, object, or function. A function stored in a property of an object is called a method.

 

JS中有两种创建对象的方式,一种是通过new运算符,还有一种是通过字面量的方式。前文的面试题中就是利用了new Object来创建一个貌似是空的对象。下面来分别分析一下这两种创建对象的方式:

 

1.字面量

字面量创建的方式很简单,根据Ecma标准语法:

Ecma 262 10.1.5
ObjectLiteral :
     { }
     { PropertyNameAndValueList }
PropertyNameAndValueList :
     PropertyName : AssignmentExpression
     PropertyNameAndValueList , PropertyName : AssignmentExpression
PropertyName :
     Identifier
     StringLiteral
     NumericLiteral

根据描述,如果创建的不是空对象,而是一个带有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内部的方法。

 

2.利用new Object()

调用new Object()也是一种创建对象的方法,如果不传参数进去,本质上来说跟直接使用字面量{}没有区别。实际上new Object的过程还是有些复杂的:

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. Ifthetypeof value isnotObject,gotostep5.
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. Ifthetypeof value is String, return ToObject(value).
6. Ifthetypeof value is Boolean, return ToObject(value).
7. Ifthetypeof value is Number, return ToObject(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....

 

 

 

0
5
分享到:
评论

相关推荐

    在js中创建类和对象

    在JavaScript中,创建类和对象是面向对象编程的基础。本文将详细介绍几种常见的创建对象的方法,包括它们的特点和优缺点。 5.1 工厂方法 工厂方法是一种创建对象的模式,通过一个函数来创建具有特定属性和行为的...

    javascript动态创建对象的属性详解.docx

    #### 二、JavaScript对象的基本概念 在JavaScript中,对象是一种复合数据类型,它可以包含属性(键值对)。属性可以是基本类型的值(如字符串、数字等),也可以是其他复杂类型(如数组、函数或对象本身)。 - **...

    JS 对象,属性

    JavaScript对象允许动态添加、修改和删除属性,无需预先声明。 五、原型与继承 在JS中,每个对象都有一个隐式原型属性 `__proto__`,指向创建它的构造函数的原型。这使得对象可以继承其他对象的属性和方法。通过 `...

    javascript面向对象之访问对象属性的两种方式分析.docx

    综上所述,JavaScript中提供了两种访问对象属性的方式:直接访问和数组访问。每种方式都有其适用场景和优势。理解这两种访问方式及其区别对于提高JavaScript代码的质量和可维护性至关重要。希望本文能够帮助你在实际...

    javascript对象的创建和访问_.docx

    创建JavaScript对象主要有三种方式: 1. **对象字面量**: ```javascript var foo = {}; foo.prop_1 = 'bar'; foo.prop_2 = false; foo.prop_3 = function() { return 'hello world'; } ``` 上述代码中,`...

    javascript如何创建对象

    在JavaScript中,对象可以分为系统对象和自定义对象,系统对象可以通过系统构造函数创建,如Array、Date等,而自定义对象则需要自行创建。 JavaScript创建对象的方法主要有以下几种: 一、直接创建 直接创建是通过...

    js 面向对象实例

    每个JavaScript对象都有一个`__proto__`属性,指向创建该对象的构造函数的原型。原型对象也是一个对象,可以通过`prototype`属性来访问。我们可以在原型上定义方法,这样所有实例都可以访问这些方法: ```...

    js面向对象之常见创建对象的几种方式(工厂模式、构造函数模式、原型模式).docx

    - **属性访问**:通过 `prototype` 添加的属性不能通过 `for...in` 循环直接访问到,除非显式指定。 ### 总结 通过以上介绍,我们可以看到每种模式都有其适用场景和局限性。选择哪种模式取决于具体的应用需求: - *...

    JS对象及属性参考手册

    2. 属性访问:对象的属性可以通过点操作符 `.` 或方括号操作符 `[]` 访问,如 `obj.name` 或 `obj["name"]`。 3. 属性创建:可以直接在对象上添加属性,例如 `obj.newProp = value`。 4. 静态属性与实例属性:类...

    JavaScript — 对象和属性的特性1

    本文将深入探讨JavaScript对象和属性的特性,包括`[[Prototype]]`、`[[Class]]`、`get`和`set`访问器、以及如何通过`Object.getOwnPropertyDescriptor()`、`Object.create()`和`Object.prototype.toString.call()`等...

    JS面向对象经典案例

    原型链是JavaScript中的一种机制,它允许对象继承另一个对象的属性和方法。在JavaScript中,每个对象都有一个原型(prototype),它指向了该对象的父对象。例如,我们可以使用以下代码来获取一个对象的原型: ```...

    JavaScript对象创建的九种方式

    JavaScript是Web开发中的核心语言,其对象创建是编程过程中不可或缺的一部分。本文将详细介绍JavaScript中九种常见的对象创建方式,帮助开发者更好地理解和应用这些技术。 1. **标准创建对象模式**: 这是最基础的...

    javascript创建对象的方式(三)

    这是最简单直接的对象创建方式,通过大括号 `{}` 将属性和方法定义在一个对象中。例如: ```javascript var person = { name: "John Doe", age: 30, sayHello: function() { console.log("Hello, my name is " ...

    JS实现给对象动态添加属性的方法

    在开始之前,我们需要理解JavaScript中的基本概念,对象(Object)在JavaScript中是一种复合数据类型,它将很多值(原始值或其他对象)聚合在一起,可以通过名称访问这些值。对象的属性可以动态地创建、读取、修改和...

    Javascript面向对象编程.

    2. **原型(Prototype)**:JavaScript中的每个对象都有一个内置的`__proto__`属性,指向其构造函数的原型对象。原型对象可以包含共享的方法和属性,使得实例可以访问。我们也可以通过`prototype`属性来修改构造函数...

    JavaScript创建一个object对象并操作对象属性的用法

    当对象创建完毕并且属性被赋予了相应的值后,我们可以使用`document.write`方法将对象的属性输出到网页上。例如,上文中的`document.write("movie:title is \"" + myMovie.title + "\"");`将输出`"movie:title is ...

    一、JavaScript 创建对象

    这篇博客将深入探讨这些方法,帮助你更好地理解和掌握JavaScript中的对象创建。 首先,我们来看最简单的**字面量语法**。这是创建单个对象最直接的方式: ```javascript let person = { name: '张三', age: 30, ...

    javascript中数组、对象

    在JavaScript中,数组和对象是两种非常基础且重要的数据结构,它们被广泛应用于各种场景,如数据存储、逻辑处理和对象表示。这篇文章将深入探讨这两种数据类型,以及相关的操作和工具。 **一、数组** 数组在...

Global site tag (gtag.js) - Google Analytics