Najtrudniejsze pytania JavaScript (z odpowiedziami)

Podobnie jak każdy inny język programowania, JavaScript ma swoje niuanse. Poniżej znajduje się 27 podchwytliwych pytań z JavaScript, które każdy programista Full-Stack powinien znać.

Opisz koncepcję domknięcia (Closure) w JavaScript najlepiej jak potrafisz

Rozważ poniższy przykład:

function makeAdder(x) {
  // parametr `x` jest zmienną wewnętrzną

  // funkcja wewnętrzna `add()` używa `x`, więc
  // posiada "closure" nad `x`
  function add(y) {
    return y + x;
  };

  return add;
}

Odniesienie do wewnętrznej funkcji add, która została zwrócona, potrafi "zapamiętać", jaka wartość x została przekazana podczas wywołania funkcji makeAdder.

var plusOne = makeAdder(1);   // x ma wartość 1, plusOne odnosi się do add(y)
var plusTen = makeAdder(10);    // x ma wartość 10

plusOne(3);  // 1 (x) + 3 (y) = 4
plusTen(13); // 10 (x) + 13 (y) = 23 

W C i w większości innych popularnych języków, po zakończeniu działania funkcji wszystkie zmienne lokalne przestają być dostępne, ponieważ ramka stosu zostaje zniszczona. W JavaScript, gdy zadeklarujesz funkcję wewnątrz innej funkcji, zmienne lokalne pozostają dostępne nawet po powrocie z wywołanej funkcji. Closure to ramka stosu, która jest alokowana, gdy funkcja rozpoczyna swoje działanie, i nie jest zwalniana po zakończeniu funkcji (tak, jakby ramka stosu była alokowana w stercie zamiast na stosie!). W JavaScript można traktować zmienną przechowującą referencję do funkcji jako posiadającą zarówno wskaźnik na funkcję, jak i ukryty wskaźnik do closure

Wyjaśnij różnicę między Object.freeze() a const

const oraz Object.freeze to dwa zupełnie różne mechanizmy.

  • const dotyczy powiązań („zmiennych”). Umożliwia utworzenie niemodyfikowalnego powiązania, tzn. nie można przypisać nowej wartości do tego powiązania.
const person = {
    name: "Leonardo"
};
let animal = {
    species: "snake"
};
person = animal; // BŁĄD: "person" jest tylko do odczytu
  • Object.freeze działa na wartościach, a konkretnie na wartościach typu object. Uczyni obiekt niemodyfikowalnym, tzn. nie można zmienić jego właściwości.
let person = {
    name: "Leonardo"
};
let animal = {
    species: "snake"
};
Object.freeze(person);
person.name = "Lima"; // TypeError: Nie można przypisać do tylko do odczytu właściwości 'name' obiektu
console.log(person);

Wyjaśnij różnicę pomiędzy undefined a not defined w JavaScript

W JavaScript, gdy próbujesz użyć zmiennej, która nie istnieje i nie została zadeklarowana, interpreter rzuci błąd var name is not defined i skrypt przestanie się wykonywać. Natomiast, jeżeli użyjesz typeof undeclared_variable, zostanie zwrócona wartość undefined.
Zanim przejdziemy do dalszej dyskusji, zrozummy różnicę między deklaracją a definicją.
var x jest deklaracją, ponieważ nie przypisujesz jeszcze żadnej wartości do zmiennej, ale deklarujesz jej istnienie oraz potrzebę przydzielenia miejsca w pamięci.

var x; // Deklaracja zmiennej x
console.log(x); // Wynik: undefined

var x = 1 to zarówno deklaracja, jak i definicja (możemy również powiedzieć, że dokonujemy inicjalizacji). Tutaj deklaracja zmiennej x oraz przypisanie wartości odbywają się jednocześnie. W JavaScript każda deklaracja zmiennej oraz funkcji jest przenoszona na początek bieżącego zakresu, a następnie przypisanie wartości odbywa się w kolejności – to zjawisko nazywane jest hoisting.
Zmienne, które zostały zadeklarowane, ale nie zdefiniowane i do których próbujemy się odwołać, skutkują wartością undefined.

var x; // Deklaracja
if (typeof x === 'undefined') // Zwróci true

Zmienna, której ani zadeklarowano, ani zdefiniowano – gdy spróbujemy odwołać się do takiej zmiennej, rezultatem będzie not defined.

console.log(y);  // Wynik: ReferenceError: y is not defined

Wyjaśnij różnice w użyciu foo między function foo() {} a var foo = function() {}

Pierwsza forma to deklaracja funkcji, natomiast druga to wyrażenie funkcyjne. Kluczową różnicą jest to, że ciała deklaracji funkcji są hoistowane (przenoszone na początek ich bieżącego zakresu), podczas gdy ciała wyrażeń funkcyjnych nie są – mają one taki sam mechanizm hoistowania jak zmienne. Jeśli spróbujesz wywołać wyrażenie funkcyjne przed jego definicją, otrzymasz błąd Uncaught TypeError: XXX is not a function.

Deklaracja funkcji

foo(); // 'FOOOOO'
function foo() {
    console.log('FOOOOO');
}

Wyrażenie funkcyjne

foo(); // Uncaught TypeError: foo is not a function
var foo = function () {
    console.log('FOOOOO');
};

Jak użyć closure do stworzenia prywatnego licznika?

Możesz stworzyć funkcję wewnątrz funkcji zewnętrznej (closure), która umożliwia aktualizację prywatnej zmiennej, jednak zmienna ta nie będzie dostępna spoza funkcji bez użycia funkcji pomocniczej.

function counter() {
    var _counter = 0;
    // zwraca obiekt z kilkoma funkcjami umożliwiającymi
    // modyfikację prywatnej zmiennej _counter
    return {
        add: function (increment) {
            _counter += increment;
        },
        retrieve: function () {
            return 'The counter is currently at: ' + _counter;
        }
    }
}

// błąd przy próbie dostępu do prywatnej zmiennej w poniższy sposób
// _counter;
// użycie funkcji counter
var c = counter();
c.add(5);
c.add(9);
// teraz możemy uzyskać dostęp do prywatnej zmiennej w następujący sposób
c.retrieve(); // => The counter is currently at: 14

Podaj kilka przykładów konwersji wartości nieboolowskich do typu boolean

Konkretna lista wartości „falsy” w JavaScript przedstawia się następująco:

  • "" (pusty string)
  • 0, -0, NaN (niewłaściwa liczba)
  • null, undefined
  • false

Każda wartość, która nie znajduje się na tej liście „falsy”, jest traktowana jako „truthy”. Oto kilka przykładów:

  • "hello"
  • 42
  • true
  • [ ], [ 1, "2", 3 ] (tablice)
  • { }, { a: 42 } (obiekty)
  • function foo() { .. } (funkcje)

Co to są IIFEs (Immediately Invoked Function Expressions)?

Jest to natychmiastowo wywoływane wyrażenie funkcyjne, w skrócie IIFE. Funkcja ta jest wykonywana zaraz po jej utworzeniu:

(function IIFE() {
    console.log("Hello!");
})();
// "Hello!"

Ten wzorzec jest często stosowany, aby uniknąć zanieczyszczania globalnego obszaru nazw, ponieważ wszystkie zmienne użyte wewnątrz IIFE (podobnie jak w każdej innej zwykłej funkcji) nie są widoczne poza jej zakresem.


Co to jest Currying?

Currying polega na rozbiciu funkcji przyjmującej wiele argumentów na serię funkcji przyjmujących części argumentów. Oto przykład w JavaScript:

function add(a, b) {
    return a + b;
}

add(3, 4); // returns 7

Powyższa funkcja przyjmuje dwa argumenty, a i b, i zwraca ich sumę. Teraz zaktualizujemy tę funkcję, stosując currying:

function add(a) {
    return function (b) {
        return a + b;
    }
}

W algebrze funkcji operowanie na funkcjach przyjmujących wiele argumentów (lub równoważnie jednego argumentu będącego N-krotką) bywa nieeleganckie. Jak zatem postąpić z czymś, co naturalnie wyrazilibyśmy jako, np. f(x,y)? Możemy potraktować to jako równoważne f(x)(y) - f(x), gdzie powstała funkcja, nazwijmy ją g, jest funkcją, którą stosujemy do y. Innymi słowy, posiadamy jedynie funkcje przyjmujące jeden argument – jednak niektóre z tych funkcji zwracają inne funkcje (również przyjmujące jeden argument).


Czym jest domknięcie i jak/dlaczego miałbyś go użyć?

Domknięcie to połączenie funkcji i środowiska leksykalnego, w którym ta funkcja została zadeklarowana. Słowo „leksykalny” odnosi się do faktu, że zakres leksykalny używa lokalizacji, w której zmienna jest zadeklarowana w kodzie źródłowym, aby określić, gdzie ta zmienna jest dostępna. Domknięcia to funkcje, które mają dostęp do zmiennych funkcji zewnętrznej (obejmującej) — łańcucha zakresu, nawet po zwróceniu funkcji zewnętrznej. Dlaczego miałbyś go użyć?


Jaka jest definicja funkcji wyższego rzędu?

Funkcja wyższego rzędu to dowolna funkcja, która przyjmuje jedną lub więcej funkcji jako argumenty, których używa do operowania na niektórych danych i/lub zwraca funkcję jako wynik. Funkcje wyższego rzędu mają na celu abstrakcję pewnej operacji, która jest wykonywana wielokrotnie. Klasycznym tego przykładem jest map, który przyjmuje tablicę i funkcję jako argumenty. map następnie używa tej funkcji do przekształcenia każdego elementu w tablicy, zwracając nową tablicę z przekształconymi danymi. Inne popularne przykłady w JavaScript to forEach, filter i reduce. Funkcja wyższego rzędu nie musi tylko manipulować tablicami, ponieważ istnieje wiele przypadków użycia zwracania funkcji z innej funkcji. Function.prototype.bind jest jednym z takich przykładów w JavaScript. Map Powiedzmy, że mamy tablicę nazw, w której musimy przekształcić każdy ciąg na wielkie litery.

// Tablica imion
const names = ['irish', 'daisy', 'anna'];

Sposób imperatywny będzie wyglądał następująco:

// Funkcja transformująca imiona na wielkie litery - sposób imperatywny
const transformNamesToUppercase = function (names) {
    const results = [];
    for (let i = 0; i < names.length; i++) {
        results.push(names[i].toUpperCase());
    }
    return results;
};
transformNamesToUppercase(names); // ['IRISH', 'DAISY', 'ANNA']

Użycie .map(transformerFn) sprawia, że kod jest krótszy i bardziej deklaratywny.

// Funkcja transformująca imiona na wielkie litery - z użyciem .map()
const transformNamesToUppercase = function (names) {
    return names.map(name => name.toUpperCase());
};
transformNamesToUppercase(names); // ['IRISH', 'DAISY', 'ANNA']

Jaka jest różnica między funkcjami anonimowymi i nazwanymi?

Rozważ:

var foo = function () { // funkcja anonimowa przypisana do zmiennej foo
// ..
};
var x = function bar() { // funkcja nazwana (bar) przypisana do zmiennej x 
// ..
};
foo(); // faktyczne wykonanie funkcji
x();

Czym jest wada tworzenia prywatnej metody w czystym JavaScript?

Jedną z wad tworzenia prawdziwej prywatnej metody w JavaScript jest to, że są one bardzo nieefektywne pod względem pamięci, ponieważ dla każdej instancji tworzona jest nowa kopia metody.

var Employee = function (name, company, salary) {
    this.name = name || "";       // Atrybut publiczny, domyślna wartość to null
    this.company = company || ""; // Atrybut publiczny, domyślna wartość to null
    this.salary = salary || 5000; // Atrybut publiczny, domyślna wartość to null
    // Prywatna metoda
    var increaseSalary = function () {
        this.salary = this.salary + 1000;
    };
    // Publiczna metoda
    this.dispalyIncreasedSalary = function () {
        increaseSalary();
        console.log(this.salary);
    };
};
// Tworzenie obiektu klasy Employee
var emp1 = new Employee("John", "Pluto", 3000);
// Tworzenie obiektu klasy Employee
var emp2 = new Employee("Merry", "Pluto", 2000);
// Tworzenie obiektu klasy Employee
var emp3 = new Employee("Ren", "Pluto", 2500);

Tutaj każda zmienna instancji emp1, emp2, emp3 ma własną kopię prywatnej metody increaseSalary. Zatem jako zalecenie, nie stosuj prywatnych metod, chyba że jest to konieczne.


Jaki jest typowy przypadek użycia funkcji anonimowych?

Można ich używać w IIFE (Immediately Invoked Function Expression), aby hermetyzować kod w zakresie lokalnym, tak aby zmienne zadeklarowane w nim nie wyciekały do zakresu globalnego.

(function () {
    // Kod tutaj.
})();

Jako callback, który jest używany raz i nie musi być używany nigdzie indziej. Kod będzie wydawał się bardziej autonomiczny i czytelny, gdy handlery są zdefiniowane bezpośrednio w kodzie, który je wywołuje, zamiast szukać ciała funkcji gdzie indziej.

setTimeout(function () {
    console.log('Hello world!');
}, 1000);

Argumenty do konstrukcji programowania funkcyjnego lub Lodash (podobnie do callbacków).

const arr = [1, 2, 3];
const double = arr.map(function (el) {
    return el * 2;
});
console.log(double); // [2, 4, 6]

Kiedy powinniśmy używać funkcji strzałkowych (arrow function) w ES6?

Obecnie stosuję następującą zasadę dla funkcji w ES6 i nowszych:

  • Używaj function w zakresie globalnym i dla właściwości Object.prototype.
  • Używaj class dla konstruktorów obiektów.
  • Używaj => wszędzie indziej. Dlaczego używać funkcji strzałkowych prawie wszędzie?
  • Bezpieczeństwo zakresu: Gdy funkcje strzałkowe są używane konsekwentnie, wszystko ma gwarancję użycia tego samego thisObject co root. Jeśli choć jeden callback funkcji standardowej zostanie zmieszany z funkcjami strzałkowymi, istnieje szansa, że zakres zostanie zaburzony.
  • Zwięzłość: Funkcje strzałkowe są łatwiejsze do czytania i pisania. (Może to wydawać się subiektywne, więc podam kilka przykładów dalej).
  • Przejrzystość: Gdy prawie wszystko jest funkcją strzałkową, każda zwykła funkcja od razu rzuca się w oczy, definiując zakres. Programista zawsze może spojrzeć na następną wyższą instrukcję funkcji, aby zobaczyć, czym jest thisObject.

Kiedy powinniśmy używać generatorów w ES6?

Mówiąc prościej, generator ma dwie cechy:

  • można wybrać, aby wyskoczyć z funkcji i pozwolić zewnętrznemu kodowi określić, kiedy wrócić do funkcji.
  • kontrola wywołania asynchronicznego może być wykonywana poza kodem Najważniejszą cechą generatorów jest to, że możemy pobrać kolejną wartość tylko wtedy, gdy jej naprawdę potrzebujemy, a nie wszystkie wartości naraz. W niektórych sytuacjach może to być bardzo wygodne.

Czy możesz opisać główną różnicę między pętlą .forEach a pętlą .map() i dlaczego wybrałbyś jedną zamiast drugiej?

Aby zrozumieć różnice między nimi, spójrzmy na to, co robi każda funkcja. forEach

  • Iteruje po elementach w tablicy.
  • Wykonuje callback dla każdego elementu.
  • Nie zwraca wartości.
const a = [1, 2, 3];
const doubled = a.forEach((num, index) => {
    // Zrób coś z num i/lub index.
});
// doubled = undefined

map

  • Iteruje po elementach w tablicy.
  • "Mapuje" każdy element na nowy element, wywołując funkcję na każdym elemencie, tworząc w rezultacie nową tablicę.
const a = [1, 2, 3];
const doubled = a.map(num => {
    return num * 2;
});
// doubled = [2, 4, 6]

Główną różnicą między .forEach a .map() jest to, że .map() zwraca nową tablicę. Jeśli potrzebujesz wyniku, ale nie chcesz modyfikować oryginalnej tablicy, .map() jest oczywistym wyborem. Jeśli po prostu chcesz iterować po tablicy, forEach jest dobrym wyborem.


Wyjaśnij dziedziczenie prototypowe w JavaScript?

W językach implementujących dziedziczenie klasyczne, takich jak Java, C# czy C++, zaczynamy od stworzenia klasy -- szablonu dla naszych obiektów -- a następnie możemy tworzyć nowe obiekty z tej klasy lub rozszerzać klasę, definiując nową klasę, która rozszerza klasę oryginalną. W JavaScript najpierw tworzymy obiekt (nie ma pojęcia klasy), a następnie możemy rozszerzyć nasz własny obiekt lub stworzyć z niego nowe obiekty. Każdy obiekt w JavaScript ma prototyp. System dziedziczenia w JavaScript jest prototypowy, a nie oparty na klasach. Kiedy komunikat dociera do obiektu, JavaScript najpierw spróbuje znaleźć właściwość w tym obiekcie, jeśli nie może jej znaleźć, to komunikat zostanie wysłany do prototypu obiektu i tak dalej. To zachowanie nazywa się **łańcuchem prototypów ** lub dziedziczeniem prototypowym. Funkcje konstruktora są najczęściej używanym sposobem w JavaScript do konstruowania łańcuchów prototypów. Kiedy używamy new, JavaScript wstrzykuje niejawną referencję do nowo tworzonego obiektu w postaci słowa kluczowego this. Zwraca również tę referencję niejawnie na końcu funkcji.

function Foo() {
    this.kind = 'foo'; // Typ 'foo'
}

var foo = new Foo();
foo.kind; //=> ‘foo’

Wyjaśnij wzorzec projektowy Prototyp

Wzorzec Prototyp tworzy nowe obiekty, ale zamiast tworzyć niezinicjalizowane obiekty, zwraca obiekty, które są zainicjalizowane wartościami skopiowanymi z obiektu prototypowego - lub wzorcowego. Wzorzec Prototyp jest również nazywany wzorcem Właściwości. Przykładem, gdzie wzorzec Prototyp jest przydatny, jest inicjalizacja obiektów biznesowych z wartościami, które odpowiadają wartościom domyślnym w bazie danych. Obiekt prototypowy przechowuje wartości domyślne, które są kopiowane do nowo utworzonego obiektu biznesowego. Języki klasyczne rzadko używają wzorca Prototyp, ale JavaScript, będąc językiem prototypowym, używa tego wzorca w konstrukcji nowych obiektów i ich prototypów.


Jak działa słowo kluczowe this? Podaj kilka przykładów kodu.

W JavaScript this zawsze odnosi się do „właściciela” funkcji, którą wykonujemy, a raczej do obiektu, którego metoda jest funkcją. Rozważ:

function foo() {
    console.log(this.bar);
}

var bar = "global"; // Zmienna globalna
var obj1 = {
    bar: "obj1", // Właściwość 'bar' w obj1
    foo: foo,
};
var obj2 = {
    bar: "obj2", // Właściwość 'bar' w obj2
};
foo();           // "global"
obj1.foo();    // "obj1"
foo.call(obj2);  // "obj2"
new foo();     // undefined

Jak utworzyłbyś prywatną zmienną w JavaScript?

Aby utworzyć prywatną zmienną w JavaScript, której nie można zmienić, należy utworzyć ją jako zmienną lokalną wewnątrz funkcji. Nawet jeśli funkcja zostanie wykonana, zmienna nie będzie dostępna poza funkcją. Na przykład:

function func() {
    var priv = "secret code"; // Tajny kod
}

console.log(priv); // wyrzuca błąd

Aby uzyskać dostęp do zmiennej, należałoby utworzyć funkcję pomocniczą, która zwraca prywatną zmienną.

function func() {
    var priv = "secret code"; // Tajny kod
    return function () {
        return priv;
    };
}

var getPriv = func();
console.log(getPriv()); // => secret code

Co to jest Hoisting w JavaScript?

Hoisting to działanie interpretera JavaScript polegające na przeniesieniu wszystkich deklaracji zmiennych i funkcji na początek bieżącego zakresu. Istnieją dwa rodzaje hoistingu:

  • hoisting zmiennych - rzadki
  • hoisting funkcji - częstszy Gdziekolwiek pojawia się var (lub deklaracja funkcji) wewnątrz zakresu, ta deklaracja jest traktowana jako należąca do całego zakresu i dostępna wszędzie w jego obrębie.
var a = 2;
foo(); // działa, ponieważ deklaracja `foo()`
// jest "podniesiona"
function foo() {
    a = 3;
    console.log(a); // 3
    var a; // deklaracja jest "podniesiona"
    // na początek `foo()`
}

console.log(a); // 2

Kiedy NIE należy używać funkcji strzałkowych (arrow functions) w ES6? Podaj trzy lub więcej przypadków.

Funkcji strzałkowych NIE należy używać:

  • Gdy potrzebujemy wciągania funkcji (ang. function hoisting) - funkcje strzałkowe są anonimowe.
  • Gdy chcemy użyć this/arguments w funkcji - funkcje strzałkowe nie mają własnego this/arguments, zależą od kontekstu zewnętrznego.
  • Gdy chcemy użyć nazwanej funkcji - funkcje strzałkowe są anonimowe.
  • Gdy chcemy użyć funkcji jako konstruktora - funkcje strzałkowe nie mają własnego this.
  • Gdy chcemy dodać funkcję jako właściwość w literału obiektu i użyć w niej obiektu - ponieważ nie możemy uzyskać dostępu do this (którym powinien być sam obiekt).

Kiedy użyłbyś funkcji bind?

Metoda bind() tworzy nową funkcję, która po wywołaniu ma słowo kluczowe this ustawione na podaną wartość, z daną sekwencją argumentów poprzedzających dowolne argumenty podane podczas wywołania nowej funkcji. Dobrym zastosowaniem funkcji bind jest sytuacja, gdy masz określoną funkcję, którą chcesz wywołać z określoną wartością this. Możesz wtedy użyć bind, aby przekazać określony obiekt do funkcji, która używa odwołania this.

function fullName() {
    return "Hello, this is " + this.first + " " + this.last; // Witaj, to jest
}

console.log(fullName()); // => Hello this is undefined undefined // Witaj to jest undefined undefined
// Utwórz obiekt osoby i przekaż jego wartości do funkcji fullName
var person = {first: "Foo", last: "Bar"};
console.log(fullName.bind(person)()); // => Hello this is Foo Bar // Witaj to jest Foo Bar

Jak głęboko zamrozić (deep freeze) obiekt w JavaScript?

Jeśli chcesz mieć pewność, że obiekt jest głęboko zamrożony, musisz utworzyć funkcję rekurencyjną, aby zamrozić każdą właściwość, która jest typu object: Bez głębokiego zamrażania:

let person = {
    name: "Leonardo", // imię
    profession: { // zawód
        name: "developer" // nazwa
    }
};
Object.freeze(person); // Uczyń obiekt niezmiennym
person.profession.name = "doctor"; // lekarz
console.log(person); //output { name: 'Leonardo', profession: { name: 'doctor' } } //wyjście { name: 'Leonardo', profession: { name: 'doctor' } }

Z głębokim zamrażaniem:

function deepFreeze(object) { // głębokieZamrożenie
    let propNames = Object.getOwnPropertyNames(object); // nazwyWłaściwości
    for (let name of propNames) { // nazwa
        let value = object[name]; // wartość
        object[name] = value && typeof value === "object" ? // obiekt
            deepFreeze(value) : value;
    }
    return Object.freeze(object);
}

let person = { // osoba
    name: "Leonardo", // imię
    profession: { // zawód
        name: "developer" // nazwa
    }
};
deepFreeze(person);
person.profession.name = "doctor"; // lekarz // TypeError: Cannot assign to read only property 'name' of object // BłądTypu: Nie można przypisać do właściwości tylko do odczytu 'name' obiektu

Dlaczego w JavaScript operator this jest niespójny?

Najważniejszą rzeczą do zrozumienia jest to, że obiekt funkcji nie ma ustalonej wartości this - wartość this zmienia się w zależności od sposobu wywołania funkcji. Mówimy, że funkcja jest wywoływana z określoną wartością this - wartość this jest określana w czasie wywołania, a nie w czasie definicji.

  • Jeśli funkcja jest wywoływana jako funkcja "surowa" (np. po prostu someFunc()), this będzie obiektem globalnym ( window w przeglądarce) (lub undefined, jeśli funkcja działa w trybie ścisłym).
  • Jeśli jest wywoływana jako metoda na obiekcie, this będzie obiektem wywołującym.
  • Jeśli wywołasz funkcję z call lub apply, this jest określone jako pierwszy argument do call lub apply.
  • Jeśli jest wywoływana jako nasłuchiwanie zdarzeń (ang. event listener), this będzie elementem, który jest celem zdarzenia.
  • Jeśli jest wywoływana jako konstruktor z new, this będzie nowo utworzonym obiektem, którego prototyp jest ustawiony na właściwość prototype funkcji konstruktora.
  • Jeśli funkcja jest wynikiem operacji bind, funkcja zawsze i na zawsze będzie miała this ustawione na pierwszy argument wywołania bind, które ją utworzyło. (Jest to jedyny wyjątek od reguły "funkcje nie mają ustalonego this" - funkcje utworzone przez bind faktycznie mają niezmienne this.)

Czy JavaScript jest językiem przekazywania przez referencję czy przez wartość?

Zawsze jest przekazywany przez wartość, ale w przypadku obiektów wartość zmiennej jest referencją. Z tego powodu, gdy przekazujesz obiekt i zmieniasz jego elementy, te zmiany pozostają poza funkcją. To sprawia, że wygląda to jak przekazywanie przez referencję. Ale jeśli faktycznie zmienisz wartość zmiennej obiektu, zobaczysz, że zmiana nie jest utrwalona, co dowodzi, że jest to naprawdę przekazywanie przez wartość. Przykład:

function changeStuff(a, b, c) // zmienRzeczy
{
    a = a * 10;
    b.item = "changed"; // zmienione
    c = {item: "changed"}; // zmienione
}

var num = 10; // liczba
var obj1 = {item: "unchanged"}; // niezmienione
var obj2 = {item: "unchanged"}; // niezmienione
changeStuff(num, obj1, obj2); // zmienRzeczy
console.log(num); // liczba
console.log(obj1.item); // element    
console.log(obj2.item); // element

Wyjście:

10
changed // zmienione
unchanged // niezmienione

Jaka jest różnica między słowem kluczowym await a słowem kluczowym yield?

yield można uznać za element składowy await.

yield przyjmuje podaną wartość i przekazuje ją do obiektu wywołującego. Obiekt wywołujący może następnie zrobić z tą wartością, co zechce (1). Później obiekt wywołujący może zwrócić wartość do generatora (poprzez generator.next()), która staje się wynikiem wyrażenia yield (2) lub błędem, który będzie wyglądał, jakby został zgłoszony przez wyrażenie yield (3).

async-await można uznać za użycie yield. W punkcie (1) obiekt wywołujący (tj. sterownik async-await - podobny do funkcji, którą opublikowałeś) opakowuje wartość w obietnicę za pomocą algorytmu podobnego do new Promise(r => r(value) (uwaga, nie Promise.resolve, ale to nie ma większego znaczenia).

Następnie czeka na rozwiązanie obietnicy. Jeśli się powiedzie, przekazuje spełnioną wartość z powrotem w punkcie (2).

Jeśli odrzuci, zgłasza powód odrzucenia jako błąd w punkcie (3).

Zatem użyteczność async-await polega na tym mechanizmie, który używa yield do rozpakowania wartości yield jako obietnicy i przekazania jej rozwiązanej wartości z powrotem, powtarzając, aż funkcja zwróci swoją ostateczną wartość.



---
Powrót do blogu

Zostaw komentarz

Pamiętaj, że komentarze muszą zostać zatwierdzone przed ich opublikowaniem.