• 原型上的方法写属性的一个陷阱


    我们都知道 , 对象的属性读写在原型链上操作方式。我们先来回顾下。

    值的赋予

    对象的命名属性可以通过为该命名属性赋值来创建,或重新赋值。即,对于:

    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.真正的写一个

        }

    }

  • 相关阅读:
    ASP.NET AJAX入门系列(1):概述
    ASP.NET中常用的文件上传下载方法
    Asp.net中DataBinder.Eval用法的总结
    ASP.NET AJAX入门系列(6):UpdateProgress控件简单介绍
    ASP.NET AJAX入门系列(8):自定义异常处理
    Javascrip常用语句
    26个常用的方法优化 ASP.NET 的性能
    JavaScript倒计时组件
    jQuery.buildFragment源码分析
    jQuery.Callbacks源码解读
  • 原文地址:https://www.cnblogs.com/litao229/p/2422990.html
Copyright © 2020-2023  润新知