本文详细解析 JavaScript 中 Native Object 与 Host Object 的区别,重点阐述对象属性(property)的动态创建与赋值机制。通过代码示例说明属性读取时原型链(prototype chain)的查找规则,以及写入操作对原型链的影响,揭示对象属性与原型之间的核心关系。
JavaScript 对象类型概述
ECMAScript 规范定义了两种主要对象类型:Native Object(原生对象)属于语言核心范畴,而 Host Object(宿主对象)则由运行环境提供,例如 document 对象。
Dom Node 等宿主对象也属于此类。
原生对象具有松散的结构,支持动态添加属性(property)。每个属性包含一个名称和一个值,该值可以是另一个对象的引用。
或者为内置数据类型,包括 String、Number、Boolean、Null 或 Undefined。
以下示例展示了 JavaScript 对象如何设置和读取属性值。
对象属性的创建与赋值
赋值操作
创建对象属性非常直接,只需通过赋值操作即可完成。
代码
1. var objectRef = new Object(); //create a generic javascript object.
可以通过以下方式创建名为 testNumber 的属性。
代码
1. objectRef.testNumber = 5;
2. /* - or:- */
3. objectRef["testNumber"] = 5;
如果属性名称已存在,赋值操作不会重新创建该属性,而是直接更新其值。
代码
1. objectRef.testNumber = 8;
2. /* - or:- */
3. objectRef["testNumber"] = 8;
对象的原型(prototype)本身也是一个对象,可以拥有属性(property)。对原型属性的赋值操作与普通对象属性创建无异。
没什么不同。
属性取值与原型链机制
取值操作
在取值操作中,property 和 prototype 的行为有所不同。首先看最简单的 property 取值操作。
代码
1. /*为一个对象的属性赋值,如果这个对象没有这个属性,那么在赋值操作后,这个对象就有这个属性了 */
2. objectRef.testNumber = 8;
3. /* 读出这个属性的值 */
4. var val = objectRef.testNumber;
5.
6. /* 现在val 就得到了刚才赋予objectRef的值8了*/
prototype 揭密
所有对象都可以拥有 prototypes,而 prototypes 本身也是对象,因此也可以拥有自己的 prototypes。这种循环引用形成了原型链(prototype chain)。
当链中指向的 prototype 为 null 时,链终止(Object 的默认 prototype 是 null)。
代码
1. var objectRef = new Object(); //create a generic javascript object.
创建一个新的 JS 对象时,其 prototype 为 Null。因此,objectRef 的原型链仅包含 Object.prototype。
接下来看下面的代码。
代码
1. /* 构建MyObject1这个类型的构造函数
2. MyObject1 - type.
3. */
4. function MyObject1(formalParameter){
5. /* 为者对象创建一个属性名字叫testNumber
6. */
7. this.testNumber = formalParameter;
8. }
9.
10. /* 构建MyObject2这个类型的构造函数
11. MyObject2 - type:-
12. */
13. function MyObject2(formalParameter){
14. /* 为者对象创建一个属性名字叫testString*/
15. this.testString = formalParameter;
16. }
17.
18. /* 下一步的操作会用MyObject1对象替换掉MyObject2默认的prototype属性*/
19. MyObject2.prototype = new MyObject1( 8 );
20.
21. /* 最后我们创建MyObject2类型的一个对象*/
22.
23. var objectRef = new MyObject2( "String_Value" );
objectRef 是 MyObject2 类型的对象,拥有一个原型链。链中的第一个对象是 MyObject1 对象,该对象也有 prototype,即 Object 默认的 prototype。Object.prototype 的 prototype 为 null,至此原型链结束。
这个prototype是Object默认的prototype,Object.prototype的prototype是null,至此这条prototype链结束。
当发生取值操作时,objectRef 的整个原型链开始工作。
代码
1. var val = objectRef.testString;
objectRef 对象有一个名为 testString 的属性,该代码将 testString 的值赋给 val。
代码
1. var val = objectRef.testNumber;
在 objectRef 对象中并没有 testNumber 这个属性,但 val 却得到了值 8,而不是 undefined。这是因为解释器在当前对象未找到目标属性后,会检查该对象的 prototype。objectRef 的 prototype 是 MyObject1 对象,该对象拥有 testNumber 属性,因此 val 得到值 8。
访问属性后,就会去检查这个对象的prototype,objectRef的prototype是MyObject1对象,这个对象有testNumber这个属性,所以val得到8这个值。
代码
1. var val = objectRef.toString;
现在 val 是一个函数引用,该函数是 Object.prototype 的 property。由于 MyObject1 和 MyObject2 均未定义 toString 属性,因此返回 Object.prototype。
所以Object.prototype返回。
代码
1. var val = objectRef.madeUpProperty;
最后 val 是 undefined,因为 MyObject1、MyObject2 和 Object 均未定义 madeUpProperty 属性,因此得到的是 undefined。
读写操作对原型链的影响
读操作会读取在obj自己和prototype 链上发现的第一个同名属性值
写操作会为obj对象本身创建一个同名属性(如果这个属性名不存在
这意味着 objectRef.testNumber = 3 会在 objectRef 对象上创建一个名为 testNumber 的 property。当下一次读取 testNumber 时,原型链将不再工作,仅返回 objectRef 的 property 值 3,而 MyObject1 的 testNumber 属性不会被修改。以下代码可以验证。
这就意味着objectRef.testNumber = 3会在objectRef对象上创建一个property,名字是testNumber,当下一次在要读取testNumber时
代码
1. /* 构建MyObject1这个类型的构造函数
2. MyObject1 - type.
3. */
4. function MyObject1(formalParameter){
5. /* 为者对象创建一个属性名字叫testNumber
6. */
7. this.testNumber = formalParameter;
8. }
9.
10. /* 构建MyObject2这个类型的构造函数
11. MyObject2 - type:-
12. */
13. function MyObject2(formalParameter){
14. /* 为者对象创建一个属性名字叫testString*/
15. this.testString = formalParameter;
16. }
17.
18. /* 下一步的操作会用MyObject1对象替换掉MyObject2默认的prototype属性*/
19. var obj1 = new MyObject1( 8 );
20. MyObject2.prototype = obj1;
21.
22. /* 最后我们创建MyObject2类型的一个对象*/
23.
24. var objectRef = new MyObject2( "String_Value" );
25.
26. alert(objectRef.testNumber);
27. objectRef.testNumber = 5;
28. alert(objectRef.testNumber);
29. alert(obj1.testNumber);