Подтвердить что ты не робот

Как лучше наследовать от нативного JavaScript-объекта? (Особенно String)

Я долговременный браузер, но первый участник. Если мне не хватает информации об этикетах, просто сообщите мне!

Кроме того, я искал высокий и низкий, включая этот сайт, но я не нашел четкого и краткого объяснения того, что я ищу. Если я просто пропустил это, пожалуйста, укажите мне в правильном направлении!

Хорошо, я хочу расширить некоторые собственные объекты 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) Между примером "реального" наследования и примером "ярлыка" можно назвать любые явные преимущества или недостатки одним способом над другим? Или, может быть, только некоторые различия в том, как можно функционировать над другим? (Потому что они в конечном итоге выглядят одинаково для меня...)

Спасибо всем!

EDIT:

Спасибо всем, кто прокомментировал/ответил. Я думаю, что информация @CMS лучше всего, потому что:

1) Он ответил на мою проблему наследования строк, указав, что частично переопределив String в моем собственном строковом объекте, я мог бы заставить его работать. (например, переопределение toString и toValue) 2) Создание нового объекта, который наследуется от Array, имеет свои собственные ограничения, которые не были сразу видны и не могут быть обработаны даже частично путем переопределения массива.

Из приведенных выше двух вещей я пришел к выводу, что требование JavaScript о наследовании распространяется только на объекты, которые вы создаете сами, и что когда дело доходит до нативных объектов, вся модель ломается. (Вероятно, поэтому 90% примеров, которые вы найдете, это Pet- > Dog или Human- > Student, а не String- > SuperString). Который может быть объяснен @chjj ответом на то, что эти объекты действительно предназначены для примитивных значений, даже если все в JS кажется объектом и поэтому должно быть на 100% наследуемым.

Если этот вывод полностью отключен, пожалуйста, исправьте меня. И если это точно, то я уверен, что это не новость никому, кроме меня самого, - но еще раз спасибо за комментирование. Полагаю, теперь у меня есть выбор:

Идите вперед с паразитическим наследованием (мой второй пример, который я теперь знаю для имени), и попытайтесь уменьшить влияние использования памяти, если это возможно, или сделайте что-то вроде @davin, @Jeff или @chjj, предлагаемого и либо psudo- переопределить или полностью переопределить эти объекты для себя (что кажется пустой тратой).

@CMS - скомпилируйте свою информацию в ответ, и я выберу ее.

4b9b3361

Ответ 1

Довольно простой, но ошибочный способ сделать это:

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];
  }
};

Очень хаки. В зависимости от реализации вы можете просто вызвать конструктор, чтобы добавить индексы и длину. Вы также можете использовать getter для длины, если вы хотите использовать ES5.

Еще раз, однако, то, что вы хотите сделать здесь, не является идеальным ни в коем случае. Это будет медленным и ненужным.

Ответ 2

Эта строка недействительна:

this = n;

this не является допустимым значением l. Значит, вы не можете назначить значение, на которое ссылается 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, расширение свойства функции, а затем его вызов. Не существует наследования.

Например, если вы выполнили x instanceof myString в моем примере выше, он оценивается как true, но в вашем примере это не так, потому что функция myString не является типом, это просто регулярная функция.

Ответ 3

Вы не можете назначить этот в конструкторе

this = n - ошибка

Ваш myarray является просто псевдонимом для собственного массива - любые изменения, которые вы делаете для myarray.prototype, являются изменениями в Array.prototype.

Ответ 4

Я бы посмотрел на создание нового объекта, используя собственный объект в качестве поля поддержки и вручную воссоздавая функции для собственных объектов. Для некоторого очень грубого, непроверенного образца кода...

var myString = {
    _value = ''
    ,substring:_value.substring
    ,indexOf:_value.indexOf
}

Теперь я уверен, что это не будет работать так, как предполагалось. Но я думаю, что с некоторой настройкой он может напоминать объект, унаследованный от String.

Ответ 5

Эта ссылка дает вам некоторые функции, которые позволяют наследовать, которые вы ищете. Посмотрите раздел "сахар".

http://www.crockford.com/javascript/inheritance.html

Ответ 6

Стандарт ECMAScript6 позволяет наследовать непосредственно из конструкторских функций собственного объекта.

class ExtendedNumber extends Number
{
    constructor(value)
    {
        super(value);
    }

    add(argument)
    {
        return this + argument;
    }
}

var number = new ExtendedNumber (2);
console.log(number instanceof Number);//true
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