Javascript-форум (https://javascript.ru/forum/)
-   Оффтопик (https://javascript.ru/forum/offtopic/)
-   -   apply и call. В чём отличия? (https://javascript.ru/forum/offtopic/5156-apply-i-call-v-chjom-otlichiya.html)

B~Vladi 21.09.2009 11:16

apply и call. В чём отличия?
 
Читал стандарт языка, но ответа не нашел... Заметил, только что в call можно передавать много аргументов... Если есть ещё какие-нибудь отличия - напишите пжлст пример, где их можно посмотреть. Спс:)

B~Vladi 21.09.2009 11:19

А и ещё один вопрос - чтобы не плодить тем... Не могу найти описание хитрой конструкции:
var i=(function(){...})();

точно помню, что где-то было... Нужно именно описание, а не примеры...

Gvozd 21.09.2009 11:28

Цитата:

Сообщение от B~Vladi
Заметил, только что в call можно передавать много аргументов.

да, в общем-то и все отличия
function sum(a,b) {
    this.c = a + b
}
 
var obj = {}
 
sum.call(obj, 1,2)
// или
sum.apply(obj, [1,2])
 
alert(obj.c)   // => 3


Цитата:

Сообщение от B~Vladi
точно помню, что где-то было

наверно ты про эту тему?
http://javascript.ru/forum/misc/4071...jj-vopros.html

B~Vladi 21.09.2009 11:35

Цитата:

Сообщение от Gvozd
наверно ты про эту тему?

Да, точно! СПС:)

Цитата:

Сообщение от Gvozd
да, в общем-то и все отличия

Я подозревал, но меня сбил с толку такой текст:
Цитата:

Метод apply применяет метод одного объекта в контексте другого объекта.
и
Цитата:

Метод call вызывает метод одного объекта в контексте другого объекта.
из одного справочника...
Но теперь я спокоен:)

B~Vladi 21.09.2009 11:52

Получается, для того, чтобы выполнить JS-код не засрав при этом window можно использовать так:
(function(){...})()

Будем иметь ввиду... Углубляться, думаю, не стоит - работает и хорошо:)

Gvozd 21.09.2009 12:08

Цитата:

Сообщение от B~Vladi
не засрав при этом window

ты не засреш при этом window только именем функции
если ты будешь внутри объявлять переменные или функции без var, то все равно засрешь

B~Vladi 21.09.2009 12:45

Цитата:

Сообщение от Gvozd
если ты будешь внутри объявлять переменные или функции без var, то все равно засрешь

Естессно, я это понимаю...

У меня вот ещё какой вопрос:
Например, есть такой код:

function addClick(){
  var i='New var';
  ele.onclick=function(){
    alert(i);
  }
}


Тут обычное замыкание и при каждом вызове addClick будет создаваться функция-обработчик. Так?! А что изменит такой код:

function addClick(){
  var i='New var';
  ele.onclick=(function(){
    return function(){alert(i)}
  })();
}


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

И ещё... В чём отличия записей:
var f=function(){}

и
var f=(function(){});


Сори, если пишу глупости:)

Riim 21.09.2009 12:59

Цитата:

Сообщение от B~Vladi
при каждом вызове addClick будет создаваться функция-обработчик. Так?!

Да, при каждом вызове ссылка (в onclick) на старую функцию будет затираться, и она удалится сборщиком мусора.

Цитата:

Сообщение от B~Vladi
А что изменит такой код:

Практически, вообще ничего. А вообще, первая функция ( (function(){ ) теперь тоже будет в памяти, т. к. "i" сначала в нее попадает, а потом уже во вторую функцию, она видит что ее "i" где-то используется и остается в памяти. Как только ссылка на вторую функцию будет удалена, они обе удалятся из памяти.

B~Vladi 21.09.2009 13:01

Аха, понятно...
Цитата:

Сообщение от Riim
она удалится сборщиком мусора

Это меня больше всего радует:)
Вот пытаюсь писать "грамотные" скрипты... Не нравятся замыкания... Вот разбираюсь...

B~Vladi 21.09.2009 13:18

Ещё вопрос:

такой вариант оставит жить [[scope]] внешней функции:
function(a,b){
  var f=new Function(a,b,'return a+b');
}

?! Если да, то как этого избежать?!

Riim 21.09.2009 13:25

Получается, что нет:
alert(
(function(a, b) {
	return new Function('return a + b;');
})(5, 6)()
);


происходит ошибка, если бы функция создаваемая через "new Function" запоминала "a" и "b", то вывелось бы 11, как здесь:
alert(
(function(a, b) {
	return function() {return a + b};
})(5, 6)()
);

B~Vladi 21.09.2009 13:34

Цитата:

Сообщение от Riim
если бы функция создаваемая через "new Function" запоминала "a" и "b", то вывелось бы 11.

И, соответственно, получилось бы замыкание...

В статье про замыкания есть такой код:
function addEvents2(divs) {
    for(var i=0; i<divs.length; i++) {    
        divs[i].innerHTML = i
        divs[i].onclick = function(x) {
            return function() { alert(x) }
        }(i)
    }
}


Т.е. как я понял,временная функция, после исполнения, возвращает уже функцию-обработчик, которая не использует внешних [[scope]] и, соответственно, нет никакого замыкания...
Хоть бы это было так:)

ZoNT 21.09.2009 13:39

Цитата:

Сообщение от Riim
alert( (function(a, b) { return new Function('return a + b;'); })(5, 6)() );

alert(
(function() {
    return new Function('a','b','return a + b;');
})()(5, 6)
);

Riim 21.09.2009 13:40

Как же нет, "x" то он (обработчик) запомнил, кроме того внутри видны "i" и "divs".

Riim 21.09.2009 13:41

ZoNT,
и что, где функция созданная через "new Function" запомнила что-то замыканием?

ZoNT 21.09.2009 13:46

Функция через new Function ничего не запоминает, так как формат её создания не подразумевает передачи в неё каких-либо ссылок...

B~Vladi 21.09.2009 13:49

Цитата:

Сообщение от Riim
Как же нет, "x" то он (обработчик) запомнил, кроме того внутри видны "i" и "divs".

:cray: блин, точно... Как мне создать ф-цию, передать ей аргументы и оградить от внешнего [[scope]]?
:help:

Riim 21.09.2009 13:55

Цитата:

Сообщение от ZoNT
Функция через new Function ничего не запоминает, так как формат её создания не подразумевает передачи в неё каких-либо ссылок...

угу, в этом и был вопрос.

ZoNT 21.09.2009 14:02

Цитата:

Сообщение от Riim
угу, в этом и был вопрос.

Если вопрос только в этом то надо было писать так:
alert(
(function(a, b) {
    return new Function('return ' + a + ' + ' + b + ';');
})(5, 6)()
);

B~Vladi 21.09.2009 14:05

Это всё понятно... как передать внешнюю переменную в создаваемую ф-цию, не оставляя ссылки на scope?! Может можно как-то через prototype решить... Ну или в заранее созданную...

Riim 21.09.2009 14:06

Цитата:

Сообщение от ZoNT
Если вопрос только в этом то надо было писать так:

замыкания опять же нет, вопрос был не в способе запомнить что-то в "new Function", а в том, что там в памяти, запомнен ли чей-то scope.

B~Vladi 21.09.2009 14:11

Цитата:

Сообщение от Riim
вопрос не в способе запомнить в что-то в "new Function", а в том что там в памяти, запомнен ли чей-то scope

Я подразумеваю, что второе - результат работы первого...Только вот этот самый результат не нужен...

Riim 21.09.2009 14:20

B~Vladi, можно через prototype, можно просто создавать нормальную структуру объектов, так что бы до всего нужного можно было добраться через window.

B~Vladi 21.09.2009 14:55

Ладно, не буду вас доставать:)

Dmitry A. Soshnikov 21.09.2009 15:45

Цитата:

Сообщение от Riim
замыкания опять же нет, вопрос был не в способе запомнить что-то в "new Function", а в том, что там в памяти, запомнен ли чей-то scope.

Цитата:

Сообщение от ZoNT
Функция через new Function ничего не запоминает

Вообще, каждая функция имеет [[Scope]], просто для функций, созданных с помощью конструктора Function, [[Scope]] содержит только глобальный объект.

А в виду того, что любая функция хранит [[Scope]] (т.е. Scope chain порождающего контекста), все функции являются замыканиями.

B~Vladi 21.09.2009 17:11

Цитата:

Сообщение от Dmitry A. Soshnikov
А в виду того, что любая функция хранит [[Scope]] (т.е. Scope chain порождающего контекста), все функции являются замыканиями.

Аха... Тогда ещё такой вопрос:
Есть код:

function fnc1(){
  var i;
  function fnc2(){
    alert(i)
  }
}


А внешний [[scope]], который хранит функция fnc2 - это клон или ссылка на [[scope]] функции fnc1?! Т.е. меня интересует как при этом расходуется память...

Dmitry A. Soshnikov 21.09.2009 17:23

Цитата:

Сообщение от B~Vladi
А внешний [[scope]], который хранит функция fnc2 - это клон или ссылка

Ссылка; там же, в статье о замыканиях можно подробно посмотреть.

Цитата:

Сообщение от B~Vladi
или ссылка на [[scope]] функции fnc1?!

Свойство [[Scope]] функции - это ссылка на Scope (Scope chain) порождающего (внешнего) контекста, а не на [[Scope]] внешней функции. Если бы была ссылка на [[Scope]] внешней функции, внутренняя функция не видела бы переменные, объявленные во внешней функции.

Т.е.

f1.[[Scope]] === Scope глобальньного контекста === [Global]


Scope (Scope chain) контекста функции f1 === Объект активации (AO) контекста функции f1 + f1.[[Scope]]:

Scope(f1 Context) == AO(f1 context) + f1.[[Scope]]


А f2.[[Scope]]:

f2.[[Scope]] === Scope(f1 Context) === [AO(f1 context), Global]

Riim 21.09.2009 17:42

Dmitry A. Soshnikov, вот такой у меня вопрос: функция запомнила какие-то переменные, если внутри этой функции создать переменную, одноименную с той, что запомнена, то она как бы экранируется, теперь если экранируется все запомненное за счет имен аргументов, то хранится ли в памяти внешняя функция? Что-то у меня не получается создать пример подтверждающий или опровергающий это. Вот код для пояснения вопроса:
var p = (function() {// func1
	var a = 5, b = 6;
	return function(a, b) {// смысла, хранить в памяти func1, вроде нет, все равно все экранировано, и убрать это экранирование вроде никак (delete применяется к свойствам объектов, но a и b непонятно чьи свойства)
		alert(a + b);
	};
})();

Dmitry A. Soshnikov 21.09.2009 18:07

Цитата:

Сообщение от Riim
если внутри этой функции создать переменную, одноименную с той, что запомнена, то она как бы экранируется

Да, "экранируется", на этом принципе и работает Scope chain.

Т.е. одноимённые переменные (а также, декларации функций и формальные параметры) более глубокого контекста, разрешаются (определяются) раньше, чем переменные с тем же именем, но в контексте выше. Цепь областей видимости контекста (Scope chain) при нахождении в коде переменной, опрашивается интерпретатором от самого глубого звена цепи вверх до глобального объекта (и, в некоторых реализациях, прототипа глобального объекта).

var a = 10;
function A() {
  var a = 20;
  function B() {
    var a = 30;
    alert(a); // найдено сразу в AO(контекста B)
  }
}


Если бы переменная "а" была объявлена один раз (глобально), то при alert(a) из внутренней функции "B", она бы разрешалась по цепи (Scope chain контекста функции "B"):

1. AO(контекста "B") - (нет) ->
2. AO(контекста "А") - (нет) ->
3. VO(Global) === Global - (да) - 10


Цитата:

Сообщение от Riim
теперь если экранируется все запомненное за счет имен аргументов, то хранится ли в памяти внешняя функция

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

С другой стороны, одна вложенная функция может не иметь свободных переменных, а вложенная во вложенную может, поэтому, надо запоминать всегда, получается:

function A() {
  var a = 10;
  function B() { // нужен ли этой функции Scope A?
    function C() {
      alert(a); // если B не запомнит Scope A, функция С не найдёт "а"
    }
  }
}


Но, повторю, по идее, может быть проведена оптимизация на уровне реализации.

Поэтому, все функцию при создании запоминаю лексический контекст (т.е. Scope chain этого контекста), в котором они порождены, отсюда - все функции - замыкания. Функция может быть ни разу не запущена, но [[Scope]] уже в неё записался.

Но, стандарт описывает предложение по оптимизации - объединённые объекты:

function a() {
  function b(z) {
    return z * z;
  }
  return b;
}
 
var x = a();
var y = a();


Т.к. [[Scope]] функций x и y - не различим (и вообще, оба объекты - не различимы), реализация вправе использовать объединённые объекты в этом случае, или даже - использовать один объект. Поэтому, не всегда внутренняя функция будет создаваться каждый раз разная.

Riim 21.09.2009 20:09

Цитата:

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

совсем обрубать цепочку, конечно, не стоит, она в любом случае должна закончиться на VO(Global) === Global , я имел в виду аккуратно удалять незначащие звенья в этой цепи, вот здесь, например:
function A() {
  var a = 10;
  function B() { // нужен ли этой функции Scope A?
    function C() {
      alert(a); // если B не запомнит Scope A, функция С не найдёт "а"
    }
  }
}

можно не запоминать AO(контекста "B") и из "C" сразу в "A" ссылаться.
Правда я уже не уверен, что такая оптимизация пойдет на пользу, памяти конечно меньше надо будет, но при каждом создании функции столько дополнительной работы делать, с другой стороны там и так этой работы очень много делается.

Dmitry A. Soshnikov 21.09.2009 20:12

Цитата:

Сообщение от Riim
я имел в виду аккуратно удалять незначащие звенья в этой цепи

Да, по логике вещей такую оптимизацию можно сделать (и скорей всего, движки делают что-то подобное, но в любом случае, чтобы знать точно - надо проанализировать все исходники движков). На уровне же специфиации - все функции, всегда, без исключения, запоминают Scope свойством [[Scope]].

Цитата:

Сообщение от Riim
можно не запоминать AO(контекста "B") и из "C" сразу в "A" ссылаться.

Тут есть важный нюанс - на момент активации "B", функция "А" уже может не существовать. А функция "С" создаётся функцией "B". Соответственно, чтобы получить доступ к объекту активации контекста функции "А", "С" должна обратиться к Scope контекста "B" и запомнить этот Scope, как C.[[Scope]].

JSprog 21.09.2009 20:32

Цитата:

Сообщение от B~Vladi
А и ещё один вопрос - чтобы не плодить тем... Не могу найти описание хитрой конструкции:

var i=(function(){...})();

ГГ
А кстате prototype и [[prototype]] вначале указывают на одно и тоже?
А то в функции extend prototype==new Object() а [[prototype]]==Function.prototype
Так в начале

B~Vladi 22.09.2009 10:38

Цитата:

Сообщение от Dmitry A. Soshnikov
чтобы знать точно - надо проанализировать все исходники движков

Может прозвучит глупо, но их как-то можно достать?! Хотя с другой стороны для каждого браузера это личное достижение:)

И ещё небольшая просьба - не могли бы вы привести пример неправильного использования памяти на примере замыканий?! Т.е. интересует не только утечка, но и просто нерациональное расходование... Если такое, конечно, возможно в данном контексте...

Dmitry A. Soshnikov 22.09.2009 12:14

Цитата:

Сообщение от B~Vladi
но их как-то можно достать?!

http://en.wikipedia.org/wiki/JavaScript_engine

Там же есть ссылки на скачивание open-source движков. Некоторые, например, SpiderMonkey, можно посмотреть online.

Цитата:

Сообщение от B~Vladi
пример неправильного использования памяти на примере замыканий?! Т.е. интересует не только утечка, но и просто нерациональное расходование

Ну, про утечки в IE с циклическими ссылками вам и так известно. Насчёт расходования памяти, надо почитать про объединённые объекты, т.к. они могут вносить оптимизацию: 13.1 и 13.2.

Если [[Scope]] функций не различим, можно повторно использовать один и тот же объект.

B~Vladi 22.09.2009 17:34

Цитата:

Сообщение от Dmitry A. Soshnikov
Ну, про утечки в IE с циклическими ссылками вам и так известно.

Да, я стараюсь обходится без этого.
Цитата:

Сообщение от Dmitry A. Soshnikov
надо почитать про объединённые объекты

Спс, почитаем:)

x-yuri 23.09.2009 00:43

Цитата:

Сообщение от B~Vladi
Читал стандарт языка, но ответа не нашел... Заметил, только что в call можно передавать много аргументов...

call удобно использовать в начале цепочки вызовов, а apply - при дальнейшей передаче параметров
var o = {...};
function a() {
    ...
    b.apply( o, arguments );
}
function b() { ... }
a.call( o, 1, 2, 3 );


Цитата:

Сообщение от B~Vladi
Вот пытаюсь писать "грамотные" скрипты... Не нравятся замыкания... Вот разбираюсь...

Цитата:

Сообщение от B~Vladi
Как мне создать ф-цию, передать ей аргументы и оградить от внешнего [[scope]]?

ты стремишься не использовать замыкания? Вроде уже нечего особо бояться, утечек быть не должно, судя по http://javascript.ru/tutorial/events...ha-iyunya-2007 . Могут быть разве что псевдоутечки

B~Vladi 23.09.2009 11:18

Цитата:

Сообщение от x-yuri
ты стремишься не использовать замыкания?

Не совсем... Мне они "не нравятся" из-за рассказов о проблемах с памятью... Вот я и пытаюсь разобраться... А на самом деле очень удобно:)
Способ известен, будем применять:)

Dmitry A. Soshnikov 23.09.2009 11:53

Цитата:

Сообщение от B~Vladi
Мне они "не нравятся" из-за рассказов о проблемах с памятью... Вот я и пытаюсь разобраться...

В плане создания методов в конструкторе, думаю в этом нет большого смысла (если только не создаются какие-то индивидуальные методы для отдельных объектов по условию).

С другой стороны, возвращаясь к оптимизации с объединёнными объектами:

Цитата:

Сообщение от ECMA-262-3-RUS; 13.1.2
Если объекты O и P объединены, для них операторы сравнения == и === дают положительный результат.

Цитата:

Сообщение от ECMA-262-3-RUS; 13.2
На практике скорее всего окажется целесообразным объединять объекты типа Function только в тех случаях, когда реализация может доказать, что различия между их свойствами [[Scope]] не различимы, и в таких случаях можно повторно использовать один объект.

Т.е. говорится, что объекты b1 и b2 в примере ниже должны давать true на == и ===, т.к. их [[Scope]] идентичный (пример из стандарта):

function A() {
   function B(x) {return x* x;} 
   return B; 
}

var b1 = A(); 
var b2 = A(); 

// но все текущие реализации дают false
alert(b1 == b2); // false
alert(b1 === b2); // false


Но, там же, в стандарте написано:

Цитата:

Сообщение от ECMA-262-3-RUS; 13.2
реализация может, но не обязана, объединить b1 и b2. Более того, она может использовать для b1 и b2 один и тот же объект, поскольку нет возможности различить их свойства [[Scope]].

Означает ли это, что реализации не используют предложенную оптимизацию? Я не знаю, поскольку не анализировал исходники всех реализаций. Но, всё-таки, я думаю, что оптимизацию они делают, просто выдают false (почему-то. Почему? Не знаю. Чтобы знать точно, повторю, надо глубоко смотреть исходники или связаться с разработчиками и спросить).

С другой стороны:

function A(x) {
  this.test = function () {
    alert(x);
  };
}

var objects = [];
for (var k = 0; k < 3; k++) {
  objects.push(new A(k));
}


внутренняя функция "this.test", по определению, может быть и не объединена, т.к. каждый раз [[Scope]] будет разный, в виду того, что параметр "x" меняется. Т.е. структура [[Scope]] будет опять же идентична, но значения в [[Scope]] будут разные. Должны ли здесь функции быть объединены? По определению, нет. В реализациях - возможно (например, хранить [[Scope]]-ы функций отдельно, а сам объект функции, код её, будет один, но это только предположение).

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

В случае же, когда можно использовать один объект (как в примере с b1 и b2) - там новый объект создаваться не должен вообще.

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

B~Vladi 23.09.2009 12:08

Цитата:

Сообщение от Dmitry A. Soshnikov
В плане создания методов в конструкторе, думаю в этом нет большого смысла

Да, я для этого использую prototype.
Цитата:

Сообщение от Dmitry A. Soshnikov
это повлечёт за собой расход памяти прямо пропорциональный количеству объектов

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

Dmitry A. Soshnikov 23.09.2009 12:15

Цитата:

Сообщение от B~Vladi
И именно для такого случая (а такие возникают) я и хотел узнать способ создания анониймной функции в конструкторе, но с передачей (и сохранением) в неё значений нужных мне переменных

А в чём проблема? Переменные не "сохраняются" во вложенной функции? Не видны? Или что?


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