有关 Javascript 中实例的 constructor 属性的问题。 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐关注
Meteor
JSLint - a Javascript code quality tool
jsFiddle
D3.js
WebStorm
推荐书目
Javascript 权威指南第 5 版
Closure: The Definitive Guide
haozhang
V2EX    Javascript

有关 Javascript 中实例的 constructor 属性的问题。

  •  1
     
  •   haozhang 2015-05-31 16:42:06 +08:00 4623 次点击
    这是一个创建于 3790 天前的主题,其中的信息可能已经有所发展或是发生改变。
    function A () {}; function B () {}; B.prototype = new A(); var a = new A(); var b = new B(); log(a.constructor); // A log(b.constructor); // A 

    js中实例是有一个constructor属性指向它的构造函数的。
    如上面的A类的实例a,它的constructor属性就指向A。
    但是当B继承了A之后,B的实例b的constructor指向的却不是B类,而是父类A。

    第 1 条附言    2015-05-31 19:58:18 +08:00
    很显然,js高级程序设计p145对于实例有个constructor属性的描述是错误的,这丫根就是原型上的,而不是实例自带的属性。
    48 条回复    2015-06-01 16:59:55 +08:00
    haozhang
        1
    haozhang  
    OP
       2015-05-31 16:46:25 +08:00
    markdown不起作用啊...谁教教我怎么搞。
    haozhang
        2
    haozhang  
    OP
       2015-05-31 16:50:55 +08:00
    原来还要选择markdown...不能改成默认的么...
    ceoimon
        3
    ceoimon  
       2015-05-31 16:58:30 +08:00   1
    constructor 属性是在构造函数的原型对象上的。你这里将 B 的原型对象赋值成一个 A 类实例,此时 B 的原型对象上的就没有 constructor,但继承了 A 的原型链 ,于是 log(b.constructor); 输出的是原型链上 A 的原型对象上的 constructor 即 A 函数。(好像有点乱,我也是个新手,个人是这么理解的。)
    haozhang
        4
    haozhang  
    OP
       2015-05-31 17:05:29 +08:00
    @ceoimon 我去...做了个实验,实例上没有constructor属性...《JS高程》上讲过实例有constructor属性的啊...xd你的理解是对的。
    Gem
        6
    Gem  
       2015-05-31 17:31:53 +08:00   1
    @haozhang 看这里,秘密花园 => https://bonsaiden.github.io/Javascript-Garden/zh/ ==>[对象.原型]
    haozhang
        7
    haozhang  
    OP
       2015-05-31 17:37:55 +08:00
    @Septembers 我讲constructor属性...你给的两个链接连毛的constructor都没见着..
    haozhang
        8
    haozhang  
    OP
       2015-05-31 17:40:23 +08:00
    @Gem 《js高程》的p145讲了实例也有constructor属性...但明显错了,原型对象才有constructor属性...实例的能访问constructor属性是因为原型上有...
    joeyzh
        9
    joeyzh  
       2015-05-31 17:48:02 +08:00
    haozhang
        10
    haozhang  
    OP
       2015-05-31 17:54:49 +08:00
    @joeyzh 看三楼的,因为原型对象是有constructor属性的,而实例没有。b.constructor是沿着原型链搜索到了A.prototype.constructor。
    joeyzh
        11
    joeyzh  
       2015-05-31 18:04:03 +08:00
    @haozhang 的确,学习了。
    哪个天煞的教材当时写了对象.constructor这样的东西!
    haozhang
        12
    haozhang  
    OP
       2015-05-31 18:04:48 +08:00
    @joeyzh 《js高级程序设计》...
    yangmls
        13
    yangmls  
       2015-05-31 18:08:48 +08:00
    引用一段来自 Backbone 的继承方法

    `
    function A () {};
    function B () {};

    var Surrogate = function(){ this.cOnstructor= B; };
    Surrogate.prototype = A.prototype;
    B.prototype = new Surrogate;

    var a = new A();
    var b = new B();
    console.log(a.constructor);
    console.log(b.constructor);
    `
    haozhang
        14
    haozhang  
    OP
       2015-05-31 18:10:54 +08:00
    @yangmls 干脆不管constructor...
    ibigbug
        15
    ibigbug  
       2015-05-31 18:14:10 +08:00
    B.prototype.cOnstructor= A

    直接覆盖原型的话,要修正一下这个属性
    joeyzh
        16
    joeyzh  
       2015-05-31 18:14:43 +08:00
    可以理解为是引用实例的构造函数的一种方式?
    yangmls
        17
    yangmls  
       2015-05-31 18:16:52 +08:00
    @haozhang 虽然这个属性没什么卵用,但我们还是应该尽量保持它的正确性,万一哪天你想在实例化以后修改原型链,可以用a. constructor.prototype.xxx = ... 来修改
    haozhang
        18
    haozhang  
    OP
       2015-05-31 18:18:30 +08:00
    @ibigbug 原型继承的话... B.prototype.constructor指向的不就是A么...
    haozhang
        19
    haozhang  
    OP
       2015-05-31 18:20:14 +08:00
    @yangmls 说的也是哦~
    haozhang
        20
    haozhang  
    OP
       2015-05-31 18:20:45 +08:00
    @joeyzh 嗯,就是找到这个对象对应的类是哪个,一般用于类型检验吧。
    CoooolChan
        21
    CoooolChan  
       2015-05-31 18:45:03 +08:00
    函数有prototype属性指向原型对象
    原型对象有constructor属性,指向构造函数
    实例内部有[[Protype]]指向原型对象
    实例的属性访问,先看实例自己有没有,有就返回,没有就沿着原型对象找
    A什么都没做,constructor指向A
    B的protype被重写了,此时指向的是A的实例,b.constructor先查实例,没找到,再查原型对象(A的实例),没找到,继续找A的原型对象,找到了A.cOnstructor=构造函数A
    ibigbug
        22
    ibigbug  
       2015-05-31 19:19:23 +08:00
    @haozhang 说错了。。 改成 B 。。
    haozhang
        23
    haozhang  
    OP
       2015-05-31 19:56:47 +08:00 via iPad
    @CoooolChan 是[[Prototype]]...
    Biwood
        24
    Biwood  
       2015-05-31 22:19:50 +08:00
    Javascript 中的所有对象都可以看作是 Object 对象的实例,因此所有对象都继承了 Object.prototype 上的方法和属性,所以,所有对象都有 constructor 这个属性,《Javascript 高级程序设计》这本书上的说法是没有问题的,实例也是对象,原型对象也是对象,凡是 Javascript 中的对象都有 constructor 属性,没有任何问题。
    FrankFang128
        25
    FrankFang128  
       2015-05-31 22:25:13 +08:00 via Android
    用 console.dir() 既知
    Mutoo
        26
    Mutoo  
       2015-05-31 22:36:32 +08:00
    话说原型的 constructor 这个属性有啥用?(然而并没有什么卵用。)
    haozhang
        27
    haozhang  
    OP
       2015-05-31 22:39:24 +08:00
    @Biwood 但是它没有指出这玩意是继承来的,而不是自带的...两个方式产生的结果完全不同,就像我一直以为这个属性是自带的,所以才会问出这个问题。
    haozhang
        28
    haozhang  
    OP
       2015-05-31 22:40:04 +08:00
    @Mutoo 用来对实例进行类型判断。
    Mutoo
        29
    Mutoo  
       2015-05-31 22:43:13 +08:00
    @haozhang 这东西没有 instanceof 靠谱吧,毕竟可以随意修改。
    Biwood
        30
    Biwood  
       2015-05-31 22:44:54 +08:00
    我用词有误, Object 是构造函数,不是对象,所有的构造函数都有 prototype 属性,prototype 里面存放的是构造函数创建的所有实例的原型对象,所有实例可以共享原型对象的属性 (properties) 和方法 (methods) ,constructor 就是所有实例共享的属性之一。
    Biwood
        31
    Biwood  
       2015-05-31 22:45:17 +08:00
    @haozhang 实例和实例的原型对象是紧密关联的,原型对象所拥有的,相对的实例也必然也有,所以不存在 constructor 属于哪一个的说法。
    如果按你的补充中的思路来看,Javascript 中只有 Object.prototype 有 constructor 属性,其他对象都只是继承而已。
    Biwood
        32
    Biwood  
       2015-05-31 22:58:26 +08:00
    所谓原型就是指一个对象最初始的样子,构造函数就像一台机器(或者说一个加工厂),不断的照着原型对象的样子来生产对象,生产出来的对象都是照着 [原型对象] 的模子做的,当你把 prototype 重写了,那么 [原型对象] 的模子也变了,所以构造函数会照着新的模子来生产对象,只不过这个新的模子也是照着另外的模子被生产出来的,所谓继承,就是这么实现。
    haozhang
        33
    haozhang  
    OP
       2015-05-31 23:00:34 +08:00
    @Biwood 你说了很多...但是这个问题仅仅就是constructor是继承来的还是自带的而已...来个方式带来两个结果...显然《js高程》的叙述并不严谨...
    Biwood
        34
    Biwood  
       2015-05-31 23:03:12 +08:00
    @haozhang 我说了很多是因为我也需要自己理清一下思路,之前我的理解并不算清晰,就着你的问题,我愿意捋一捋之前所学的知识 :)
    haozhang
        35
    haozhang  
    OP
       2015-05-31 23:04:36 +08:00
    @Biwood →_→ 我花了半年刷完《js高程》,然而并没有什么卵用...
    joyee
        36
    joyee  
       2015-05-31 23:07:15 +08:00
    因为

    function B () {}; // B.prototype.cOnstructor=== B
    B.prototype = new A(); // B.prototype.__proto__.cOnstructor=== A

    另外其实想知道每一步执行之后A和B的原型链都是什么模样可以用console啊……Chrome的不太好用,Firefox的可以看得很清楚,执行完之后点击A或者B看右边会列出所有的东西
    haozhang
        37
    haozhang  
    OP
       2015-05-31 23:10:47 +08:00
    @joyee 直接说constructor是继承来的不就得了...
    joyee
        38
    joyee  
       2015-05-31 23:14:33 +08:00
    @haozhang 其实我一直觉得在Javascript的范围里说继承是件挺奇怪的事……就是搭原型链而已,说继承联想到普通的OO反而更迷惑
    Biwood
        39
    Biwood  
       2015-05-31 23:18:40 +08:00
    @haozhang 继承来的也就是自带的不是么,总不能凭空出现吧,打个通俗的比喻,一个男孩继承了他父亲一样的棕色的眼镜,但这双眼睛是男孩自己的不是别人的,只是眼睛的颜色恰好跟他父亲的眼睛的颜色一样叫“棕色”。上面所说的 constructor 就是这个“棕色”一样的东西。
    haozhang
        40
    haozhang  
    OP
       2015-05-31 23:20:16 +08:00
    @joyee 因为习惯了继承...
    haozhang
        41
    haozhang  
    OP
       2015-05-31 23:21:38 +08:00
    @Biwood 不是这个意思,自带就像是function A(){},然后A就有prototype这个属性了。是语言设计层面的...
    joyee
        42
    joyee  
       2015-05-31 23:26:17 +08:00
    @haozhang 其实只要把 Javascript 的 OO 部分都看成在原型链这个“搜索路径”上做文章,然后知道 new 和创建函数会对这条搜索路径干什么事,各种引用是怎么搜索的,完全不要去联想 C++/Java 那种 OO,就一切都很自然……
    liebesbrief
        43
    liebesbrief  
       2015-06-01 01:24:15 +08:00
    昂......路.......表示题主头像是我做的哦............好欣慰~
    haozhang
        44
    haozhang  
    OP
       2015-06-01 10:16:58 +08:00
    @liebesbrief 我在lofter上看到的...直接盗用了...
    haozhang
        45
    haozhang  
    OP
       2015-06-01 10:19:43 +08:00
    @joyee 看看es6就知道js肯定会向Java那类语言的OO靠拢的...慢慢变成工业级语言...
    andy1987
        46
    andy1987  
       2015-06-01 16:31:37 +08:00
    Class.prototype.constructor
    joyee
        47
    joyee  
       2015-06-01 16:56:59 +08:00
    @haozhang ES6 的 class 本质还是 prototype,类似于 CoffeeScript 的 class,纯粹的语法糖……而且 TC39 也是倾向于只让它停留在语法糖的层面上,现在在 es-discuss 谈论给 Javascript 加上“真正的OO”的人基本都会被婊……
    kenshin
        48
    kenshin  
       2015-06-01 16:59:55 +08:00
    Javascript基于原型链的继承,B.prototype = new A(); 相当于 B.prototype.cOnstructor=== a.cOnstructor=== A

    所以:
    log(a.constructor); // A
    log(b.constructor); // A

    类似这种情况,需要手动改回B.property.constructor,即:B.prototype.cOnstructor= B

    但这种继承方式多了一步 new A(相当于做了2次new A),为了避免内存浪费,可以使用下面的方式:
    function A () {};
    function B () {};
    function F () {}; (空函数)

    F.prototype = A.prototype
    B.prototype = new F()
    B.prototype.cOnstructor= B

    var a = new A();
    var b = new B();

    log(a.constructor); // A
    log(b.constructor); // B

    PS,这种方式也有缺点(B只继承了A.prototype上的属性),如这样就有错误了:
    function A () {
    this.foo = "only a";
    };
    function B () {};

    function F () {};
    F.prototype = A.prototype
    B.prototype = new F()
    B.prototype.cOnstructor= B

    var a = new A();
    var b = new B();

    log(b.foo) //undefined


    更完美的方案,可以看看 CoffeeScript / TypeScript 生成的JS代码,很清晰。 :)
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     4833 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 26ms UTC 09:47 PVG 17:47 LAX 02:47 JFK 05:47
    Do have faith in what you're doing.
    ubao 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