我是一个长期浏览者,但第一次参与。如果我遗漏了任何礼仪细节,请告诉我!
此外,我已经搜索了高低,包括这个网站,但我还没有找到关于我想要做什么的清晰简洁的解释。如果我错过了,请指出正确的方向!
好的,我想扩展一些原生 JavaScript 对象,例如 Array 和 String。但是,我不想实际扩展它们,而是创建从它们继承的新对象,然后修改它们。
对于数组,这有效:
var myArray = function (n){
this.push(n);
this.a = function (){
alert(this[0]);
};
}
myArray.prototype = Array.prototype;
var x = new myArray("foo");
x.a();
但是,对于 String,同样不起作用:
var myString = function (n){
this = n;
this.a = function (){
alert(this);
};
}
myString.prototype = String.prototype;
var x = new myString("foo");
x.a();
我也试过:
myString.prototype = new String();
现在,在尝试对此进行研究时,我发现这确实有效:
var myString = function (n){
var s = new String(n);
s.a = function (){
alert(this);
};
return s;
}
var x = myString("foo");
x.a();
然而,这对我来说几乎感觉像是“作弊”。就像,我应该使用“真正的”继承模型,而不是这个捷径。
所以,我的问题:
1)你能告诉我关于从String继承我做错了什么吗? (最好有一个工作示例......)
2)在“真正的”继承示例和“快捷方式”示例之间,您能说出一种方式相对于另一种方式的任何明显的好处或坏处吗?或者也许只是在一个功能上如何操作另一个方面的一些差异? (因为它们在我看来最终是一样的......)
谢谢大家!
编辑:
感谢所有评论/回答的人。我认为@CMS 的信息是最好的,因为:
1)他回答了我的字符串继承问题,指出通过在我自己的字符串对象中部分重新定义一个字符串,我可以使它工作。 (例如覆盖 toString 和 toValue)
2) 创建一个继承自 Array.
从以上两件事,我得出结论,JavaScript 的可继承性声明仅适用于您自己创建的对象,并且当涉及到 native 对象时,整个模型就会崩溃。 (这可能就是为什么您找到的 90% 的示例是 Pet->Dog 或 Human->Student,而不是 String->SuperString)。 @chjj 的回答可以解释这些对象实际上是原始值,即使 JS 中的所有内容似乎都是一个对象,因此应该是 100% 可继承的。
如果这个结论完全错误,请纠正我。如果它是准确的,那么我相信这对除了我自己以外的任何人来说都不是新闻——但再次感谢大家的评论。我想我现在有一个选择:
要么继续使用寄生继承(我现在知道名称的第二个例子),并尽可能减少它对内存使用的影响,要么做类似@davin、@Jeff 或 @chjj 建议的事情,要么 psudo-redefine 要么完全为自己重新定义这些对象(这似乎是一种浪费)。
@CMS - 将您的信息编译成答案,我会选择它。
最佳答案
这样做的痛苦简单但有缺陷的方法是:
var MyString = function() {};
MyString.prototype = new String();
您要求的内容很奇怪,因为通常在 JS 中,您不会将它们视为字符串对象,而是将它们视为“字符串”类型,即原始值。此外,字符串根本不可变。您可以通过指定
.toString
让任何对象像字符串一样工作。方法:var obj = {};
obj.toString = function() {
return this.value;
};
obj.value = 'hello';
console.log(obj + ' world!');
但显然它不会有任何字符串方法。您可以通过几种方式进行继承。其中之一是javascript应该使用的“原始”方法,您和我在上面发布了该方法,或者:
var MyString = function() {};
var fn = function() {};
fn.prototype = String.prototype;
MyString.prototype = new fn();
这允许在不调用构造函数的情况下添加到原型(prototype)链。
ES5 的方式是:
MyString.prototype = Object.create(String.prototype, {
constructor: { value: MyString }
});
非标准但最方便的方法是:
MyString.prototype.__proto__ = String.prototype;
所以,最后,你可以做的是:
var MyString = function(str) {
this._value = str;
};
// non-standard, this is just an example
MyString.prototype.__proto__ = String.prototype;
MyString.prototype.toString = function() {
return this._value;
};
继承的字符串方法可能使用该方法工作,我不确定。我认为他们可能是因为有一个 toString 方法。这取决于任何特定的 JS 引擎如何在内部实现它们。但他们可能不会。你必须简单地定义你自己的。再一次,你所要求的很奇怪。
您也可以尝试直接调用父构造函数:
var MyString = function(str) {
String.call(this, str);
};
MyString.prototype.__proto__ = String.prototype;
但这也有点粗略。
无论你试图用这个做什么可能都不值得。我敢打赌,无论您尝试将其用于什么,都有更好的方法。
如果您想要 绝对可靠的方法:
// warning, not backwardly compatible with non-ES5 engines
var MyString = function(str) {
this._value = str;
};
Object.getOwnPropertyNames(String.prototype).forEach(function(key) {
var func = String.prototype[key];
MyString.prototype[key] = function() {
return func.apply(this._value, arguments);
};
});
这将在
this._value
上进行。到每个 String 方法。这会很有趣,因为你的字符串是可变的,不像真正的 javascript 字符串。你可以这样做:
return this._value = func.apply(this._value, arguments);
这将增加一个有趣的动态。如果您希望它返回您的字符串之一而不是 native 字符串:
return new MyString(func.apply(this._value, arguments));
或者简单地说:
this._value = func.apply(this._value, arguments);
return this;
根据您想要的行为,有几种方法可以解决它。
此外,您的字符串不会像 javascript 字符串那样具有长度或索引,解决此问题的一种方法是放入构造函数:
var MyString = function(str) {
this._value = str;
this.length = str.length;
// very rough to instantiate
for (var i = 0, l = str.length; i < l; i++) {
this[i] = str[i];
}
};
非常hacky。根据实现,您可能只能调用那里的构造函数来添加索引和长度。如果你想使用 ES5,你也可以使用 getter 作为长度。
尽管如此,你想在这里做的事情无论如何都不理想。这将是缓慢且不必要的。
关于javascript - 如何最好地从原生 JavaScript 对象继承? (尤其是字符串),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6804370/