JavaScript 继承:当构造函数有参数时
Posted
技术标签:
【中文标题】JavaScript 继承:当构造函数有参数时【英文标题】:JavaScript inheritance: when constructor has arguments 【发布时间】:2011-11-23 21:52:00 【问题描述】:使用纯javascript做继承,这是我平时做的:
function A()
A.prototype.run = function () ;
function B()
B.prototype = new A;
B.prototype.constructor = B;
由于没有要传递给构造函数的参数,因此 new A 没有什么可抱怨的。现在,如果构造函数有要传递的参数,我还没有找到一个很好的继承方法。例如,
function A(x, y)
A.prototype.run = function () ;
function B(x, y)
B.prototype = new A;
B.prototype.constructor = B;
我可以传递一些任意值,例如:
B.prototype = new A(null, null);
在某些情况下,我可能需要在 A 的构造函数中验证 x 和 y。在某些极端情况下,我需要在检查 x 或 y 时抛出错误。那么,B 就无法使用新的 A 从 A 继承。
有什么建议吗?
谢谢!
【问题讨论】:
【参考方案1】:虽然这是一个老话题,但我想我还是会回应的。 两种方法:
虽然伪经典方式是最流行的,但它也有它的缺点,因为它需要在子构造函数中调用一次父构造函数,并且在继承原型时调用一次。此外,孩子的原型将包含父构造函数的所有属性,当调用子构造函数时,这些属性无论如何都会被覆盖。我个人的选择是原型继承。
1。伪经典继承:
function A(x, y)
A.prototype.run = function () ;
function B(x, y)
A.call(this,x,y);
B.prototype = new A();
B.prototype.constructor = B;
2。原型继承:
function A(x, y)
A.prototype.run = function () ;
function B(x, y)
A.call(this,x,y);
B.prototype = Object.create(A.prototype);
B.prototype.constructor = B;
【讨论】:
我是 JS 新手,能否请您解释一下如何使用它以及调用构造函数的顺序?我将它与构造函数一起使用? @Chugaister,我不确定我是否完全理解你的问题。在上面的两个示例中,函数 A 和 B 都是构造函数。 B 是从 A 继承的构造函数。您可以使用这些构造函数来创建实例,如下所示: var a = new A(); var b = new B(); 这非常有用。感谢简单优雅的代码sn-ps进行对比。【参考方案2】:考虑一下:
function B( x, y )
var b = Object.create( new A( x, y ) );
// augment b with properties or methods if you want to
return b;
然后
var b = new B( 12, 13 );
现在b
继承自A
的一个实例,而后者又继承自A.prototype
。
现场演示: http://jsfiddle.net/BfFkU/
Object.create
没有在 IE8 中实现,但是可以很容易地手动实现它:
if ( !Object.create )
Object.create = function ( o )
function F()
F.prototype = o;
return new F();
;
这可以放在 ie8.js
文件中,该文件仅通过条件 cmets 为 IE8 及以下版本加载。
【讨论】:
@Grace 是的,Object.create
是 ES5。在此处查看有关浏览器中实现级别的信息:kangax.github.com/es5-compat-table
您可以在developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…找到更多关于对象创建的信息【参考方案3】:
问题是您不能轻易地为B
创建原型对象,因为无法调用A
的构造函数。这是因为在执行new B
之前,构造函数的参数是未知的。您需要一个虚拟构造函数来为B
构造一个原型,该原型链接到A
的原型。
B.prototype = (function(parent)
function protoCreator();
protoCreator.prototype = parent.prototype;
// Construct an object linking to A.prototype without calling constructor of A
return new protoCreator();
)(A);
一旦你设置好了B
的原型对象,你需要确保在B
的构造函数中调用A
的构造函数。
function B(x, y)
// Replace arguments by an array with A's arguments in case A and B differ in parameters
A.apply(this, arguments);
您现在应该可以通过调用 new B(x, y)
来实例化 B
。
如需在A
中包含参数验证的完整内容,请参阅a jsFiddle。
在您的原始代码中,您正在设置B.prototype.constructor = B
。我不明白你为什么要这样做。 constructor
属性不影响prototype
属性负责的继承层次结构。如果你想在 constructor
属性中包含命名构造函数,你需要从上面扩展一点代码:
// Create child's prototype – Without calling A
B.prototype = (function(parent, child)
function protoCreator()
this.constructor = child.prototype.constructor
;
protoCreator.prototype = parent.prototype;
return new protoCreator();
)(A, B);
使用B.prototype
的第一个定义,您会得到以下结果:
var b = new B(4, 6);
b.constructor // A
console.info(b instanceof A); // true
console.info(b instanceof B); // true
使用extended version,您将获得:
var b = new B(4, 6);
b.constructor // B
console.info(b instanceof A); // true
console.info(b instanceof B); // true
不同输出的原因是instanceof
跟踪b
的整个原型链并尝试为A.prototype
或B.prototype
(在另一个调用中)找到匹配的原型对象。 b.constructor
原型确实是指用于定义实例原型的函数。如果您想知道为什么它不指向protoCreator
,这是因为它的原型在创建B.prototype
期间被A.prototype
覆盖。 the updated example 中显示的扩展定义将 constructor
属性修复为指向更合适(因为可能更符合预期)的功能。
对于日常使用,我建议完全放弃使用实例的constructor
属性的想法。请改用instanceof
,因为它的结果更容易预测/预期。
【讨论】:
感谢您的回答!我想我有同样的问题要问你。 .我想知道为什么不使用调解器就不能直接执行此操作: function A() ; A.prototype.run = function () ;函数 B() ; B.prototype = A.prototype; B.prototype.constructor = B; 如果您设置B.prototype = A.prototype
,您将失去向B
实例添加方法或属性的能力,而A
实例中不存在它们。您的分配将导致A
和B
的实例共享同一个原型——原型是实例的属性和方法的来源。
我之所以使用B.prototype.constructor = B
,是因为B.prototype = new A
之后,B的构造函数实际上指向了A,这样我就无法扩展B的构造函数了. 实际上你的扩展例子也说明了这个问题。【参考方案4】:
好吧,如果你想让B.prototype
继承自A.prototype
的对象,而不执行A
构造函数,为了避免所有可能的副作用,你可以使用一个虚拟构造函数来完成它,例如:
function tmp()
tmp.prototype = A.prototype;
B.prototype = new tmp();
B.prototype.constructor = B;
你可以创建一个函数来封装这个新对象的创建逻辑,例如:
function inherit(o)
function F() ; // Dummy constructor
F.prototype = o;
return new F();
//...
B.prototype = inherit(A.prototype);
B.prototype.constructor = B;
如果您以现代浏览器为目标,您可以使用 ECMAScript 5 Object.create
方法实现相同目的,例如:
B.prototype = Object.create(A.prototype);
B.prototype.constructor = B;
//..
【讨论】:
我认为副作用是 OP 所希望的。 "在某些情况下,我可能需要在 A 的构造函数中验证 x 和 y。" @patrickdw 可能是我理解错了,但是当那些验证抛出错误时我认为他有问题 “在某些极端情况下,我需要在检查 x 或 y 时抛出错误。那么,没有办法让 B 使用 new A 从 A 继承。”,我认为他正在寻找一种方法来避免在分配B.prototype
对象时执行 A 构造函数...
是的,我想多了,我认为你是对的。虽然看起来A
中的验证可能需要应用于B
构造的对象,但这只是一个附带问题。为了避免将new A
分配给B.prototype
时出现验证问题,这是有道理的。
@CMS 谢谢你的回答。我想知道为什么我不能这样做:function A() ; A.prototype.run = function () ;函数 B() ; B.prototype = A.prototype; B.prototype.constructor = B;
@GraceShao 不客气,如果您这样做 A.prototype
和 B.prototype
将引用同一个对象,您可以添加到 B.prototype
的任何特定属性将在 A.prototype
和两个构造函数的实例都将继承它们。创建一个继承自A.prototype
的对象可以解决这个问题。此外,您可能希望在B
中运行A
构造函数,也许是为了共享相同的初始化逻辑,如果两个构造函数共享相同的参数,您可以在B
中执行A.apply(this, arguments);
,否则,获取必要的参数A
,你可以使用A.call(this, arg1, arg2);
以上是关于JavaScript 继承:当构造函数有参数时的主要内容,如果未能解决你的问题,请参考以下文章
34JavaScript面向对象(内置构造函数&相关方法|属性|运算符&继承&面向对象)