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 · 4263 次点击
    这是一个创建于 3464 天前的主题,其中的信息可能已经有所发展或是发生改变。
    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   ·   实用小工具   ·   1563 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 17:09 · PVG 01:09 · LAX 09:09 · JFK 12:09
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.