Pytania z JavaScript
Odpowiedzi do Front-end Job Interview Questions - JS Questions. Pull requesty mile widziane, jeśli masz sugestie i poprawki!
- Wyjaśnij delegowanie zdarzeń
- Wyjaśnij jak
this
działa w JavaScript - Wyjaśnij, jak działa dziedziczenie prototypowe
- Co sądzisz o AMD vs CommonJS?
- Wyjaśnij, dlaczego następujące elementy nie działają jako IIFE:
function foo(){ }();
. Co należy zmienić, aby poprawnie uczynić to IIFE? - Jaka jest różnica między zmienną:
null
,undefined
lub niezadeklarowaną? Jak sprawdziłbyś którykolwiek z tych stanów? - Czym jest domknięcie i jak/dlaczego miałbyś je zastosować?
- Czy możesz opisać główną różnicę pomiędzy pętlą
.forEach
, a pętlą.map()
i dlaczego wybrałbyś jeden albo drugi? - Jaki jest typowy przypadek użycia funkcji anonimowych?
- Jak organizujesz swój kod? (wzorzec modułu, klasyczne dziedziczenie?)
- Jaka jest różnica między obiektami hosta a obiektami macierzystymi?
- Różnica pomiędzy:
function Person(){}
,var person = Person()
, ivar person = new Person()
? - Jaka jest różnica pomiędzy
.call
i.apply
? - Wytłumacz
Function.prototype.bind
. - Kiedy użyłbyś
document.write()
? - Jaka jest różnica między wykrywaniem funkcji, feature inference i używaniem UA string?
- Wyjaśnij Ajax tak szczegółowo, jak to możliwe.
- Jakie są zalety i wady korzystania z Ajax?
- Wyjaśnij, jak działa JSONP (i jak to naprawdę nie jest Ajax).
- Czy kiedykolwiek używałeś szablonów JavaScript? Jeśli tak, z jakich bibliotek korzystałeś?
- Wytłumacz "hoisting".
- Opisz event bubbling.
- Jaka jest różnica pomiędzy "attribute", a "property"?
- Dlaczego rozszerzenie wbudowanych obiektów JavaScript nie jest dobrym pomysłem?
- Różnica między document
load
event, a documentDOMContentLoaded
event? - Jaka jest różnica pomiędzy
==
i===
? - Wyjaśnij same-origin policy w odniesieniu do JavaScript.
- Spraw aby działało:
- Dlaczego nazywa się to wyrażeniem trójargumentowym, co oznacza słowo "trójargumentowe"?
- Czym jest
"use strict";
? Jakie są zalety i wady korzystania z tego? - Utwórz pętlę for, która będzie się powtarzać do
100
podczas wysyłania "fizz" w wielokrotnościach3
, "buzz" w wielokrotnościach5
i "fizzbuzz" w wielokrotnościach3
oraz5
. - Dlaczego generalnie dobrym pomysłem jest pozostawienie globalnego zasięgu witryny takim, jakim jest, i nigdy go nie dotykać?
- Dlaczego miałbyś używać czegoś takiego jak zdarzenie
load
? Czy to wydarzenie ma wady? Czy znasz jakieś alternatywy i dlaczego miałbyś je wykorzystać? - Wyjaśnij, czym jest SAP i jak uczynić ją przyjazną SEO.
- Jaki jest zakres twojego doświadczenia z Promises i/lub ich polyfills?
- Jakie są zalety i wady korzystania z obietnic zamiast callbacks?
- Jakie są zalety/wady pisania kodu JavaScript w języku kompilującym się w JavaScript?
- Jakich narzędzi i technik używasz do debugowania kodu JavaScript?
- Jakich konstrukcji językowych używasz do iteracji właściwości obiektów i elementów tablicy?
- Wyjaśnij różnicę między obiektami mutable, a immutable.
- Wyjaśnij różnicę między funkcjami synchronicznymi i asynchronicznymi.
- Co to jest pętla zdarzeń? Jaka jest różnica między stosem wywołań (call stack) a kolejką zadań (task queue)?
- Wyjaśnij różnice w korzystaniu z
foo
pomiędzyfunction foo() {}
ivar foo = function() {}
- Jakie są różnice między zmiennymi utworzonymi za pomocą
let
,var
lubconst
? - Jakie są różnice między konstruktorami funkcji ES6 i ES5?
- Czy możesz podać przypadek użycia nowej składni funkcji arrow =>? Czym ta nowa składnia różni się od innych funkcji?
- Jaka jest zaleta korzystania ze składni arrow syntax dla metody w konstruktorze?
- Jaka jest definicja funkcji wyższego rzędu?
- Czy możesz podać przykład destrukturyzacji obiektu lub tablicy?
- ES6 Template Literals oferują dużą elastyczność w generowaniu ciągów, czy możesz podać przykład?
- Czy możesz podać przykład curry function i dlaczego ta składnia ma tę zaletę?
- Jakie są zalety korzystania ze składni spread syntax i czym różni się od rest syntax?
- Jak współdzielić kod między plikami?
- Dlaczego warto tworzyć członków klasy statycznej?
- Inne odpowiedzi
Wyjaśnij delegowanie zdarzeń
Delegowanie zdarzeń to technika polegająca na dodawaniu event listenerów do elementu nadrzędnego zamiast dodawania ich do elementów potomnych. Listener będzie wyzwalany za każdym razem, gdy zdarzenie zostanie wyzwolone na elementach potomnych z powodu wystąpienia zdarzenia propagującego DOM. Korzyści z tej techniki to:
- Memory footprint zmniejsza się, ponieważ do elementu nadrzędnego potrzebny jest tylko jeden moduł obsługi, zamiast konieczności dołączania modułów obsługi zdarzeń do każdego elementu potomnego.
- Nie ma potrzeby odłączania modułu obsługi od usuwanych elementów i wiązania zdarzenia dla nowych elementów.
Bibliografia
- https://davidwalsh.name/event-delegate
- https://stackoverflow.com/questions/1687296/what-is-dom-event-delegation
Wyjaśnij jak this
działa w JavaScript
Nie ma prostego wyjaśnienia dla this
; jest to jedna z najbardziej mylących koncepcji w JavaScript. Wytłumaczeniem na szybko jest to, że wartość this
zależy od tego, jak wywoływana jest funkcja. Przeczytałem wiele wyjaśnień na temat this
w Internecie i znalazłem wytłumaczenie od Arnav Aggrawal, jako najbardziej klarowne. Stosuje się następujące zasady:
- Jeśli słowo kluczowe
new
jest używane podczas wywoływania funkcji,this
wewnątrz funkcji jest nowym obiektem. - Jeśli
apply
,call
, lubbind
służą do wywoływania/tworzenia funkcji,this
wewnątrz funkcji jest obiektem przekazywanym jako argument. - Jeśli funkcja jest wywoływana jako metoda, na przykład
obj.method()
—this
jest obiektem, którego funkcja jest własnością. - Jeśli funkcja jest wywoływana jako wywołanie wolnej funkcji, co oznacza, że została wywołana bez żadnego z powyższych warunków,
this
jest globalnym obiektem. W przeglądarce, jest obiektemwindow
. Jeśli jest w trybie strict mode ('use strict'
),this
będzieundefined
zamiast globalnego obiektu. - Jeśli stosuje się wiele powyższych zasad, reguła, która jest wyższa, wygrywa i ustawi wartość
this
. - Jeśli funkcja jest funkcją strzałkową (arrow function) ES2015, ignoruje wszystkie powyższe reguły i otrzymuje wartość
this
swojego otaczającego zakresu w momencie jej tworzenia.
Aby uzyskać szczegółowe wyjaśnienie, sprawdź jego artykuł na Medium.
Czy możesz podać przykład jednego ze sposobów, w jaki praca z tym zmieniła się w ES6?
ES6 umożliwia korzystanie z funkcji strzałkowych (arrow functions) które wykorzystują enclosing lexical scope. Jest to zwykle wygodne, ale nie zabezpiecza caller przed kontrolowaniem kontekstu przez .call
lub .apply
— konsekwencją jest to, że biblioteka taka jak jQuery
nie będzie poprawnie bindować this
w funkcjach obsługi zdarzeń. Dlatego ważne jest, aby o tym pamiętać przy refaktoryzacji dużych aplikacji.
Bibliografia
- https://codeburst.io/the-simple-rules-to-this-in-javascript-35d97f31bde3
- https://stackoverflow.com/a/3127440/1751946
Wyjaśnij, jak działa dziedziczenie prototypowe
To jest bardzo częste pytanie dotyczące rozmowy rekrutacyjnej w JavaScript. Wszystkie obiekty JavaScript mają właściwość __proto__
, jest to odniesienie do innego obiektu, który nazywa się "prototypem" obiektu. Gdy właściwość jest udostępniana na obiekt i jeśli właściwość nie została znaleziona na tym obiekcie, silnik JavaScript sprawdza __proto__
obiektu oraz __proto__
z __proto__
i tak dalej, dopóki nie znajdzie właściwości zdefiniowanej w jednym z __proto__
lub dopóki nie osiągnie końca łańcucha prototypów. To zachowanie symuluje klasyczne dziedziczenie, ale tak naprawdę jest bardziej delegowaniem niż dziedziczeniem.
Przykład dziedziczenia prototypowego
Mamy już wbudowane Object.create
, ale gdybyś dostarczył dla niego polyfill, mogłoby to wyglądać:
if (typeof Object.create !== 'function') {
Object.create = function (parent) {
function Tmp() {}
Tmp.prototype = parent;
return new Tmp();
};
}
const Parent = function () {
this.name = 'Parent';
};
Parent.prototype.greet = function () {
console.log('hello from Parent');
};
const child = Object.create(Parent.prototype);
child.cry = function () {
console.log('waaaaaahhhh!');
};
child.cry();
// Outputs: waaaaaahhhh!
child.greet();
// Outputs: hello from Parent
Warto zwrócić uwagę na:
.greet
nie jest zdefiniowane w child, więc silnik idzie w górę łańcucha prototypów i znajduje.greet
odziedziczone z Parent.- Musimy wywołać
Object.create
na jeden z następujących sposobów dziedziczenia prototypowych metod:- Object.create(Parent.prototype);
- Object.create(new Parent(null));
- Object.create(objLiteral);
- W tej chwili,
child.constructor
wskazuje naParent
:
child.constructor
ƒ () {
this.name = "Parent";
}
child.constructor.name
"Parent"
- Jeśli chcielibyśmy to naprawić, jedną z opcji byłoby:
function Child() {
Parent.call(this);
this.name = 'child';
}
Child.prototype = Parent.prototype;
Child.prototype.constructor = Child;
const c = new Child();
c.cry();
// Outputs: waaaaaahhhh!
c.greet();
// Outputs: hello from Parent
c.constructor.name;
// Outputs: "Child"
Bibliografia
- http://dmitrysoshnikov.com/ecmascript/javascript-the-core/
- https://www.quora.com/What-is-prototypal-inheritance/answer/Kyle-Simpson
- https://davidwalsh.name/javascript-objects
- https://crockford.com/javascript/prototypal.html
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Inheritance_and_the_prototype_chain
Co sądzisz o AMD vs CommonJS?
Oba sposoby implementacji systemu modułowego, który nie pojawił się natywnie w JavaScript przed pojawieniem się ES2015. CommonJS jest synchroniczny, podczas gdy AMD (definicja modułu asynchronicznego) jest oczywiście asynchroniczny. CommonJS został zaprojektowany z myślą o rozwoju po stronie serwera, podczas gdy AMD, ze wsparciem dla asynchronicznego ładowania modułów, jest bardziej przeznaczone dla przeglądarek.
Uważam, że składnia AMD jest dość wymowna, a CommonJS jest bliższy stylowi, w którym pisałbyś instrukcje importu w innych językach. Przez większość czasu uważam, że AMD jest niepotrzebne, ponieważ jeśli podałeś cały JavaScript w jednym połączonym pliku pakietu, nie skorzystałbyś z właściwości ładowania asynchronicznego. Ponadto składnia CommonJS jest bliższa stylowi pisania modułów w Node, a przełączanie między tworzeniem JavaScript po stronie klienta i serwera jest mniejsze.
Cieszę się, że dzięki modułom ES2015, które obsługują zarówno ładowanie synchroniczne, jak i asynchroniczne, możemy w końcu zastosować jedno podejście. Chociaż nie został w pełni wdrożony w przeglądarkach i w Node, zawsze możemy użyć transpilatorów do konwersji naszego kodu.
Bibliografia
- https://auth0.com/blog/javascript-module-systems-showdown/
- https://stackoverflow.com/questions/16521471/relation-between-commonjs-amd-and-requirejs
Wyjaśnij, dlaczego następujące elementy nie działają jako IIFE: function foo(){ }();
. Co należy zmienić, aby poprawnie uczynić to IIFE?
IIFE oznacza Immediately Invoked Function Expressions. Parser JavaScript czyta function foo(){ }();
jako function foo(){ }
i ();
, gdzie ten pierwszy to deklaracja funkcji i ten drugi (para nawiasów) jest próbą wywołania funkcji, ale nie ma określonej nazwy, dlatego rzuca Uncaught SyntaxError: Unexpected token )
.
Oto dwa sposoby rozwiązania tego problemu, polegające na dodaniu większej liczby nawiasów: (function foo(){ })()
oraz (function foo(){ }())
. Deklaracje zaczynające się od function
są uważane za deklaracji funkcji; poprzez zawinięcie tej funkcji wewnątrz ()
, staje się wyrażeniem funkcji które mogą być następnie wykonane z kolejnym ()
. Funkcje te nie są ujawniane w zakresie globalnym i można nawet pominąć jego nazwę, jeśli nie trzeba odwoływać się do ciała.
Możesz także użyć operatora void
: void function foo(){ }();
. Niestety istnieje jeden problem związany z takim podejściem. Ocena danego wyrażenia jest zawsze undefined
, więc jeśli funkcja IIFE zwraca cokolwiek, nie możesz jej użyć. Przykład:
const foo = void (function bar() {
return 'foo';
})();
console.log(foo); // undefined
Bibliografia
- http://lucybain.com/blog/2014/immediately-invoked-function-expression/
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/void
Jaka jest różnica między zmienną: null
, undefined
lub niezadeklarowaną? Jak sprawdziłbyś którykolwiek z tych stanów?
Niezadeklarowane zmienne są tworzone, gdy przypisujesz wartość do identyfikatora, który nie był wcześniej tworzony przy użyciu var
, let
lub const
. Niezadeklarowane zmienne zostaną zdefiniowane globalnie, poza bieżącym zakresem. W trybie strict mode, ReferenceError
zostanie rzucony, gdy spróbujesz przypisać do niezadeklarowanej zmiennej. Niezadeklarowane zmienne są złe, tak jak zmienne globalne są złe. Unikaj ich za wszelką cenę! Aby je sprawdzić, zawiń jego użycie w bloku try
/catch
.
function foo() {
x = 1; // Throws a ReferenceError in strict mode
}
foo();
console.log(x); // 1
Zmienna undefined
jest zmienną, która została zadeklarowana, ale nie ma przypisanej wartości. Jest to typu undefined
. Jeśli funkcja nie zwraca żadnej wartości, ponieważ w wyniku jej wykonania jest przypisana do zmiennej, zmienna ma również wartość undefined
. Aby to sprawdzić, porównaj, stosując operatora ścisłej równości (===
) lub typeof
który da string 'undefined'
. Zauważ, że nie powinieneś używać operatora abstrakcyjnej równości do sprawdzania, ponieważ zwróci on również wartość true
, jeśli wartość wynosi null
.
var foo;
console.log(foo); // undefined
console.log(foo === undefined); // true
console.log(typeof foo === 'undefined'); // true
console.log(foo == null); // true. Wrong, don't use this to check!
function bar() {}
var baz = bar();
console.log(baz); // undefined
Zmienna która jest null
zostanie wyraźnie przypisana do wartości null
. Nie reprezentuje żadnej wartości i różni się od undefined
w tym sensie, że zostało to wyraźnie przypisane. Aby sprawdzić null,
po prostu porównaj, używając operatora ścisłej równości. Pamiętaj, że podobnie jak powyżej, nie powinieneś używać abstrakcyjnego operatora równości (==
) do sprawdzenia, to również zwróci true
jeśli wartość jest undefined
.
var foo = null;
console.log(foo === null); // true
console.log(typeof foo === 'object'); // true
console.log(foo == undefined); // true. Wrong, don't use this to check!
Jako osobisty nawyk nigdy nie pozostawiam moich zmiennych niezadeklarowanych ani nieprzypisanych. Wyraźnie przypiszę im null
po zadeklarowaniu, jeśli nie zamierzam jej jeszcze używać. Jeśli użyjesz lintera w swoim przepływie pracy, zwykle będzie on również w stanie sprawdzić, czy nie odwołujesz się do niezadeklarowanych zmiennych.
Bibliografia
- https://stackoverflow.com/questions/15985875/effect-of-declared-and-undeclared-variables
- https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/undefined
Czym jest domknięcie i jak/dlaczego miałbyś je zastosować?
Domknięcie (closure) jest kombinacją funkcji i środowiska leksykalnego, w którym zadeklarowano tę funkcję. Słowo "leksykalny" odnosi się do faktu, że zakres leksykalny wykorzystuje lokalizację, w której zmienna jest zadeklarowana w kodzie źródłowym, w celu ustalenia, gdzie ta zmienna jest dostępna. Domknięcia to funkcje, które mają dostęp do zmiennych funkcji zewnętrznej (obejmującej) - łańcuch zasięgu nawet po zwróceniu funkcji zewnętrznej.
Dlaczego miałbyś skorzystać z tego?
- Prywatność danych/emulacja prywatnych metod przy domknięciach. Powszechnie stosowane we wzorcu modułu.
- Partial applications lub currying.
Bibliografia
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures
- https://medium.com/javascript-scene/master-the-javascript-interview-what-is-a-closure-b2f0d2152b36
Czy możesz opisać główną różnicę pomiędzy pętlą .forEach
, a pętlą .map()
i dlaczego wybrałbyś jeden albo drugi?
Aby zrozumieć różnice między nimi, spójrzmy na to, co robi każda funkcja.
forEach
- Iteruje przez elementy w tablicy.
- Wykonuje callback dla każdego elementu.
- Nie zwraca wartości.
const a = [1, 2, 3];
const doubled = a.forEach((num, index) => {
// Do something with num and/or index.
});
// doubled = undefined
map
- Iteruje przez elementy w tablicy.
- "Mapuje" każdy element do nowego elementu, 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łówna różnica między .forEach
i .map()
to to, że .map()
zwraca nową tablicę. Jeśli potrzebujesz wyniku, ale nie chcesz mutować oryginalnej tablicy, .map()
jest jasnym wyborem. Jeśli potrzebujesz po prostu iterować tablicę, forEach
jest dobrym wyborem.
Bibliografia
Jaki jest typowy przypadek użycia funkcji anonimowych?
Można ich użyć w IIFE do enkapsulacji części kodu w zakresie lokalnym, tak aby zmienne zadeklarowane w nim nie przenikały do zakresu globalnego.
(function () {
// Some code here.
})();
Jako callback, które jest używane raz i nie musi być używane nigdzie indziej. Kod będzie wydawał się bardziej samodzielny i czytelny, gdy procedury obsługi zostaną zdefiniowane bezpośrednio w kodzie wywołującym je, zamiast konieczności szukania gdzie indziej w celu znalezienia w ciele funkcji.
setTimeout(function () {
console.log('Hello world!');
}, 1000);
Argumenty do konstrukcji funkcjonalnego programowania lub Lodasha (podobne do callbacków).
const arr = [1, 2, 3];
const double = arr.map(function (el) {
return el * 2;
});
console.log(double); // [2, 4, 6]
Bibliografia
- https://www.quora.com/What-is-a-typical-usecase-for-anonymous-functions
- https://stackoverflow.com/questions/10273185/what-are-the-benefits-to-using-anonymous-functions-instead-of-named-functions-fo
Jak organizujesz swój kod? (wzorzec modułu, klasyczne dziedziczenie?)
W przeszłości używałem Backbone do moich modeli, co zachęca do bardziej otwartego podejścia, tworzenia modeli Backbone i dołączania do nich metod.
Wzorzec modułu jest nadal świetny, ale obecnie używam React/Redux, które wykorzystują jednokierunkowy przepływ danych oparty na architekturze Flux. Reprezentowałbym modele mojej aplikacji przy użyciu prostych obiektów i pisał funkcje czysto użytkowe do manipulowania tymi obiektami. Stan jest manipulowany za pomocą akcji i reduktorów, jak w każdej innej aplikacji Redux.
W miarę możliwości unikam dziedziczenia klasycznego. Kiedy już i jeśli to zrobię, trzymam się tych reguł.
Jaka jest różnica między obiektami hosta a obiektami macierzystymi?
Obiekty macierzyste to obiekty, które są częścią języka JavaScript zdefiniowanego w specyfikacji ECMAScript, takie jak String
, Math
, RegExp
, Object
, Function
, etc.
Obiekty hosta są dostarczane przez środowisko wykonawcze (przeglądarkę lub Node), takie jak window
, XMLHTTPRequest
, etc.
Bibliografia
Różnica pomiędzy: function Person(){}
, var person = Person()
, i var person = new Person()
?
To pytanie jest dość niejasne. Myślę, że jego intencją jest to, że pyta o konstruktory w JavaScript. Z technicznego punktu widzenia, function Person(){}
jest zwykłą deklaracją funkcji. Konwencja polega na wykorzystaniu PascalCase do funkcji, które mają być używane jako konstruktory.
var person = Person()
wywołuje Person
jako funkcję, i nie jako konstruktor. Wywołanie jako takie jest częstym błędem, jeśli funkcja ma być używana jako konstruktor. Zazwyczaj konstruktor niczego nie zwraca, dlatego wywołanie konstruktora jak normalnej funkcji zwróci undefined
, a to zostanie przypisane do zmiennej przeznaczonej jako instancja.
var person = new Person()
tworzy instancję obiektu Person
za pomocą operatora new
, który dziedziczy po Person.prototype
. Alternatywą byłoby użycie Object.create
, tak jak: Object.create(Person.prototype)
.
function Person(name) {
this.name = name;
}
var person = Person('John');
console.log(person); // undefined
console.log(person.name); // Uncaught TypeError: Cannot read property 'name' of undefined
var person = new Person('John');
console.log(person); // Person { name: "John" }
console.log(person.name); // "john"
Bibliografia
Jaka jest różnica pomiędzy .call
i .apply
?
Zarówno .call
, jak i .apply
są używane do wywoływania funkcji, a pierwszy parametr zostanie użyty jako wartość this
w funkcji. Jednak .call
przyjmuje argumenty oddzielone przecinkami jako kolejne argumenty, podczas gdy .apply
przyjmuje tablicę argumentów jako następny argument. Łatwym sposobem na zapamiętanie tego jest C dla call
i oddzielone przecinkami, i A dla apply
oraz tablica argumentów.
function add(a, b) {
return a + b;
}
console.log(add.call(null, 1, 2)); // 3
console.log(add.apply(null, [1, 2])); // 3
Wytłumacz Function.prototype.bind
.
Wzięte słowo w słowo zMDN:
Metoda
bind()
tworzy nową funkcję, która po wywołaniu ma ustawione słowo kluczowethis
na podaną wartość, z podaną sekwencją argumentów poprzedzającą dowolną podaną podczas wywoływania nowej funkcji.
Z mojego doświadczenia wynika, że jest to najbardziej przydatne do bindowania wartości this
w metodach klas, które chcesz przekazać innym funkcjom. Często odbywa się to w komponentach React.
Bibliografia
Kiedy użyłbyś document.write()
?
document.write()
zapisuje ciąg tekstu do strumienia dokumentów otwartego przez document.open()
. Kiedy document.write()
jest wykonywane po załadowaniu strony, wywoła document.open
, który usuwa cały dokument (<head>
i <body>
usunięto!) i zamienia zawartość na podaną wartość parametru. Dlatego jest zwykle uważany za niebezpieczny i podatny na niewłaściwe użycie.
Istnieje kilka odpowiedzi online, które wyjaśniają, że w kodzie analitycznym używany jest document.write()
lub gdy chcesz dołączyć style, które powinny działać tylko wtedy, gdy JavaScript jest włączony. Jest nawet używany w HTML5 boilerplate do równoległego ładowania skryptów i zachowania kolejności wykonywania! Podejrzewam jednak, że przyczyny te mogą być nieaktualne i we współczesnych czasach można je osiągnąć bez użycia document.write()
. Proszę, popraw mnie, jeśli się mylę.
Bibliografia
- https://www.quirksmode.org/blog/archives/2005/06/three_javascrip_1.html
- https://github.com/h5bp/html5-boilerplate/wiki/Script-Loading-Techniques#documentwrite-script-tag
Jaka jest różnica między wykrywaniem funkcji, feature inference i używaniem UA string?
Feature Detection
Wykrywanie funkcji polega na sprawdzeniu, czy przeglądarka obsługuje określony blok kodu, i uruchomieniu innego kodu w zależności od tego, czy to robi (czy nie), tak aby przeglądarka zawsze zapewniała działanie w przypadku awarii/błędów w niektórych przeglądarkach. Na przykład:
if ('geolocation' in navigator) {
// Can use navigator.geolocation
} else {
// Handle lack of feature
}
Modernizr to świetna biblioteka do obsługi feature detection.
Feature Inference
Feature inference sprawdza funkcję podobnie jak wykrywanie funkcji, ale używa innej funkcji, ponieważ zakłada, że ona również będzie istnieć, np .:
if (document.getElementsByTagName) {
element = document.getElementById(id);
}
To nie jest naprawdę zalecane. Wykrywanie funkcji jest bardziej niezawodne.
UA String
Jest to string zgłaszany przez przeglądarkę, który umożliwia elementom protokołu sieciowego identyfikowanie typu aplikacji, systemu operacyjnego, dostawcy oprogramowania lub wersji oprogramowania żądającego agenta użytkownika oprogramowania. Można uzyskać do niego dostęp za pośrednictwem navigator.userAgent
. Jednak string jest trudny do przeanalizowania i może zostać sfałszowany. Na przykład Chrome zgłasza zarówno Chrome, jak i Safari. Aby wykryć Safari, musisz sprawdzić string Safari i nieobecność Chrome string. Unikaj tej metody.
Bibliografia
- https://developer.mozilla.org/en-US/docs/Learn/Tools_and_testing/Cross_browser_testing/Feature_detection
- https://stackoverflow.com/questions/20104930/whats-the-difference-between-feature-detection-feature-inference-and-using-th
- https://developer.mozilla.org/en-US/docs/Web/HTTP/Browser_detection_using_the_user_agent
Wyjaśnij Ajax tak szczegółowo, jak to możliwe.
Ajax (asynchronous JavaScript and XML - asynchroniczny JavaScript i XML) to zestaw technik tworzenia stron internetowych wykorzystujących wiele technologii sieciowych po stronie klienta do tworzenia asynchronicznych aplikacji internetowych. Dzięki Ajax aplikacje internetowe mogą wysyłać dane i pobierać je z serwera asynchronicznie (w tle) bez ingerencji w wyświetlanie i zachowanie istniejącej strony. Oddzielając warstwę wymiany danych od warstwy prezentacji, Ajax pozwala stronom internetowym, a poprzez rozszerzenia aplikacji internetowych, dynamicznie zmieniać treść bez konieczności ponownego ładowania całej strony. W praktyce nowoczesne implementacje często zastępują użycie JSON zamiast XML, ze względu na zalety natywnej obsługi JSON w JavaScript.
API XMLHttpRequest
jest często używany do komunikacji asynchronicznej lub w dzisiejszych czasach, fetch()
API.
Bibliografia
Jakie są zalety i wady korzystania z Ajax?
Zalety
- Lepsza interaktywność. Nowa zawartość z serwera może być zmieniana dynamicznie bez potrzeby przeładowywania całej strony.
- Zmniejsza liczbę połączeń z serwerem, ponieważ skrypty i arkusze stylów muszą być wymagane tylko raz.
- Stan można utrzymać na stronie. Zmienne JavaScript i stan DOM zostaną zachowane, ponieważ główna strona kontenera nie została ponownie załadowana.
- Zasadniczo większość zalet SPA.
Wady
- Dynamiczne strony internetowe są trudniejsze do dodania do zakładek.
- Nie działa, jeśli JavaScript jest wyłączony w przeglądarce.
- Niektóre przeglądarki internetowe nie wykonują JavaScript i nie widzą treści załadowanych przez JavaScript.
- Strony internetowe wykorzystujące Ajax do pobierania danych prawdopodobnie będą musiały połączyć pobrane dane zdalne z szablonami po stronie klienta, aby zaktualizować DOM. Aby tak się stało, JavaScript musi zostać przeanalizowany i wykonany w przeglądarce, a urządzenia mobilne z niższej półki mogą mieć z tym problem.
- Zasadniczo większość wad SPA.
Wyjaśnij, jak działa JSONP (i jak to naprawdę nie jest Ajax).
JSONP (JSON with Padding) jest metodą powszechnie używaną do omijania zasad międzydomenowych w przeglądarkach internetowych, ponieważ żądania Ajax z bieżącej strony do domeny międzydomenowej są niedozwolone.
JSONP działa poprzez wysłanie żądania do domeny cross-origin za pomocą znacznika <script>
i zwykle za pomocą parametru zapytania callback
, na przykład: https://example.com?callback=printData
. Serwer następnie opakowuje dane w funkcję o nazwie printData
i zwraca je klientowi.
<!-- https://mydomain.com -->
<script>
function printData(data) {
console.log(`My name is ${data.name}!`);
}
</script>
<script src="https://example.com?callback=printData"></script>
// File loaded from https://example.com?callback=printData
printData({ name: 'Yang Shun' });
Klient musi mieć funkcję printData
w swoim globalnym zasięgu, a funkcja zostanie wykonana przez klienta po otrzymaniu odpowiedzi z domeny cross-origin.
JSONP może być niebezpieczny i ma pewne implikacje dla bezpieczeństwa. Ponieważ JSONP to tak naprawdę JavaScript, może robić wszystko, co potrafi JavaScript, więc musisz zaufać dostawcy danych JSONP.
Obecnie, CORS jest zalecanym podejściem, a JSONP jest postrzegany jako hack.
Bibliografia
Czy kiedykolwiek używałeś szablonów JavaScript? Jeśli tak, z jakich bibliotek korzystałeś?
Tak. Handlebars, Underscore, Lodash, AngularJS, i JSX. Nie podobało mi się tworzenie szablonów w AngularJS, ponieważ często używało łańcuchów w dyrektywach, a literówki nie zostały złapane. JSX jest moim nowym ulubionym, ponieważ jest bliżej JavaScript i nie ma prawie żadnej składni do nauki. W dzisiejszych czasach możesz nawet używać literałów ciągów szablonów ES2015 jako szybkiego sposobu tworzenia szablonów bez konieczności korzystania z kodu innej firmy.
const template = `<div>My name is: ${name}</div>`;
Należy jednak pamiętać o potencjalnym XSS w powyższym podejściu, ponieważ zawartość nie jest dla ciebie umykająca, w przeciwieństwie do bibliotek szablonów.
Wytłumacz "hoisting".
Hoisting to termin używany do wyjaśnienia zachowania deklaracji zmiennych w kodzie. Zmienne zadeklarowane lub zainicjowane słowem kluczowym var
będą miały swoją deklarację "przeniesioną" na górę zakresu na poziomie modułu/funkcji, który nazywamy windowaniem. Jednak tylko deklaracja jest windowana, przydział (jeśli taki istnieje) pozostanie tam, gdzie jest.
Zauważ, że deklaracja nie została faktycznie przeniesiona - silnik JavaScript analizuje deklaracje podczas kompilacji i dowiaduje się o deklaracjach i ich zakresach. Po prostu łatwiej jest zrozumieć to zachowanie, wizualizując deklaracje jako podnoszone na szczyt ich zakresu. Wyjaśnijmy kilka przykładów.
console.log(foo); // undefined
var foo = 1;
console.log(foo); // 1
W deklaracjach funkcji podnoszone jest ciało, podczas gdy w wyrażeniach funkcji (zapisanych w formie deklaracji zmiennych) windowanana jest tylko deklaracja zmiennej.
// Function Declaration
console.log(foo); // [Function: foo]
foo(); // 'FOOOOO'
function foo() {
console.log('FOOOOO');
}
console.log(foo); // [Function: foo]
// Function Expression
console.log(bar); // undefined
bar(); // Uncaught TypeError: bar is not a function
var bar = function () {
console.log('BARRRR');
};
console.log(bar); // [Function: bar]
Windowane są również zmienne zadeklarowane za pomocą let
i const
. Jednak w przeciwieństwie do var
i function
, nie są one inicjowane i dostęp do nich przed deklaracją spowoduje wyjątek ReferenceError
. Zmienna znajduje się w "czasowej martwej strefie" od początku bloku do momentu przetworzenia deklaracji.
x; // undefined
y; // Reference error: y is not defined
var x = 'local';
let y = 'local';
Bibliografia
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Grammar_and_Types#Variable_hoisting
- https://stackoverflow.com/questions/31219420/are-variables-declared-with-let-or-const-not-hoisted-in-es6/31222689#31222689
Opisz event bubbling.
Kiedy zdarzenie zostanie wyzwolone na elemencie DOM, spróbuje obsłużyć to zdarzenie, jeśli dołączony jest detektor, a następnie zdarzenie zostanie przekazane do jego obiektu nadrzędnego i nastąpi to samo. To 'bubbling' występuje u przodków elementu aż do document
. Event bubbling jest mechanizmem delegowania zdarzeń.
Jaka jest różnica pomiędzy "attribute", a "property"?
Atrybuty są zdefiniowane w znacznikach HTML, ale właściwości są zdefiniowane w DOM. Aby zilustrować różnicę, wyobraź sobie, że mamy to pole tekstowe w naszym HTML: <input type="text" value="Hello">
.
const input = document.querySelector('input');
console.log(input.getAttribute('value')); // Hello
console.log(input.value); // Hello
Ale po zmianie wartości pola tekstowego przez dodanie "World!" staje się to:
console.log(input.getAttribute('value')); // Hello
console.log(input.value); // Hello World!
Bibliografia
Dlaczego rozszerzenie wbudowanych obiektów JavaScript nie jest dobrym pomysłem?
Rozszerzenie wbudowanego/natywnego obiektu JavaScript oznacza dodanie właściwości/funkcji do jego prototype
. Choć na początku może się to wydawać dobrym pomysłem, w praktyce jest niebezpieczne. Wyobraź sobie, że twój kod używa kilku bibliotek, które rozszerzają Array.prototype
poprzez dodanie tej samej metody contains
, implementacje się nadpisują, a twój kod się zepsuje, jeśli zachowanie tych dwóch metod nie będzie takie samo.
Jedynym momentem, w którym możesz chcieć rozszerzyć obiekt macierzysty, jest utworzenie polyfill, zasadniczo zapewniające własną implementację metody, która jest częścią specyfikacji JavaScript, ale może nie istnieć w przeglądarce użytkownika, jeśli jest to starsza przeglądarka.