javascript - js constructor问题?
阿神
阿神 2017-04-10 15:18:41
[JavaScript讨论组]
function Father(){
 console.log('father');
}

function Sub(){
  console.log('sub');
}

Sub.prototype = Object.create(Father.prototype);
Sub.prototype.constructor = Sub;    //为什么实现继承必须要有这句?没有这句会产生什么后果?

我知道最简单的后果就是Sub.prototype.constructor === Father(){...},然而知道这个貌似也没什么用。

阿神
阿神

闭关修行中......

全部回复(1)
怪我咯

你认为Sub.prototype === Father(){...},是不对的,关于JavaScript的面向对象,具体来说是原型这种设计,有几个点需要注意。
先说结论:那一句只是为了指明constructor,如果你不用constructor的话,这确实没什么用。
不过你说的

我知道最简单的后果就是Sub.prototype === Father(){...},然而知道这个貌似也没什么用。

是不对的。
你可以亲自试验一下


function Father(){ console.log('father'); } function Sub(){ console.log('sub'); } Sub.prototype = Object.create(Father.prototype); //并没有修改Sub.prototype.constructor //Sub.prototype.constructor = Sub; var sub = new Sub(); console.log(sub.constructor); //Father console.log(Sub.prototype); //Object { } console.log(Sub.prototype === Father); //false console.log(Sub.prototype === Father.prototype); //false console.log(Sub.prototype.__proto__ === Father.prototype); //true //下面两行说明JavaScript对对象所属的“类”的判断并不依赖于constructor属性 console.log(sub instanceof Sub); //true console.log(sub instanceof Father); //true console.log(sub instanceof Object); //true

题主需要重点了解一下Object.create方法,以及JavaScript的原型链。
特别是区分对象的原型以及构造函数的prototype属性。

另外建议题主把《JavaScript高级程序设计》的第六章再仔细读一读, 尤其注意分析其中的配图。


下面详细说一说:

  1. JavaScript中对象的构建方式
    使用字面量构建的对象其原型是Object.prototype,使用Object.create(proto[, propObj])所构建的对象的原型是你所提供的proto,而是用构造函数创建的对象其原型即为构造函数的prototype属性指向的对象。下面提供几段代码以供实验:

    • 使用对象字面量
    var s = {};
    console.log(s instaceof Object); //true
    console.log(s.constructor === Object); //true
    console.log(s.__proto__ === Object.prototype); //true
    

    我看你在另一个答案下的评论提到了__proto__是非标准实现,确实是这样的,但是这是目前各个浏览器都采用了的方案,Node.js内部也是使用这种实现的。而且__proto__确实是窥探对象原型(标准中指定的[[Prototype]])的一种方式。

    • 使用Object.create()
    var apple = { color: 'red' };
    var fuji = Object.create(apple, { price: {value: 15, configurable: true, enumerable: true, writable: true} });
    console.log(fuji.hasOwnProperty('price')); //true
    console.log(fuji.__proto__ === apple); //true
    

    这说明Object.create(proto[, propObj])为新对象添加了propObj中指定的属性,还指定了proto为其原型。

    • 使用构造函数
    function Fruit(name, price) {
        this.name = name;
        this.price = price;
    }
    Fruit.prototype.sale = function () {
        console.log(name + ' is $' + price);
    };
    
    var fruit = new Fruit('banana', 20);
    fruit.sale(); //banana is $20
    console.log(fruit instanceof Fruit); //true
    console.log(fruit.__proto__ === Fruit.prototype); //true   
    

    这说明在使用new创建对象时,新对象作为this传入构造函数中,并且新对象的__proto__属性(标准中的[[Prototype]]内部属性)被置为Fruit.prototype,也就是构造函数的prototype属性。

    这里需要注意区别构造函数的prototype属性,以及构造函数本身作为一种对象(函数也是一种对象)它自己的原型[[Prototype]]。构造函数的prototype属性就是为了指定新对象的原型而存在的,而与函数本身没有太大关系。函数本身的[[Prototype]]可以通过Fruit.__proto__这种形式窥探到,它实际上是Function.prototype。

  2. JavaScript中instanceof的工作方式
    instanceof运算符(obj instanceof Constructor)会以obj为参数调用Constructor的[[HasInstance]]内部方法,这个方法的操作就是循着左侧操作符的原型链检查是否有与Constructor.prototype相同的原型,如果有就返回true,否则返回false。
    例如:

    function C1() {
    }
    C1.prototype = { old: 1 };
    var c1 = new C1();
    console.log(c1 instaceof C1); //true
    console.log(c1.__proto__ === C1.prototype); //true
    //下面改变函数C1的prototype的指向
    C1.prototype = { new: 2 };
    console.log(c1 instanceof C1); //false
    console.log(c1.__proto__ === C1.prototype); //false
    console.log(c1.__proto__); //{ old: 1 }
    console.log(C1.prototype); //{ new: 2 }
    
  3. JavaScript的原型继承

    JavaScript的继承采用了原型链这种方式,但又加入了模仿Java、C++中“类”这种概念的成分,因此出现了一些比较令人纠结的概念。
    在1.中提到了要区别构造函数的prototype属性以及函数的原型[[Prototype]]、对象的原型这些概念,一旦理清这几个概念,JavaScript的原型继承也就非常清楚了。

    function Fruit(name, price) {
        this.name = name;
        this.price = price;
    }
    //函数的默认prototype属性中是有constructor属性的(值为函数本身),这是由JavaScript解析器做好的
    console.log(Fruit.prototype.constructor); //Fruit
    
    Fruit.prototype.sale = function () {
        console.log(name + ' is $' + price);
    };
    
    function Apple(name, price, color) {
        Fruit.call(this, name, price);
        this.color = color;
    }
    
    Apple.prototype = Object.create(Fruit.prototype);
    console.log(Apple.prototype === Fruit.prototype); //false, 前面已经说了,Object.create(proto)函数是创建一个以proto为的对象
    
    //前面修改了Apple.prototype的指向,默认的prototype对象已经丢失了, 新建立的对象并没有指定constructor
    console.log(Apple.prototype.hasOwnProperty('constructor')); //false
    console.log(Apple.prototype.__proto__.hasOwnProperty('constructor')); //true, Apple.prototype.__proto__就是Fruit.prototype
    
    //暂时不指明Apple的构造函数, 直接创建新对象
    var fuji = new Apple('fuji', 20);
    
    console.log(fuji.constructor); //Fruit
    //来看一看constructor到底是谁的属性
    console.log(fuji.hasOwnProperty('constructor')); //false
    console.log(fuji.__proto__.hasOwnProperty('constructor')); //false   fuji.__proto__就是Apple.prototype, 我们并没有给Apple.prototype指定constructor属性,因此这里也是false
    console.log(fuji.__proto__.__proto__.hasOwnProperty('constructor')) //true  fuji.__proto__.__proto__就是Apple.prototype.__proto__,也就是Fruit.prototype
    
    //现在指定Apple的构造函数
    Apple.prototype.constructor = Apple;
    console.log(fuji.constructor); //Apple
    console.log(fuji.hasOwnProperty('constructor')); //false
    console.log(fuji.__proto__.hasOwnProperty('constructor')); //true
    

    把上面的实验操作一遍, 仔细分析结果, 应该就可以理清JavaScript的原型链与构造函数, 构造函数的prototype之间的关系了。

热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号