Модераторы: Sardar, Aliance

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Функции и методы в JS, детали реализации, обсуждение ECMA и дру 
:(
    Опции темы
Sardar
Дата 18.2.2007, 20:40 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бегун
****


Профиль
Группа: Модератор
Сообщений: 6986
Регистрация: 19.4.2002
Где: Нидерланды, Groni ngen

Репутация: 78
Всего: 317



Материал находится тут: javascript: Методы. А в этой теме Вам предлагается его обсудить.




--------------------
 Опыт - сын ошибок трудных  © А. С. Пушкин
 Процесс написания своего велосипеда повышает профессиональный уровень программиста. © Opik
 Оценить мои качества можно тут.
PM   Вверх
Sardar
Дата 19.2.2007, 04:12 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бегун
****


Профиль
Группа: Модератор
Сообщений: 6986
Регистрация: 19.4.2002
Где: Нидерланды, Groni ngen

Репутация: 78
Всего: 317



Ну раз такая тема создалась автоматом...

Вимание: далее рассматриваем метод с точки зрения реализации в JS, а не философию ООП. 
Надеюсь это вам поможет делать меньше не свойственных/глупых для JS вещей.

Что такое метод в JS? Для этого нужно разобраться что такое функция, объект 
и контекст выполнения функции.


Сразу поясним, что  в JS функция это не, как обычно, некий блок кода, доступный по имени. 
Функция это значение, такое же как строка или любой другой объект. На значение могут 
ссылаться масса ссылок, которые вы и видите как переменные. Это должно быть уже знакомо 
программистам на Java/C#/PHP/etc. Повторим: переменная это ссылка на значение.

Объект функции это фактически ссылка на скомпилированный код функции и на контекст, в 
котором функция была определена. Созданный объект-функция помещается в переменную с "именем 
функции", подчеркнём что эта переменная самая обычная переменная и её значение можно изменить на 
что угодно.

Что же такое контекст? Это ключевой объект во всей этой истории, фактически это 
пространство в котором находятся все переменные определённые в этом контексте. Контекст 
ссылается на родительский контекст, в котором он был создан, именно по этому мы можем видеть 
переменные определённые выше по контексту. Повторим: контекст содержит только переменные.

Контекстом обладают только функции. Внутри функции любой if/while/for/etc может 
очерчивать любые блоки с фигурными скобками, всё равно все "определённые" в них переменные будут 
видны по всей функции (только try/catch имеют "собственную" переменную). Переменные в контексте 
создаются ключевым словом var. Фактически место под все переменные уже зарезервировано 
при входе в функцию, так что вынос всех var в блоки "перед тем как они потребуются" не 
даёт реально никаких оптимизаций. Что бы стало ясно, рассмотрим пример:
Код
function test(exp) {
    var a = "test";
    var b = 90;
    
    if(exp) { //что бы кто не подумал на какие оптимизации =)
        //мы сейчас не в "новом контексте", а всё в той же функции test
        a = "vingrad";
        b = 90;
        var b = 100;
        var c = 30;
        //потому все var "создают" переменные в контексте test
    }
    alert(a); //vingrad
    alert(b); //100
    alert(c); //30
    
    //здесь должно быть ясно, что это краткий синтаксис var cool = function() {...
    function cool() {
        //а сейчас мы в новом контексте, принадлежащим функции cool  (да, я помню что у функций нет имён, но как то же на них надо указывать ;-) )
        a = "JavaScript";
        alert(b); //undefined -- переменная b существует в этом контексте ещё до того,  как мы начали выполнение, помним про var, 
        //потому нет ошибки, но и значения тоже нет. помним что undefined это значение,  такое же как например null
        //null имеет особый смысл, это ссылка на объект, которая никуда не указывает
        //undefined это значение переменной, не имеющей пока "значения" вообще.
        b = 3;
        var b = 70;
        try { alert(non_existing_var); } //сейчас мы к этому подойдём
        catch(e) { alert(e); }
    }
    cool();
    alert(a); //JavaScript -- как видим из cool мы изменили а
    alert(b); //100 -- значение не изменилось
}

test(true);


В примере видим, что на момент входа в функцию все переменные определённые в контексте в любых 
if'ах и других конструкциях уже существуют. Фактически контекст имеет фиксированный размер, 
такой что бы вместить все переменные этого контекста, известный уже на этапе компиляции в 
байткод/AST. При обращении к переменной контекст смотрит в своих переменных, если не нашёл, то 
обращается к родительскому контексту. Так происходит до тех пор, пока не упрёмся в самый верхний 
контекст (гипотетический метод run объекта window или global для настоящих ECMA-262). Далее 
зависит от типа операции, если это get, то будет ошибка, которую мы успешно ловим в 
try/catch. Если это set, то переменная с этим именем будет создана в самом верхнем 
контексте*. Именно по этому я часто говорю, не забываем про var, иначе вы плодите 
"глобальные" переменные.

* А можно ликовать по имени на этапе трансляции, а не искать по хешь-таблицам? Да так и 
происходит, все обращения к переменным контекстов идут по прямым ссылкам, кроме тех что 
определены в global. Ещё на этапе трансляции известно, какие переменные не найдены, но компилер 
не будет ругаться, т.к. они могут "внезапно" появиться в global контексте. Впрочем это не верно 
для кривого IE, судя по подсказкам к оптимизации, которые предлагает мелкософт.


Так, собрали мысли до кучи, попробуем по шагам выполнить код выше. Сначала трансляция скрипта в 
байткод, получим:
Код
//функция это плоский кусок кода, вложений "функция в функцию" уже нет
[function: 0001 //test -- имён фактически нет, но мы будем их использовать
   [context(3)] //в этой функции определены 3 переменной: a, b и c
   ..... байткод ....
]

[function: 0002 //cool
   [context(1)] //собственно контекст содержит только b
   ... батйкод ...
]


Определим первую операцию над функциями - call(current_context):
  • создать контекст заданного размера, передать current_context (текущий контекст) как 
    родительский. Кстати на стеке создавать не удобно для JS, но можно если подойти креативно.
  • инициализировать все переменные в контексте значением undefined
  • выполнить байткод
Только самый верхний контекст создаётся браузером без родительского, он принадлежит самой 
"внешней функции", в которой весь скрипт и выполняется.

Теперь определим операцию create_function_reference(current_context) - именно этой 
операции соотвествует конструкция function()...:
  • создать объект "ссылка на функцию", инициализировать функцией 000х и текущим 
    контекстом current_context
  • положит (ссылку на) объект функцию в соответствующую переменную в контексте.
То есть ничем не отличается, например, от строки, которая создаёт ссылу на строку-константу. 
Впрочем речь не об этом, внимательно смотрим, что вместе с ссылкой на функцию мы положили и 
ссылку на текущий контекст
. Теперь выполняя эту функцию мы выполняем её в сохранённом 
контексте и "видим" те переменные. Примером будет ясно:
Код
<div id="text1">Some text...</div>
<div id="text2">Some other text...</div>
<script type="text/javascript">
function blink(obj) {
    var show = true;
    return function() { //возвращаем функцию (closure)
        //obj по прежнему видим, для каждого вызова blink он свой
        obj.style.visibility = (show = !show)? 'visible': 'hidden';
    }
}

//разные контексты blink, свой для каждого вызова ;-)
var functor1 = blink(document.getElementById("text1"));
var functor2 = blink(document.getElementById("text2"));

//переменная functorN содержит объект-функцию, также как *переменная* blink
window.setInterval(functor1, 400);
window.setInterval(functor2, 1000);
</script>


Кто не знаком с closure может быть сейчас немного удивлён. Да, это удобно, мощно, 
встречается часто в скриптовых языках поновее, ну и естественно в функциональных (декларативных) 
языках.Видим, что при вызове функции сохранённый вместе с ссылкой контекст определяет поведение 
для каждого instance одной и той же функции. Собственно это и называется closure.
Повторим: все функции в JS это closure.



А где тут объекты и методы, мужики ?!
Да, увлеклись, вернёмся к нашим баранам. Мы знаем как вызывается функция и даже определили 
гипотетическую операцию call для этого. Сразу скажу, этой операции в реальном JS нет, все 
функции всегда вызываются как методы. Подчеркнём слово как методы, потому 
как в привычном для Java/C#/C++ программиста, в JS методов нет. Есть вызов в контексте 
объекта
. Ну а объектом может быть что угодно smile

Многие привыкли, что метод это приватное свойство класса, где каждый объект этого класса имеет 
доступ до этого метода и более никто. Доступ в смысле метод можно выполнить только на этом 
объекте или на обьектов классов потомков. В JS метод это переменная/поле обьекта, в 
которой лежит объект-функция. Отсюда будет ясно, почему два объекта, порождённые от одного 
конструктора и следовательно instanceof constructor_function будет выдавать для них true, 
могут иметь совершенно разный интерфейс. Это конечно особо извращённый случай, но можно.

Объект в JS это пустая хеш-таблица с ссылкой на протитип. Что такое прототип 
поясним позже, сейчас выделим следующее:
  • при создании объекта всегда создаётся новая пустая хеш-таблица
  • в отличии от контекстов в эту хеш-таблицу можно свободно добавлять новые ключи (поля объекта) 
    или удалять старые
  • конструктор инициализирует объект путём создания новых полей, содержащих что угодно, включая 
    функции (методы)
  • все поля объекта публичны, т.е. доступны кому угодно
К объекту внутри функции обращаемся через this. На ком функцию вызвали, на того 
this и указывает. Только для global (в браузере window) можно опустить 
указание объекта и вызвать "просто функцию". На самом деле мы её вызываем как метод global, 
следовательно и this указывает на global. Пример, что бы осмыслить всё это:

Код
//внимание, приготовтесь думать немного более гибче, чем в Java/C++
//программисты Python/Ruby/Groovy велкам =)

//глобальная переменная, она же поле global.cool (в браузере window.cool)
//глобальный контекст единственный, где var это "ещё и поле объекта", потому как он сам
//и используется при вызове "простых функций"
var cool = "vingrad";

function test() {
    //this будет объектом test только при вызове с new
    //дабы не убить глобальную cool при простом вызове (помним, что this это глобал при простом вызове)
    //мы делаем вот такое условие
    if(this instanceof test) {
        this.cool = "created by test";
        this.method = bla;
    } else {
        //вообще что возвращаем из конструктора, то и будет результирующим объектом
        //у нас же при простом вызове нужно возвратить функцию, потому "с дефолтным возвратом" для конструктора
        return bla;
    }
    
    function bla() {
        //так на кого там this указывало...?
        alert(this.cool);
    }
}

//вызов в контексте global/window, по дефолту
test()(); //vingrad

//это литеральный объект, эквивалентно new Object(); b.cool=...
var b = {cool: "some value"};
test().apply(b); //вызываем в контексте нашего объекта, т.е. как его метод

//или "устанавливаем методом" и вызываем, конкретно механизма не меняет
b.method = test();
b.method();

//на чужих объектах пробовали, а на самой test нет =)
var c = new test();
//этот метод вкупе с полем cool мы *создали* в конструкторе
c.method();
//я надеюсь после всего этого чтива ясно почему так работает
c.other_method = c.method;
c.other_method();


Вроде всё так гармонично сложилось. Сейчас пойдём в детали и попытаемся угадать ту траву, что 
курили нетскейповцы и ECMA'вцы, когда писали спецификацию JS.

В примере методом служила вложенная функция bla. Фактически не важно какая функция будет 
использована, можно создавать целые коллекции функционала и "добавлять" его в нужные объекты. 
Это немного по другому, чем привычное многим наследование по классам, программисты 
Smalltalk и Objective-C сейчас сразу увидели своё родное. А если ещё и вспомнить 
всё прочитанное 15 минут назад, то поймём, что все функци в JS это closure, значит и метод может 
иметь свой собственный контекст откуда угодно. А это значит, что метод будет иметь свои 
"приватные" поля, которые не обязательно определены в конструкторе объекта.

А есть ли способ объявить общие для всех объектов поля? Да, есть и имя ему 
prototype. Любой объект имеет ссылку на функцию конструктор, которая и инициализировала 
объект. Эта функция содержит поле .prototype, которое содержит пустой объект. Все поля 
добавленные в прототип сразу видны и в контексте самого объекта. Пример:
Код
function test() {
    this.local_field = 90;
}
test.prototype.global_field = "cool";

var a = new test();
alert(a.local_field + ", " + a.global_field);


Принцип элементарный, если в хеш-таблице объекта ключа не обнаружено, то иди в прототип 
конструктора и посмотри там. Вот тут и начинается прикол, в прототип можно добавлять новые поля 
просто создавая их как в примере выше, а можно заменить объект прототипа полностью другим 
объектом
. При чём чужой объект имеет собственный конструктор с прототипом, т.е. мы автоматом 
"наследуем" функционал чужого конструктора. Пример:

Код
function test() {
    this.local_field = 90;
}
test.prototype.global_field = "cool";

function bla() {
    this.bla_field = "wow!";
}
bla.prototype = new test();

var a = new bla();
//по смыслу унаследовали, в реале просто обращаемся к своему протипу, который в свою очередь  обращается к своему прототипу и т.д.
alert(a.bla_field + ", " + a.local_field + ", " + a.global_field)


Вот оно и наследование. Естественно любые изменения в прототипах bla или test тут 
же будут видны во всех объектах, порождённых от bla (* маленькая оговорка чуть позже). 
Например если создать  String.prototype.my_action = function()..., то во всех строках 
появится новый метод и можно сразу писать "строка".my_action().

В JS типы динамические, мало того, как мы видим объекты можно собрать с любым интерфейсом тут же 
на лету. Но оператор <object> instanceof <function> есть, проверяет принадлежность 
объекта к:
  • конструктору прототипа -- так можно иметь принадлежность к целой цепочке функций, от 
    прототипа к прототипу
  • к функции Object -- эту связь тоже нельзя изменить
Оператор возвращает true если функция является любым из перечисленных выше. Заметим, что 
ссылка на прототип сохраняется в самом объекте. Это значит, что породив кучу обьектов, 
затем сменив прототип и породив ещё кучу обьектов, мы увидим, что только вторая группа будет 
иметь принадлежность к конструктору прототипа. Сменив прототип функции полностью оригинальный 
объект мы теряем навсегда
, достать даже из ранее созданных обьектов былой прототип нельзя. 
Хотя эти объекты это прототип "видеть" будут. Также любой объект имеет специальное поле 
.constructor, от которое указывает на функцию конструктор, т.е. можно создать новые 
объекты не зная от какой функции этот объект. Впрочем всё гладко работает, пока мы полностью не 
заменим прототип конструктора...
Это всё лучше понять на примере:

Код
//внимание! следующий код может хорошо промыть мозги
//лучше понять как оно фактически/физически работает, вместо попытки осмыслить "зачем"
//потому что не было никакого "зачем", все эти тонкости родились чисто как следствие реализации

function test() { //первый конкструктор
    this.local_field = 90; //собственное поле объекта
}
test.prototype.global_field = "cool"; //видимое/унаследованное поле

function bla() { //второй конструктор, никакой "родственной" связи
    this.bla_field = "wow!";
}

var t = new bla(); //этот объект создан до изменения прототипа, запомним поведение instanceof
alert(t instanceof bla); //true -- логично
alert(t instanceof t.constructor); //true -- тоже логично, помним что .constructor указывает на  bla
alert(t.constructor === bla); //true -- верно, помним что .constructor указывает на bla
alert(t instanceof test); //false -- верно, никаких связей с test пока не имеем

//мы *полностью* меняем прототип, оригинальный прототип утерян навсегда
bla.prototype = new test();

//этот объект ещё *унаследовал* функционал test
var a = new bla();

//проверяем
alert(t.bla_field + ", " + t.local_field + ", " + t.global_field); //wow!, undefined, undefined  -- верно, мы ничего не наследовали
alert(a.bla_field + ", " + a.local_field + ", " + a.global_field); //wow!, 90, cool

//заметим, что вроде как всё доступно, впрочем позже поймём что само поле .constructor уже не то  что было ;-)
alert(t.constructor.prototype.local_field); //90

//опа, а конструктор то уже не bla, а взят из прототипа от test, следовательно и конструктор  test
alert(a.constructor); //function test() {...
//а t со своим былым прототипом, указывающим на bla... да нет тот blа...
alert(t.constructor); //function bla() {...
alert(a.constructor == t.constructor); //false -- естественно

alert(a instanceof bla); //true -- принадлежность к настоящему конструктору убить нельзя
alert(a instanceof test); //true -- новая принадлежность по прототипу

//внимание, приготовтесь!!!
alert(t instanceof bla); //false -- а какого х.. спросите вы?  
alert(t instanceof t.constructor); //false -- [censored 2] , а что за bla тогда в .constructor лежит? =)
alert(t.constructor === bla); //true
alert(t instanceof test); //false -- связи по прежнему нет

alert(t instanceof Object); //true -- этот долбанный Object имеет ко всем принадлежность

//магия на прототипах, добавили новые поля
test.prototype.vingrad = "some value";
test.prototype.method = function() { alert(this.vingrad); }

var b = new test();
b.method(); //some value
a.method(); //some value -- видим все изменения прототипа
//t.method(); //будет ошибка, прототипа test естественно не видим


Из примера видим, что сменив прототип у функции конструктора, все созданные ранее объекты 
объявляются как бы "не при делах" и вроде как уже не принадлежат своему конструктору. Мало того, 
поле constructor они сохраняют, но раз функции родителю больше не принадлежат, значит и 
собственному конструктору больше не принадлежат. Это не могло быть придумано специально, просто 
не подумали и допустили полную смену прототипа.

Выводы:
  • все вызовы функций в JS это фактически вызов методов, потому в любом месте this доступно
  • все функции и методы это переменные/поля, содержащие объект-функцию
  • объявленные в контексте переменные должны работать быстрей, чем в объекте (через this)
  • создание объекта это создание пустой хеш-таблицы и последующая инициализация её через 
    конструктор
  • любую функцию можно вызвать как метод любого объекта
Домашнее задание: написать простейший интерпретатор JS, не оптимизирующий и можно эвалюирующий 
AST.
Для продвинутых: реализовать вызов функции с созданием контекста на стеке smile

На "краткие" размышления ушло почти 4 часа, на этом заканчиваю. Потом может распишу мелочи, но 
лучше просто задавайте вопросы кому это всё интересно и не ясно.

Добавлено @ 04:22 
Млин, это ж обсуждение  smile 

Все согласные и не согласные с точкой зрения выше прошу отписаться. Если кто сумеет отформатировать с проверкой грамматики текст выше, то поместите пожалуйста на вики. У меня самого точно руки не сразу дойдут до этого.


--------------------
 Опыт - сын ошибок трудных  © А. С. Пушкин
 Процесс написания своего велосипеда повышает профессиональный уровень программиста. © Opik
 Оценить мои качества можно тут.
PM   Вверх
Cheba
Дата 19.2.2007, 14:49 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


pointless one
***


Профиль
Группа: Vingrad developer
Сообщений: 1777
Регистрация: 27.11.2003
Где: /dev/null

Репутация: 1
Всего: 62



А почему это не оформленно в виде статьи в вики?
PM MAIL ICQ   Вверх
Sardar
Дата 19.2.2007, 15:10 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бегун
****


Профиль
Группа: Модератор
Сообщений: 6986
Регистрация: 19.4.2002
Где: Нидерланды, Groni ngen

Репутация: 78
Всего: 317



Потому что началось с двух строк, а затем меня понесло  smile 
Форма изложения не для статьи и сам контент нужно взвесить с дополнениями, вдруг чего упустил.


--------------------
 Опыт - сын ошибок трудных  © А. С. Пушкин
 Процесс написания своего велосипеда повышает профессиональный уровень программиста. © Opik
 Оценить мои качества можно тут.
PM   Вверх
Zeroglif
Дата 19.2.2007, 17:07 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 644
Регистрация: 22.9.2005

Репутация: 28
Всего: 66



Цитата(Sardar @  19.2.2007,  04:12 Найти цитируемый пост)
Все согласные и не согласные с точкой зрения выше прошу отписаться.

Щаз отпишусь. smile

Цитата(Sardar @  19.2.2007,  04:12 Найти цитируемый пост)
переменная это ссылка на значение

Мне по-прежнему режет слух слово "ссылка", переменные в javascript содержат только значения.

Цитата(Sardar @  19.2.2007,  04:12 Найти цитируемый пост)
Объект функции это фактически ссылка на скомпилированный код функции и на контекст, в котором функция была определена.

Контекст - понятие аморфное, более широкое, с ним, например, ассоциировано/связано значение this, для функций есть объект arguments и т.д. и т.п. В более узком конкретном смысле функция (function-value) - это код плюс цепь объектов в лексическом окружении (scope chain), а не плюс "родительский" контекст.

Цитата(Sardar @  19.2.2007,  04:12 Найти цитируемый пост)
контекст содержит только переменные

Не контекст, а цепь объектов в лексическом окружении (scope chain), которая состоит из определённых объектов (variable objects), у которых в момент конкретизации переменных (variable instantiation) создаются свойства с именами, равными именам переменных, именам формальных параметров, именам объявленных функций. А контекст содержит (если можно так сказать) много чего.

Цитата(Sardar @  19.2.2007,  04:12 Найти цитируемый пост)
Контекстом обладают только функции.

Конечно, нет. Зависит от исполняемого кода (global code, function code, eval code). И, в приниципе, можно исключить built-in функции. Исполнение глобального кода согласно документальных указаний партии не связывается с некой run-функцией, отсюда нет смысла это предполагать.

Цитата(Sardar @  19.2.2007,  04:12 Найти цитируемый пост)
Внутри функции любой if/while/for/etc может очерчивать любые блоки с фигурными скобками, всё равно все "определённые" в них переменные будут видны по всей функции (только try/catch имеют "собственную" переменную).

Если говорить принципиально о блоках, то, например, реализация от Мозиллы может скрыть внутри блока объявленную функцию.

Цитата(Sardar @  19.2.2007,  04:12 Найти цитируемый пост)

//здесь должно быть ясно, что это краткий синтаксис var cool = function() {...
function cool() {

Почему же ты упорно смешиваешь FunctionDeclaration и FunctionExpression? Мало того, что это разные вещи, так ещё и в разных браузерах эти различия умножены на два. Особенно если у FunctionExpression есть опциональное имя. Никакой это не краткий синтаксис.

Цитата(Sardar @  19.2.2007,  04:12 Найти цитируемый пост)
Именно по этому я часто говорю, не забываем про var, иначе вы плодите "глобальные" переменные.

В смысле плодим свойства объекта Global (window в браузерах).

Цитата(Sardar @  19.2.2007,  04:12 Найти цитируемый пост)
все функции в JS это closure

Спорно. С одной стороны действительно функция несёт на борту цепь объектов в лексическом окружении, а, с другой стороны, замыкание начинает "играть всеми красками" только после того, как переживёт свой лексический контекст, а ещё лучше порождаясь от разных вызовов. Иными словами, то ли считать все функции замыканиями, то ли (как говорит 12345с) только "зомби".

Цитата(Sardar @  19.2.2007,  04:12 Найти цитируемый пост)
Сразу скажу, этой операции в реальном JS нет, все функции всегда вызываются как методы.

Метод это... Первоисточник нам разъясняет: A function stored in a property of an object is called a method. Отсюда вопрос, чьим методом является "анонимная" function(){} и что это за вызов function(){}()?

Цитата(Sardar @  19.2.2007,  04:12 Найти цитируемый пост)
при создании объекта всегда создаётся новая пустая хеш-таблица

В какой реализации такое происходит?

Цитата(Sardar @  19.2.2007,  04:12 Найти цитируемый пост)
К объекту внутри функции обращаемся через this. На ком функцию вызвали, на того 
this и указывает.

Это слишком идеально звучит. Внутри функции через this обращаемся к значению this, а этим значением может быть частенько совем не то, что предписывает ES, особенно в части DOM/BOM перверсий.

Цитата(Sardar @  19.2.2007,  04:12 Найти цитируемый пост)
Также любой объект имеет специальное поле .constructor

Мы с тобой уже это как-то обсуждали. У созданных объектов нет такого свойства, это свойства их прототипа.
PM MAIL WWW   Вверх
Sardar
Дата 19.2.2007, 21:57 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бегун
****


Профиль
Группа: Модератор
Сообщений: 6986
Регистрация: 19.4.2002
Где: Нидерланды, Groni ngen

Репутация: 78
Всего: 317



Цитата(Zeroglif @  19.2.2007,  16:07 Найти цитируемый пост)
Мне по-прежнему режет слух слово "ссылка", переменные в javascript содержат только значения.

Ну это опять же, смотря кто/что считает ссылкой и значением smile  Да, переменная это tagged union, который помимо чисел может содержать значение-ссылку, которая ссылается на объект в куче. Только вот куча народа, включая меня называет последнее ссылкой, потому и говорим, что объекты передаются по ссылке, а примитивы по значению.

Цитата(Zeroglif @  19.2.2007,  16:07 Найти цитируемый пост)
Контекст - понятие аморфное, более широкое, с ним, например, ассоциировано/связано значение this

Ошибаешься, this к контексту функции не имеет отношения. Впрочем определение контексту я дал сам, обозначив только переменные функции (куда входят аргументы функции), с точки зрения реализации. this указывает на совершенно другой объект. Кстати поля по this не реально связать на момент трансляции (обращение  по имени), переменные контекста наоборот легко "скомпилировать", что бы обращаться по прямой ссылке (имён на этапе выполнения уже нет). Это совершенно два разных механизма.

Впрочем с филосовско/логической точки зрения к контексту можно отнести что угодно smile

Цитата(Zeroglif @  19.2.2007,  16:07 Найти цитируемый пост)
Не контекст, а цепь объектов в лексическом окружении (scope chain), которая состоит из определённых объектов (variable objects), у которых в момент конкретизации переменных (variable instantiation) создаются свойства с именами, равными именам переменных, именам формальных параметров, именам объявленных функций. А контекст содержит (если можно так сказать) много чего.

Определение верное, последнее предложение как уже сказал выше, не совсем. Впрочем это всё вопросы терминологии, говорим об одном и том же.

Цитата(Zeroglif @  19.2.2007,  16:07 Найти цитируемый пост)
Конечно, нет. Зависит от исполняемого кода (global code, function code, eval code). И, в приниципе, можно исключить built-in функции. Исполнение глобального кода согласно документальных указаний партии не связывается с некой run-функцией, отсюда нет смысла это предполагать.

Аргументированно доказать сможешь? Какие нибудь свойства с кодом привести? Единственное, что на ум приходит, это свойство глобального кода, где локальны переменные также являются пропртями/полями global (window), т.е. видимы по this из функций, вызванных на global. Если бы я реализовывал JS, то глобальный код у меня был бы просто самой внешней функцией, ибо логично.

Цитата(Zeroglif @  19.2.2007,  16:07 Найти цитируемый пост)
Если говорить принципиально о блоках, то, например, реализация от Мозиллы может скрыть внутри блока объявленную функцию.

Кстати, а это не правильное поведение. Поднять бы ECMA, там точно стоит, что перед началом выполнения функции все переменные должны быть видны сразу, только мозилловцы не поняли, что функции это тоже переменные. Видать какие их особые детали реализации.
Код
function test(a) {
    if(a) {
        function bla() { alert('OK'); }
        var b = "test";
    }
    alert(b);
    alert(bla); //здесь мозилла кинет исключение
}
test(true);
test(false);

IE и Opera отрабатывают правильно показывая bla в обоих вызовах. Лиса и вероятно все мозиллоподобные на втором вызове кидают исключение, т.к. bla не видна, если if не отработал.

Цитата(Zeroglif @  19.2.2007,  16:07 Найти цитируемый пост)
Почему же ты упорно смешиваешь FunctionDeclaration и FunctionExpression?

Нука приведи ка цитату из ECMA, где это две разные вещи? Хотя это было моим мнением, как я бы реализовал. Недавно язык свой написал, Forthy, там блоки похожи на функции/closure в JS, физически различий не было, т.к. всё это значение (expression):
Код
function test(a) {
    (function bla() { alert('OK: ' + a); })();
}
test(true);


Цитата(Zeroglif @  19.2.2007,  16:07 Найти цитируемый пост)
В смысле плодим свойства объекта Global (window в браузерах).

Сколько по твоему прокрутим цикл:
Код
function test() {
    for(i = 0; i < 4; i++) bla();
}
function bla() {
    for(i = 0; i < 2; i++) alert('OK: ' + i);
}
test();



Цитата(Zeroglif @  19.2.2007,  16:07 Найти цитируемый пост)
Спорно. С одной стороны действительно функция несёт на борту цепь объектов в лексическом окружении, а, с другой стороны, замыкание начинает "играть всеми красками" только после того, как переживёт свой лексический контекст, а ещё лучше порождаясь от разных вызовов. Иными словами, то ли считать все функции замыканиями, то ли (как говорит 12345с) только "зомби".

Э... другими словами попробуй пояснить, что имеешь в виду? Любая функция в JS может "пережить" свой лексический контекст, если её вернуть или записать в какую переменную.

Цитата(Zeroglif @  19.2.2007,  16:07 Найти цитируемый пост)
Отсюда вопрос, чьим методом является "анонимная" function(){} и что это за вызов function(){}()?

Повторил несколько раз, это методы global. Повторяем: в JS не методов, есть вызов функии как метода. Просто функция вызывается как метод объекта global. Повторяю: говорим не о философии ООП, а о технической реализации.

Цитата(Zeroglif @  19.2.2007,  16:07 Найти цитируемый пост)
В какой реализации такое происходит?

В моей мнимой, в Rhino по моему тоже, хотя плотно не копался.

Цитата(Zeroglif @  19.2.2007,  16:07 Найти цитируемый пост)
Это слишком идеально звучит. Внутри функции через this обращаемся к значению this, а этим значением может быть частенько совем не то, что предписывает ES, особенно в части DOM/BOM перверсий.

Примеры? "Не то" в логическом смысле (ты не получил то, что хотел) серьёзно отличается от "не то" когда не согласуется со спецификацией (такого я не видел). Скрипт делает ровно то, что ты ему велишь.

Цитата(Zeroglif @  19.2.2007,  16:07 Найти цитируемый пост)
Мы с тобой уже это как-то обсуждали. У созданных объектов нет такого свойства, это свойства их прототипа. 

Угу, только этим и объясняется то странное поведение при смене прототипа.


--------------------
 Опыт - сын ошибок трудных  © А. С. Пушкин
 Процесс написания своего велосипеда повышает профессиональный уровень программиста. © Opik
 Оценить мои качества можно тут.
PM   Вверх
Zeroglif
Дата 19.2.2007, 23:18 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 644
Регистрация: 22.9.2005

Репутация: 28
Всего: 66



Цитата(Sardar @  19.2.2007,  21:57 Найти цитируемый пост)
Ошибаешься, this к контексту функции не имеет отношения.

Не ошибаюсь. Из ECMAScript (10.1.3, 10.1.4, 10.1.7): "Every execution context has associated with it a variable object. Every execution context has associated with it a scope chain. There is a this value associated with every active execution context". Как видишь призрачность контекста раскрывается через основные его механизмы, в кои входят и variable object, и scope chain, и this value и т.д. Если их убрать, то вообще непонятно станет who is who... execution context...

Цитата(Sardar @  19.2.2007,  21:57 Найти цитируемый пост)
Аргументированно доказать сможешь?

А чего доказывать, если нет каких-либо свидетельств того, что глобальный код исполняет некая функция. Нет специфики этой функции, нет особенностей, зато есть чёткое разделение на виды исполняемого кода, где глобальный код выделен отдельно:

10.1.2 Types of Executable Code 
There are three types of ECMAScript executable code

10.2.1 Global Code 
The scope chain is created and initialised to contain the global object and no others. 
Variable instantiation is performed using the global object as the variable object and using property attributes { DontDelete }. 
The this value is the global object.


Цитата(Sardar @  19.2.2007,  21:57 Найти цитируемый пост)
Кстати, а это не правильное поведение.

Своеобразное, как и IE-шное. У каждого свои тараканы. Здесь когда-то пытался разобраться, а позже я нашёл в багзилле разъяснение, в общем-то подтверждающее идею.

Цитата(Sardar @  19.2.2007,  21:57 Найти цитируемый пост)
Нука приведи ка цитату из ECMA, где это две разные вещи?

ES (13), там много цитат. Я бы выделил 4 основных различия:
1) время создания функции, FD как и переменная создаётся (наполняется значением) сразу же, а FE в рантайм;
2) опциональное имя для FE, когда согласно ES создаётся свой спецобъект, встраиваемый в scope chain;
3) зависимость от места обитания, FE c именем именно потому FE, а не FD, ибо находится там, где может быть выражения;
4) совершенно разная реализация в трёх основных браузерах.

Цитата(Sardar @  19.2.2007,  21:57 Найти цитируемый пост)
Любая функция в JS может "пережить" свой лексический контекст, если её вернуть или записать в какую переменную.

Это понятно, я хотел донести, что более правильно, не называть каждую функцию изначально замыканием (вроде "все функции в JS это closure"), а считать таковой только ту, что пережила контекст.

Цитата(Sardar @  19.2.2007,  21:57 Найти цитируемый пост)
Повторил несколько раз, это методы global. Повторяем: в JS не методов, есть вызов функии как метода. Просто функция вызывается как метод объекта global. Повторяю: говорим не о философии ООП, а о технической реализации.

Не дави :-) я всё равно не соглашусь. Всё наоборот. В javascript есть методы, и есть этому совершенно чёткие и логические определения:
 
1) function stored in a property of an object is called a method
2) method is a function associated with an object via a property

А вот относительно вызова функции никакого разделения (метод не метод) нет, просто работает(должен работать) строгий алгоритм разбора того, что стоит слева от оператора вызова. И конечно же, согласно этого алгоритма анонимная функция никак не связана с глобальным объектом (каким боком она бы к нему прикрепилась без имени?). При вызове в this будет передан null, который потом сменится на global object. Но справедливости ради, я бы сказал, что анонимная функция тоже может быть неким методом, но для этого её надо записать соответствующим образом, вроде:

Код

(function(){alert(this)}).prototype.constructor();


Если же брать именованные функции, или функции сохранённые в переменной или непосредственно методы какого-либо объекта, то на этапе создания с точки зрения ООП их можно смело называть методами (как это описывает ES выше), а вот во время вызова всё зависит от формы вызова, свою принадлежность через this они могут и не показывать.

Цитата(Sardar @  19.2.2007,  21:57 Найти цитируемый пост)
Скрипт делает ровно то, что ты ему велишь.

В идеале так, это когда сам завёл объект и метод ему прописал. Но DOM/BOM объекты и методы ты же не руками прописываешь, можешь ожидать чего угодно, вспомни self===this в IE, вроде простая операция, а объекты не сводит, чего уж тут о более сложной реализации контекста. Или к примеру, считается, что IE не прав, когда не передаёт this в обработчик через attachEvent, а Мозилла, наоборот, права. А что, Мозилла права с точки зрения ECMAScript и this value? Я сильно сомневаюсь.

PM MAIL WWW   Вверх
Sardar
Дата 20.2.2007, 01:47 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бегун
****


Профиль
Группа: Модератор
Сообщений: 6986
Регистрация: 19.4.2002
Где: Нидерланды, Groni ngen

Репутация: 78
Всего: 317



Цитата(Zeroglif @  19.2.2007,  22:18 Найти цитируемый пост)
Не ошибаюсь. 

Нда... это всё таки не понятки с термином контекста. Ладно, я освобожу его в пользу ECMA. Объявим два Х контекста:
  • this - расширяемый объект, почти всегда реализован как хеш-таблица
  • var переменные функции - контекст функции, создаваемый на момент "входа/выполнения" функции, обычно жёсткий массив ячеек, адресуемых прямой ссылкой
Сути не меняет, это совершенно разные две вещи, реализованные по разному. Термин контекст выполнения это, согласно ECMA, всё то до кучи с точки зрения программиста, а не реализации.

Цитата(Zeroglif @  19.2.2007,  22:18 Найти цитируемый пост)
А чего доказывать,

Ладно, этот контекст выполнения действительно отличается, как я уже говорил, тем что не известная переменная с запросом set будет создана и все переменные становятся пропертями global, в остальном поведение как в функции.

Цитата(Zeroglif @  19.2.2007,  22:18 Найти цитируемый пост)
 Здесь когда-то пытался разобраться

Интересный пример. Нашёл в ECMA различия между FunctionDeclaration и FunctionExpression. Чем они руководствовались не знаю, но ИМХО ИЕ выдал самый разумный ответ. Заметим, что убери эти поправки из ECMA, то всё функционально останется тем же, просто будет меньше геморроя и ошибок с понятиями чем является сейчас объявление функции, выражением или всё таки декларацией. Спасибо за мысль smile

Цитата(Zeroglif @  19.2.2007,  22:18 Найти цитируемый пост)
а считать таковой только ту, что пережила контекст.

Фактически можно отследить на этапе трансляции будет ли функция "взята" как closure, следовательно можно провести оптимизации и создавать ссылки с текущим контекстом только когда нужно. Но это скорее детали реализации.

Цитата(Zeroglif @  19.2.2007,  22:18 Найти цитируемый пост)
В javascript есть методы, и есть этому совершенно чёткие и логические определения

Это логическое определение, мы же описываем техническую реализацию. При чём не просто "а вот так хочу", а реально причины, по которым разработчик интерпретатора мог бы выделить специальные конструкции/случаи, когда это метод не просто значение в проперте. Тут я тоже буду стоять на своём smile


Цитата(Zeroglif @  19.2.2007,  22:18 Найти цитируемый пост)
И конечно же, согласно этого алгоритма анонимная функция никак не связана с глобальным объектом (каким боком она бы к нему прикрепилась без имени?). При вызове в this будет передан null, который потом сменится на global object.

А почему так? Зачем это дополнительное условие в интерпретаторе? Ведь ещё на этапе трансляции можно определить на ком (переменная или global) будет вызов. Впрочем это детали, в конкретной реализации может быть и по твоему.

Цитата(Zeroglif @  19.2.2007,  22:18 Найти цитируемый пост)
Если же брать именованные функции, или функции сохранённые в переменной

Цитата(Zeroglif @  19.2.2007,  22:18 Найти цитируемый пост)
или непосредственно методы какого-либо объекта

Не согласен с такими формулировками, нет именнованных функций, нет различия между гипотетической "именнованной" и сохранённой в переменной. Не понимаю утверждения "непосредственно методы", ибо нет такого понятия JS. Впрочем если ECMA называет эти вещи так как ты, то пусть будет так, но я всё равно бы реализовал как можно проще, без лишних свойств никак не проявляемых (ну почти никак) в коде.

Цитата(Zeroglif @  19.2.2007,  22:18 Найти цитируемый пост)
а вот во время вызова всё зависит от формы вызова, свою принадлежность через this они могут и не показывать

Поясни что имел в виду под "показывать принадлежность".

Цитата(Zeroglif @  19.2.2007,  22:18 Найти цитируемый пост)
Или к примеру, считается, что IE не прав, когда не передаёт this в обработчик через attachEvent

Ну это уже логика окружения, в принципе к JS не имеет отношения. Впрочем ладно, оставим это smile


Так, поправок много, с частью согласен, с частью нет. Надо бы переписать всё это под общую ноту, но как бы не переписать ECMA в новом изложении smile
Предлагаю перенести дискуссию в тему как создать эффективный и быстрый интерпретатор/транслятор JS. Моменты, где ECMA описывает "лишние" условия поведения рассмотреть подробней и попытаться понять, что парнями двигало. Расписать собственную реализацию и плюсы по производительности/логичности, чем те же механизмы описанные в ECMA. В идеале этого может хватить для детальной проработки движка JS, может потом напишу для опыта.


--------------------
 Опыт - сын ошибок трудных  © А. С. Пушкин
 Процесс написания своего велосипеда повышает профессиональный уровень программиста. © Opik
 Оценить мои качества можно тут.
PM   Вверх
Zeroglif
Дата 20.2.2007, 03:22 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 644
Регистрация: 22.9.2005

Репутация: 28
Всего: 66



Дискуссию потихоньку надо сводить на нет, т.к. речь пошла об интерпретаторе/трансляторе JS и его разработке. smile

Цитата(Sardar @  20.2.2007,  01:47 Найти цитируемый пост)
Термин контекст выполнения это, согласно ECMA, всё то до кучи с точки зрения программиста, а не реализации.

Чтоб мы понимали друг друга всё равно нужна единая терминология, тем более, что ES это хлеб не столько даже для программиста, сколько для ваятеля комфорного движка. Расписано детально. Почти.

Цитата(Sardar @  20.2.2007,  01:47 Найти цитируемый пост)
Не согласен с такими формулировками, нет именнованных функций, нет различия между гипотетической "именнованной" и сохранённой в переменной.

Не согласен, что FD создаётся сразу (статически), а FE в рантайм (динамически)? Не согласен, что FD не может быть в блоке, а FE может? Не согласен, что у FD своё строгое королевское место, а FE может жить везде, где живёт Expression? Я уж совсем молчу, если у сохранённой в переменной функции есть имя, вот где бушует море различий (правда уже в реализациях):
 
Код

/* согласно ECMAScript идентификатор функции-выражения FE
   не может быть виден снаружи, только изнутри, но IE видит всё
*/
try {
    alert(A); //-> IE видит, причём не только чистый код функции
} catch(e) {
    alert(e); //-> Mz не видит 
}
(function A(){});


/* IE может "задваивать функции"; на этапе конкретизации переменных создаётся
    функция-декларация FD, а затем, если эта функция в рантайме должна быть по 
    правилам функцией-выражением FE, создаётся двойник
*/
//-> 1-ый вызов
(function B() {
    alert('ссылки на один объект? - ' + (arguments.callee == B)); //-> false в IE, true в Mz
})();

//-> 2-ой вызов
try {
    B();      //-> а сейчас уже true в IE
} catch (e) {
    alert(e); //-> Mz не видит
}


/* или ещё более простой пример, доказывающий, что ссылки ведут на разные объекты,
    хотя объект должен быть один и тот же
*/

var C = function D(){
    alert('ссылки на один объект? - ' + (C == D));
}
C(); //-> false в IE, true в Mz

try {
    D();      //-> снова false в IE
} catch (e) {
    alert(e); //-> Mz не видит
} 


Цитата(Sardar @  20.2.2007,  01:47 Найти цитируемый пост)
Не понимаю утверждения "непосредственно методы", ибо нет такого понятия JS.

Я имел в виду методы явно созданные, например, obj.method, в отличие от не явного создания, как это бывает с переменными, декларациями функций и т.д.

Цитата(Sardar @  20.2.2007,  01:47 Найти цитируемый пост)
Поясни что имел в виду под "показывать принадлежность".

Это когда значением this является объект, чей метод в данный момент исполняется, например, obj.method(). Идеальный вариант - полное имя, видна принадлежность метода. Или, допустим, мы объявили в глобальном коде функцию F, получим метод объекта Global. Потом вызываем F() или window.F(). И в том и в другом случае значением this является объект, свойством которого является функция F. А если эта функция F объявлена внутри другой функции? Тогда, с одной стороны, это метод Variable Object (метод всё-таки), а, с другой стороны, при вызове "принадлежности" нет (и где же метод). Отсюда вопрос, с какого ракурса вообще рассматривать метод: с точки зрения свойства объекта, с точки зрения вызова и this, с точки зрения...
PM MAIL WWW   Вверх
Sardar
Дата 20.2.2007, 16:54 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бегун
****


Профиль
Группа: Модератор
Сообщений: 6986
Регистрация: 19.4.2002
Где: Нидерланды, Groni ngen

Репутация: 78
Всего: 317



Цитата(Zeroglif @  20.2.2007,  02:22 Найти цитируемый пост)
Дискуссию потихоньку надо сводить на нет, т.к. речь пошла об интерпретаторе/трансляторе JS и его разработке. 

ОК, действительно надо будет вырезать это всё в ветку JS, но здесь у меня прав нет.

Цитата(Zeroglif @  20.2.2007,  02:22 Найти цитируемый пост)
Не согласен, что FD создаётся сразу (статически), а FE в рантайм (динамически)?

Убедил, но поведение ИЕ ИМХО более удобное.

Цитата(Zeroglif @  20.2.2007,  02:22 Найти цитируемый пост)
Не согласен, что FD не может быть в блоке, а FE может?

Синтаксически и то и другое может быть где угодно, мало того, и то и другое является выражением (возвращает значение - функция). Но разделение ради видимости функции выглядит логично, хотя и путает. Убедил, но поведение ИЕ считаю более логичным.

Цитата(Zeroglif @  20.2.2007,  02:22 Найти цитируемый пост)
Не согласен, что у FD своё строгое королевское место, а FE может жить везде, где живёт Expression?

Нет, определить функцию FD синтаксисом можно где угодно, в любом выражении. Другое дело, что FD становится реально декларацией, только если в синтаксическом контексте, где возможен/ожидается statement. А различия всего то в видимости функции.

Цитата(Zeroglif @  20.2.2007,  02:22 Найти цитируемый пост)
Я уж совсем молчу, если у сохранённой в переменной функции есть имя, вот где бушует море различий (правда уже в реализациях)

Имя есть, но по нему нельзя обратится, оно играет только информативную роль (только для восстановления сорца). 
Код
function test() {
    alert('OK');
}
test = 'vingrad';

Не важно какое у функции имя, по нему нельзя обратиться к функции. В примере функция потеряна. Для ясности, имя функции должно давать доступ до функции или как нибудь функционально проявляться, а не просто тешить нас мыслью, что в каком то из пропертей обьекта-функции записано "имя".

Цитата(Zeroglif @  20.2.2007,  02:22 Найти цитируемый пост)
в отличие от не явного создания, как это бывает с переменными, декларациями функций и т.д.

А это что за механизм "не явного создания метода"?

Цитата(Zeroglif @  20.2.2007,  02:22 Найти цитируемый пост)
Отсюда вопрос, с какого ракурса вообще рассматривать метод: с точки зрения свойства объекта, с точки зрения вызова и this, с точки зрения... 

Ты рассматриваешь метод с позиции синтаксиса, я же с принципа выполнения/рантайма. При любом вызове функции будет доступен this и это никогда не будет null. Функцию можно вызвать на любом объекте, не обязательно, что бы это был его метод. Хотя последнее выполняется через метод самой функции, следовательно можно считать рантайм-хаком smile  Если отменить .call/.apply, то да, объект должен будет иметь поле с функцией, что бы вызвать её как метод "на себе", отсюда ладно, соглашусь с тобой/ECMA.

2Модератор: пора бы тему порезать и слить в JS.


--------------------
 Опыт - сын ошибок трудных  © А. С. Пушкин
 Процесс написания своего велосипеда повышает профессиональный уровень программиста. © Opik
 Оценить мои качества можно тут.
PM   Вверх
Wowa
Дата 20.2.2007, 17:53 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
Group Icon


Профиль
Группа: Админ
Сообщений: 15017
Регистрация: 14.9.2000
Где: Винград

Репутация: 1
Всего: 290



Цитата(Sardar @  20.2.2007,  14:54 Найти цитируемый пост)
ОК, действительно надо будет вырезать это всё в ветку JS, но здесь у меня прав нет.

Есть. Тут есть права у всех модеров и всех ко-модеров. Да и зачем резать? Быть может всю тему в раздел JS засунуть? Одновременно тема является обсуждением одноимённой страницы в Вики.
PM WWW   Вверх
AKS
Дата 20.2.2007, 19:56 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Участник форума
**


Профиль
Группа: Участник
Сообщений: 725
Регистрация: 20.9.2006

Репутация: 27
Всего: 52



Zeroglif
Код

(function A(){});

 - скобки вокруг функции A называются оператором группировки (11.1.6 The Grouping Operator)? 
PM MAIL   Вверх
vasac
Дата 20.2.2007, 21:07 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1060
Регистрация: 4.5.2006

Репутация: 13
Всего: 36



Прошу прощения. Ссылка в первом сообщении не работает. Что послужило началом данного диспута? Как я понял кто-то решил реализовать js-движок или нет?
PM WWW   Вверх
Wowa
Дата 20.2.2007, 21:11 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
Group Icon


Профиль
Группа: Админ
Сообщений: 15017
Регистрация: 14.9.2000
Где: Винград

Репутация: 1
Всего: 290



vasac, ссылка работает. Просто там пока статьи нет. Здесь идет обсуждение будущей статьи.
PM WWW   Вверх
vasac
Дата 20.2.2007, 21:13 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1060
Регистрация: 4.5.2006

Репутация: 13
Всего: 36



Понятно, спасибо.
PM WWW   Вверх
Ответ в темуСоздание новой темы Создание опроса
Форум для вопросов, которые имеются в справочниках, но их поиск вызвал затруднения, или для разработчика требуется совет или просьба отыскать ошибку. Напоминаем: 1) чётко формулируйте вопрос, 2) приведите пример того, что уже сделано, 3) укажите явно, нужен работающий пример или подсказка о том, где найти информацию.
 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | JavaScript: Общие вопросы | Следующая тема »


 




[ Время генерации скрипта: 0.0811 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.