Przejdź do treści

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?

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
Facebook