search

Home  >  Q&A  >  body text

javascript面向对象 - JavaScript 继承问题

function Rectangle(width, height){
    this.width = width;
    this.height = height;
}

Rectangle.prototype.getArea = function(){
    return this.width * this.height;
}

function Square(size){
    this.width = size;
    this.height= size;
}

// Square 继承 Rectangle
Square.prototype = new Rectangle();
Square.prototype.constructor = Square;

var square = new Square(5);

console.log(square instanceof Square); // true;
console.log(square instanceof Rectangle); // true
console.log(square.getArea());

为什么 Square 继承 Rectangle 时,不是用 Square.prototype 指向 Rectangle.prototype,而是指向 Rectangle 的对象实例?(而实际上继承就是用 Square.prototype 指向 Rectangle.prototype)。

PHPzPHPz2902 days ago326

reply all(5)I'll reply

  • 怪我咯

    怪我咯2017-04-10 16:55:06

    如果:

    Square.prototype = Rectangle.prototype;

    那么对Square.prototype.getArea的修改就会影响到Rectangle.prototype.getArea;

    Square.prototype.getArea = function() {return "new";};
    Rectangle.prototype.getArea // => function() {return "new";};
    Square.prototype = new Rectangle();

    就不会。

    reply
    0
  • 阿神

    阿神2017-04-10 16:55:06

    prototype实现继承的方式:
    js中每个类都有prototype方法,他是一个原型链,如果本对象上找不到相关的方法,就会沿着这个原型链向父类,祖父类中找相关方法。
    所以如果a类的prototype指向b类的实例,当a类中调用继承b类的方法的时候就会沿原型链找到b类对应的方法。
    这个时候如果你把a类的prototype直接指向了b类的prototype,a类就找不到b类的方法,他去b类的父类找方法去了。

    reply
    0
  • 天蓬老师

    天蓬老师2017-04-10 16:55:06

    可以参考《JavaScript高级程序设计》 P.163页所描述的:继承是通过将SuperType的实例赋给SubType.prototype实现的。实现的本质是重写原型对象,代之以一个新类型的实例。换句话说,原来存在于SuperType的实例中的所有方法和属性,现在也存在于SubType.prototype中了。

    在你给出的例子中,可以这样理解:
    Square.prototype = new Rectangle() //重写Square的原型对象(如果你不指定它的原型对象,Square这个构造函数的原型对象就是Object)为Rectangle的实例,这时Square就继承了这个实例的属性和方法(这里只继承了属性)
    ,而getArea()这个方法是从Rectangle这个实例的原型中继承的。

    reply
    0
  • 伊谢尔伦

    伊谢尔伦2017-04-10 16:55:06

    (function () {
        var cl = console.log;
    
        //原型继承的问题:
        function Animal() {
            this.isAnimal = true;
            this.colors = [];
        };//父类
        function Cat() {
            this.isCat = true;
        };//子类
        Cat.prototype = Animal.prototype; //这样可以符合继承的要求吗?
        //不可以,看下面代码:
        var cat1 = new Cat();
        cl(cat1.isAnimal);//undefined  猫也是动物啊,但是没有继承到Animal的isAnimal属性,继承失败
        
        Cat.prototype = Animal; //这样可以符合继承的要求吗?
        //不可以,看下面代码:
        var cat2 = new Cat();
        cl(cat2.isAnimal);//undefined 理由同上
        
        Cat.prototype = new Animal(); //这样呢?
        //相当于var cat3 = Object.create(new Animal());//这么写的话就不用声明Cat类了,最终结果是一致的
        //基本上可以了
        var cat3 = new Cat();
        cl(cat3.isAnimal);//true 继承过来了
        var cat4 = new Cat();
        cat3.isAnimal = false;
        cat3.isCat = false;
        cl(cat4.isAnimal, cat4.isCat);//还是true,没有被cat3影响到
        //问题还是有的,就怕父类的某个属性是引用类型
        cat3.colors.push('yellow');
        cl(cat4.colors);// [ 'yellow' ] 被影响到了!
        //这个问题暂时不管
        
        //上面的Animal类把属性声明放在构造函数里,这意味着Animal的属性和Animal.prototype一点关系都没有
        //那么试试这样呢:
        function Animal2() { };
        Animal2.prototype.isAnimal = true;//把属性直接放在Animal2的原型上,因此它就有可能被继承了
        Cat.prototype = Animal2;
        var cat5 = new Cat();
        cl(cat5.isAnimal);//undefined 还是不行
        Cat.prototype = Animal2.prototype;
        var cat6 = new Cat();
        cl(cat6.isAnimal);//true 可以了!
        //不过,Cat的原型和父类Animal2的原型混同之后,一个大问题是对Cat增加属性和方法会影响父类:
        Cat.prototype.sound = 'meeeeeeeww';
        //因此也会影响其他继承自父类的子类:
        function Dog() { }
        Dog.prototype = Animal2.prototype;
        var dog = new Dog();
        cl(dog.sound);//'meeeeeeeww'
        //这种情况下,Cat和Dog就没有区别了,声明Dog类是毫无意义的
        //上面为了增强Cat,把sound属性放在Cat的原型上,这同时就污染了父类的原型
        //为了防止污染,可不可以规定sound必须放在Cat的构造函数里呢:
        function Animal3() { };
        function Cat2() {
            this.sound = 'meeeeeeeww';
        }
        Cat2.prototype = Animal3.prototype;
        //上面的Cat2类的sound属性不在原型上,所以不会影响到其他子类了。
        //但是这么做,意味着Cat2所有自定义的属性和方法都要声明在构造函数里,这些属性和方法也无法被子类继承
        //例如,猫还可以有子类“波斯猫”
        function PersianCat() { }
        PersianCat.prototype = Cat2.prototype;
        var pcat = new PersianCat();
        cl(pcat.sound);// undefined 这是显然的
        //综上,使用子类.prototype = 父类.prototype来实现继承,陷阱太多,不好
        //因此最终结论是,使用子类.prototype = new 父类()更好
        //这种方法,可以形成继承链:
        function Super() { }
        Super.prototype.superName = { name: 'super' };
        function Sub() { }
        Sub.prototype = new Super();
        Sub.prototype.subName = { name: 'sub' };
        function Child() { }
        Child.prototype = new Sub();
        var child = new Child();
        cl(child.superName, child.subName);//super sub
        //对同一层次的子类不会互相影响
        function Sub2() { }
        Sub2.prototype = new Super();
        var sub2 = new Sub2();
        cl(sub2.superName, sub2.subName);//super undefined -----ok,没受到影响
        
    })();

    reply
    0
  • 高洛峰

    高洛峰2017-04-10 16:55:06

    如果使用 Square.prototype.constructor = Square; 那么Square.prototype就没有了 width/height 属性,Square对象也就继承不了width/height

    reply
    0
  • Cancelreply