在《关于JavaScript中的继承(一):类式继承》 中已经基本上实现了类式继承,但仍然还存在一些问题,接下来对之前的实现进一步进行完善。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 var Parent = function (name) { this .name = name || 'heroic' ; }; Parent.prototype.print = function () { console .log('name: ' , this .name); }; var parent = new Parent();var Child = function (name) { Parent.apply(this , arguments ); }; Child.prototype = parent; Child.prototype.setChildAge = function (age) { this .age = age; }; parent.setChildAge(10 ); console .log(parent.age);
正如代码所见,在为子对象原型添加自己独有方法的时候,父对象也受到了影响,这可不是期望的结果。
1 2 3 4 5 var child = new Child('child' );console .log(child.name); delete child.name;console .log(child.name);
同样如代码所示,child
对象持有了两个name
属性,一个是通过构造函数拷贝的,另一个是原型链上的,当删除掉本身的name
属性后,便访问到了原型链上的了。对于这个问题,解决方案很简单直接:
1 2 3 4 5 Child.prototype = Parent.prototype; delete child.name;console .log(child.name);
但是这种方法也并没有解决最开始的那个问题,即添加或删除子对象原型上的属性时,会一并反映到父对象中。这个时候就需要用到《关于JavaScript中的继承(二):原型式继承》 中用到的临时构造函数了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 var inherit = function (subClass, superClass) { var F = function () {}; F.prototype = superClass.prototype; subClass.prototype = new F; }; inherit(Child, Parent); Child.prototype.setChildAge = function (age) { this .age = age; }; parent.setChildAge(10 );
不再影响父对象的行为了,而且还可以为inherit
方法增加子对象访问父对象行为的特性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 var inherit = function (subClass, superClass) { var F = function () {}; F.prototype = superClass.prototype; subClass.prototype = new F; subClass.prototype._super = superClass.prototype; }; Child.prototype.print = function () { console .log('before print...balabala...' ); this ._super.print.call(this ); }; var child = new Child('child' );child.print();
最后,如果说这个类式继承模式还有哪点不够完美的话,那就是在子对象继承父对象之后,子对象的构造函数指向被改写了。
1 console .log(child.constructor === Parent);
没办法,只有在继承的最后,把constructor
修正回来就是。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 var inherit = function () { var F = function () {}; return function (subClass, superClass) { F.prototype = superClass.prototype; subClass.prototype = new F; subClass.prototype._super = superClass.prototype; subClass.prototype.constructor = subClass; }; }(); console .log(child.constructor === Child); console .log(child.constructor === Parent);
同时,也如《关于JavaScript中的继承(二):原型式继承》 中提到的那样,利用闭包来减少每次调用inherit()
都会生成一个临时构造函数的开销。
写在最后,类式继承为我们带来了JavaScript中不存在的完整的类的概念,这对于从面向对象语言转过来的程序员来说,可能是很好的方式。但是它也有可能让我们忽略了JavaScript真正的原型式继承。不过这些模式都没有好与坏之分,应该在适合的场景使用合适的方法才是。