#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
. Но это приведет к сбою, поэтому я предполагаю, что в лучшем случае.