我是一个长期浏览器,但是第一次参与。如果我遗漏任何礼仪细节,请告诉我!
另外,我搜索过高低,包括这个网站,但我还没有找到一个明确而简洁的解释,正是我要做的事情。如果我错过了它,请指出我正确的方向!
好吧,我想扩展一些原生的JavaScript对象,比如Array和String。但是,我不想实际扩展它们,而是创建从它们继承的新对象,然后修改它们。
对于Array,这可行:
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)他回答了我的String继承问题,指出通过在我自己的字符串对象中部分重新定义String,我可以使它工作。 (例如重写toString和toValue) 2)创建一个继承自Array的新对象具有自己的局限性,即使部分重新定义Array,也无法立即看到并无法解决。
从上面的两件事中,我得出结论,JavaScript的继承性声明只扩展到您自己创建的对象,而当涉及到本机对象时,整个模型就会崩溃。 (这可能就是为什么你找到的90%的例子是Pet-> Dog或Human-> Student,而不是String-> SuperString)。这可以解释为@ chjj的答案,即这些对象实际上是原始值,即使JS中的所有内容似乎都是一个对象,因此应该是100%可继承的。
如果结论完全没有,请纠正我。如果它是准确的,那么我确信除了我自己之外,这对任何人都不是新闻 - 但是再次感谢大家的评论。我想我现在可以选择:
要么继续使用寄生继承(我的第二个例子,我现在知道它的名称)并尝试尽可能减少其内存使用影响,或做一些像@ davin,@ Jeff或@chjj建议和psudo-重新定义或完全重新定义这些对象(这似乎是浪费)。
@CMS - 将您的信息编译成答案,我会选择它。
答案 0 :(得分:12)
这种痛苦简单但有缺陷的方式是:
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();
这允许在不调用构造函数的情况下添加到原型链。
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);
这会增加一个有趣的动态。如果您希望它返回一个字符串而不是本机字符串:
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。
再一次,你想做什么在任何方面都不理想。这将是缓慢和不必要的。
答案 1 :(得分:5)
此行无效:
this = n;
this
不是有效的左值。意思是,您无法分配this
引用的值。永远。这只是无效的javascript。如果您这样做,您的示例将起作用:
var myString = function (n){
this.prop = n;
this.a = function (){
alert(this.prop);
};
}
myString.prototype = new String; // or String.prototype;
var x = new myString("foo");
x.a();
关于你的解决方法,你应该意识到你所做的只是创建一个String对象,扩充一个函数属性,然后调用它。发生 no 继承。
例如,如果您在上面的示例中执行x instanceof myString
,则评估为true
,但在您的示例中则不是,因为函数myString
不是类型,这只是一个常规功能。
答案 2 :(得分:1)
您无法在构造函数
中指定 this这= n是错误
您的myarray只是原生数组的别名 - 您对myarray.prototype所做的任何更改都是对Array.prototype的更改。
答案 3 :(得分:1)
我将研究创建一个新对象,使用本机对象作为支持字段并手动重新创建本机对象的函数。对于一些非常粗略,未经测试的示例代码...
var myString = {
_value = ''
,substring:_value.substring
,indexOf:_value.indexOf
}
现在,我确信这不会按预期工作。但我想通过一些调整,它可能类似于继承自String的对象。
答案 4 :(得分:0)
此链接为您提供了一些允许您正在寻找的继承的功能。看看“糖”部分。
答案 5 :(得分:0)
ECMAScript6标准允许直接从本机对象的构造函数继承。
class ExtendedNumber extends Number
{
constructor(value)
{
super(value);
}
add(argument)
{
return this + argument;
}
}
var number = new ExtendedNumber(2);
console.log(number instanceof Number); //真实
console.log(数字+ 1); // 4
的console.log(number.add(3)); // 5
的console.log(编号(1)); // 1
在ECMAScript5中,可以像这样继承:
function ExtendedNumber(value)
{
if(!(this instanceof arguments.callee)) { return Number(value); }
var self = new Number(value);
self.add = function(argument)
{
return self + argument;
};
return self;
}
但是,生成的对象不是原始的:
console.log(number); // ExtendedNumber {[[PrimitiveValue]]: 2}
但是你可以通过扩展他使用的实际原型来扩展原始类型:
var str = "some text";
var proto = Object.getPrototypeOf(str);
proto.replaceAll = function(substring, newstring)
{
return this.replace(new RegExp(substring, 'g'), newstring);
}
console.log(str.replaceAll('e', 'E')); // somE tExt
对面向类的表示法的强制转换有点棘手:
function ExtendedString(value)
{
if(this instanceof arguments.callee) { throw new Error("Calling " + arguments.callee.name + " as a constructor function is not allowed."); }
if(this.constructor != String) { value = value == undefined || value == null || value.constructor != String ? "" : value; arguments.callee.bind(Object.getPrototypeOf(value))(); return value; }
this.replaceAll = function(substring, newstring)
{
return this.replace(new RegExp(substring, 'g'), newstring);
}
}
console.log(!!ExtendedString("str").replaceAll); // true