首页 > 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)。


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

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


(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,没受到影响
    
})();

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


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


如果:

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();

就不会。

【热门文章】
【热门文章】