我们都知道 , 对象的属性读写在原型链上操作方式。我们先来回顾下。
值的赋予
对象的命名属性可以通过为该命名属性赋值来创建,或重新赋值。即,对于:
var objectRef = new Object(); //创建一个普通的 JavaScript 对象。
可以通过下面语句来创建名为 “testNumber” 的属性:
objectRef.testNumber = 5;
/* – 或- */
objectRef["testNumber"] = 5;
在赋值之前,对象中没有“testNumber” 属性,但在赋值后,则创建一个属性。之后的任何赋值语句都不需要再创建这个属性,而只会重新设置它的值:
objectRef.testNumber = 8;
/* – or:- */
objectRef["testNumber"] = 8;
稍后我们会介绍,Javascript 对象都有原型(prototypes)属性,而这些原型本身也是对象,因而也可以带有命名的属性。但是,原型对象命名属性的作用并不体现在赋值阶段。同样,在将值赋给其命名属性时,如果对象没有该属性则会创建该命名属性,否则会重设该属性的值。
值的读取
当读取对象的属性值时,原型对象的作用便体现出来。如果对象的原型中包含属性访问器(property accessor)所使用的属性名,那么该属性的值就会返回:
/* 为命名属性赋值。如果在赋值前对象没有相应的属性,那么赋值后就会得到一个:*/
objectRef.testNumber = 8;
/* 从属性中读取值 */
var val = objectRef.testNumber;
/* 现在, – val – 中保存着刚赋给对象命名属性的值 8*/
而且,由于所有对象都有原型,而原型本身也是对象,所以原型也可能有原型,这样就构成了所谓的原型链。原型链终止于链中原型为 null 的对象。Object
构造函数的默认原型就有一个 null 原型,因此:
var objectRef = new Object(); //创建一个普通的 JavaScript 对象。
创建了一个原型为 Object.prototype
的对象,而该原型自身则拥有一个值为 null 的原型。也就是说, objectRef
的原型链中只包含一个对象-- Object.prototype
。但对于下面的代码而言:
/* 创建 – MyObject1 – 类型对象的函数*/
function MyObject1(formalParameter){
/* 给创建的对象添加一个名为 – testNumber – 的属性
并将传递给构造函数的第一个参数指定为该属性的值:*/
this.testNumber = formalParameter;
}
/* 创建 – MyObject2 – 类型对象的函数*/
function MyObject2(formalParameter){
/* 给创建的对象添加一个名为 – testString – 的属性
并将传递给构造函数的第一个参数指定为该属性的值:*/
this.testString = formalParameter;
}
/* 接下来的操作用 MyObject1 类的实例替换了所有与 MyObject2 类的实例相关联的原型。而且,为 MyObject1 构造函数传递了参数 – 8 – ,因而其 – testNumber – 属性被赋予该值:*/
MyObject2.prototype = new MyObject1( 8 );
/* 最后,将一个字符串作为构造函数的第一个参数,创建一个 – MyObject2 – 的实例,并将指向该对象的引用赋给变量 – objectRef – :*/
var objectRef = new MyObject2( “String_Value” );
被变量 objectRef
所引用的 MyObject2
的实例拥有一个原型链。该链中的第一个对象是在创建后被指定给 MyObject2
构造函数的 prototype
属性的 MyObject1
的一个实例。MyObject1
的实例也有一个原型,即与 Object.prototype
所引用的对象对应的默认的 Object 对象的原型。最后, Object.prototype
有一个值为 null 的原型,因此这条原型链到此结束。
当某个属性访问器尝试读取由 objectRef
所引用的对象的属性值时,整个原型链都会被搜索。在下面这种简单的情况下:
var val = objectRef.testString;
因为 objectRef
所引用的 MyObject2
的实例有一个名为“testString”的属性,因此被设置为“String_Value”的该属性的值被赋给了变量 val
。但是:
var val = objectRef.testNumber;
则不能从 MyObject2
实例自身中读取到相应的命名属性值,因为该实例没有这个属性。然而,变量 val
的值仍然被设置为 8
,而不是未定义--这是因为在该实例中查找相应的命名属性失败后,解释程序会继续检查其原型对象。而该实例的原型对象是 MyObject1
的实例,这个实例有一个名为“testNumber”的属性并且值为 8
,所以这个属性访问器最后会取得值 8
。而且,虽然 MyObject1
和 MyObject2
都没有定义 toString
方法,但是当属性访问器通过 objectRef
读取 toString
属性的值时:
var val = objectRef.toString;
变量 val
也会被赋予一个函数的引用。这个函数就是在 Object.prototype
的 toString
属性中所保存的函数。之所以会返回这个函数,是因为发生了搜索 objectRef
原型链的过程。当在作为对象的 objectRef
中发现没有“toString”属性存在时,会搜索其原型对象,而当原型对象中不存在该属性时,则会继续搜索原型的原型。而原型链中最终的原型是Object.prototype
,这个对象确实有一个 toString
方法,因此该方法的引用被返回。
最后:
var val = objectRef.madeUpProperty;
返回 undefined
,因为在搜索原型链的过程中,直至 Object.prototype
的原型--null,都没有找到任何对象有名为“madeUpPeoperty”的属性,因此最终返回 undefined
。
不论是在对象或对象的原型中,读取命名属性值的时候只返回首先找到的属性值。而当为对象的命名属性赋值时,如果对象自身不存在该属性则创建相应的属性。
这意味着,如果执行像 objectRef.testNumber = 3
这样一条赋值语句,那么这个 MyObject2
的实例自身也会创建一个名为“testNumber”的属性,而之后任何读取该命名属性的尝试都将获得相同的新值。这时候,属性访问器不会再进一步搜索原型链,但 MyObject1
实例值为 8
的“testNumber”属性并没有被修改。给 objectRef
对象的赋值只是遮挡了其原型链中相应的属性。
上面这两段内容来自 李松峰老师的 翻译文档 http://www.cn-cuckoo.com/2010/09/22/a-brief-history-of-markup-2079.html
嗯,好吧, 回顾了下基础知识 ,我们可以回来了回到我的问题,是这样的。
var pt={ children:[], appendChild:function( child ){ this.children.push( child ) } } var fn=function(){} fn.prototype=pt; var obj=new fn(); obj.appendChild( {name:'nick'} )
嗯,写完收工,
这段代码,看上去其实很完美,没有什么问题吧。。。
你还是在看看
-------------------------------我是思考的分割线---------------------------------------------------------------
嗯嗯,我来提示下吧。
alert(obj.hasOwnProperty("children")) ; 你会发现为false
那么问题来了。写属性不是应该写在obj本身的么。
但是this.children.push() 却把对象实实在在的写到了pt上。
也就是说this.children 这个时候 用 = 号赋值的 都会给Obj写一个新的 但是通过 push 等来写入新的数据就都不会
好吧, 我们这样理解就清楚了,其实。= 才是写, push等都是修改
所以,我们这里要处理
appendChild = function(child){
if (this.hasOwnProperty("children")){
this.children = [] ;//这里就给this.真正的写一个
}
}