Хороший способ изменить прототип объекта, чтобы изменить результаты instanceof?

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

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

  1. Объект obj создается с помощью конструктора Base. obj instanceof Base возвращает истину.
  2. I want to change the prototype of obj such that it appears as if obj was constructed from Derived. That is, I want
    • obj to get access to the methods of Derived
    • obj instanceof Derived, чтобы вернуть истину

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

Я верю, что могу сделать это с

obj.__proto__ = Derived.prototype;

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

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

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

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

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

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


person John Freeman    schedule 27.03.2012    source источник
comment
__proto__ уже устарел. Вы можете изменить [[Prototype]] объекта только при его создании, вы не можете изменить цепочку впоследствии.   -  person RobG    schedule 28.03.2012
comment
Хорошо ... и все же он все еще доступен в моей среде. Я ищу более портативное решение. Вы можете помочь с этим?   -  person John Freeman    schedule 28.03.2012
comment
То, что устарело, не удаляется из системы, оно, по сути, помечено для удаления в будущем и поэтому не должно использоваться. Поддержка поддерживается, чтобы дать время для изменения кода, использующего устаревшие функции.   -  person RobG    schedule 28.03.2012
comment
Да и что толку об этом говорить? Я уже упоминал, что в настоящее время полагаюсь на непереносимое поведение и ищу альтернативу. Ваш комментарий не способствовал обсуждению.   -  person John Freeman    schedule 28.03.2012
comment
Хорошо, кажется, вы были удивлены, обнаружив, что устаревшая функция все еще доступна, просто подумал, что я объясню.   -  person RobG    schedule 28.03.2012


Ответы (3)


Я не вижу возможности сделать это. Как продемонстрировал RobG, вы можете заставить instanceof возвращать 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 :-)

person Bergi    schedule 28.03.2012
comment
Хорошо, я думаю, вы могли бы остановиться после первого предложения. Я собираюсь дать еще немного времени для других идей, но я приму это, если никто не сможет придумать решение. - person John Freeman; 28.03.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 Производный объект для возврата true

Это можно сделать, сделав Base.prototype экземпляром Derived перед созданием каких-либо экземпляров Base.

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

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

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

person RobG    schedule 28.03.2012
comment
Да я бы тоже так сделал. Но я думал, что вопрос был о конкретных экземплярах, а это решение меняет прототипы всего класса. - person Bergi; 28.03.2012
comment
Берги прав. См. скрипту, которую я добавил к вопросу. - person John Freeman; 28.03.2012
comment
Но ваша скрипка не делает вышеперечисленного, она назначает новый прототип Derived, но затем все еще создает экземпляр Base (который не соединяет прототипы так, как вы хотите). - person RobG; 28.03.2012
comment
Derived получает назначенный прототип, поэтому его экземпляры будут производными от Base. Он изменяет цепочку прототипов obj для выполнения двух указанных мною критериев. - person John Freeman; 28.03.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

Скрипка находится здесь.

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

person The Alpha    schedule 27.03.2012
comment
Нет, вопрос был в том, как сделать new car() instanceof plane?! - person Bergi; 28.03.2012
comment
@ Берги, здесь транспортное средство - это базовый конструктор, а автомобиль является производным от транспортного средства, а Форд - это объект, созданный автомобилем, так что вы имели в виду под самолетом, пожалуйста? - person The Alpha; 28.03.2012
comment
Шейх, я думал, что ясно объяснил в исходном вопросе, но я попробую еще раз: вы создали obj как экземпляр Car. Теперь создайте новый класс Dragster, производный от Car, и измените obj (но не его конструкцию из Car), чтобы это выражение возвращало true: obj instanceof Dragster - person John Freeman; 28.03.2012
comment
Дай понять, скоро вернусь. - person The Alpha; 28.03.2012
comment
Но тогда я не могу использовать другой прототип для Драгстера, не меняя Машину, так что нет, это не нормально. - person John Freeman; 28.03.2012
comment
+1, вы просто переименовываете Car в Dragster. - person Bergi; 28.03.2012
comment
Если вы найдете лучший ответ, пожалуйста, сообщите мне, [email protected] и всего наилучшего. - person The Alpha; 28.03.2012
comment
Обучение еще не закончилось, мой друг, впереди еще долгий путь, надеюсь, я смогу узнать что-то новое из этого. Каждый день я узнаю что-то новое от Со. - person The Alpha; 28.03.2012