V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
83f420984
V2EX  ›  程序员

请教下关于 JavaScript 原型对象理解

  •  
  •   83f420984 · 2015-07-14 22:12:03 +08:00 · 2363 次点击
    这是一个创建于 3401 天前的主题,其中的信息可能已经有所发展或是发生改变。
    当我创建一个 Foo( ) 函数时,它就一个 prototype 属性,并指向一个对象,则 Foo prototype 原型对象,这个原型对象默认有一个 constructor 属性,并指向 Foo( ) 函数本身。

    问题一:这个 Foo( ) 函数怎么会存在 prototype 属性的同时,还有一个 Foo prototype 原型对象?
    问题二:这个 constructor 属性在这里面到底起的是什么作用?

    以上有表达有不对的或者其它地方问题的,请谅解,被 prototype 绕晕了,谢谢。
    6 条回复    2015-07-15 10:55:46 +08:00
    FrankFang128
        1
    FrankFang128  
       2015-07-14 22:21:54 +08:00 via Android   ❤️ 1
    每个对象的 __proto__ 属性指向其构造函数的 prototype 属性
    方法 function 是一种特殊的对象 Object

    琢磨这两句话

    constructor 的作用就是指向构造函数。
    买本美国人写的JS书看看吧。
    YuJianrong
        2
    YuJianrong  
       2015-07-14 22:53:11 +08:00   ❤️ 3
    1. 所有函数都是对象,和其他对象区别在于有一个内部成员 [[call]],你调用函数的时候其实就是调用这个成员
    2. 所以函数上可以挂任何成员,prototype 是其中之一,一般会挂上一个对象,这时候就叫prototype 原型对象(所以问题一是这是一样的)
    3. 当你调用 new FuncA() 的时候,其实做了这些事情
    a. 生成一个空对象 {}
    b. 设置这个空对象的内部成员 [[prototype]] 为 FuncA.prototype ,这个内部成员在v8中是可以访问的__proto__
    c. 以这个空对象为 this 调用 FuncA, 即 FuncA.call(this, xxx)
    4. 至于 FuncA.prototype.constructor, 对于一个函数的缺省 prototype来说就是函数本身,即
    FuncA.prototype.constructor === FuncA
    不过这个其实几乎没有任何用处,你大可忽视这个东西。

    盗张图:
    http://www.codeproject.com/KB/scripting/687093/PrototypeGraph.png
    Biwood
        3
    Biwood  
       2015-07-15 00:03:32 +08:00   ❤️ 2
    一、为什么函数都有一个原型对象?
    因为 JavaScript 里面的任何一个函数都可以作为构造函数使用,所谓构造函数,就是用来构造对象用的函数,使用 new Foo() 操作就能生成一个新的对象,这个新的对象是基于 Foo() 函数的原型对象来生成的,这就是原型对象存在的意义。构造函数在构造一个新对象的过程中需要有一个原型来做参考,这个原型就是构造函数的原型对象。

    二、这个 constructor 属性起什么作用?
    constructor 翻译过来就是构造器,在这里就是指 new 出来的新对象的构造函数,一般我们通过新对象的 constructor 属性可以找到是哪个函数构造了它,这就是 constructor 属性的作用。原型对象上之所以有 constructor 属性,是因为这个属性要继承给新生成的对象,以方便能够找到当前这个构造函数。
    an168bang521
        4
    an168bang521  
       2015-07-15 00:54:30 +08:00   ❤️ 2
    [问题一] :这个 Foo( ) 函数怎么会存在 prototype 属性的同时,还有一个 Foo prototype 原型对象?
    答:这个要从继承方面来理解的,Jquery就是通过这个继承来搞的,而且这么模式有良好的可扩展性;我的理解是prototype存在的意义就是为了原型链继承;传统的单例模式,工厂模式,就不说了,这个你应该理解的;因为工厂模式,得到的数据,都是一样的,不利于保护私有变量;所以引发出了原型链继承这种模式,这种模式,不仅可以有公有的,也有私有的;Foo这个是属于构造函数,Foo相当于自然界中的类;假设f1,和f2是Foo这个构造函数的两个实例;f1和f2会继承Foo本身包含的字符串;这些继承过来的东西都是私有的,虽然f1和f2里面的内容一样,但是他们并不想等;f1.a!==f2.a;举个例子,就好比我们俩都属于人类(人类相当于构造Foo函数),我们俩都继承了人类这个类的特征,我有一个鼻子两只手,你也有一个鼻子两只手;但是我的手不等于你的手;当然f1和f2也要有相同的属性;这个就是prototype出现的原因,这里还有了解一下,每一个对象都有__proto__这个属性,f1和f2是Foo的是例,函数派生自Object,所以函数上也有__proto__的;换句话说,Foo除了有prototype外,还有__proto__这个属性;在f1和f2的实例中查找不到的时候,会通过f1.__proto__来查询。f1.__proto__指向Foo.prototype开辟的那个内存地址;f1.__proto__===Foo.prototype;同样f2__proto__===Foo.prototype;如果在Foo.prototype定义了一个b方法;f1.__proto__.b===f2.__proto__.b;然后再说下查找顺序。这个如果foo.prototype也没有,会一直找的,直到找到Object这个基类上;如果Object的原型也没有。那就报错了。你也可以在控制台输出console.dir(f1),你把f1详细输出看下,就知道他们是怎么回事了;如果想在Foo扩展公有的东西,可以Foo.prototype={ XX:xx,AA:aa,BB:bb}这样写;
    但是上面扩展的虽然可以用,但是有一个例外,就是contructor属性不再指向Foo了;这是因为写函数.prototype的时候,本质上重写了默认的prototype对象;因此 constructor属性也就变成了新对象的 constructor属性,指向Object构造函数了,不再指向Foo;原理就不再继续说了,因为我好不容易才扯到第二个constructor上来的,扯多了就跑远了;

    [问题] 二:这个 constructor 属性在这里面到底起的是什么作用?
    好吧终于从问题一引申到问题二了,这个比较好理解,就是起到当默认指向的改变时候,向上面讲的那个情况下,可以强制写回来;上面的写法可以这样改进:
    Foo.prototype={ constructor:Foo,XX:xx,AA:aa,BB:bb}这样就妥妥的了;
    ariestiger
        5
    ariestiger  
       2015-07-15 01:23:13 +08:00
    有没有兴起了解一下这门语言?http://iolanguage.org/
    83f420984
        6
    83f420984  
    OP
       2015-07-15 10:55:46 +08:00
    @FrankFang128
    @YuJianrong
    @Biwood
    @an168bang521

    谢谢老兄耐心的解答,解决了我心中疑问 :)
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3162 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 35ms · UTC 12:32 · PVG 20:32 · LAX 04:32 · JFK 07:32
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.