Хороший способ изменить прототип объекта, чтобы изменить результаты instanceof? - PullRequest
3 голосов
/ 28 марта 2012

Я хотел бы прокомментировать этот старый вопрос , но, похоже, он заблокирован.

Вот мой пример использования:

  1. Объект obj создается с помощью конструктора Base. obj instanceof Base возвращает истину.
  2. Я хочу изменить прототип obj так, чтобы он выглядел так, как будто obj был создан из Derived. То есть я хочу
    • obj для получения доступа к методам Derived
    • obj instanceof Derived чтобы вернуть true

Причина в том, что obj должен иметь тип в иерархии, который неизвестен во время его создания и определяется тем, что происходит после. Я хочу иметь возможность перемещать его вниз по иерархии.

Мне кажется, я могу сделать это с

obj.__proto__ = Derived.prototype;

, но __proto__ будет устаревшим в следующей версии JavaScript. прокси-интерфейс API, который изменился после того, как был задан вопрос, который я связал выше, похоже, не поддерживает мой вариант использования.

Есть ли альтернативная реализация для моего варианта использования, которая существует сейчас или запланирована на будущее?

Единственная альтернатива, которую я вижу сейчас, - это использовать

obj2 = Object.create(Derived.prototype);
obj2.extend(obj);

и никогда не храните более одной ссылки на obj, но такая стоимость доставляет немало неудобств.

Вот скрипка , демонстрирующая проблему.

Ответы [ 3 ]

3 голосов
/ 28 марта 2012

Я не вижу возможности сделать это.Как продемонстрировал RobG, вы можете создать экземпляр, возвращающий false путем изменения , наносящий ущерб свойству prototype класса.

Видя это, я подумал, что вы могли бы сделать это с дополнительным классом, вы знаете,, как F из общей Object.create прокладки:

Object.newChangeable = function create(b) {
    function F(){b.call(this);}
    F.prototype = b.prototype;
    var f = new F();
    f.change = function change(n) {
        F.prototype = n.prototype;
    };
    return f;
}

var obj = Object.newChangeable(Base);
obj instanceof Base; // true

obj.change(Derived);

Но нет :

obj instanceof Derived; // still false
obj instanceof Base; // still true

, потому что внутренний [[Prototype]] изobj по-прежнему указывает на то же, что и Base.prototype.Что вы можете сделать, это сделать Derived новым Base:

var obj = new Base;
obj instanceof Base; // true

Derived.prototype = Base.prototype;
Base.prototype = {}; // something else

alert(obj instanceof Derived); // true
alert(obj instanceof Base); // false

Но я не думаю, что это то, что вы хотели, манипулируя правой стороной выражения вместо того, чтобы что-то менять в obj: -)

1 голос
/ 28 марта 2012

Оператор instanceof просто проверяет, находится ли общедоступное свойство конструктора prototype в цепочке [[Prototype]] объекта. Один из способов разорвать цепь - изменить прототип конструктора:

function Base() {}

var base = new Base();

alert( base instanceof Base); // true

Base.prototype = {};

alert( base instanceof Base); // false
alert( base instanceof Object); // true

Второе предупреждение является ложным, потому что новый Base.prototype больше не находится в цепочке [[Prototype]] из base (хотя исходное еще есть). Обратите внимание, что Object.protoyype все еще есть. Вышесказанное является одной из причин, почему оператор instanceof не считается особенно полезным.

Чтобы сделать то, что вы пытаетесь сделать, вы должны создать цепочку [[Prototype]] при создании объекта, потому что вы не можете изменить его позже:

Derived.prototype = new Base();

var base = new Derived();

alert(base instanceof Base); // true
alert(base instanceof Derived); // true

Редактировать

Требования были:

  1. Объект obj создается с помощью конструктора Base. obj instanceof Base возвращает true.

Как показано, это не обязательно так. Если у вас есть стратегия, которая зависит от instanceof возврата определенного значения, тогда вы накладываете (вероятно, неразумное) ограничение на дизайн без какой-либо явной выгоды.

2. Я хочу изменить прототип obj так, чтобы он выглядел так, как будто объект obj был создан из Derived. То есть я хочу

• obj для получения доступа к методам Derived

Вы можете сделать это, сделав Base.prototype экземпляром Derived (как показано) или скопировав свойства в Base.prototype.

• obj instanceof Derived для возврата true

Вы можете сделать это, сделав Base.prototype экземпляр Производный до , создав любые экземпляры Base .

Вы не можете изменить цепочку после создания экземпляра. Если вы отбросите ограничение instanceof, вы можете добавить методы Derived.prototype, просто скопировав их в Base.prototype. Другой способ - использовать call или apply :

Derived.prototype.someMethod.call(base, ...);

но я подозреваю, что вы пытаетесь сделать что-то, что невозможно.

0 голосов
/ 28 марта 2012

Вы имели в виду это (немного смущен вопросом, а я все еще учусь)

function vehicle(name, color)
{
    if(this instanceof arguments.callee)
    {    
        this.name = name;
        this.color = color;
    }
    else return new arguments.callee(arguments);   
}

vehicle.prototype = {
    getName : function()
    {
        alert(this.name);
    } 
}

function Car(name, color, speed)
{
        this.name = name;
        this.color = color;
        this.speed = speed;
}

Car.prototype = vehicle();
var obj = new Car("Ford", "Red", "200mph");

obj.getName(); // alerts `Ford` using vehicle's/parent's method
alert(obj instanceof vehicle); alerts `True` instanceof vehicle's/parent's constructor

Скрипка здесь .

Статья Джона Резига .

...