Javascript-форум (https://javascript.ru/forum/)
-   Общие вопросы Javascript (https://javascript.ru/forum/misc/)
-   -   Получить объект содержащий caller (https://javascript.ru/forum/misc/3120-poluchit-obekt-soderzhashhijj-caller.html)

no_alex 19.03.2009 23:21

Получить объект содержащий caller
 
Данная тема создана с целью разделить два вопроса смешанных в одной теме здесь:
http://javascript.ru/forum/misc/3116...ascript-2.html

Вкратце повторю суть задачи.

Есть некий объект (A) у которого будут вызываться различные методы. И есть серия объектов (B1, B2, B3,...) которые будут обращаться к методам A.
У B1, B2, B3,... общий прототип (P). В прототипе P или в конструкторе, при создании объекта, я могу создать что-либо, что позволит потом получить ссылку на этот объект.

Теперь сама задача: когда B1, B2, B3,... обращается к методам A в этих методах я могу обратиться к "caller", но при этом я получаю ссылку на саму функцию прототипа P. А мне еще надо получить ссылку на объект у которого она была вызвана.

Пока объект B1 существует в единственном экземпляре проблем нет - я его цепляю к прототипу и потом легко получаю, но как только появляется B2, B3,... уже достучаться до текущего объекта не получается.


Объект A сейчас создается один раз и затраты на его создание сейчас большого значения не имеют. А вот объекты типа B будут создаваться регулярно и затраты на их создание мне надо максимально уменьшить. Т.е. минимализировать количество процедур в объекте B и аргументов передаваемых в объект A.

Т.е. надо усложнять объект A, для того чтобы максимально упростить вызов его методов из объектов B. Так-же не будет проблемой если мы усложним конструктор объекта B.


Теперь пример "не рабочего" кода, который я обещал привести:
var A = {
    t : function()
    {
        var this_func = arguments.callee; // Ссылка на текущую функцию
        var caller = this_func.caller; // Ссылка на вызывающую функцию (в данном случае я получаю ссылку на свойство из прототипа)
        var that = caller.ref // Попытка получить ссылку на объект из которого произошел вызов
        alert(that.x);
    }
}

var B = function(x)
{
	this.x = x; // Сохраняю свойство, для тестирования
	this.z.ref = this; // Пытаюсь сохранить ссылку на текущий объект, чтобы потом его получить. Но поскольку this.z фактически является свойством прототипа, это свойство потом презапишется
}
B.prototype = {
	x : "",
	z : function()
	{
		A.t();
	}
}


var B1 = new B("LALA1");
B1.z();
var B2 = new B("LALA2");
B2.z();


Ключевые моменты в коде я прокомментировал.

Если запустить код, том виде как я привел - вроде-бы все работает как надо. Мы получим, как и ожидаем два алерта: "LALA1" и "LALA2".

Но если изменить финальную часть этого кода, например так:
var B1 = new B("LALA1");
var B2 = new B("LALA2");
B1.z();
B2.z();

то все работает не правильно. Оба алерта будут "LALA2".

Хотелось бы получить что-либо подобное, но правильно работающее. ;)

Gvozd 19.03.2009 23:44

хоть убейте до сих пор не пойму чем вам apply не подходит:
var A = {
    t : function()
    {
        alert(this.x);
    }
}
 
B = function(x)
{
    this.x = x; // Сохраняю свойство, для тестирования
}
B.prototype = {
    x : "",
    z : function()
    {
        A.t.apply(this);
    }
}
 
 
B1 = new B("LALA1");
B2 = new B("LALA2");
B1.z();
B2.z();

no_alex 20.03.2009 00:00

Цитата:

хоть убейте до сих пор не пойму чем вам apply не подходит
Потому что метод t() на самом деле очень сложный и мне надо в нем через this обращаться к другим свойствам объекта A.

Ну и вторая цель - вызовы из B1, мне надо сделать максимально простыми.
Сейчас я делаю примерно так:
var A = {
    t : function(that)
    {
        alert(that.x);
    }
}
 
B = function(x)
{
    this.x = x; // Сохраняю свойство, для тестирования
}
B.prototype = {
    x : "",
    z : function()
    {
        A.t(this);
    }
}
 
 
B1 = new B("LALA1");
B2 = new B("LALA2");
B1.z();
B2.z();

и потом получаю ссылку на текущий объект как аргумент метода t(). По сути, это даже проще, чем то что Вы предлагаете.

Но мне сейчас очень хочется отказаться от этого аргумента, если получится до него "достучаться" каким-либо другим способом.

Gvozd 20.03.2009 00:19

Цитата:

Сообщение от no_alex
Но мне сейчас очень хочется отказаться от этого аргумента, если получиться до него "достучаться" каким-либо другим способом.

почему?он вам сильно мозолит глаза?
или вы считаете что так будет медленнее?мне кажется навряд ли медленне, хотя не уверен

no_alex 20.03.2009 00:30

Gvozd,
Ну я же объяснил свои цели в начале:
Цитата:

Объект A сейчас создается один раз и затраты на его создание сейчас большого значения не имеют. А вот объекты типа B будут создаваться регулярно и затраты на их создание мне надо максимально уменьшить. Т.е. минимализировать количество процедур в объекте B и аргументов передаваемых в объект A.
Можно еще так сказать: Объект A сейчас создаю я. А вот объекты B1, B2, B3,... потом будут создавать другие люди. И у меня сейчас цель облегчить другим жизнь. ;)
Но при этом сделать это так, чтобы это не вызвало путаницы.

Gvozd 20.03.2009 00:43

То есть усложнять конструктор B() и прототип создаваемых объектов нельзя?
ну, тогда не знаю как решить вашу задачу

no_alex 20.03.2009 00:57

Цитата:

То есть усложнять конструктор B() и прототип создаваемых объектов нельзя?
Конструктор усложнять можно. Там, на самом деле, конструктор будет только мой.
В прототипе так же можно создать какие-то вспомогательные методы, но только для того чтобы эти методы вызывались сами в какой-то момент. Чтобы тому кто будет писать B1, B2, B3,... не пришлось специально вызывать эти методы, иначе это будет сложнее чем передавать this через аргументы.

Gvozd 20.03.2009 01:09

я правильно понимаю:
var A = {
    t : function(that)
    {
        alert(that.x);
    }
}
 
B = function(x)
{
    this.x = x; // Сохраняю свойство, для тестирования
}
B.prototype = {
    x : "",
    z : function()
    {
        A.t(this);
    }
}
//выше находится ваш код.там творим, все что нам надо
//ниже код других людей.тут надо сделать чтобы все было максимально просто и прозрачно
 
B1 = new B("LALA1");
B2 = new B("LALA2");
B1.z();
B2.z();

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

no_alex 20.03.2009 01:34

ok.
Вот так примерно работает тот скрипт, что у меня есть:
var A = {
    t : function(that)
    {
        alert(that.x);
    }
}
 
var B = function()
{
    this.tttt = 123; // Делаю что-то свое
    if(this.__constructor !== undefined && this.__constructor.constructor == Function) {
        this.__constructor.apply(this, arguments); // Вызываю "кастомерский" конструктор
    }
}
B.prototype = {
    some_special_method : function()
    {
    },
//выше находится наш код. Там творим, все что нам надо
//ниже код других людей. Тут надо сделать чтобы все было максимально просто и прозрачно
    x : "",
    __constructor : function(x) // Конструктор других людей
    {
        this.x = x; // Сохраняю свойство, для тестирования
    },
    z : function()
    {
        A.t(this);
    }
}
 
B1 = new B("LALA1");
B2 = new B("LALA2");
B1.z();
B2.z();


Теперь еще раз, с Вашего позволения, повторю задачу (для тех, кто возможно, не внимательно следил за нашим диалогом, но хочет сейчас к нему подключится).
Надо в этой строчке:
A.t(this);

избавится от this, но таким образом, чтобы в вызываемом методе получить ссылку на текущий объект.

Gvozd 20.03.2009 01:51

так подойдет?
var A = {
    t : function()
    {
        alert(this.x);
    }
}
 
B = function(qwe)
{
    this.x = qwe; // Делаю что-то свое
    if(this.__constructor !== undefined && this.__constructor == Function) {
        this.__constructor.apply(this, arguments);
    }
}

//конструкция, добавляющая A в верхнюю часть прототипирования
B_proto_constr=function(){}
B_proto_constr.prototype=A
B.prototype=new B_proto_constr()
//конструкция, добавляющая A в верхнюю часть прототипирования

    B.prototype.some_special_method = function()
    {
    };
//выше находится наш код. Там творим, все что нам надо
//ниже код других людей. Тут надо сделать чтобы все было максимально просто и прозрачно
    B.prototype.x = "",
    B.prototype.__constructor = function() // Конструктор других людей
    {
    };
    B.prototype.z = function()
    {
        this.t();//вынесли this из параметров.но приписали вначале))
    }
 
B1 = new B("LALA1");
B2 = new B("LALA2");
B1.z();
B2.z();

я решил добавить A в качестве прототипа прототипа объектов B

no_alex 20.03.2009 02:16

Цитата:

так подойдет?
Увы нет. Это совершенно разные объекты. Они не должны иметь ничего общего.

Хотя я уже думал над чем-то подобным. Но я хотел в прототип как-то "вплести" третий (вспомогательный) объект. Но пока не придумаю как...

Gvozd 20.03.2009 02:24

я исчерпался

Zeroglif 20.03.2009 16:06

no_alex,

Если вы внутри функции 'A.t' откажетесь от той идеи, что значением 'this' должен быть исключительно объект 'A' и станете обращаться к нему по имени, то можно уйти от аргумента в пользу версии с 'apply' или её более упрощённого собрата. Это если вы не хотите кого-то инструктировать про аргумент...

C другой стороны, вариант с передачей аргумента вряд ли можно назвать "нагружающим" процесс создания объектов, прототип создаётся один раз, вычисление значения 'this' в момент вызова мгновенное и т.д. Что касается варианта с 'caller', то это не кроссбраузерное решение, к тому же само свойство 'deprecated'...

no_alex 20.03.2009 17:59

Цитата:

Если вы внутри функции 'A.t' откажетесь от той идеи, что значением 'this' должен быть исключительно объект 'A'
Там так не получится. Это здесь, в качестве примера, я показал A, как один простой объект. На самом деле, у меня таких обектов будет несколько и каждый со своим законченным функционалом. Именно поэтому мне нельзя их "переплетать" между собой и this там должен обязательно указывать на "оригинал".

Цитата:

вариант с передачей аргумента вряд ли можно назвать "нагружающим" процесс создания объектов
Не скажу чтобы оно сильно нагружало, но если бы можно было его убрать, было-бы хорошо. Сейчас у меня такие вызовы (с "this") всречаются примерно в каждой ~5-10 строке.

Цитата:

с 'caller', то это не кроссбраузерное решение
Действительно старая Opera 8.53 его не понимает. :(
В остальных браузерах:
- FF2, FF3 и другие браузеры на мозилловском движке (NN, Flock);
- IE6, IE7;
- Opera 9;
- Safari;
- Google Chrome;
нормально отрабатывает.
Я думаю, что если речь идет только об Opere 8 и ниже, то на это можно "забить".

Цитата:

к тому же само свойство 'deprecated'
Вот это уже заставляет задуматься! Можете показать где это написано?

Zeroglif 20.03.2009 21:37

Цитата:

Сообщение от no_alex
если речь идет только об Opere 8 и ниже

Боюсь ошибиться, но свойство 'Function.caller' появилось очень давно, где-то в JavaScript 1.1, в JScript соответственно следом. Позднее в JavaScript 1.2 добавили свойство 'arguments.caller', которое в следующей же версии от страха прибили (вот оно как раз открытым текстом deprecated в JavaScript 1.3). При этом свойство 'Function.caller' из offline-спеков JavaScript выпало вообще (deprecated закрытым текстом), вы его найдёте только в новомодной редактируемой MDC, B.Eich объяснял пропажу так:

All traces of a caller property were removed a while ago, to follow ECMA-262 and to avoid any chance of a security exploit. The removal seems to me to have been overzealous, because it axed the caller property of function objects *and* the much-more-exploitable caller (or __caller__) property of activation objects.

В общем, в основных движках свойство сидит с прошлого века, но в одном из них временно отстутствовало, выпадая из спеков. Все остальные традиционно бедны последовательной документацией, в Opera точно не было долго (помню был такой вопрос на винграде), в Safari ещё труднее сказать... свойство вне стандарта, редко используемое, может взбрыкнуть. ;)


Часовой пояс GMT +3, время: 03:34.