javascript - 如何最好地从原生 JavaScript 对象继承? (尤其是字符串)

标签 javascript inheritance

我是一个长期浏览者,但第一次参与。如果我遗漏了任何礼仪细节,请告诉我!

此外,我已经搜索了高低,包括这个网站,但我还没有找到关于我想要做什么的清晰简洁的解释。如果我错过了,请指出正确的方向!

好的,我想扩展一些原生 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/

相关文章:

javascript - filepicker.io - 将 Canvas 保存为 PNG

javascript - 在 ZingChart 上绘制形状作为值

c++ - copy-and-swap 习语,继承

java - 为父类(super class)分配一个引用java

c# - 为什么泛型类型参数中的隐式子到父转换是不可能的?

javascript - 如何创建一个 JavaScript float div 注册表单

javascript - 如何在单击时将 href 设置为复选框?

javascript - CodeMirror 简单模式 - 正则表达式未按预期突出显示

inheritance - Kotlin 强制实现类成为另一种类型的父类(super class)型

c# - 在最大化代码重用的同时使用 C# 接口(interface)