Dynamiczne wywoływanie funkcji w JavaScript - Function.prototype
W jaki sposób dynamicznie wywoływać funkcje?
- Konstruktor funkcji w JavaScript - constructor
Gdzie jest zapisany konstruktor funkcji?
- Pobieranie kodu funkcji - toString
Jak pobrać kod (definicję) funkcji?
- Wywoływanie funkcji z tablicą argumentów - apply
Jak wywołać dowolną funkcję z listą argumentów zapisaną w tablicy?
- Wybieranie funkcji do wykonania - call
Jak zbudować program, który pozwoli wybrać użytkownikowi funkcję, którą należy wykonać?
- Ustawianie właściwej instancji obiektu w metodzie - bind
Co zrobić, żeby w metodzie była widoczna właściwa instancja obiektu?
Konstruktor funkcji w JavaScript - constructor
Gdzie jest zapisany konstruktor funkcji?
Function.prototype.constructor
- Wartość:
Function
- konstruktor obiektu
Zawiera wbudowany konstruktor obiektu Function
.
Przykład Function.prototype.constructor
Function.prototype.constructor === Function; // true new Function().constructor === Function; // true Function.prototype.constructor === Object; // false
Pobieranie kodu funkcji - toString
Jak pobrać kod (definicję) funkcji?
Function.prototype.toString()
- Wartość:
String
- reprezentacja tekstowa instancji funkcji- Wyjątki:
TypeError
- obiekt nie jest instancją funkcji
Zwraca definicję funkcji w postaci tekstu. Specyfikacja nie określa jednoznacznego sposobu działanie tej metody. Dlatego wynik może się różnić w zależności od środowiska, w którym kod jest uruchamiany.
Funkcja toString
jest wywoływana automatycznie zawsze wtedy, gdy oczekiwana jest wartość tekstowa, a przekazano instancję funkcji.
Przykład Function.prototype.toString
new Function().toString(); // np.: "function anonymous() {\n\n}" new Function("a", "b", "return a + b") + ""; // np.: "function anonymous(a, b) {\nreturn a + b\n}" Function.prototype.toString.call({}); // TypeError Function.prototype.toString.call(null); // TypeError Function.prototype.toString.call(undefined); // TypeError
Wywoływanie funkcji z tablicą argumentów - apply
Jak wywołać dowolną funkcję z listą argumentów zapisaną w tablicy?
Function.prototype.apply(thisArg) Function.prototype.apply(thisArg, argArray)
- Parametry:
- thisArg - wartość bieżącego obiektu
this
dostępnego w czasie wywołania wewnątrz ciała funkcji Array
argArray - lista argumentów wywołania funkcji- Wartość:
- wartość którą normalnie zwraca funkcja
- Wyjątki:
TypeError
- nastąpiła próba wywołania na obiekcie, który nie jest funkcją albo argArray nie jest obiektem
Zdarza się, że w naszym programie mamy dostępną listę argumentów w postaci tablicy i chcielibyśmy wywołać zadaną funkcję, przekazując jej taką właśnie listę argumentów. Niestety gdy zrobimy to w tradycyjny sposób, funkcja otrzyma tylko jeden argument - w postaci tablicy. Aby w takiej sytuacji skonwertować tablicę na tradycyjną listę argumentów, możemy się posłużyć funkcją apply
.
Dzięki tej funkcji, możemy również wywołać metodę w kontekście innego obiektu niż ten, w którym została zdefiniowana, nawet jeżeli jedna klasa nie dziedziczy po drugiej.
Przykład Function.prototype.apply
var f = function (a, b) { return a + b; }; var args = [1, 2]; f(args); // "1,2undefined" f.apply(null, args); // 3 var Person = function (name) { this.name = name; }; Person.prototype.speak = function (speech) { return this.name + ": " + speech; }; var Computer = function (name) { this.name = name; }; var obj = new Computer('HAL 9000'); var txt = 'My mind is going. I can feel it.'; Person.prototype.speak.apply(obj, [txt]); // "HAL 9000: My mind is going. I can feel it." Function.prototype.apply.call(null); // TypeError Function.prototype.apply.call(undefined); // TypeError Function.prototype.apply.call("test"); // TypeError Function.prototype.apply.call({}); // TypeError f.apply(null, "test"); // TypeError
Wybieranie funkcji do wykonania - call
Jak zbudować program, który pozwoli wybrać użytkownikowi funkcję, którą należy wykonać?
Function.prototype.call(thisArg) Function.prototype.call(thisArg, arg1, arg2... argn)
- Parametry:
- thisArg - wartość bieżącego obiektu
this
dostępnego w czasie wywołania wewnątrz ciała funkcji - arg1, arg2... argn - lista argumentów wywołania funkcji
- Wartość:
- wartość którą normalnie zwraca funkcja
- Wyjątki:
TypeError
- nastąpiła próba wywołania na obiekcie, który nie jest funkcją
Działa analogicznie jak Object.prototype.apply, ale pozwala przekazać listę argumentów w sposób tradycyjny, a nie poprzez tablicę. Może to być przydatne, jeżeli wiemy z góry, jakie argumenty będziemy chcieli przekazać, ale w momencie tworzenia programu nie wiadomo jeszcze, którą funkcję będziemy chcieli wywołać.
Przykład Function.prototype.call
var f = function (a, b) { return a + b; }; f(1, 2); // 3 f.call(null, 1, 2); // 3 var Person = function (name) { this.name = name; }; Person.prototype.speak = function (speech) { return this.name + ": " + speech; }; var Computer = function (name) { this.name = name; }; var obj = new Computer('HAL 9000'); var txt = 'My mind is going. I can feel it.'; Person.prototype.speak.call(obj, txt); // "HAL 9000: My mind is going. I can feel it." Function.prototype.call.call(null); // TypeError Function.prototype.call.call(undefined); // TypeError Function.prototype.call.call("test"); // TypeError Function.prototype.call.call({}); // TypeError
Ustawianie właściwej instancji obiektu w metodzie - bind
Co zrobić, żeby w metodzie była widoczna właściwa instancja obiektu?
(interpretuje: Internet Explorer 9, Firefox 4, Opera 12, Chrome 7)
Function.prototype.bind(thisArg) Function.prototype.call(thisArg, arg1, arg2... argn)
- Parametry:
- thisArg - wartość bieżącego obiektu
this
dostępnego w czasie wywołania wewnątrz ciała funkcji - arg1, arg2... argn - lista początkowych argumentów wywołania funkcji
- Wartość:
Function
- nowa instancja funkcji- Wyjątki:
TypeError
- nastąpiła próba wywołania na obiekcie, który nie jest funkcją
W asynchronicznych językach programowania bardzo często używa się tzw. funkcji zwrotnej. Na przykład możemy spróbować pobrać dane z zewnętrznego źródła, ale ponieważ odpowiedź nie pojawi się natychmiast, program kontynuuje normalnie dalsze działanie. Dzięki temu interfejs użytkownika nie zacina się w oczekiwaniu na odpowiedź z zewnętrznego źródła. Kiedy odpowiedź nadejdzie, zostanie wywołana właśnie wcześniej zdefiniowana funkcja zwrotna, która odbierze przesłane dane.
Taki sposób działania programu jest bardzo wydajny, ale często powoduje utratę kontekstu wywołania. W funkcji zwrotnej wartość obiektu bieżącego this
może nie być tym, czego się spodziewamy. Można temu zapobiec, używając wcześniej funkcji bind
. Dzięki niej w czasie wywołania naszej funkcji zwrotnej obiekt bieżący this
będzie zawierał zawsze podaną wartość. Możemy również uzupełnić początkowe argumenty funkcji, tak aby przy każdym późniejszym jej wywołaniu miały taką samą wartość, a dodatkowe argumenty były dołączane na końcu listy.
Przykład Function.prototype.bind
var Person = function (name) { this.name = name; }; Person.prototype.speak = function (volume, speech) { return this.name + " (" + volume + "): " + speech; }; var Computer = function (name) { this.name = name; }; Computer.prototype.execute = function (command, args) { return command.apply(this, args); }; var person = new Person("John"); var f = person.speak.bind(person, "loudly"); var computer = new Computer("HAL 9000"); computer.execute(f, ["Hello world!"]); // "John (loudly): Hello world!" var args = ["silently", "My mind is going. I can feel it."]; computer.execute(person.speak, args); // "HAL 9000 (silently): My mind is going. I can feel it." Function.prototype.bind.call(null, person); // TypeError Function.prototype.bind.call(undefined, person); // TypeError Function.prototype.bind.call("test", person); // TypeError Function.prototype.bind.call({}, person); // TypeError