关于js的原型的一个问题 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
Tankpt
V2EX    问与答

关于js的原型的一个问题

  •  
  •   < href="/member/Tankpt">Tankpt 2014-01-23 22:00:48 +08:00 3646 次点击
    这是一个创建于 4346 天前的主题,其中的信息可能已经有所发展或是发生改变。
    先附上一段代码
    function fTest(name){}

    fTest.prototype.name = "hello";

    fTest.prototype.setname = function(name){
    this.name = name;
    };

    fTest.prototype.getname = function(){
    console.log(this.name);
    };

    var oTest1 = new fTest();
    var oTest2 = new fTest();

    oTest2.__proto__ === oTest1.__proto__;//返回true


    然后就是我查看了下oTest1中的属性,发现有name,一点疑问,我之前对他的理解都是fTest.prototype.name = "hello";,这句话表明在fTest.prototype中有一个变量为name,然后oTest1和oTest2全部指向他,我的理解是两个共有的一个属性,只要在一个改变了,那另一个去访问也会变化,但是我实际看了下在OTest1对象中有name,这个我在function fTest(name){}中又没进行申明,为啥有这个属性呢?

    我的一点怀疑是不是我这个东西的this出了问题,有点乱指了,一点感觉不知道对不对,还望前辈们指点下问题
    28 条回复    1970-01-01 08:00:00 +08:00
    FrankFang128
        1
    FrankFang128  
       2014-01-23 22:08:36 +08:00
    fTest.prototype.name = "hello";
    这不是 name 的声明吗?
    FrankFang128
        2
    FrankFang128  
       2014-01-23 22:09:49 +08:00
    不太明白你的疑问在哪里。
    你明明声明了为什么觉得自己没声明?
    FrankFang128
        3
    FrankFang128  
       2014-01-23 22:15:37 +08:00
    console.log(oTest1)

    > fTest {name: "hello", setname: function, getname: function}
    > > __proto__: fTest
    > > > constructor: function fTest(name){}
    > > > getname: function (){
    > > > name: "hello"
    > > > setname: function (name){
    > > > __proto__: Object

    oTest1 和 oTest2 的 name 属性都是从 __proto__ 里得到的,而 __proto__ 指向 fTest.prototype,而 fTest.prototype.name = "hello";.
    所以 oTest1 和 oTest2 的 name 属性来自同一个地方,但是 name 不是它们自身的属性
    oTest1.hasOwnProperty('name')
    false
    Tankpt
        4
    Tankpt  
    OP
       2014-01-23 22:45:47 +08:00
    @FrankFang128 嗯,我看了下你第三条回复,完全同意,现在我就是比如调用了下setname方法,用oTest1.setname("hi");我之前的理解是这个操作会改变fTest.prototype中name属性为hi ,然后调用oTest2.getname()返回的也是hi,但是实际测试的时候返回的还是hello,这样感觉就跟name
    然后用oTest1.hasOwnProperty('name')返回的是true,我想问的是是不是在setname中的this出了问题了
    FrankFang128
        5
    FrankFang128  
       2014-01-23 22:49:20 +08:00
    在 context 是 oTest1 的情况下,你只能操作 oTest1 的属性,无法通过 oTest1 改变它的 __proto__ 的属性,也就是说,你添加了一个实例属性,覆盖了『类』属性。
    FrankFang128
        6
    FrankFang128  
       2014-01-23 22:50:22 +08:00
    但是对于没用覆盖过 name 的 oTest2 来说, name 还是 __proto__ 的 name,而不是 its own property。
    FrankFang128
        7
    FrankFang128  
       2014-01-23 22:54:36 +08:00
    应该不能称为「类属性」。 暂时还没想到合适的词。
    在你执行 oTest1.setname 时,在 oTest1 上添加了一个 name,同时 oTest1 的 __proto__ 里也有一个 name。
    根据 JS 查找成员的规则,它如果在 oTest1 上找到了 name, 就不会再去看 oTest1 的 __proto__ 了。
    而 oTest2,因为自身没有 name 属性,所以 JS 引擎会去看它的 __proto__
    FrankFang128
        8
    FrankFang128  
       2014-01-23 22:56:12 +08:00
    已经很久没看面向对象的东西,不过不建议你从面向对象的角度去理解,而是从原型链的角度来理解。
    Tankpt
        9
    Tankpt  
    OP
       2014-01-23 22:57:32 +08:00
    @FrankFang128 嗯嗯。有些明白了。那就是说这个setname方法中的this.name指向的不是fTest.prototype中的name么?
    Tankpt
        10
    Tankpt  
    OP
       2014-01-23 23:00:32 +08:00
    @FrankFang128 嗯,那个js寻找一个属性从实例开始找这点我是明白的,我感觉根据测试的结果来看,这个时候在oTest1实例中就已经有了一个name的属性,然后覆盖了fTest.prototype中的name,然后么就是一下子没想明白这个实例中的name是在哪里加进去的。有点困惑了
    FrankFang128
        11
    FrankFang128  
       2014-01-23 23:01:30 +08:00
    this.name 指向哪里,是不确定的。
    唯一确定的是 JS 引擎的查找规则。
    在你 setname 之前,如果你想读取 oTest1 的 name,那么 JS 引擎首先看 oTest1 自身有么有 name 属性(没有),然后再看 __proto__ 里有没有(有),如果还找不到,它会继续看 __proto__ 的 __proto__。
    但是你 setname 之后, oTest1 就有了「自己的」name 属性。你再让 JS 引擎找 oTest1.name 的时候,它依然用刚才的逻辑找 name。
    现在应该懂了吧。
    FrankFang128
        12
    FrankFang128  
       2014-01-23 23:04:56 +08:00
    这是因为 oTest1.setname 中的 this ,指的是 oTest1。所以 oTest1.setname 会在 oTest1(this)上添加 name 属性。



    如果你运行 oTest1.setname.call(fTest.prototype,'another name'),那么期间的 this 就是 fTest.prototype 了。
    这里涉及的概念是 context。 你可以试着运行下。
    FrankFang128
        13
    FrankFang128  
       2014-01-23 23:07:15 +08:00
    this 和 prototype 就是 JS 最难理解的两个地方啊。
    FrankFang128
        14
    FrankFang128  
       2014-01-23 23:08:37 +08:00
    读 name 的过程和写 name 的过程是不一样的。11楼说的是读的过程。
    FrankFang128
        15
    FrankFang128  
       2014-01-23 23:10:29 +08:00
    而写的过程呢,就简单很多了。
    this.name = 'hi'
    JS 引擎会看 this 有没有 name 这个属性。没有就新建一个 name ,并赋值为1(没有查看__proto__ 的过程);有就直接赋值。
    Tankpt
        16
    Tankpt  
    OP
       2014-01-23 23:11:33 +08:00
    @FrankFang128 嗯嗯。现在prototype自己琢磨了下,然后差不多能画一些图了,前天看到了this的问题,感觉好玄乎,嗯。你刚那么一说,算是明白了,set中的this指向的是oTest1,那就没错了,难得放假有时间整理下东西,谢谢啦。非常感谢你的耐心指导~~
    FrankFang128
        17
    FrankFang128  
       2014-01-23 23:15:08 +08:00
    至于 this 指向哪里,也是不确定的,要看具体环境。
    如果你直接运行一个方法,比如
    function foo(){
    console.log(this) // window
    console.log(this === window) // true
    }

    如果你运行一个 object 的方法,不如

    var obj = {
    foo : function(){
    console.log(this===obj)
    }
    }
    obj.foo() // true

    但是你可以使用 call 来指定 this,比如

    var obj = {
    foo : function(){
    console.log(this===myObj)
    }
    }

    var myObj = {}
    obj.foo.call(myObj) // true

    so...不知道你懂了没。
    FrankFang128
        18
    FrankFang128  
       2014-01-23 23:17:16 +08:00
    你理懂还有段距离吧。 建议你看看李战的《悟透 JS》http://www.cnblogs.com/leadzen/archive/2008/02/25/1073404.html
    是他的博客,后来编成书了。第一部分很不错,后面的就差一点。
    FrankFang128
        19
    FrankFang128  
       2014-01-23 23:24:04 +08:00
    我理解到现在的程度,用了大半年的时间,还是觉得没理解透啊。嘿嘿
    Tankpt
        20
    Tankpt  
    OP
       2014-01-23 23:26:07 +08:00
    @FrankFang128 嗯嗯。所以在试着整理一下自己理解的东西,准备写个文章,然后看看。嗯嗯。这个文章我有看过点,当时太长了,然后就放在了书签里==!
    otakustay
        21
    otakustay  
       2014-01-24 02:34:38 +08:00
    js找属性的逻辑很简单:
    1. 自己有没有这属性,有的返回
    2. 原型有没有这属性,有的返回
    3. 原型的原型有没有这属性,有的返回
    4. 如果原型的原型的...的原型不存在,返回个undefined

    js写属性的逻辑更简单:直接往上面写,和原型没关系

    然后你的代码:
    1. oTest1和oTest2共享一个原型,这个无误
    2. 在没有调用setName前,找oTest1的name找到的是原型上的,oTest2的name也是原型上的,他们一样
    3. oTest1.setName('hello')后,oTest1的name找的是自己的,这是上面说的步骤的第1步。而oTest2找name是原型上的,是上面的第2步。因此它们返回的东西不一样,且你能看到oTest1有name了

    至于你说的oTest1的name变了oTest2会变,这个显然是错的,根据上面找属性的步骤来看,oTest1.setName('hello')改的是oTest1上的东西(在原型的前面),导致原型上的那个name在oTest1读取时被“隐藏”起来了,但仅仅是隐藏,其值是没有被改动的。
    otakustay
        22
    otakustay  
       2014-01-24 02:40:56 +08:00   2
    至于this更简单,就是4种调用方法来决定this是啥
    我想说js其实没有任何难的,无论是this还是原型链还是作用域链,统统简单的要死,根本就是一帮半调子开发者自己玩不出来就到处说这难那难,把人吓得看一眼就觉得复杂没敢认真去理解
    给你看一下我讲课和技术分享时用的一些PPT:https://www.dropbox.com/sh/alem0za5y1y53j4/DpqaYZJeIn
    其中的Brief Javascript.pptx就是js的入门课程,虽然没有对应的讲述会稍微有些难理解,不过你可以看一下,关于原型、作用域、this在这里面都有讲到,我认为这个PPT上的东西都能理解的话,js就算入门了
    想继续深入的可以看所谓闭包.pptx,和闭包、作用域链有关的概念这里面应该全讲清楚了,再往下可以看Inside the browser.pptx和异步编程与浏览器执行模型.pptx,这是浏览器底下的知识了
    sd4399340
        23
    sd4399340  
       2014-01-24 10:15:08 +08:00
    otakustay
        25
    otakustay  
       2014-01-24 11:44:27 +08:00
    @sd4399340 我自己能打开,搞不懂dropbox,给一个skydrive的吧不过可能版本有些老,错误倒是没有:https://skydrive.live.com/redir?resid=556CE34496EA9887%21105
    FrankFang128
        26
    FrankFang128  
       2014-01-24 12:43:07 +08:00
    @otakustay 对啊,JS最难的地方其实在于“怎样用C++/Java/C#的思维方式来思考JS”,可惜很多半路转职的开发者在这一点上越陷越深。


    @Tankpt 不错,都是对的。不过我觉得图画得太复杂了。“对象的 __proto__ 指向它构造函数的 prototype”这一句话就能解释那几张图了。

    oTest1 是个对象,那么它的 __proto__ 指向构造函数 fTest 的 prototype;
    构造函数 fTest 的 prototype 也是个对象,其 __proto__ 指向构造函数 Object 的 prototype;
    依此类推。
    Tankpt
        27
    Tankpt  
    OP
       2014-01-24 14:17:17 +08:00
    @FrankFang128 恩恩。那个图么。就是感觉这么画出来直观点。我怕自己解释不清,哈哈。我刚又看了一遍你们的回复,明白多了。灰常感谢
    Tankpt
        28
    Tankpt  
    OP
       2014-01-25 16:26:47 +08:00
    @FrankFang128 不晓得论坛里怎么私信,今天对那个constructor对象有点疑问了,主要是在实现继承的过程中,发现contructor指向了父类的构造函数,这个从面向对象的理解上,我觉得没问题,就是在实现上有点疑惑,不晓得在哪里改动了,或者说这个constructor属性是在什么情况下会修改,就比如下面的这个段代码
    function SuperType(){
    this.name ="person";
    }

    SuperType.prototype.getname = function(){
    console.log(this.name);
    };

    function Subtype(){
    this.name = "man";
    }

    Subtype.prototype = new SuperType();

    在Subtype.prototype 中的constructor指向了SuperType,我的理解这个不是应该指向subtype么
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     910 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 45ms UTC 21:43 PVG 05:43 LAX 13:43 JFK 16:43
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86