JavaScript — это из этого

#javascript #oop

#javascript #ооп

Вопрос:

 String.prototype.foo = {};
String.prototype.foo.bar = function() {
    //How can you reference the "grandparent" string?
    console.log(this.parent.parent); //obviously, doesn't exist
}
  

Как и в, "Hello, Nurse!".foo.bar() будет регистрироваться «Привет, медсестра!».

Будет ли это иметь значение, если есть контроль над foo?

Редактировать: Там определено foo .

Правка2: Нормально, вместо this.this.this , this.parent.parent . Конечно, parent не существует, но, надеюсь, теперь семантика не будет мешать.

Правка 3: Здесь нет конкретного случая. Предоставленные сведения — это практически все, что я получил: есть объект foo, часть прототипа. foo.bar является методом foo , и предполагается, что он имеет доступ к своему прародителю. Вот и все. Больше ничего. Это вся информация, которой я располагаю.

Правка 4: решена. На основе предоставленного ответа (и некоторой помощи из вторых рук от Дугласа Крокфорда):

 String.prototype.foo = function() {
    var that = this;
    return {
        bar : function() {
            console.log(that.valueOf());
        }
    }
}
//Called:
"Hello, Nurse!".foo().bar();
  

Комментарии:

1. Для того же эффекта, this.this. это не работает.

2. this по-прежнему является self, а не родительским.

3. @Zirak: Однажды я зашел в магазин в чужой стране, не зная слова, обозначающего «помидоры». Поэтому я просто пожал плечами и попросил вместо клубники, ожидая, что продавец поймет, что я имел в виду. На самом деле это не сработало.

4. @Zirak: Если вы знаете, что здесь уместно, а что нет, то вы должны быть в состоянии ответить на вопрос самостоятельно, не так ли? 🙂

5. Кстати, это не всегда self, это контекст выполнения функции. Если вы вызываете функцию с помощью call и apply, вы можете установить для нее что угодно.

Ответ №1:

Единственный способ, которым это можно сделать, — превратить foo() в функцию. Думайте об этом как об инициализации foo пространства имен для определенной строки:

 String.prototype.foo = function () {
    var str = String(this);
    var o = Object(this)
    o.bar = function () {
         console.log(str);
    };
    return o;
};
  

Затем вы можете использовать:

 "foobar".foo().bar(); // logs "foobar"
  

Или если мы переименуем foo и bar во что-то более захватывающее:

 "Hello!".console().log(); // logs "Hello!"
  

Почему это так сложно?

Каждая функция вызывается с определенным контекстом, который представляет собой отдельный объект. Вызывается ли это с помощью a.b() или a.b.c.d() не имеет значения — объект передается непосредственно слева от вызова функции в качестве ее контекста. Таким образом, контекст для a.b() был бы a , а контекст для a.b.c.d() есть c . Ключевое слово this ссылается на контекст. Поскольку c это просто объект (не выполняемая функция), у него нет контекста, и у него нет понятия this , поэтому this.this это не имеет смысла.

Следовательно, общий доступ к так называемому «родителю» невозможен. Ответ Хуана дает хорошее концептуальное объяснение, почему. Однако, если то, чего вы хотите достичь, — это пространство имен в функциях-прототипах, то вы можете сделать это, вернув расширенный объект из foo .

Обратите внимание, что мне также пришлось преобразовать this в Object приведенный выше. Это потому, что вы не можете присоединять свойства к примитивным значениям, таким как строки. var str = "foo"; str.bar = 1 будет работать, но только потому, что JS автоматически преобразует "foo" в объект. Однако, поскольку str ссылается на примитив, а не на автоматически созданный объект, объект затем немедленно отбрасывается, и мы проигрываем bar .

Комментарии:

1. Как это позволяет вам получить доступ к родительскому объекту универсальным способом?

2. @Juan, str в данном случае это так называемый родительский объект.

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

4. @Box9: Я, очевидно, уже просмотрел пример! Не смог разобраться в этом иначе, как я вам сказал. Пожалуйста, объясните, как это можно использовать для общего доступа к родительскому объекту объекта.

5. @Juan Я никогда не утверждал, что имею общий доступ к родительскому объекту объекта. Может быть, я добавлю туда заявление, чтобы посмотреть на ваш ответ, потому что я не оспариваю это! Я просто пытаюсь решить проблему с пространством имен функций в прототипе.

Ответ №2:

У объекта нет способа узнать, для какого объекта это свойство. Один и тот же объект (в вашем случае функция) может быть присоединен к нескольким объектам. Вот пример:

 var myObj = {};
var objA = {prop: myObj};
var objB = {nother: myObj}
  

Если дана ссылка на myObj, как вы могли бы узнать, на какой родительский объект вы ссылаетесь? В одном случае это ничто, в другом случае это objA, в последнем случае это «дочерний» объект objB. Если вы объясните, почему вам нравится такое поведение, мы можем помочь вам решить проблему. Но ответ на вопрос заключается в том, что вы НЕ МОЖЕТЕ этого сделать.

Комментарии:

1. Помогло бы ли это знать, что foo всегда является методом String , а это bar всегда метод foo ?

2. Дело в том, что вы не можете знать. Любой всегда мог сделать var b = String.prototype.foo.bar . Такова природа этого динамического языка.

3. Конечно, ваш способ также работает, если я это сделаю Object.toString = "hi".toLowerCase . Но это приведет к сбою, поэтому я предполагаю, что в лучшем случае.