如何最好地继承本机JavaScript对象?(特别是字符串)

enc*_*der 18 javascript inheritance

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

此外,我搜索了高低,包括这个网站,但我还没有找到一个明确和简洁的解释,我正在寻找什么.如果我错过了它,请指出我正确的方向!

好吧,我想扩展一些原生的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();
Run Code Online (Sandbox Code Playgroud)

但是,对于String,同样不起作用:

var myString = function (n){
    this = n;

    this.a = function (){
        alert(this);
    };
}

myString.prototype = String.prototype;

var x = new myString("foo");
x.a();
Run Code Online (Sandbox Code Playgroud)

我也尝试过:

myString.prototype = new String();
Run Code Online (Sandbox Code Playgroud)

现在,在尝试研究这个时,我发现这确实有效:

var myString = function (n){
    var s = new String(n);

    s.a = function (){
        alert(this);
    };

    return s;
}

var x = myString("foo");
x.a();
Run Code Online (Sandbox Code Playgroud)

然而,这几乎就像是"欺骗"我.就像,我应该使用"真正的"继承模型,而不是这个捷径.

所以,我的问题:

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-redefine或者完全为自己重新定义这些对象(这似乎是浪费).

@CMS - 将您的信息编译成答案,我会选择它.

chj*_*hjj 12

这种痛苦简单但有缺陷的方式是:

var MyString = function() {};

MyString.prototype = new String();
Run Code Online (Sandbox Code Playgroud)

您要求的是奇怪的,因为通常在JS中,您不将它们视为字符串对象,您将它们视为"字符串"类型,作为原始值.而且,字符串根本不可变.你可以有任何对象的行为,就好像它是一个字符串,通过指定.toString方式:

var obj = {};
obj.toString = function() {
  return this.value;
};
obj.value = 'hello';

console.log(obj + ' world!');
Run Code Online (Sandbox Code Playgroud)

但显然它不会有任何字符串方法.你可以通过几种方式做到继承.其中一个是javascript应该使用的"原始"方法,以及你和我在上面发布的,或者:

var MyString = function() {};
var fn = function() {};
fn.prototype = String.prototype;
MyString.prototype = new fn();
Run Code Online (Sandbox Code Playgroud)

这允许在不调用构造函数的情况下添加到原型链.

ES5的方式是:

MyString.prototype = Object.create(String.prototype, {
  constructor: { value: MyString }
});
Run Code Online (Sandbox Code Playgroud)

非标准,但最方便的方式是:

MyString.prototype.__proto__ = String.prototype;
Run Code Online (Sandbox Code Playgroud)

所以,最后,你可以做到这一点:

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;
};
Run Code Online (Sandbox Code Playgroud)

继承的字符串方法可能使用该方法,我不确定.我认为他们可能是因为有一个toString方法.这取决于它们如何在内部由任何特定的JS引擎实现.但他们可能不会.你只需要定义自己的.再一次,你所要求的是非常奇怪的.

您也可以尝试直接调用父构造函数:

var MyString = function(str) {
  String.call(this, str);
};

MyString.prototype.__proto__ = String.prototype;
Run Code Online (Sandbox Code Playgroud)

但这也略显粗略.

无论你想用它做什么都可能是不值得的.我打赌有一种更好的方法可以解决你想要使用它的问题.

如果你想要一个绝对可靠的方法:

// 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);
  };
});
Run Code Online (Sandbox Code Playgroud)

那将讨论this._value每个String方法.这将是有趣的,因为你的字符串将是可变的,不像真正的javascript字符串.

你可以这样做:

    return this._value = func.apply(this._value, arguments);
Run Code Online (Sandbox Code Playgroud)

这会增加一个有趣的动态.如果您希望它返回一个字符串而不是本机字符串:

    return new MyString(func.apply(this._value, arguments));
Run Code Online (Sandbox Code Playgroud)

或者干脆:

    this._value = func.apply(this._value, arguments);
    return this;
Run Code Online (Sandbox Code Playgroud)

根据您想要的行为,有几种方法可以解决它.

此外,你的字符串不会像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];
  }
};
Run Code Online (Sandbox Code Playgroud)

非常hacky.根据实现,您可能只能在那里调用构造函数来添加索引和长度.如果要使用ES5,也可以使用长度的getter.

再一次,你想做什么在任何方面都不理想.这将是缓慢和不必要的.


dav*_*vin 5

此行无效:

this = n;
Run Code Online (Sandbox Code Playgroud)

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();
Run Code Online (Sandbox Code Playgroud)

关于你的解决方法,你应该意识到你所做的只是创建一个String对象,扩充一个函数属性,然后调用它.有没有继承发生.

例如,如果你x instanceof myString在上面的例子中执行它的计算结果true,但在你的例子中它不是,因为函数myString不是一个类型,它只是一个常规函数.


ken*_*bec 1

您不能在构造函数中分配this

this=n 是一个错误

您的 myarray 只是本机 Array 的别名 - 您对 myarray.prototype 所做的任何更改都是对 Array.prototype 的更改。