LINUX.ORG.RU

Скрестить ужа с ежём: first-class функции vs методы, как частный вид функций

 , , ,


1

1

Навеяно чьими-то сообщениями в темах про ООП, что прочел давно, но не было времени сформулировать проблему...

Итак предположим, что в нашем языке функции - это объекты, а объекты могут иметь методы. У функций есть специальный метод apply(call, operator(), etc.), который вызывается при вызове функции. В этом случае получается, что методы не могут являться first-class объектами и разновидностью функций, т.к. тогда у них должен быть метод apply, который в свою очередь должен иметь метод apply, который и т.д. и т.д.

Решения у нас могут быть примерно такие:

- или мы делаем как в крестах и получаем не first-class функции/методы, но имеем возможность сделать «объект-функцию»(в том числе через лямбды), если потребуется;

- или мы делаем как в scala и получаем довольно стройную систему, но отказываемся от простой очевидной концепции метода как частного вида функции и, как следствие, от first-class методов;

- или мы делаем «функции» особым видом объектов, который нельзя сделать в обход механизма создания функций(определив для объектов какого-то типа apply или operator());

Я правильно понимаю? Или можно таки разорвать цепочку красиво?


Или можно таки разорвать цепочку красиво?

Можно. default method.

То есть функция может быть определена как функция. Тогда у неё встроенный метод apply(). Может быть определена с явным методом apply. Который также может быть определён либо как функция, либо как объект.

Также и вызов. Можно писать f(x, y), можно f.apply(x, y), можно f.apply.apply(x, y), ..., f.apply.apply.apply.apply.apply.apply(x, y). Они все эквивалентны.

Реализацию смотри в питоне (метод __call__)

monk ★★★★★
()
Ответ на: комментарий от monk

То есть функция может быть определена как функция. Тогда у неё встроенный метод apply(). Может быть определена с явным методом apply. Который также может быть определён либо как функция, либо как объект.

Вот где реально сильная модель. Имеем first class функции с first-class окружениями,( которые являются к тому же, объектами с наследованием и всеми плюшками), и, разумеется, лексическим скопом. call и apply захватывают произвольный контекст.


console.log(
  function(x){return function(y){return x + y + this.x}}(1).call({x: 2}, 5),
  function(x){return function(y){return x + y + this.x}}(1).apply({x: 2}, [5])
)

//  8 8

idiottoop
()
Ответ на: комментарий от idiottoop

И, при этом, функции сами являются полноценными объектами, BTW. Ну и естественно, любой метод — есть все та же first-class ф-ция.

idiottoop
()

У функций есть специальный метод apply(call, operator(), etc.), который вызывается при вызове функции. В этом случае получается, что методы не могут являться first-class объектами и разновидностью функций, т.к. тогда у них должен быть метод apply, который в свою очередь должен иметь метод apply, который и т.д. и т.д.

Я чет не понял, почему утебя так получается, но в JS так оно и есть, как ты описал: у ф-ций есть специальные методы call и apply, и при этом методы являются 100% first.

idiottoop
()
Ответ на: комментарий от monk

Реализацию смотри в питоне

ТС ведь спрашивал про первоклассные функции? Разве в питоне они есть? По-моему — нет. Там просто сахар для однострочников, типа lambda ко-ко-ко

anonymous
()

просто оставлю это тут:

$ python
Python 3.4.2 (default, Oct  8 2014, 13:44:52) 
[GCC 4.9.1 20140903 (prerelease)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> x = 'world'
>>> def f(): print('hello {}!'.format(x))
... 
>>> f()
hello world!
>>> type(f).__call__(f)
hello world!
>>> type(type(f).__call__).__call__(type(f).__call__, f)
hello world!
>>> type(type(type(f).__call__).__call__).__call__(type(type(f).__call__).__call__, type(f).__call__, f)
hello world!
>>> type(type(f).__call__).__call__ == type(type(type(f).__call__).__call__).__call__
True
>>> type(type(f).__call__).__call__ is type(type(type(f).__call__).__call__).__call__
True
>>> 
user_id_68054 ★★★★★
()
Ответ на: комментарий от user_id_68054

просто оставлю это тут:

просто оставлю это тут:


Object.prototype.call=function(f){return f.call(this)}
Object.prototype.show=function(){var v=this.valueOf(); console.log(v); return v}

;(function(){return {x: this.x*2, y: this.y}}).call({x: 10, y: 5}).show().call(function(){return this.x+this.y}).show()
//  { x: 20, y: 5 }
//  25

idiottoop
()
Ответ на: комментарий от user_id_68054

просто оставлю это тут:

до кучи:


Object.prototype.call=function(f){return f.call(this)}
Object.prototype.show=function(){var v=this.valueOf(); console.log(v); return v}

"foo".call(function(){return this+this}).call(show)
.call(show)
.call(function(){return this+"bar"})
.show()

fact=function(n){return !n||n*fact(--n)}

1..call(function(){return this+this})
.call(show)
.call(show)
.call(function(){return this*2+1})
.call(show)
.call(function(){return fact(this)})
.call(show)

//  foofoo
//  foofoo
//  foofoobar
//  2
//  2
//  5
//  120

python sucks

idiottoop
()
Ответ на: комментарий от idiottoop

этот твой пример — очень интересен (как мне показалось) в том что если внутри него автозаменой заменить все слова call на слово pony...

...то в итоге будет ошибка при выполнении InternalError: too much recursion

:-)

user_id_68054 ★★★★★
()
Ответ на: комментарий от idiottoop

python sucks

ну извини. я же не знал что у нас соревнование на тему кто больше напишет слов «call»

# P.S.:

я то думал что тебе покажется интересным тот факт что в Python верно равенство:

type(type(f).__call__).__call__ == type(type(type(f).__call__).__call__).__call__

а ты просто начал придумывать примеры связанные с тем как можно вызвать функции через call

user_id_68054 ★★★★★
()
Последнее исправление: user_id_68054 (всего исправлений: 1)
Ответ на: комментарий от user_id_68054

Просто пони — это зарезервированное слово, отсюда и ошибка. Можешь сделать вот так


Object.prototype.pony___=function(f){return f.call(this)}
Object.prototype.show=function(){var v=this.valueOf(); console.log(v); return v}

"foo".pony___(function(){return this+this}).pony___(show)
.pony___(show)
.pony___(function(){return this+"bar"})
.show()

fact=function(n){return !n||n*fact(--n)}

1..pony___(function(){return this+this})
.pony___(show)
.pony___(show)
.pony___(function(){return this*2+1})
.pony___(show)
.pony___(function(){return fact(this)})
.pony___(show)

//  foofoo
//  foofoo
//  foofoobar
//  2
//  2
//  5
//  120

idiottoop
()
Ответ на: комментарий от user_id_68054

Я там не только функции вызываю через call, но и строки числа, объекты, etc. В JS все есть объект, в отличии от, ИЧСХ, все есть первоклассный объект. А в твоем равенстве я ничего интересного не вижу. Там просто возвращается одно и тоже, насколько я понял.

idiottoop
()

- или мы делаем как в scala и получаем довольно стройную систему, но отказываемся от простой очевидной концепции метода как частного вида функции и, как следствие, от first-class методов;

Но в scala методы - первоклассные объекты. Что-то у тебя не сходится.

anonymous
()
Ответ на: комментарий от idiottoop

В JS все есть объект, в отличии от, ИЧСХ, все есть первоклассный объект.

то есть именно из-за отсутствия «все есть первоклассный объект» — полная (вся-вся-вся) автозамена «call» на «pony» вызывает бесконечную рекурсию, но таже самая операция но со словом «call» — уже бесконечную рекурсию НЕ вызывает?

небольшой изъян — для того чтобы вся система JS не рухнула бы.. :-) забавно..

ну вот и что я думаю.. в этом же весь JS. он полон исключений из правил :-)

А в твоем равенстве я ничего интересного не вижу.

автор темы завёл разговор о том что если есть некий __call__ у функции, то должен быть и этот __call__ у самой функции __call__ .

и он начал опасаться что этих __call__ будет бесконечно!

но в равенство type(type(f).__call__).__call__ == type(type(type(f).__call__).__call__).__call__ показывает что в определённом уровне метакласса — просто цепочка объектов ссылается сама на себя (замкнутая связь).

Там просто возвращается одно и тоже, насколько я понял.

разумеется! поэтому равенство и является верным! :-)

это происходит — потому что верно и равенство: type(type(f)) == type(type(type(f)))

к слову сказать — вернувшись к JS — можно вспомнить что в JS попросту нет метаклассов, поэтому в JS и нет такой замкнутой логичной системы.

вот в LUA есть метатаблицы. которые по сути являются аналогами метаклассов. и там (в LUA) — опять таки всё более логично чем в JS.

user_id_68054 ★★★★★
()
Ответ на: комментарий от user_id_68054

что в JS попросту нет метаклассов

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

idiottoop
()
Ответ на: комментарий от user_id_68054

> В JS все есть объект, в отличии от, ИЧСХ, все есть первоклассный объект.

то есть именно из-за отсутствия «все есть первоклассный объект»
— полная (вся-вся-вся) автозамена «call» на «pony» вызывает
бесконечную рекурсию, но таже самая операция но со словом
«call» — уже бесконечную рекурсию НЕ вызывает?

небольшой изъян — для того чтобы вся система JS не рухнула
бы.. :-) забавно..

ну вот и что я думаю.. в этом же весь JS. он полон исключений
из правил :-)

если якобы «всё есть объект», то объясни мне почему у тебя функция «Object.prototype.show» — не является объектом, тем самым объектом у которого должен быть переопределённый call ? (берём версию без поней :))


Object.prototype.call=123
Object.prototype.show=function(){}
console.log(Object.prototype.show.call) // function call() . но я хотел бы ожидать 123
user_id_68054 ★★★★★
()
Последнее исправление: user_id_68054 (всего исправлений: 2)
Ответ на: комментарий от anonymous

Что, по-твоему, есть first-class функция?

>>> def foo(): pass
... 
>>> def bar(x): print x
... 
>>> bar(foo)
<function foo at 0xb744f6f4>
>>> 
foo первоклассная функция?

Virtuos86 ★★★★★
()
Ответ на: комментарий от user_id_68054

не является объектом

почему это не является? это обычная функция, а ф-ция в js такой же первоклассный объект как и все прочее


f=function(){}
f.a=1
console.log(f.a)// 1

насчет твоих непоняток насчет замены, я тебе сейчас не смогу объяснить это быстро, поскольку, судя по твоим комментам, ты слабовато понимаешь концепцию прототипного наследования и делегирования. Почитай где нибудь об этом, например, почитай, как устроен SELF. К этому вопросу невозможно подойти с «питоньей» колокольни, слишком большая разница в моделях ООП

idiottoop
()

Доктор ЛОР, мне везде мерещатся анонiмоусы

anonymous
()
Ответ на: комментарий от idiottoop

в этот раз ник какой-то не стильный.

anonymous
()
Ответ на: комментарий от Virtuos86

вот так вот можешь сделать

arr=[function(){return 1}, function(){return 2}, "foo", function(){return 3}]

console.log(arr[1]())

//и строго говоря, это еще не первоклассный объект, с точки зрения обекта как такового в контексте "все есть объект". Все есть объект и все есть первоклассный объект эт так

f=function(){return "baz"}
f.foo="bar"
console.log([f,f,f][1].foo)
console.log([f,f,f][1]())
//  2
//  bar
//  baz

Но первого варианта достаточно, для трактовки в смысле «первоклассная сущность», как в лиспе, хотя бы. Сохраняешь в переменной, массиве, хеше, etc, передаешь в качестве аргумента, возвращаешь в качестве значения — хотя бы это.

idiottoop
()
Ответ на: комментарий от idiottoop

К этому вопросу невозможно подойти с «питоньей» колокольни, слишком большая разница в моделях ООП

К тому, что такая модель может быть реализована в питоне (в js она просто встроена в язык) нельзя подойти с jsной колокольни, недоразвито абстрактное мышление, поэтому ты не можешь в нормальное проектирование.

anonymous
()
Ответ на: комментарий от Virtuos86

Вот так, например, ЕМНИП,

;(function(){return function(){return function(x){console.log(x)}}})()()(1) // 1
в питоне не сделаешь. Так что, сомнительно, что там они первокласны.

idiottoop
()

С ежом.

anonymous
()
Ответ на: комментарий от idiottoop

Хорошо. Объясните такую вещь. Какова необходимость в методах - объектах первого класса. Когда функции - объекты первого класса, это да, понятно. А что насчет методов? Чего это позволит достичь?

bogus_result
()
Ответ на: комментарий от bogus_result

А самое важное - с какими базовыми идеями объектно-ориентированного проектирования это идёт вразрез?

bogus_result
()
Ответ на: комментарий от idiottoop

;(function(){return function(){return function(x){console.log(x)}}})()()(1) // 1

в питоне не сделаешь. Так что, сомнительно, что там они первокласны.

так чтоль сделать нельзя?

$ python
Python 3.4.2 (default, Oct  8 2014, 13:44:52) 
[GCC 4.9.1 20140903 (prerelease)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> def f():
...   def f():
...     def f(x):
...       print('всё нормально: ', x)
...     return f
...   return f
... 
>>> # проверяем
... 
>>> f()()(1)
всё нормально:  1
user_id_68054 ★★★★★
()
Ответ на: комментарий от bogus_result

Гибкость, прежде всего, вот например, простенький случай


callName=function(){console.log(this.name)}

O=function(name){this.name=name}
A=function(name){this.name=name}
O.prototype.callName=A.prototype.callName=callName

o1=new O("bar")
a1=new A("foo")

o1.callName()
a1.callName()

//  bar
//  foo
одну и ту же функцию — один экземпляр ее — используем в произвольном кол-ве классов. Память экономится, как минимум, повторное использование кода, модульность. Много чего, вообще, когда в ЯП все является первоклассным объектом, это всегда хорошо. Юзкейсов немеряно. Я бы объяснил подрбней, но спать надо:)

idiottoop
()
Ответ на: комментарий от idiottoop

вот так вот можешь сделать

«портировал» :) строчку-за-строчкой:

$ python
Python 3.4.2 (default, Oct  8 2014, 13:44:52) 
[GCC 4.9.1 20140903 (prerelease)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> def f1():
...     return 1
... 
>>> def f2():
...     return 2
... 
>>> def f3():
...     return 3
... 
>>> arr = [f1, f2, 'foo', f3]
>>> del f1, f2, f3 # было не обязательно удалять. но удалим для показухи :)
>>> print(arr[1]())
2
>>> def f():
...     return 'baz'
... 
>>> f.foo = 'bar'
>>> print([f,f,f][1].foo)
bar
>>> print([f,f,f][1]())
baz

user_id_68054 ★★★★★
()
Ответ на: комментарий от user_id_68054

так чтоль сделать нельзя?

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

Скрестить ужа с ежём: first-class функции vs методы, как частный вид функций (комментарий)

Этих примеров?

idiottoop
()
Ответ на: комментарий от idiottoop
>>> arr = [lambda: 1, lambda: 2, "foo", lambda: 3]
>>> arr[1]()
2
>>> f = lambda: "baz"
>>> f.foo = "bar"
>>> [f, f, f][1].foo
'bar'
>>> [f, f, f][1]()
'baz'
>>> 
Virtuos86 ★★★★★
()
Ответ на: комментарий от idiottoop

там у меня безымянные функции используются.

заметил ли ты строчку эту:

del f1, f2, f3 # было не обязательно удалять. но удалим для показухи :)

???

или не заметил? :-)

как думаешь — что именно (какое действие) эта срочка сделала? :-)

user_id_68054 ★★★★★
()
Последнее исправление: user_id_68054 (всего исправлений: 1)
Ответ на: комментарий от idiottoop
Python 2.7.5+ (default, Feb 27 2014, 19:39:55) 
[GCC 4.8.1] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from __future__ import print_function
>>> (lambda: lambda: lambda x: print(x))()()(1)
1
>>> 
Virtuos86 ★★★★★
()
Ответ на: комментарий от idiottoop

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

вот тебе анонимность:

$ python
Python 3.4.2 (default, Oct  8 2014, 13:44:52) 
[GCC 4.9.1 20140903 (prerelease)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> def super_anonimous_protected_from_FBI_and_CIA():
...   print("фигня")
... 
>>> x = [super_anonimous_protected_from_FBI_and_CIA]
>>> def super_anonimous_protected_from_FBI_and_CIA():
...   print("я секретная функция")
... 
>>> x.append(super_anonimous_protected_from_FBI_and_CIA)
>>> def super_anonimous_protected_from_FBI_and_CIA():
...   print("только для тех кто шифруется")
... 
>>> x.append(super_anonimous_protected_from_FBI_and_CIA)
>>> del super_anonimous_protected_from_FBI_and_CIA
>>> 
>>> x[0]()
фигня
>>> x[1]()
я секретная функция
>>> x[2]()
только для тех кто шифруется
>>> 
>>> # пробуем выполнить напрямую:
... super_anonimous_protected_from_FBI_and_CIA()
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
NameError: name 'super_anonimous_protected_from_FBI_and_CIA' is not defined
>>> 
user_id_68054 ★★★★★
()
Ответ на: комментарий от Virtuos86

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

idiottoop
()
Ответ на: комментарий от user_id_68054

что именно (какое действие) эта срочка сделала? :-)

>>> (lambda: None).__name__
'<lambda>'
>>> def foo(): pass
... 
>>> foo.__name__
'foo'
>>> x = [foo]
>>> del foo
>>> x[0]
<function foo at 0xb7457a04>
>>> 

По-твоему, foo стала безымянной, если ты удалил ее имя из пространства имен?

Virtuos86 ★★★★★
()
Ответ на: комментарий от Virtuos86

По-твоему, foo стала безымянной, если ты удалил ее имя из пространства имен?

да. именно так. (более точный термин — стала анонимной функцией)

>>> foo.__name__
'foo'

foo.__name__ это просто формальность. эта инфа поможет тебе во время отладки, например когда будет напечатан стэк.

фактически функция стала анонимной, но при этом ты можешь видеть её «фальшивое» имя.

user_id_68054 ★★★★★
()
Последнее исправление: user_id_68054 (всего исправлений: 1)
Ответ на: комментарий от idiottoop

Нет. В Python 2.* print это инструкция, а lambda-функции могут содержать только выражения.

from __future__ import <некая фича, реализованная в будущих версиях интерпретатора>

Указываем, что нужно включить какую-то фичу, в моем случае, я указываю, что хочу использовать print как функцию (это возможность реализована в Python 3), что позволит мне использовать его в выражениях.

Virtuos86 ★★★★★
()
Ответ на: комментарий от Virtuos86

а lambda-функции могут содержать только выражения.

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

idiottoop
()
Ответ на: комментарий от idiottoop

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

даже сами Питонисты не любят lambda-функции (в python-синтаксисе, а не вообще).

да, пусть использовании выражения lambda в Python — и создаёт небольшую экономию в количестве строчек, но сильно портит внешний вид исходного кода Python-программы. код начинает выглядеть нерасшияемым.

что касается обычных функций в Python, то:

def my_func(...):
  ...

это эквивалентно как если бы в JS мы написали бы:

var my_func = function(...) {
    ...
}

то есть в Python — функция это в первую очередь переменная (объект). её можно удалять, передавать, и она ни в коем случае не глобальная (ну если только ты сам этого не пожелаешь).

user_id_68054 ★★★★★
()
Ответ на: комментарий от user_id_68054

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


o={a: 1, f: function(){console.log(this.a)}}
o1={a: 10, f: o.f}
o1.f()
//  10
так работает?

idiottoop
()
Ответ на: комментарий от user_id_68054

о том что если есть некий __call__ у функции, то должен быть и этот __call__ у самой функции __call__ .

кстати, в js то же самое, call и apply являясь функциями, наследуют сами себя.


console.log(
   Function.call===Function.call.call,
   Function.apply===Function.apply.apply
)
// true true

idiottoop
()
Ответ на: комментарий от idiottoop

А методы там тоже являются первоклассными функциями?

да. но this передаётся другим способом (чем в JS).

то есть [...] так работает?

если переписать этот пример (сточку за строчкой) в JS->Python — то результат будет 1 а не 10.

вот:

$ python
Python 3.4.2 (default, Oct  8 2014, 13:44:52) 
[GCC 4.9.1 20140903 (prerelease)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> class Obj:
...     def f(self):
...         print(self.a)
... 
>>> class Obj1:
...     pass
... 
>>> o = Obj()
>>> o.a = 1
>>> 
>>> o1 = Obj1()
>>> o1.a = 10
>>> o1.f = o.f
>>> 
>>> o.f() # результат будет 1
1
>>> o1.f() # опять 1 . так как выполнит тож же самый код (с тем же самым this как o)
1
>>> 

но...

>>> # но можно сделать это:
... 
>>> o2 = Obj1()
>>> o2.a = 20
>>> Obj1.f = Obj.f
>>> 
>>> o2.f() # результат 20
20
>>> 

то есть главное отличии здесь от JS в том что:

>>> o.f == Obj.f
False

«o.f» и «Obj.f» — это разные функции. «Obj.f» пишем мы, а «o.f» динамически создаётся метаклассом (на основе «Obj.f»).

user_id_68054 ★★★★★
()
Последнее исправление: user_id_68054 (всего исправлений: 3)
Ответ на: комментарий от idiottoop

кстати, в js то же самое, call и apply являясь функциями, наследуют сами себя.
Function.call===Function.call.call,
Function.apply===Function.apply.apply

эт хорошо. а иначе было бы неприятно :)

user_id_68054 ★★★★★
()
Последнее исправление: user_id_68054 (всего исправлений: 1)
Ответ на: комментарий от user_id_68054

Короче, я понял. Питон не может динамически биндить контекст. Он для каждого объекта пересоздает заново ф-цию с нужными биндингами. Аналогом этого в JS будет


o={a: 1, f: function(){console.log(this.a)}}
o1={a: 10}
o1.f=o.f.bind(o1)

o1.f()
console.log(o.f===o1.f)
//  10
//  false

idiottoop
()
Ответ на: комментарий от idiottoop

Питон не может динамически биндить контекст

Python не прикрепляет this во время выполнения функции, а делает это заранее.

фразу «не может» — было бы странно произность. просто нет смысла прикреплять this, если она уже прикреплена чуть ранее :-)

а ещё можно например написать:

$ python
Python 3.4.2 (default, Oct  8 2014, 13:44:52) 
[GCC 4.9.1 20140903 (prerelease)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> class Obj:
...     def f(self):
...         print(self.a)
... 
>>> class Obj1:
...     pass
... 
>>> o = Obj()
>>> o.a = 1
>>> 
>>> o1 = Obj1()
>>> o1.a = 10
>>> 
>>> Obj.f(o1) # это cработает -- но фактически это безсмысленная штука
10

в JS аналоге для этого пришлось бы использовать call (или apply) .

в python можно сделать и так:

>>> x = o.f
>>> 
>>> del o, o1, Obj, Obj1 # мы всё-всё-всё поудаляли(!!!), кроме x
>>> x()
1
>>> 

а в JS для этого пришлось бы использовать bind.

************************************************************

то есть я к сему это пишу?

к тому что те вещи которые в Python делаются «естественным» образом. эти же вещи в JS делаются НЕ «естественным» образом а через использование call и bind

user_id_68054 ★★★★★
()
Ответ на: комментарий от Aswed

Ну как зачем? Мощно же. И абстрактно.

anonymous
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.