Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > JavaScript: Общие вопросы > function


Автор: Се ля ви 15.8.2008, 12:05
Продолжаю экспериментировать с тонкостями JS в свободное время.

На этот раз объектом исследования стало использование ключевого слова 'function'.

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

Код
function A(){
    //...Some operations
}

вот так:
Код
var A = new Function('<operations>');


Однако это не очень наглядно и на самом деле, как будет показано ниже, не является одним и тем же.


Специфика этого объекта довольно сильная. Так, например, операция typeof вообще не признает функцию объектом:
Код
alert(typeof function(){alert('!!')}) // Выведет 'function', а не 'Object'!


Тем не менее, это не примитив, ибо операция instanceof однозначно свидейтельствует, что это экземпляр, созданный конструктором Function:
Код

alert(function(){alert('!!')} instanceof Function) //Выводит 'true'!


Почему я поставил перед объявлением var? Проверим:
Код
function A(){
    function B(){alert('!');};
    B();
}
var a = new A; //выводит '!'
//a.B(); // Выводит ошибку 'a.B is not a function'
//A.B(); //Выводит ошибку 'A.B is not a function'
//B(); //Выводит ошибку 'B is not defined'

Т.е. внутренняя функция доступна только внутри closure, где расположена, как и переменные, объявленные с ключевым словом 'var'

Так в чём же отличие? В том, что во втором случае мы не задействуем closure:
Код
function A(){
    
    var x = 5;
    function B(){alert('x = ' + x);};
    B(); //Выводит 'x = 5'
    
    var C = new Function('alert(\'x = \' + x);');
    C(); //Выводит ошибку 'x is not defined'
}
var a = new A();


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

Автор: dsCode 15.8.2008, 16:32
Цитата(Се ля ви @  15.8.2008,  12:05 Найти цитируемый пост)
Так что в нашем распоряжении прекрасный метод борьбы с closure в случаях, когда оно нам не нужно


Да нет, тут дело в том, что функции, созданные через конструктор Function, работаю с глобальным скопом: "Pass in a scope chain consisting of the *global object as the Scope* parameter." - ECMAScript specification, 15.3.2.1.

Поэтому:

Код

function test() {
var a = 10;
var b = Function('alert(a);');
b(); // падает по эксепшену, "а" - не определена
}


Если же, приплюсовать значение переменной "а" к строке - var b = Function('alert(' + a + ');'); - то все нормально. Данное строковое тело функции парсится каждый раз (в отличии от function () {...}), поэтому этот метод (Function('param', 'parsableFunctionBody')) менее эффективен в скорости (да и работает с глобальным скопом).

Или же (т.к. Function(...) в качестве скопа имеет глобальный объект) - объявить var a = 10; - глобально:

Код

var a = 10;
function test() {
var b = Function('alert(a);');
b(); // все ок - 10
}


Цитата(Се ля ви @  15.8.2008,  12:05 Найти цитируемый пост)
//a.B(); // Выводит ошибку 'a.B is not a function'
//A.B(); //Выводит ошибку 'A.B is not a function'
//B(); //Выводит ошибку 'B is not defined'


Формальные параметры, var'ы и внутренние декларации функций - становятся свойствами variable object'a функции (VO) (10.1.3 - уже отмечалось не раз), а не самой функции. Т.е., если где и искать B, то в A.VO.B(); (утрированно, ествественно =)). 

Цитата(Се ля ви @  15.8.2008,  12:05 Найти цитируемый пост)
Т.е. внутренняя функция доступна только внутри closure, где расположена, как и переменные, объявленные с ключевым словом 'var'


Почему это только внутри? Ее же можно вернуть и она будет доступна снаружи.

Код

function a() {
  return function b() {
    alert(1); 
  };
}
var fn = a();
fn();


И более того, не все функции (вернее сказать, не все типы функций) доступны внутри функции. Се ля ви, как ты объяснишь это поведение?:

Код

(function a() {
  (function b() {alert(1);});
  b(); // b - undefined?
})();


Подсказка (вернее, можно считать ответом =)): все дело в различном воздействии function-declaration и function-expression на variable object функции.

Автор: Zeroglif 15.8.2008, 17:10
Цитата(dsCode @  15.8.2008,  16:32 Найти цитируемый пост)
строковое тело функции парсится каждый раз (в отличии от function () {...}), поэтому этот метод (Function('param', 'parsableFunctionBody')) менее эффективен в скорости (да и работает с глобальным скопом).


Теоретически парсится также, как и любое выражение (каждый раз или не каждый... э... построчно, в общем), также как и, например, var x = function () {};...

Цитата(dsCode @  15.8.2008,  16:32 Найти цитируемый пост)
все дело в различном воздействии function-declaration и function-expression на variable object функции


IE-юзвери пример не поймут. ;) 

Автор: dsCode 15.8.2008, 18:59
Цитата(Zeroglif @  15.8.2008,  17:10 Найти цитируемый пост)
Теоретически парсится также, как и любое выражение (каждый раз или не каждый... э... построчно, в общем), также как и, например, var x = function () {};...


Хм... Ну да, тут, смотря с какой стороны смотреть (а как в спецификации написано? - я что-то упустил это) - понятно, что все интерпретируется построчно при вызове. Ну а все же - есть разница? С одной стороны - уже готовый к вызову callable-объект (функция), с другой - строка - которую нужно сначала превратить в этот объект, а потом уже начинать выполнять построчно. Или... Что? Как только парсится первая строчка (в большой строке кода), - она сразу выполняется?

Цитата(Zeroglif @  15.8.2008,  17:10 Найти цитируемый пост)
IE-юзвери пример не поймут. ;)


а IE вообще function-expression не различает в этом отношении (в плане скопа)? =) Или все-таки есть случаи, когда IE соответствует спецификации в этом месте? - я посмотрел - любой из видов function-expression доступен для вызова за пределами объявления.

Автор: Се ля ви 19.8.2008, 12:10
Цитата(dsCode @  15.8.2008,  16:32 Найти цитируемый пост)
Цитата(Се ля ви @  15.8.2008,  12:05 Найти цитируемый пост)
Так что в нашем распоряжении прекрасный метод борьбы с closure в случаях, когда оно нам не нужно

Да нет, тут дело в том, что функции, созданные через конструктор Function, работаю с глобальным скопом

Я это и имел в виду smile


Цитата(dsCode @  15.8.2008,  16:32 Найти цитируемый пост)
Если же, приплюсовать значение переменной "а" к строке - var b = Function('alert(' + a + ');'); - то все нормально. Данное строковое тело функции парсится каждый раз (в отличии от function () {...}), поэтому этот метод (Function('param', 'parsableFunctionBody')) менее эффективен в скорости (да и работает с глобальным скопом).

А вот это интересно...


Цитата(dsCode @  15.8.2008,  16:32 Найти цитируемый пост)
Или же (т.к. Function(...) в качестве скопа имеет глобальный объект) - объявить var a = 10; - глобально:

Ну тогда весь смысл теряется. Я же играюсь с инкапсуляцией smile


Цитата(dsCode @  15.8.2008,  16:32 Найти цитируемый пост)
Формальные параметры, var'ы и внутренние декларации функций - становятся свойствами variable object'a функции (VO) (10.1.3 - уже отмечалось не раз), а не самой функции. Т.е., если где и искать B, то в A.VO.B(); (утрированно, ествественно =)).

Ну да, то, что я называю private`ами. Об том и речь smile

Цитата(dsCode @  15.8.2008,  16:32 Найти цитируемый пост)
Почему это только внутри? Ее же можно вернуть и она будет доступна снаружи.

Так любой private-объект, если его вернуть, будет доступен снаружи - кто бы сомневался. Просто возможность его скрыть и не зацепить при этом в closure то, что не нужно, представляет особый интерес smile
Цитата(dsCode @  15.8.2008,  16:32 Найти цитируемый пост)
И более того, не все функции (вернее сказать, не все типы функций) доступны внутри функции. Се ля ви, как ты объяснишь это поведение?:
    
Код
(function a() {
  (function b() {alert(1);});
  b(); // b - undefined?
})();


Подсказка (вернее, можно считать ответом =)): все дело в различном воздействии function-declaration и function-expression на variable object функции.

Ух ты! А вот это действительно интересная штука... Надо будет с ней покопаться, как время найду. Спасибо! smile

Автор: JSman 23.8.2008, 00:46
Цитата(Се ля ви @  19.8.2008,  12:10 Найти цитируемый пост)
 играюсь с инкапсуляцией

проблема с private была решена в base2, если не ошибаюсь.

Автор: S.A.P. 24.8.2008, 22:11
На основе этих рассуждений появилась бредовая идея как бороться со столько ненавистными нам замыканиями:
Код

function newFunction (body)
{
    return new Function ('return ('+String(body)+').apply(this, arguments);');
}

var f1 = newFunction(function (a)
{
    return a*2;
});

alert(f1(4));


создаваемые функции не используют глобальный скоп и при этом не лезут в наше замыкание + лишены приведённого недостатка с написанием и сопровождением.

Но всё равно както жутковато  smile 

Автор: JSman 17.9.2008, 00:40
кстати, еще хотел бы просто дополнить в тему по поводу копирования и ссылок на функции.

копирование функции (без всяких конвертирований) через оператор = можно сделать только при прямом присваивании функции к встроенному событию, например, onclick. в остальных случаях будет только ссылка. Причем копирование в этом случае будет только самого тела функции, без всяких расширений типа func.prop

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)