Live Coding

Controleren of een object leeg is

Hoe controleer ik of een JavaScript-object leeg is?

Uitleg: Een object is leeg als het geen eigen opsombare eigenschappen heeft. We kunnen Object.keys(obj) gebruiken, wat een array retourneert van de eigen opsombare eigenschapsnamen van een opgegeven object. Als de lengte van deze array 0 is, is het object leeg.

function isEmpty(obj) { return Object.keys(obj).length === 0; } console.log(isEmpty({})); // true console.log(isEmpty({ a: 1 })); // false

Een string omkeren

Schrijf een functie om een gegeven string om te keren.

Uitleg: De eenvoudigste manier is om de string om te zetten in een array van karakters, de ingebouwde reverse()-methode voor arrays te gebruiken en vervolgens de karakters weer samen te voegen tot een string.

function reverseString(str) { return str.split('').reverse().join(''); } console.log(reverseString('hello')); // 'olleh'

Palindroomcontrole

Schrijf een functie die controleert of een gegeven string een palindroom is.

Uitleg: Een palindroom is een woord of zin die zowel vooruit als achteruit hetzelfde leest. We kunnen dit controleren door de string om te keren (gevoeligheid voor hoofdletters en niet-alfanumerieke karakters negeren voor een robuustere controle, maar hier doen we een eenvoudige controle) en deze te vergelijken met het origineel.

function isPalindrome(str) { const reversed = str.split('').reverse().join(''); return str === reversed; } console.log(isPalindrome('racecar')); // true console.log(isPalindrome('hello')); // false

Het maximumgetal in een array vinden

Schrijf een functie om het grootste getal in een array van getallen te vinden.

Uitleg: Je kunt door de array itereren en het tot nu toe gevonden grootste getal bijhouden. Als alternatief kun je de Math.max()-functie gebruiken samen met de spread-syntax (...) om array-elementen als argumenten door te geven.

function findMaxNumber(arr) { if (arr.length === 0) return undefined; // Of gooi een fout return Math.max(...arr); } console.log(findMaxNumber([1, 5, 2, 9, 3])); // 9

FizzBuzz

Schrijf een programma dat getallen van 1 tot n afdrukt. Maar voor veelvouden van drie, print 'Fizz' in plaats van het getal en voor veelvouden van vijf, print 'Buzz'. Voor getallen die veelvouden zijn van zowel drie als vijf, print 'FizzBuzz'.

Uitleg: Dit klassieke probleem test basislus- en conditionele logica. Je moet van 1 tot n itereren en de modulo-operator (%) gebruiken om te controleren op deelbaarheid door 3 en 5.

function fizzBuzz(n) { for (let i = 1; i <= n; i++) { if (i % 3 === 0 && i % 5 === 0) { console.log('FizzBuzz'); } else if (i % 3 === 0) { console.log('Fizz'); } else if (i % 5 === 0) { console.log('Buzz'); } else { console.log(i); } } } fizzBuzz(15);

Duplicaten uit een array verwijderen

Schrijf een functie die dubbele elementen uit een array verwijdert.

Uitleg: Een moderne en beknopte manier om dit te bereiken is door een Set te gebruiken. Sets slaan alleen unieke waarden op. Je kunt de array converteren naar een Set en vervolgens weer terug naar een array.

function removeDuplicates(arr) { return [...new Set(arr)]; } console.log(removeDuplicates([1, 2, 2, 3, 4, 4, 5])); // [1, 2, 3, 4, 5]

Anagramcontrole

Schrijf een functie om te controleren of twee strings anagrammen van elkaar zijn.

Uitleg: Anagrammen zijn woorden die zijn gevormd door de letters van een ander woord te herschikken. Om te controleren, kunnen we de strings opschonen, sorteren en vergelijken. Opschonen omvat het verwijderen van niet-alfanumerieke karakters en het converteren naar een consistente hoofdletter.

function isAnagram(str1, str2) { const clean = (str) => str.replace(/[^a-z0-9]/gi, '').toLowerCase(); const sorted = (str) => clean(str).split('').sort().join(''); return sorted(str1) === sorted(str2); } console.log(isAnagram('listen', 'silent')); // true console.log(isAnagram('hello', 'world')); // false

Faculteit berekenen

Schrijf een functie om de faculteit van een niet-negatief geheel getal te berekenen.

Uitleg: De faculteit (n!) van een getal is het product van alle positieve gehele getallen kleiner dan of gelijk aan n. We kunnen dit iteratief berekenen door getallen van 1 tot n te vermenigvuldigen.

function factorial(n) { if (n < 0) return undefined; // Faculteit is niet gedefinieerd voor negatieve getallen if (n === 0) return 1; let result = 1; for (let i = 1; i <= n; i++) { result *= i; } return result; } console.log(factorial(5)); // 120

Alle getallen in een array optellen

Schrijf een functie die de som van alle getallen in een array retourneert.

Uitleg: Je kunt de reduce-methode gebruiken, die ideaal is voor het accumuleren van een enkele waarde uit een array. Het neemt een callback-functie en een initiële waarde (0 voor optelling).

function sumArray(arr) { return arr.reduce((accumulator, currentValue) => accumulator + currentValue, 0); } console.log(sumArray([1, 2, 3, 4])); // 10

Een geneste array platmaken

Schrijf een functie om een geneste array plat te maken. Ga voor de eenvoud uit van slechts één niveau van nesting.

Uitleg: Voor één niveau van nesting kun je Array.prototype.concat() gebruiken met de spread-operator of de flat()-methode (ES2019).

function flattenArray(arr) { // flat() gebruiken (eenvoudiger als ES2019+ oké is) // return arr.flat(); // reduce en concat gebruiken return arr.reduce((acc, val) => acc.concat(val), []); } console.log(flattenArray([1, [2, 3], 4, [5]])); // [1, 2, 3, 4, 5]

Klinkers tellen in een string

Schrijf een functie die het aantal klinkers (a, e, i, o, u) in een gegeven string telt.

Uitleg: Itereren door de string (ongevoelig voor hoofdletters) en controleren of elk karakter een klinker is. Houd een teller bij.

function countVowels(str) { const vowels = 'aeiou'; let count = 0; for (let char of str.toLowerCase()) { if (vowels.includes(char)) { count++; } } return count; } console.log(countVowels('Hello World')); // 3

Een zin in titelletters plaatsen

Schrijf een functie die een zin omzet in titelletters (de eerste letter van elk woord met een hoofdletter).

Uitleg: Splits de zin in woorden, itereer vervolgens door elk woord, waarbij de eerste letter een hoofdletter krijgt en de rest kleine letters. Voeg tot slot de woorden weer samen.

function titleCase(str) { return str.toLowerCase().split(' ').map(word => { return word.charAt(0).toUpperCase() + word.slice(1); }).join(' '); } console.log(titleCase('i am a little tea pot')); // 'I Am A Little Tea Pot'

Two Sum Probleem

Gegeven een array van gehele getallen nums en een geheel getal target, retourneer de indices van de twee getallen zodat ze samen target zijn.

Uitleg: Een veelgebruikte aanpak is het gebruik van een hash map (of een JavaScript-object) om getallen en hun indices op te slaan terwijl je itereert. Voor elk getal controleer je of target - current_number bestaat in de map.

function twoSum(nums, target) { const map = {}; for (let i = 0; i < nums.length; i++) { const complement = target - nums[i]; if (map[complement] !== undefined) { return [map[complement], i]; } map[nums[i]] = i; } return []; // Of null, of gooi een fout als er geen oplossing is } console.log(twoSum([2, 7, 11, 15], 9)); // [0, 1]

Een teller implementeren met closures

Maak een functie die een tellingsfunctie retourneert. Elke keer dat de geretourneerde functie wordt aangeroepen, moet deze een telling verhogen en retourneren.

Uitleg: Dit demonstreert closures. De innerlijke functie heeft toegang tot de count-variabele van het bereik van de buitenste functie, zelfs nadat de buitenste functie is voltooid.

function createCounter() { let count = 0; return function() { count++; return count; }; } const counter1 = createCounter(); console.log(counter1()); // 1 console.log(counter1()); // 2 const counter2 = createCounter(); console.log(counter2()); // 1

Het eerste niet-herhalende karakter vinden

Schrijf een functie die het eerste niet-herhalende karakter in een string vindt.

Uitleg: Je kunt een hash map gebruiken om karakterfrequenties te tellen. Itereer eerst door de string om de frequentiemap op te bouwen. Itereer vervolgens opnieuw door de string en retourneer het eerste karakter met een telling van 1.

function firstNonRepeatingChar(str) { const charCount = {}; for (const char of str) { charCount[char] = (charCount[char] || 0) + 1; } for (const char of str) { if (charCount[char] === 1) { return char; } } return null; // Of een indicator als alles herhaalt } console.log(firstNonRepeatingChar('aabbcdeeff')); // 'c' console.log(firstNonRepeatingChar('swiss')); // 'w'

Array-chunks maken

Schrijf een functie die een array in groepen van een opgegeven grootte splitst.

Uitleg: Itereren door de array, slices van de opgegeven grootte nemen en deze in een nieuwe array pushen. Behandel het geval waarin het laatste chunk kleiner kan zijn.

function chunkArray(arr, size) { const chunked = []; let index = 0; while (index < arr.length) { chunked.push(arr.slice(index, index + size)); index += size; } return chunked; } console.log(chunkArray([1, 2, 3, 4, 5, 6, 7], 3)); // [[1, 2, 3], [4, 5, 6], [7]]

Controleren of een getal priem is

Schrijf een functie om te bepalen of een gegeven getal een priemgetal is.

Uitleg: Een priemgetal is een natuurlijk getal groter dan 1 dat geen andere positieve delers heeft dan 1 en zichzelf. Itereren van 2 tot de vierkantswortel van het getal, controleren op deelbaarheid. Behandel randgevallen zoals 1 en 2.

function isPrime(num) { if (num <= 1) return false; if (num <= 3) return true; if (num % 2 === 0 || num % 3 === 0) return false; for (let i = 5; i * i <= num; i = i + 6) { if (num % i === 0 || num % (i + 2) === 0) return false; } return true; } console.log(isPrime(7)); // true console.log(isPrime(10)); // false

Het langste woord in een string vinden

Schrijf een functie die het langste woord in een zin vindt.

Uitleg: Splits de string in een array van woorden. Itereren vervolgens door de array en houd het langste woord bij dat tot nu toe is gevonden (of de lengte ervan).

function findLongestWord(str) { const words = str.split(' '); let longestWord = ''; for (const word of words) { // Woorden opschonen indien nodig (bijv. leestekens verwijderen) if (word.length > longestWord.length) { longestWord = word; } } return longestWord; } console.log(findLongestWord('The quick brown fox jumped over the lazy dog')); // 'jumped'

Implementeer `Array.prototype.map`

Implementeer je eigen versie van de Array.prototype.map-functie.

Uitleg: De map-functie neemt een callback en creëert een nieuwe array door de callback toe te passen op elk element van de originele array. Je functie moet door de array itereren en de nieuwe array opbouwen.

function myMap(arr, callback) { const mappedArray = []; for (let i = 0; i < arr.length; i++) { mappedArray.push(callback(arr[i], i, arr)); } return mappedArray; } const numbers = [1, 4, 9]; const roots = myMap(numbers, Math.sqrt); console.log(roots); // [1, 2, 3]

Implementeer `Array.prototype.filter`

Implementeer je eigen versie van de Array.prototype.filter-functie.

Uitleg: De filter-functie neemt een callback en creëert een nieuwe array met alle elementen die de test doorstaan die is geïmplementeerd door de meegeleverde callback-functie.

function myFilter(arr, callback) { const filteredArray = []; for (let i = 0; i < arr.length; i++) { if (callback(arr[i], i, arr)) { filteredArray.push(arr[i]); } } return filteredArray; } const words = ['spray', 'limit', 'elite', 'exuberant', 'destruction', 'present']; const result = myFilter(words, word => word.length > 6); console.log(result); // ['exuberant', 'destruction', 'present']

Implementeer `Array.prototype.reduce`

Implementeer je eigen versie van de Array.prototype.reduce-functie.

Uitleg: De reduce-functie voert een door de gebruiker geleverde 'reducer'-callback-functie uit op elk element van de array, waarbij de retourwaarde van de berekening op het voorafgaande element wordt doorgegeven. Het eindresultaat van het uitvoeren van de reducer over alle elementen van de array is een enkele waarde. Behandel het initiële waarde-argument.

function myReduce(arr, callback, initialValue) { let accumulator = initialValue; let startIndex = 0; if (initialValue === undefined) { if (arr.length === 0) throw new TypeError('Reduce of empty array with no initial value'); accumulator = arr[0]; startIndex = 1; } for (let i = startIndex; i < arr.length; i++) { accumulator = callback(accumulator, arr[i], i, arr); } return accumulator; } const array1 = [1, 2, 3, 4]; const sum = myReduce(array1, (acc, curr) => acc + curr, 0); console.log(sum); // 10

Memoization - Fibonacci-reeks

Implementeer een Fibonacci-functie met behulp van memoization om de prestaties te optimaliseren.

Uitleg: De Fibonacci-reeks omvat herhaalde berekeningen. Memoization slaat de resultaten van dure functieaanroepen op en retourneert het gecachete resultaat wanneer dezelfde invoer opnieuw optreedt.

function memoizedFib() { const cache = {}; function fib(n) { if (n in cache) { return cache[n]; } if (n <= 1) { return n; } const result = fib(n - 1) + fib(n - 2); cache[n] = result; return result; } return fib; } const fibonacci = memoizedFib(); console.log(fibonacci(10)); // 55 console.log(fibonacci(40)); // 102334155 (veel sneller dan niet-gemoizeerde)

Controleren op gebalanceerde haken

Schrijf een functie om te controleren of een string die haken {}[]() bevat gebalanceerd is.

Uitleg: Gebruik een stapel. Wanneer je een openingshaak tegenkomt, push je deze op de stapel. Wanneer je een sluitingshaak tegenkomt, controleer je of de stapel leeg is of dat het bovenste element de overeenkomende openingshaak is. Als het overeenkomt, pop je de stapel. Zo niet, of als de stapel leeg is, is het ongebalanceerd. Aan het einde betekent een lege stapel gebalanceerd.

function isBalanced(str) { const stack = []; const map = { '(': ')', '{': '}', '[': ']' }; for (let char of str) { if (map[char]) { stack.push(char); } else if (Object.values(map).includes(char)) { if (stack.length === 0) return false; const lastOpen = stack.pop(); if (map[lastOpen] !== char) return false; } } return stack.length === 0; } console.log(isBalanced('({[]})')); // true console.log(isBalanced('([)]')); // false

Een eenvoudige wachtrij implementeren

Implementeer een wachtrijgegevensstructuur met enqueue- (toevoegen aan achterkant) en dequeue- (verwijderen van voorkant) methoden.

Uitleg: Een wachtrij volgt het First-In, First-Out (FIFO) principe. Een array kan worden gebruikt, met push voor enqueue en shift voor dequeue.

class Queue { constructor() { this.items = []; } enqueue(element) { this.items.push(element); } dequeue() { if (this.isEmpty()) return 'Underflow'; return this.items.shift(); } front() { if (this.isEmpty()) return 'No elements in Queue'; return this.items[0]; } isEmpty() { return this.items.length === 0; } } const q = new Queue(); q.enqueue(10); q.enqueue(20); console.log(q.dequeue()); // 10 console.log(q.front()); // 20

Een eenvoudige stapel implementeren

Implementeer een stapelgegevensstructuur met push- (toevoegen aan bovenkant) en pop- (verwijderen van bovenkant) methoden.

Uitleg: Een stapel volgt het Last-In, First-Out (LIFO) principe. Een array kan worden gebruikt, met push en pop methoden.

class Stack { constructor() { this.items = []; } push(element) { this.items.push(element); } pop() { if (this.isEmpty()) return 'Underflow'; return this.items.pop(); } peek() { return this.items[this.items.length - 1]; } isEmpty() { return this.items.length === 0; } } const s = new Stack(); s.push(10); s.push(20); console.log(s.pop()); // 20 console.log(s.peek()); // 10

Het ontbrekende getal in een reeks vinden

Gegeven een array met n verschillende getallen genomen uit 0, 1, 2, ..., n, vind het getal dat ontbreekt in de array.

Uitleg: De som van getallen van 0 tot n kan worden berekend met behulp van de formule n*(n+1)/2. De werkelijke som van de array-elementen kan worden berekend. Het verschil tussen deze twee sommen is het ontbrekende getal.

function findMissingNumber(nums) { const n = nums.length; const expectedSum = n * (n + 1) / 2; const actualSum = nums.reduce((sum, num) => sum + num, 0); return expectedSum - actualSum; } console.log(findMissingNumber([3, 0, 1])); // 2 console.log(findMissingNumber([9, 6, 4, 2, 3, 5, 7, 0, 1])); // 8

Debounce-functie

Implementeer een debounce-functie. Debouncing zorgt ervoor dat een functie pas opnieuw wordt aangeroepen nadat een bepaalde hoeveelheid tijd is verstreken zonder dat deze is aangeroepen.

Uitleg: Gebruik setTimeout en clearTimeout. Elke keer dat de gedebounceerde functie wordt aangeroepen, wis je de vorige timeout en stel je een nieuwe in. De feitelijke functieaanroep vindt alleen plaats wanneer de timeout is voltooid.

function debounce(func, delay) { let timeoutId; return function(...args) { clearTimeout(timeoutId); timeoutId = setTimeout(() => { func.apply(this, args); }, delay); }; } // Voorbeeldgebruik: const sayHello = () => console.log('Hello!'); const debouncedHello = debounce(sayHello, 1000); debouncedHello(); // Aangeroepen na 1 sec (als niet opnieuw aangeroepen) debouncedHello(); // Reset timer debouncedHello(); // Reset timer, zal daadwerkelijk 1 sec na deze oproep worden uitgevoerd.

Throttle-functie

Implementeer een throttle-functie. Throttling zorgt ervoor dat een functie maximaal één keer binnen een opgegeven tijdsinterval wordt aangeroepen.

Uitleg: Gebruik een vlag om aan te geven of een oproep is toegestaan. Indien toegestaan, voer de functie uit, zet de vlag op false en gebruik setTimeout om de vlag na het interval te resetten.

function throttle(func, limit) { let inThrottle = false; return function(...args) { if (!inThrottle) { func.apply(this, args); inThrottle = true; setTimeout(() => inThrottle = false, limit); } }; } // Voorbeeldgebruik: const logScroll = () => console.log('Scrolling...'); const throttledScroll = throttle(logScroll, 1000); // window.addEventListener('scroll', throttledScroll); // Zal maximaal één keer per seconde loggen.

Currying-functie

Schrijf een functie die een functie met twee argumenten neemt en een gecurriede versie ervan retourneert.

Uitleg: Currying transformeert een functie met meerdere argumenten in een reeks functies, elk met een enkel argument. f(a, b) wordt f(a)(b).

function curry(fn) { return function(a) { return function(b) { return fn(a, b); }; }; } function add(a, b) { return a + b; } const curriedAdd = curry(add); const add5 = curriedAdd(5); console.log(add5(3)); // 8 console.log(curriedAdd(10)(20)); // 30

Een object diep klonen

Schrijf een functie om een diepe kloon van een JavaScript-object uit te voeren.

Uitleg: Een ondiepe kloon kopieert alleen de eigenschappen op het hoogste niveau. Een diepe kloon kopieert recursief alle eigenschappen, inclusief geneste objecten en arrays. Een eenvoudige manier (met beperkingen, bijv. verwerkt functies, datums, regex niet goed) is het gebruik van JSON.parse(JSON.stringify(obj)). Een recursieve benadering is robuuster.

function deepClone(obj) { // Eenvoudige versie (met beperkingen) try { return JSON.parse(JSON.stringify(obj)); } catch (e) { console.error("Cloning failed:", e); return null; } // Robustere recursieve (basisvoorbeeld): /* if (obj === null || typeof obj !== 'object') { return obj; } let clone = Array.isArray(obj) ? [] : {}; for (let key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { clone[key] = deepClone(obj[key]); } } return clone; */ } const original = { a: 1, b: { c: 2 } }; const cloned = deepClone(original); cloned.b.c = 3; console.log(original.b.c); // 2 (bewijst dat het een diepe kloon is) console.log(cloned.b.c); // 3

Object-sleutels ophalen

Hoe krijg ik een array van sleutels uit een object?

Uitleg: Gebruik Object.keys(obj).

function getKeys(obj) { return Object.keys(obj); } console.log(getKeys({a: 1, b: 2})); // ['a', 'b']

Object-waarden ophalen

Hoe krijg ik een array van waarden uit een object?

Uitleg: Gebruik Object.values(obj).

function getValues(obj) { return Object.values(obj); } console.log(getValues({a: 1, b: 2})); // [1, 2]

Controleren of array een waarde bevat

Hoe controleer ik of een array een specifieke waarde bevat?

Uitleg: Gebruik Array.prototype.includes(value).

function checkIncludes(arr, val) { return arr.includes(val); } console.log(checkIncludes([1, 2, 3], 2)); // true

Twee arrays samenvoegen

Hoe voeg ik twee arrays samen tot één?

Uitleg: Gebruik de spread-syntax (...) of Array.prototype.concat().

function mergeArrays(arr1, arr2) { return [...arr1, ...arr2]; } console.log(mergeArrays([1, 2], [3, 4])); // [1, 2, 3, 4]

Uitleg 'this' in globale scope

Waar verwijst this naar in de globale scope in een browser?

Uitleg: In de globale uitvoeringscontext (buiten elke functie) verwijst this naar het globale object, dat window is in webbrowsers.

console.log(this === window); // true (in een browseromgeving)

Uitleg 'this' in een objectmethode

Waar verwijst this naar wanneer het wordt gebruikt in een objectmethode?

Uitleg: Wanneer een functie wordt aangeroepen als een methode van een object, verwijst this naar het object waarop de methode wordt aangeroepen.

const myObject = { prop: 'Hello', greet() { return this.prop; } }; console.log(myObject.greet()); // 'Hello'

Uitleg 'this' met pijpfuncties (arrow functions)

Hoe gaan pijpfuncties (arrow functions) om met this?

Uitleg: Pijpfuncties hebben geen eigen this-context. In plaats daarvan erven ze this van de omringende (lexicale) scope waarin ze zijn gedefinieerd.

function MyClass() { this.value = 42; setTimeout(() => { console.log(this.value); // Logt 42 omdat 'this' is geërfd }, 100); } new MyClass();

Verschil tussen `let`, `const` en `var`

Wat zijn de belangrijkste verschillen tussen let, const en var?

Uitleg: var is functie-scoped en gehoisted. let en const zijn blok-scoped en gehoisted, maar bevinden zich in een 'temporele dode zone' tot de declaratie. const moet worden geïnitialiseerd en kan niet opnieuw worden toegewezen.

function scopeTest() { var a = 1; let b = 2; const c = 3; if (true) { var a = 10; // Herdeclareert en beïnvloedt buitenste 'a' let b = 20; // Nieuwe 'b' binnen blok // const c = 30; // Zou een nieuwe 'c' zijn console.log(a, b, c); // 10, 20, 3 } console.log(a, b, c); // 10, 2, 3 } scopeTest();

Wat is een Promise?

Leg uit wat een Promise is in JavaScript.

Uitleg: Een Promise is een object dat de uiteindelijke voltooiing (of mislukking) van een asynchrone bewerking en de resulterende waarde vertegenwoordigt. Het kan zich in een van de drie staten bevinden: pending, fulfilled of rejected.

const myPromise = new Promise((resolve, reject) => { setTimeout(() => { resolve('Success!'); // reject('Error!'); }, 1000); }); myPromise .then(result => console.log(result)) .catch(error => console.error(error));

Gebruik van `async/await`

Hoe vereenvoudigen async en await het werken met Promises?

Uitleg: async/await biedt syntactische suiker over Promises, waardoor asynchrone code er meer synchroon uitziet en zich gedraagt. Een async-functie retourneert altijd een Promise, en await pauzeert de uitvoering totdat een Promise is afgerond.

function delay(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } async function run() { console.log('Starting...'); await delay(1000); console.log('Waited 1 second.'); await delay(500); console.log('Waited another 0.5 seconds.'); return 'Finished!'; } run().then(console.log);

String naar getal converteren

Hoe converteer ik een string naar een getal?

Uitleg: Gebruik parseInt(), parseFloat() of de unaire plus-operator (+).

const str = '123.45'; console.log(parseInt(str)); // 123 console.log(parseFloat(str)); // 123.45 console.log(+str); // 123.45

Getal naar string converteren

Hoe converteer ik een getal naar een string?

Uitleg: Gebruik String(), number.toString() of string-concatenatie.

const num = 123; console.log(String(num)); // '123' console.log(num.toString()); // '123' console.log('' + num); // '123'

Wat is `JSON.stringify`?

Wat doet JSON.stringify?

Uitleg: Het converteert een JavaScript-object of -waarde naar een JSON-string.

const obj = { name: 'Alice', age: 30 }; const jsonString = JSON.stringify(obj); console.log(jsonString); // '{"name":"Alice","age":30}'

Wat is `JSON.parse`?

Wat doet JSON.parse?

Uitleg: Het parseert een JSON-string en construeert de JavaScript-waarde of het object dat door de string wordt beschreven.

const jsonString = '{"name":"Alice","age":30}'; const obj = JSON.parse(jsonString); console.log(obj.name); // 'Alice'

Spread-operator in arrays

Hoe wordt de spread-operator gebruikt met arrays?

Uitleg: Het stelt een iterable (zoals een array) in staat om te worden uitgebreid op plaatsen waar nul of meer argumenten of elementen worden verwacht. Handig voor kopiëren, samenvoegen en toevoegen van elementen.

const arr1 = [1, 2]; const arr2 = [3, 4]; const combined = [...arr1, ...arr2]; // [1, 2, 3, 4] const copy = [...arr1]; // [1, 2]

Spread-operator in objecten

Hoe wordt de spread-operator gebruikt met objecten?

Uitleg: Het maakt het kopiëren en samenvoegen van objecteigenschappen mogelijk.

const obj1 = { a: 1, b: 2 }; const obj2 = { b: 3, c: 4 }; const merged = { ...obj1, ...obj2 }; // { a: 1, b: 3, c: 4 } const copy = { ...obj1 }; // { a: 1, b: 2 }

Arrays ontleden (Destructuring Arrays)

Leg array-ontleding uit met een voorbeeld.

Uitleg: Het is een syntax die het mogelijk maakt om waarden uit arrays uit te pakken in afzonderlijke variabelen.

const [a, b] = [10, 20]; console.log(a); // 10 console.log(b); // 20 const [x, , z] = [1, 2, 3]; console.log(z); // 3

Objecten ontleden (Destructuring Objects)

Leg object-ontleding uit met een voorbeeld.

Uitleg: Het maakt het mogelijk om eigenschappen uit objecten uit te pakken in afzonderlijke variabelen.

const person = { name: 'Bob', age: 25 }; const { name, age } = person; console.log(name); // 'Bob' console.log(age); // 25 const { name: personName } = person; console.log(personName); // 'Bob'

Implementeer `call`

Hoe zou je een basisversie van Function.prototype.call kunnen implementeren?

Uitleg: call roept een functie aan met een opgegeven this-waarde en argumenten die afzonderlijk zijn opgegeven. Je kunt de functie aan de this-context koppelen, deze aanroepen en vervolgens verwijderen.

Function.prototype.myCall = function(context, ...args) { context = context || window; const uniqueId = Symbol(); // Gebruik een unieke sleutel context[uniqueId] = this; const result = context[uniqueId](...args); delete context[uniqueId]; return result; } function greet(greeting, punctuation) { console.log(`${greeting}, ${this.name}${punctuation}`); } greet.myCall({ name: 'Charlie' }, 'Hi', '!'); // Hi, Charlie!

Implementeer `apply`

Hoe zou je een basisversie van Function.prototype.apply kunnen implementeren?

Uitleg: apply is vergelijkbaar met call, maar het accepteert argumenten als een array.

Function.prototype.myApply = function(context, argsArray) { context = context || window; const uniqueId = Symbol(); context[uniqueId] = this; const result = context[uniqueId](...(argsArray || [])); delete context[uniqueId]; return result; } function greet(greeting, punctuation) { console.log(`${greeting}, ${this.name}${punctuation}`); } greet.myApply({ name: 'David' }, ['Hello', '.']); // Hello, David.

Leg uit Event Loop

Leg in het kort de JavaScript Event Loop uit.

Uitleg: JavaScript is single-threaded, maar bereikt gelijktijdigheid met behulp van een event loop. De call stack verwerkt synchrone code. Web-API's verwerken asynchrone bewerkingen (zoals setTimeout, fetch). Wanneer een asynchrone bewerking is voltooid, gaat de callback naar de callback queue (of microtask queue voor Promises). De event loop controleert constant of de call stack leeg is; als dat zo is, verplaatst het de volgende callback van de queue naar de stack voor uitvoering.

console.log('Start'); setTimeout(() => { console.log('Timeout Callback'); // Gaat naar Callback Queue }, 0); Promise.resolve().then(() => { console.log('Promise Resolved'); // Gaat naar Microtask Queue }); console.log('End'); // Uitvoer Volgorde: Start, End, Promise Resolved, Timeout Callback // (Microtasks worden uitgevoerd vóór Macrotasks/Callbacks)

Binaire Zoekactie

Implementeer een binaire zoekfunctie voor een gesorteerde array.

Uitleg: Binaire zoekactie vindt efficiënt een item in een gesorteerde array door het zoekinterval herhaaldelijk in tweeën te delen. Als de waarde van de zoektoets kleiner is dan het item in het midden van het interval, verklein dan het interval tot de onderste helft. Anders verklein je het tot de bovenste helft. Dit doe je totdat de waarde is gevonden of het interval leeg is.

function binarySearch(sortedArray, key) { let start = 0; let end = sortedArray.length - 1; while (start <= end) { let middle = Math.floor((start + end) / 2); if (sortedArray[middle] === key) { return middle; // Gevonden } else if (sortedArray[middle] < key) { start = middle + 1; // Zoek rechterhelft } else { end = middle - 1; // Zoek linkerhelft } } return -1; // Niet gevonden } console.log(binarySearch([1, 3, 5, 7, 9, 11], 7)); // 3 console.log(binarySearch([1, 3, 5, 7, 9, 11], 2)); // -1

Merge Sort

Implementeer het Merge Sort algoritme.

Uitleg: Merge Sort is een verdeel-en-heers algoritme. Het verdeelt de invoerarray in twee helften, roept zichzelf aan voor de twee helften en voegt vervolgens de twee gesorteerde helften samen. De merge-functie is cruciaal; deze combineert twee gesorteerde sub-arrays tot één gesorteerde array.

function mergeSort(arr) { if (arr.length <= 1) return arr; const mid = Math.floor(arr.length / 2); const left = mergeSort(arr.slice(0, mid)); const right = mergeSort(arr.slice(mid)); return merge(left, right); } function merge(left, right) { let result = []; let leftIndex = 0; let rightIndex = 0; while (leftIndex < left.length && rightIndex < right.length) { if (left[leftIndex] < right[rightIndex]) { result.push(left[leftIndex]); leftIndex++; } else { result.push(right[rightIndex]); rightIndex++; } } return result.concat(left.slice(leftIndex)).concat(right.slice(rightIndex)); } console.log(mergeSort([38, 27, 43, 3, 9, 82, 10])); // [3, 9, 10, 27, 38, 43, 82]

Quick Sort

Implementeer het Quick Sort algoritme.

Uitleg: Quick Sort is ook een verdeel-en-heers algoritme. Het kiest een element als spil en verdeelt de gegeven array rond de gekozen spil. Elementen kleiner dan de spil gaan naar links, en elementen groter gaan naar rechts. Vervolgens worden de sub-arrays recursief gesorteerd.

function quickSort(arr) { if (arr.length <= 1) return arr; const pivot = arr[arr.length - 1]; const left = []; const right = []; for (let i = 0; i < arr.length - 1; i++) { if (arr[i] < pivot) { left.push(arr[i]); } else { right.push(arr[i]); } } return [...quickSort(left), pivot, ...quickSort(right)]; } console.log(quickSort([10, 8, 2, 1, 6, 3, 9, 4, 7, 5])); // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

Bubble Sort

Implementeer het Bubble Sort algoritme.

Uitleg: Bubble Sort is een eenvoudig sorteeralgoritme dat herhaaldelijk door de lijst stapt, aangrenzende elementen vergelijkt en ze omwisselt als ze in de verkeerde volgorde staan. De doorgang door de lijst wordt herhaald totdat de lijst is gesorteerd.

function bubbleSort(arr) { let n = arr.length; let swapped; do { swapped = false; for (let i = 0; i < n - 1; i++) { if (arr[i] > arr[i + 1]) { [arr[i], arr[i + 1]] = [arr[i + 1], arr[i]]; // Wissel swapped = true; } } n--; // Optimalisatie: laatste element is al op zijn plaats } while (swapped); return arr; } console.log(bubbleSort([64, 34, 25, 12, 22, 11, 90])); // [11, 12, 22, 25, 34, 64, 90]

Langste Substring Zonder Herhalende Karakters

Gegeven een string, vind de lengte van de langste substring zonder herhalende karakters.

Uitleg: Gebruik de schuifvenster techniek. Handhaaf een venster (substring) en een set karakters in dat venster. Breid het venster uit door de rechterwijzer te verplaatsen. Als een herhalend karakter wordt gevonden, verklein dan het venster vanaf links totdat het herhalende karakter is verwijderd.

function lengthOfLongestSubstring(s) { let maxLength = 0; let start = 0; const charMap = {}; for (let end = 0; end < s.length; end++) { const char = s[end]; if (charMap[char] >= start) { start = charMap[char] + 1; } charMap[char] = end; maxLength = Math.max(maxLength, end - start + 1); } return maxLength; } console.log(lengthOfLongestSubstring('abcabcbb')); // 3 ('abc') console.log(lengthOfLongestSubstring('bbbbb')); // 1 ('b') console.log(lengthOfLongestSubstring('pwwkew')); // 3 ('wke')

Implementeer een Gekoppelde Lijst

Implementeer een Enkelvoudig Gekoppelde Lijst met add- en print-methoden.

Uitleg: Een gekoppelde lijst is een lineaire datastructuur waarbij elementen niet op aaneengesloten geheugenlocaties worden opgeslagen. Elk element (knooppunt) wijst naar het volgende. Je hebt een Node-klasse en een LinkedList-klasse nodig om de kop en het toevoegen van nieuwe knooppunten te beheren.

class Node { constructor(data, next = null) { this.data = data; this.next = next; } } class LinkedList { constructor() { this.head = null; } add(data) { const newNode = new Node(data); if (!this.head) { this.head = newNode; } else { let current = this.head; while (current.next) { current = current.next; } current.next = newNode; } } print() { let current = this.head; let list = ''; while (current) { list += current.data + ' -> '; current = current.next; } console.log(list + 'null'); } } const list = new LinkedList(); list.add(10); list.add(20); list.add(30); list.print(); // 10 -> 20 -> 30 -> null

Implementeer een Binaire Zoekboom (BST)

Implementeer een Binaire Zoekboom met insert- en find-methoden.

Uitleg: Een BST is een knooppuntgebaseerde binaire boomdatastructuur die de volgende eigenschappen heeft: De linker subboom van een knooppunt bevat alleen knooppunten met sleutels die kleiner zijn dan de sleutel van het knooppunt. De rechter subboom van een knooppunt bevat alleen knooppunten met sleutels die groter zijn dan de sleutel van het knooppunt. Beide subbomen moeten ook binaire zoekbomen zijn.

class Node { constructor(data) { this.data = data; this.left = null; this.right = null; } } class BST { constructor() { this.root = null; } insert(data) { const newNode = new Node(data); if (!this.root) { this.root = newNode; return; } this._insertNode(this.root, newNode); } _insertNode(node, newNode) { if (newNode.data < node.data) { if (!node.left) node.left = newNode; else this._insertNode(node.left, newNode); } else { if (!node.right) node.right = newNode; else this._insertNode(node.right, newNode); } } find(data) { return this._findNode(this.root, data); } _findNode(node, data) { if (!node) return null; if (data < node.data) return this._findNode(node.left, data); else if (data > node.data) return this._findNode(node.right, data); else return node; } } const bst = new BST(); bst.insert(10); bst.insert(5); bst.insert(15); bst.insert(7); console.log(bst.find(7)); // Node { data: 7, left: null, right: null } console.log(bst.find(12)); // null

Roteer een Array

Schrijf een functie om een array naar rechts te roteren met k stappen.

Uitleg: Een array roteren betekent het verplaatsen van de elementen. Voor een rotatie naar rechts wordt het laatste element het eerste. Dit k keer herhalen werkt, maar is inefficiënt. Een betere manier is om array-manipulatie te gebruiken of de nieuwe posities te berekenen.

function rotateArray(nums, k) { k = k % nums.length; // Behandel gevallen waarbij k > lengte if (k === 0) return nums; const partToMove = nums.splice(nums.length - k); nums.unshift(...partToMove); return nums; } console.log(rotateArray([1, 2, 3, 4, 5, 6, 7], 3)); // [5, 6, 7, 1, 2, 3, 4]

Vind Doorsnede van Twee Arrays

Gegeven twee arrays, schrijf een functie om hun doorsnede (elementen die in beide voorkomen) te berekenen.

Uitleg: Een goede aanpak is om één array om te zetten in een Set voor O(1) gemiddelde zoektijden. Doorloop vervolgens de tweede array en controleer of elk element in de set bestaat. Verzamel de overeenkomsten.

function intersection(nums1, nums2) { const set1 = new Set(nums1); const resultSet = new Set(); for (const num of nums2) { if (set1.has(num)) { resultSet.add(num); } } return [...resultSet]; } console.log(intersection([1, 2, 2, 1], [2, 2])); // [2] console.log(intersection([4, 9, 5], [9, 4, 9, 8, 4])); // [9, 4]

Groepeer Anagrammen

Gegeven een array van strings, groepeer anagrammen samen.

Uitleg: De sleutel is om een unieke handtekening te vinden voor elke groep anagrammen. Een veelgebruikte manier is om elk woord alfabetisch te sorteren. Woorden die resulteren in dezelfde gesorteerde string zijn anagrammen. Gebruik een hash map waarbij de sleutels gesorteerde woorden zijn en de waarden arrays van originele woorden.

function groupAnagrams(strs) { const map = {}; for (const str of strs) { const sortedStr = str.split('').sort().join(''); if (!map[sortedStr]) { map[sortedStr] = []; } map[sortedStr].push(str); } return Object.values(map); } console.log(groupAnagrams(['eat', 'tea', 'tan', 'ate', 'nat', 'bat'])); // Uitvoer: [['eat', 'tea', 'ate'], ['tan', 'nat'], ['bat']]

Verplaats Nullen naar het Einde

Gegeven een array nums, schrijf een functie om alle nullen naar het einde te verplaatsen met behoud van de relatieve volgorde van de niet-nul-elementen.

Uitleg: Gebruik een twee-pointer- of sneeuwbal-aanpak. Eén wijzer houdt de positie bij waar het volgende niet-nul-element moet komen. Doorloop de array; als een element niet-nul is, plaats het dan op de positie van de wijzer en verhoog de wijzer. Vul ten slotte de rest met nullen.

function moveZeroes(nums) { let nonZeroIndex = 0; // Verplaats alle niet-nul elementen naar voren for (let i = 0; i < nums.length; i++) { if (nums[i] !== 0) { nums[nonZeroIndex] = nums[i]; nonZeroIndex++; } } // Vul de rest met nullen for (let i = nonZeroIndex; i < nums.length; i++) { nums[i] = 0; } return nums; } console.log(moveZeroes([0, 1, 0, 3, 12])); // [1, 3, 12, 0, 0]

Maximale Subarray (Kadane's Algoritme)

Gegeven een integer array nums, vind de aaneengesloten sub-array (die ten minste één getal bevat) met de grootste som en retourneer de som.

Uitleg: Kadane's algoritme is een efficiënte manier om dit op te lossen. Doorloop de array, houd de currentMax som die eindigt op de huidige positie en de globalMax som die tot nu toe is gevonden bij. Als currentMax negatief wordt, zet het dan terug naar 0 (of liever, het huidige element).

function maxSubArray(nums) { let globalMax = -Infinity; let currentMax = 0; for (let i = 0; i < nums.length; i++) { currentMax = Math.max(nums[i], currentMax + nums[i]); if (currentMax > globalMax) { globalMax = currentMax; } } return globalMax; } console.log(maxSubArray([-2, 1, -3, 4, -1, 2, 1, -5, 4])); // 6 (van [4, -1, 2, 1])

Permutaties van een String

Schrijf een functie om alle permutaties van een gegeven string te genereren.

Uitleg: Dit wordt doorgaans opgelost met recursie en backtracking. Voor elk karakter, fixeer het en genereer recursief permutaties voor de rest van de string. Basisgeval: wanneer de string slechts één karakter heeft, retourneer dat dan.

function stringPermutations(str) { if (str.length === 0) return ['']; if (str.length === 1) return [str]; const results = []; for (let i = 0; i < str.length; i++) { const char = str[i]; const remainingChars = str.slice(0, i) + str.slice(i + 1); const perms = stringPermutations(remainingChars); for (const perm of perms) { results.push(char + perm); } } return [...new Set(results)]; // Gebruik Set om dubbele karakters te behandelen indien nodig } console.log(stringPermutations('abc')); // ['abc', 'acb', 'bac', 'bca', 'cab', 'cba'] console.log(stringPermutations('aab')); // ['aab', 'aba', 'baa']

Grootste Gemene Deler (GGD)

Schrijf een functie om de Grootste Gemene Deler (GGD) van twee getallen te vinden.

Uitleg: Het Euclidische algoritme is een efficiënte methode. Als b 0 is, is a de GGD. Anders is de GGD van a en b hetzelfde als de GGD van b en a % b (de rest van a gedeeld door b).

function gcd(a, b) { while (b !== 0) { let temp = b; b = a % b; a = temp; } return a; } console.log(gcd(48, 18)); // 6 console.log(gcd(101, 103)); // 1

Kleinst Gemeen Veelvoud (KGV)

Schrijf een functie om het Kleinst Gemeen Veelvoud (KGV) van twee getallen te vinden.

Uitleg: Het KGV kan worden berekend met behulp van de GGD met de formule: KGV(a, b) = |a * b| / GGD(a, b).

function gcd(a, b) { // Hulpfunctie van vorig probleem while (b !== 0) { let temp = b; b = a % b; a = temp; } return a; } function lcm(a, b) { if (a === 0 || b === 0) return 0; return Math.abs(a * b) / gcd(a, b); } console.log(lcm(15, 20)); // 60 console.log(lcm(7, 5)); // 35

Implementeer Promise.all

Implementeer een functie die zich gedraagt als Promise.all.

Uitleg: Promise.all neemt een array van promises en retourneert een enkele promise die wordt opgelost wanneer alle invoerpromises zijn opgelost, of wordt geweigerd als een van de invoerpromises wordt geweigerd. De opgeloste waarde is een array van de opgeloste waarden.

function myPromiseAll(promises) { return new Promise((resolve, reject) => { const results = []; let completedCount = 0; const numPromises = promises.length; if (numPromises === 0) { resolve([]); return; } promises.forEach((promise, index) => { Promise.resolve(promise) .then(value => { results[index] = value; completedCount++; if (completedCount === numPromises) { resolve(results); } }) .catch(reject); // Onmiddellijk weigeren bij een fout }); }); } // Voorbeeldgebruik: const p1 = Promise.resolve(3); const p2 = 42; const p3 = new Promise((resolve) => setTimeout(resolve, 100, 'foo')); myPromiseAll([p1, p2, p3]).then(values => console.log(values)); // [3, 42, 'foo']

Inverteer Binaire Boom

Schrijf een functie om een binaire boom te inverteren.

Uitleg: Om een binaire boom te inverteren, verwissel je de linker- en rechterkinderen voor elk knooppunt. Dit kan recursief of iteratief (met behulp van een queue of stack) worden gedaan.

class Node { constructor(val, left = null, right = null) { this.val = val; this.left = left; this.right = right; } } function invertTree(root) { if (root === null) { return null; } // Wissel de kinderen [root.left, root.right] = [root.right, root.left]; // Recur voor linker- en rechterkinderen invertTree(root.left); invertTree(root.right); return root; } // Voorbeeld: 4 -> [2, 7] -> [1, 3, 6, 9] // Wordt: 4 -> [7, 2] -> [9, 6, 3, 1]

Maximale Diepte van Binaire Boom

Gegeven een binaire boom, vind de maximale diepte ervan.

Uitleg: De maximale diepte is het aantal knooppunten langs het langste pad van het hoofdknooppunt naar het verst gelegen bladknooppunt. Dit kan recursief worden gevonden: MaxDepth = 1 + max(MaxDepth(links), MaxDepth(rechts)). Het basisgeval is wanneer een knooppunt null is, de diepte is dan 0.

class Node { constructor(val, left = null, right = null) { this.val = val; this.left = left; this.right = right; } } function maxDepth(root) { if (root === null) { return 0; } const leftDepth = maxDepth(root.left); const rightDepth = maxDepth(root.right); return Math.max(leftDepth, rightDepth) + 1; } // Voorbeeld: 3 -> [9, 20] -> [null, null, 15, 7] const tree = new Node(3, new Node(9), new Node(20, new Node(15), new Node(7))); console.log(maxDepth(tree)); // 3

Beste Tijd om Aandeel te Kopen/Verkopen

Je krijgt een array prices waarbij prices[i] de prijs is van een bepaald aandeel op de i-de dag. Je wilt je winst maximaliseren door één dag te kiezen om één aandeel te kopen en een andere dag in de toekomst te kiezen om dat aandeel te verkopen. Retourneer de maximale winst.

Uitleg: Doorloop de prijzen en houd de tot nu toe laagste prijs (minPrice) en de tot nu toe hoogste winst (maxProfit) bij. Bereken voor elke dag de potentiële winst als je vandaag zou verkopen (huidige prijs - minPrice) en update maxProfit als deze hoger is.

function maxProfit(prices) { let minPrice = Infinity; let maxProfit = 0; for (let i = 0; i < prices.length; i++) { if (prices[i] < minPrice) { minPrice = prices[i]; } else if (prices[i] - minPrice > maxProfit) { maxProfit = prices[i] - minPrice; } } return maxProfit; } console.log(maxProfit([7, 1, 5, 3, 6, 4])); // 5 (Kopen op 1, Verkopen op 6) console.log(maxProfit([7, 6, 4, 3, 1])); // 0 (Geen winst mogelijk)

Enkel Getal

Gegeven een niet-lege array van integers nums, verschijnt elk element twee keer behalve één. Vind dat ene enkele getal.

Uitleg: Een zeer efficiënte manier om dit op te lossen is door de XOR bitwise operator (^) te gebruiken. Een getal XOR'en met zichzelf resulteert in 0. Een getal XOR'en met 0 resulteert in het getal zelf. Als je alle getallen in de array XOR't, zullen de paren elkaar opheffen (worden 0), waardoor alleen het enkele getal overblijft.

function singleNumber(nums) { let result = 0; for (const num of nums) { result ^= num; } return result; } console.log(singleNumber([2, 2, 1])); // 1 console.log(singleNumber([4, 1, 2, 1, 2])); // 4

Meerderheidselement

Gegeven een array nums van grootte n, retourneer het meerderheidselement. Het meerderheidselement is het element dat meer dan ⌊n / 2⌋ keer voorkomt.

Uitleg: Het Boyer-Moore Stem Algoritme is een efficiënte manier. Initialiseer een candidate en een count. Doorloop de array. Als count 0 is, stel het huidige element in als de candidate. Als het huidige element overeenkomt met de candidate, verhoog dan count; anders, verlaag count.

function majorityElement(nums) { let candidate = null; let count = 0; for (const num of nums) { if (count === 0) { candidate = num; } count += (num === candidate) ? 1 : -1; } return candidate; } console.log(majorityElement([3, 2, 3])); // 3 console.log(majorityElement([2, 2, 1, 1, 1, 2, 2])); // 2

Trappen Beklimmen

Je beklimt een trap. Het kost n stappen om de top te bereiken. Elke keer kun je 1 of 2 stappen tegelijk klimmen. Op hoeveel verschillende manieren kun je de top bereiken?

Uitleg: Dit is een klassiek dynamisch programmeringsprobleem, zeer vergelijkbaar met de Fibonacci-reeks. Het aantal manieren om stap n te bereiken is de som van de manieren om stap n-1 te bereiken (door 1 stap te nemen) en de manieren om stap n-2 te bereiken (door 2 stappen te nemen). dp[n] = dp[n-1] + dp[n-2].

function climbStairs(n) { if (n <= 2) return n; let oneStepBefore = 2; let twoStepsBefore = 1; let allWays = 0; for (let i = 3; i <= n; i++) { allWays = oneStepBefore + twoStepsBefore; twoStepsBefore = oneStepBefore; oneStepBefore = allWays; } return allWays; } console.log(climbStairs(2)); // 2 (1+1, 2) console.log(climbStairs(3)); // 3 (1+1+1, 1+2, 2+1) console.log(climbStairs(4)); // 5

Product van Array Behalve Zichzelf

Gegeven een integer array nums, retourneer een array answer zodanig dat answer[i] gelijk is aan het product van alle elementen van nums behalve nums[i]. Gebruik de deleroperator niet.

Uitleg: Je kunt dit oplossen door prefix-producten en suffix-producten te berekenen. Het resultaat op index i is het product van alle elementen vóór i vermenigvuldigd met het product van alle elementen ná i.

function productExceptSelf(nums) { const n = nums.length; const answer = new Array(n).fill(1); // Bereken prefix-producten let prefix = 1; for (let i = 0; i < n; i++) { answer[i] = prefix; prefix *= nums[i]; } // Bereken suffix-producten en vermenigvuldig met prefix let suffix = 1; for (let i = n - 1; i >= 0; i--) { answer[i] *= suffix; suffix *= nums[i]; } return answer; } console.log(productExceptSelf([1, 2, 3, 4])); // [24, 12, 8, 6]

Aantal Eilanden

Gegeven een 2D-rasterkaart van 1en (land) en 0en (water), tel het aantal eilanden. Een eiland is omringd door water en wordt gevormd door aangrenzende landen horizontaal of verticaal met elkaar te verbinden.

Uitleg: Doorloop elke cel van het raster. Als je een 1 (land) vindt, verhoog dan de eilandteller en start een Depth First Search (DFS) of Breadth First Search (BFS) vanaf die cel. Markeer tijdens het zoeken alle verbonden 1en als 0 (of bezocht) om te voorkomen dat ze opnieuw worden geteld.

function numIslands(grid) { if (!grid || grid.length === 0) return 0; let count = 0; const rows = grid.length; const cols = grid[0].length; function dfs(r, c) { if (r < 0 || c < 0 || r >= rows || c >= cols || grid[r][c] === '0') { return; } grid[r][c] = '0'; // Markeer als bezocht dfs(r + 1, c); dfs(r - 1, c); dfs(r, c + 1); dfs(r, c - 1); } for (let r = 0; r < rows; r++) { for (let c = 0; c < cols; c++) { if (grid[r][c] === '1') { count++; dfs(r, c); } } } return count; } const grid1 = [ ['1', '1', '1', '1', '0'], ['1', '1', '0', '1', '0'], ['1', '1', '0', '0', '0'], ['0', '0', '0', '0', '0'] ]; // console.log(numIslands(grid1)); // 1 (Moet op een kopie draaien of raster herstellen)

LRU Cache

Ontwerp en implementeer een datastructuur voor Least Recently Used (LRU) Cache. Het moet get- en put-bewerkingen ondersteunen.

Uitleg: Een LRU-cache verwijdert het minst recent gebruikte item wanneer de capaciteit is bereikt. Een veelvoorkomende implementatie maakt gebruik van een Map (of een hash map) voor O(1) zoekopdrachten en een Dubbel Gekoppelde Lijst voor O(1) updates van de recent gebruikte status. JavaScript's Map behoudt de invoegvolgorde, wat een basisimplementatie kan vereenvoudigen.

class LRUCache { constructor(capacity) { this.capacity = capacity; this.cache = new Map(); } get(key) { if (!this.cache.has(key)) return -1; const value = this.cache.get(key); this.cache.delete(key); // Verwijderen om opnieuw in te voegen aan het einde (meest recent) this.cache.set(key, value); return value; } put(key, value) { if (this.cache.has(key)) { this.cache.delete(key); } else if (this.cache.size >= this.capacity) { const oldestKey = this.cache.keys().next().value; this.cache.delete(oldestKey); } this.cache.set(key, value); } } const cache = new LRUCache(2); cache.put(1, 1); cache.put(2, 2); console.log(cache.get(1)); // 1 cache.put(3, 3); // Verdrijft 2 console.log(cache.get(2)); // -1

Genereer Haakjes

Gegeven n paren haakjes, schrijf een functie om alle combinaties van goed gevormde haakjes te genereren.

Uitleg: Dit is een klassiek backtracking probleem. Houd tellingen bij voor open en gesloten haakjes. Je kunt een open haakje toevoegen als open < n. Je kunt een gesloten haakje toevoegen als close < open. Het basisgeval is wanneer de stringlengte 2 * n bereikt.

function generateParenthesis(n) { const results = []; function backtrack(currentString, openCount, closeCount) { if (currentString.length === n * 2) { results.push(currentString); return; } if (openCount < n) { backtrack(currentString + '(', openCount + 1, closeCount); } if (closeCount < openCount) { backtrack(currentString + ')', openCount, closeCount + 1); } } backtrack('', 0, 0); return results; } console.log(generateParenthesis(3)); // ['((()))', '(()())', '(())()', '()(())', '()()()']

Container Met Meeste Water

Gegeven n niet-negatieve integers a1, a2, ..., an, waarbij elk een punt op coördinaat (i, ai) voorstelt. n verticale lijnen worden getrokken zodat de twee eindpunten van lijn i op (i, ai) en (i, 0) zijn. Vind twee lijnen die samen met de x-as een container vormen, zodanig dat de container het meeste water bevat.

Uitleg: Gebruik de twee-pointer-aanpak. Begin met één wijzer aan het begin en één aan het einde. Bereken het oppervlak. Het oppervlak wordt beperkt door de kortere lijn. Om potentieel een groter oppervlak te vinden, verplaats je de wijzer die naar de kortere lijn wijst naar binnen.

function maxArea(height) { let max = 0; let left = 0; let right = height.length - 1; while (left < right) { const h = Math.min(height[left], height[right]); const w = right - left; max = Math.max(max, h * w); if (height[left] < height[right]) { left++; } else { right--; } } return max; } console.log(maxArea([1, 8, 6, 2, 5, 4, 8, 3, 7])); // 49

3Som

Gegeven een array nums van n integers, zijn er elementen a, b, c in nums zodanig dat a + b + c = 0? Vind alle unieke drietallen in de array die de som van nul geven.

Uitleg: Sorteer eerst de array. Doorloop vervolgens de array met één wijzer i. Gebruik voor elke i twee extra wijzers, left (beginnend bij i+1) en right (beginnend bij het einde), om twee getallen te vinden die optellen tot -nums[i]. Behandel duplicaten om unieke drietallen te garanderen.

function threeSum(nums) { nums.sort((a, b) => a - b); const results = []; for (let i = 0; i < nums.length - 2; i++) { if (i > 0 && nums[i] === nums[i - 1]) continue; // Sla duplicaten over voor i let left = i + 1; let right = nums.length - 1; let target = -nums[i]; while (left < right) { let sum = nums[left] + nums[right]; if (sum === target) { results.push([nums[i], nums[left], nums[right]]); while (left < right && nums[left] === nums[left + 1]) left++; // Sla duplicaten over while (left < right && nums[right] === nums[right - 1]) right--; // Sla duplicaten over left++; right--; } else if (sum < target) { left++; } else { right--; } } } return results; } console.log(threeSum([-1, 0, 1, 2, -1, -4])); // [[-1, -1, 2], [-1, 0, 1]]

Zoek Invoegpositie

Gegeven een gesorteerde array van unieke integers en een doelwaarde, retourneer de index als het doel wordt gevonden. Zo niet, retourneer dan de index waar het zou zijn als het in volgorde zou worden ingevoegd.

Uitleg: Dit is een variatie op binaire zoekactie. Voer binaire zoekactie uit, maar als het element niet wordt gevonden, zal de start-wijzer eindigen op de positie waar het element moet worden ingevoegd.

function searchInsert(nums, target) { let start = 0; let end = nums.length - 1; while (start <= end) { let mid = Math.floor((start + end) / 2); if (nums[mid] === target) { return mid; } else if (nums[mid] < target) { start = mid + 1; } else { end = mid - 1; } } return start; // Indien niet gevonden, is start het invoegpunt } console.log(searchInsert([1, 3, 5, 6], 5)); // 2 console.log(searchInsert([1, 3, 5, 6], 2)); // 1 console.log(searchInsert([1, 3, 5, 6], 7)); // 4

Voeg Twee Gesorteerde Lijsten Samen (Gekoppelde Lijsten)

Voeg twee gesorteerde gekoppelde lijsten samen en retourneer het als een nieuwe gesorteerde lijst. De nieuwe lijst moet worden gemaakt door de knooppunten van de eerste twee lijsten samen te voegen.

Uitleg: Gebruik een dummy-hoofdknooppunt om het proces te vereenvoudigen. Gebruik een wijzer om de nieuwe lijst te bouwen. Vergelijk de huidige knooppunten van beide invoerlijsten en voeg de kleinere toe aan de nieuwe lijst, waarbij de wijzer van die lijst wordt doorgeschoven. Herhaal dit totdat één lijst is uitgeput, voeg dan de rest van de andere lijst toe.

class ListNode { constructor(val = 0, next = null) { this.val = val; this.next = next; } } function mergeTwoLists(l1, l2) { let dummyHead = new ListNode(); let current = dummyHead; while (l1 !== null && l2 !== null) { if (l1.val < l2.val) { current.next = l1; l1 = l1.next; } else { current.next = l2; l2 = l2.next; } current = current.next; } current.next = l1 !== null ? l1 : l2; return dummyHead.next; } // Voorbeeld: 1->2->4 en 1->3->4 => 1->1->2->3->4->4

Twee Gesorteerde Lijsten Samenvoegen (Gekoppelde Lijsten)

Voeg twee gesorteerde gekoppelde lijsten samen en retourneer deze als een nieuwe gesorteerde lijst. De nieuwe lijst moet worden gemaakt door de knooppunten van de eerste twee lijsten samen te voegen.

Uitleg: Gebruik een dummy-kopknooppunt om het proces te vereenvoudigen. Gebruik een aanwijzer om de nieuwe lijst op te bouwen. Vergelijk de huidige knooppunten van beide invoerlijsten en voeg de kleinere toe aan de nieuwe lijst, waarbij de aanwijzer van die lijst wordt verplaatst. Herhaal dit totdat één lijst is uitgeput en voeg vervolgens de rest van de andere lijst toe.

class ListNode { constructor(val = 0, next = null) { this.val = val; this.next = next; } } function mergeTwoLists(l1, l2) { let dummyHead = new ListNode(); let current = dummyHead; while (l1 !== null && l2 !== null) { if (l1.val < l2.val) { current.next = l1; l1 = l1.next; } else { current.next = l2; l2 = l2.next; } current = current.next; } current.next = l1 !== null ? l1 : l2; return dummyHead.next; } // Voorbeeld: 1->2->4 en 1->3->4 => 1->1->2->3->4->4

Symmetrische Boom

Gegeven een binaire boom, controleer of deze een spiegelbeeld van zichzelf is (d.w.z. symmetrisch rond het midden).

Uitleg: Dit kan recursief worden opgelost. Een boom is symmetrisch als de linker subboom een spiegelbeeld is van de rechter subboom. Maak een hulpfunctie isMirror(t1, t2) die controleert of twee bomen spiegels zijn. Deze moet controleren: 1. t1.val === t2.val. 2. t1.left is een spiegel van t2.right. 3. t1.right is een spiegel van t2.left.

class Node { constructor(val, left = null, right = null) { this.val = val; this.left = left; this.right = right; } } function isSymmetric(root) { if (!root) return true; function isMirror(t1, t2) { if (!t1 && !t2) return true; if (!t1 || !t2 || t1.val !== t2.val) return false; return isMirror(t1.left, t2.right) && isMirror(t1.right, t2.left); } return isMirror(root.left, root.right); } // Voorbeeld: 1 -> [2, 2] -> [3, 4, 4, 3] is symmetrisch.

Niveauvolgorde Traversal (BST/Boom)

Gegeven een binaire boom, retourneer de niveauvolgorde traversal van de knooppuntwaarden (d.w.z. van links naar rechts, niveau voor niveau).

Uitleg: Dit is een Breadth-First Search (BFS). Gebruik een wachtrij. Begin met het toevoegen van de root aan de wachtrij. Zolang de wachtrij niet leeg is, verwerk alle knooppunten op het huidige niveau. Voor elk verwerkt knooppunt, voeg de kinderen (indien aanwezig) toe aan de wachtrij voor het volgende niveau.

class Node { constructor(val, left = null, right = null) { this.val = val; this.left = left; this.right = right; } } function levelOrder(root) { if (!root) return []; const result = []; const queue = [root]; while (queue.length > 0) { const levelSize = queue.length; const currentLevel = []; for (let i = 0; i < levelSize; i++) { const node = queue.shift(); currentLevel.push(node.val); if (node.left) queue.push(node.left); if (node.right) queue.push(node.right); } result.push(currentLevel); } return result; } // Voorbeeld: 3 -> [9, 20] -> [null, null, 15, 7] // Uitvoer: [[3], [9, 20], [15, 7]]

Gesorteerde Array Converteren naar Hoogte-Gebalanceerde BST

Gegeven een array waarvan de elementen in oplopende volgorde zijn gesorteerd, converteer deze naar een hoogte-gebalanceerde binaire zoekboom (BST).

Uitleg: Om een hoogte-gebalanceerde BST te maken, moet je het middelste element van de array kiezen als de root. Bouw vervolgens recursief de linker subboom uit de linkerhelft van de array en de rechter subboom uit de rechterhelft.

class TreeNode { constructor(val = 0, left = null, right = null) { this.val = val; this.left = left; this.right = right; } } function sortedArrayToBST(nums) { if (!nums || nums.length === 0) return null; function buildBST(start, end) { if (start > end) return null; const mid = Math.floor((start + end) / 2); const node = new TreeNode(nums[mid]); node.left = buildBST(start, mid - 1); node.right = buildBST(mid + 1, end); return node; } return buildBST(0, nums.length - 1); } // Voorbeeld: [-10, -3, 0, 5, 9] // Uitvoer: Een boom zoals [0, -3, 9, -10, null, 5, null]

Bind Implementeren

Implementeer je eigen versie van Function.prototype.bind.

Uitleg: bind creëert een nieuwe functie die, wanneer aangeroepen, zijn this-sleutelwoord ingesteld heeft op de opgegeven waarde, met een gegeven reeks argumenten voorafgaand aan alle argumenten die worden meegegeven wanneer de nieuwe functie wordt aangeroepen. Gebruik apply of call binnen een geretourneerde functie, en verwerk partiële applicatie (vooraf ingestelde argumenten).

Function.prototype.myBind = function(context, ...bindArgs) { const originalFunc = this; return function(...callArgs) { return originalFunc.apply(context, [...bindArgs, ...callArgs]); }; } const module = { x: 42, getX: function() { return this.x; } }; const unboundGetX = module.getX; console.log(unboundGetX()); // undefined (this is global/window) const boundGetX = unboundGetX.myBind(module); console.log(boundGetX()); // 42

Het K-de Grootste Element Vinden

Zoek het k-de grootste element in een ongesorteerde array. Merk op dat het het k-de grootste element in de gesorteerde volgorde is, niet het k-de unieke element.

Uitleg: Een eenvoudige benadering is om de array te sorteren en vervolgens het element op index n - k te kiezen. Een efficiëntere benadering voor grote arrays omvat het gebruik van een min-heap of een selectie-algoritme zoals Quickselect.

function findKthLargest(nums, k) { // Eenvoudige benadering: Sorteren nums.sort((a, b) => b - a); // Sorteer in aflopende volgorde return nums[k - 1]; // Opmerking: Quickselect zou efficiënter zijn in een interview // maar sorteren is gemakkelijker snel te implementeren. } console.log(findKthLargest([3, 2, 1, 5, 6, 4], 2)); // 5 console.log(findKthLargest([3, 2, 3, 1, 2, 4, 5, 5, 6], 4)); // 4

Controleren of Object een Eigenschap Heeft

Hoe kun je controleren of een object een specifieke eigenschap heeft?

Uitleg: Je kunt de 'in'-operator gebruiken (controleert eigen en geërfde eigenschappen), Object.prototype.hasOwnProperty.call(obj, prop) (controleert alleen eigen eigenschappen), of eenvoudigweg obj.prop !== undefined (kan lastig zijn met undefined-waarden).

const person = { name: 'Eve', age: 28 }; function hasProp(obj, prop) { console.log(`Gebruik 'in': ${prop in obj}`); console.log(`Gebruik 'hasOwnProperty': ${Object.prototype.hasOwnProperty.call(obj, prop)}`); } hasProp(person, 'name'); // true, true hasProp(person, 'toString'); // true, false (toString is geërfd)

Integer naar Romeins cijfer

Schrijf een functie om een integer om te zetten naar zijn Romeinse cijferrepresentatie.

Uitleg: Creëer een mapping van Romeinse cijfers en hun corresponderende waarden, geordend van grootst naar kleinst. Itereer door deze map. Trek voor elke waarde deze zo vaak mogelijk af van het invoernummer, waarbij telkens het Romeinse cijfer wordt toegevoegd.

function intToRoman(num) { const map = { M: 1000, CM: 900, D: 500, CD: 400, C: 100, XC: 90, L: 50, XL: 40, X: 10, IX: 9, V: 5, IV: 4, I: 1 }; let result = ''; for (let key in map) { while (num >= map[key]) { result += key; num -= map[key]; } } return result; } console.log(intToRoman(3)); // III console.log(intToRoman(58)); // LVIII console.log(intToRoman(1994)); // MCMXCIV

Romeins cijfer naar Integer

Schrijf een functie om een Romeins cijfer om te zetten naar een integer.

Uitleg: Maak een kaart van Romeinse symbolen naar waarden. Itereer door de string. Als de waarde van het huidige symbool kleiner is dan de waarde van het volgende symbool (zoals 'IV' of 'IX'), trek dan de huidige waarde af; anders, tel deze op.

function romanToInt(s) { const map = { I: 1, V: 5, X: 10, L: 50, C: 100, D: 500, M: 1000 }; let result = 0; for (let i = 0; i < s.length; i++) { const currentVal = map[s[i]]; const nextVal = map[s[i + 1]]; if (nextVal > currentVal) { result -= currentVal; } else { result += currentVal; } } return result; } console.log(romanToInt('III')); // 3 console.log(romanToInt('LVIII')); // 58 console.log(romanToInt('MCMXCIV')); // 1994

Een 'Set' Implementeren

Implementeer een basis Set-datastructuur (zonder de ingebouwde Set te gebruiken) met de methoden add, has, delete en size.

Uitleg: Je kunt een JavaScript-object (hash map) gebruiken als de onderliggende opslag. Sleutels zijn de set-elementen (je moet mogelijk omgaan met hoe verschillende typen op te slaan en uniciteit als sleutels te garanderen).

class MySet { constructor() { this.items = {}; this._size = 0; } add(element) { if (!this.has(element)) { this.items[element] = element; // Sla het element op this._size++; return true; } return false; } delete(element) { if (this.has(element)) { delete this.items[element]; this._size--; return true; } return false; } has(element) { return Object.prototype.hasOwnProperty.call(this.items, element); } get size() { return this._size; } } const set = new MySet(); set.add(1); set.add(2); console.log(set.has(1)); // true console.log(set.size); // 2 set.delete(1); console.log(set.has(1)); // false

Driehoek van Pascal

Gegeven een integer numRows, genereer de eerste numRows van de driehoek van Pascal.

Uitleg: In de driehoek van Pascal is elk getal de som van de twee getallen direct erboven. Begin met [[1]]. Voor elke volgende rij, begin en eindig met 1. Elk middenelement is de som van het element op dezelfde index en de vorige index van de rij erboven.

function generatePascalsTriangle(numRows) { if (numRows === 0) return []; const triangle = [[1]]; for (let i = 1; i < numRows; i++) { const prevRow = triangle[i - 1]; const newRow = [1]; for (let j = 1; j < i; j++) { newRow.push(prevRow[j - 1] + prevRow[j]); } newRow.push(1); triangle.push(newRow); } return triangle; } console.log(generatePascalsTriangle(5)); // [[1], [1, 1], [1, 2, 1], [1, 3, 3, 1], [1, 4, 6, 4, 1]]

Woord Zoeken

Gegeven een 2D-bord en een woord, zoek of het woord bestaat in het raster. Het woord kan worden geconstrueerd uit letters van opeenvolgend aangrenzende cellen, waarbij 'aangrenzend' cellen horizontaal of verticaal naast elkaar zijn.

Uitleg: Dit vereist een Depth First Search (DFS) met backtracking. Itereer door elke cel. Als een cel overeenkomt met de eerste letter van het woord, start dan een DFS. De DFS-functie controleert buren, en zorgt ervoor dat ze overeenkomen met de volgende letter en niet zijn bezocht in het huidige pad. Als een pad mislukt, backtrack dan door de cel ongedaan te maken.

function exist(board, word) { const rows = board.length; const cols = board[0].length; function dfs(r, c, index) { if (index === word.length) return true; // Woord gevonden if (r < 0 || c < 0 || r >= rows || c >= cols || board[r][c] !== word[index]) { return false; } const temp = board[r][c]; board[r][c] = '#'; // Markeren als bezocht const found = dfs(r + 1, c, index + 1) || dfs(r - 1, c, index + 1) || dfs(r, c + 1, index + 1) || dfs(r, c - 1, index + 1); board[r][c] = temp; // Backtrack return found; } for (let r = 0; r < rows; r++) { for (let c = 0; c < cols; c++) { if (board[r][c] === word[0] && dfs(r, c, 0)) { return true; } } } return false; } const board = [['A','B','C','E'],['S','F','C','S'],['A','D','E','E']]; console.log(exist(board, 'ABCCED')); // true console.log(exist(board, 'SEE')); // true console.log(exist(board, 'ABCB')); // false

Minimum Venster Substring

Gegeven twee strings s en t, zoek het minimale venster in s dat alle tekens in t bevat. Als er geen dergelijk venster in s is dat alle tekens in t dekt, retourneer dan de lege string.

Uitleg: Gebruik een schuifvensterbenadering met twee aanwijzers (left en right) en hash-maps. Eén map slaat de benodigde aantal tekens van t op. Een andere map slaat de aantal in het huidige venster op. Breid het venster uit met right. Zodra het venster alle benodigde tekens bevat, probeer het te verkleinen met left om het minimaal mogelijke venster te vinden.

function minWindow(s, t) { if (!t || !s || s.length < t.length) return ""; const tMap = {}; for (const char of t) tMap[char] = (tMap[char] || 0) + 1; let required = Object.keys(tMap).length; let formed = 0; const windowMap = {}; let left = 0; let minLen = Infinity; let result = ""; for (let right = 0; right < s.length; right++) { const char = s[right]; windowMap[char] = (windowMap[char] || 0) + 1; if (tMap[char] && windowMap[char] === tMap[char]) { formed++; } while (left <= right && formed === required) { if (right - left + 1 < minLen) { minLen = right - left + 1; result = s.substring(left, right + 1); } const leftChar = s[left]; windowMap[leftChar]--; if (tMap[leftChar] && windowMap[leftChar] < tMap[leftChar]) { formed--; } left++; } } return result; } console.log(minWindow('ADOBECODEBANC', 'ABC')); // 'BANC'

Gekoppelde Lijst Omkeren

Gegeven de head van een enkel gekoppelde lijst, keer de lijst om en retourneer de omgekeerde lijst.

Uitleg: Je moet de lijst doorlopen en de 'next'-aanwijzer van elk knooppunt wijzigen om naar het vorige knooppunt te wijzen. Houd tijdens de iteratie de 'previous', 'current' en 'next' knooppunten bij.

class ListNode { constructor(val = 0, next = null) { this.val = val; this.next = next; } } function reverseList(head) { let prev = null; let current = head; let next = null; while (current !== null) { next = current.next; // Sla het volgende knooppunt op current.next = prev; // Keer de aanwijzer van het huidige knooppunt om prev = current; // Verplaats prev één stap vooruit current = next; // Verplaats current één stap vooruit } return prev; // Nieuwe head is de laatste 'prev' } // Voorbeeld: 1->2->3->4->5 wordt 5->4->3->2->1

Cyclus Detecteren in Gekoppelde Lijst

Gegeven head, de kop van een gekoppelde lijst, bepaal of de gekoppelde lijst een cyclus bevat.

Uitleg: Gebruik het 'Floyd's Tortoise and Hare'-algoritme. Gebruik twee aanwijzers, één die één stap tegelijk beweegt (slow) en één die twee stappen tegelijk beweegt (fast). Als er een cyclus is, zal de fast-aanwijzer uiteindelijk de slow-aanwijzer inhalen.

class ListNode { constructor(val = 0, next = null) { this.val = val; this.next = next; } } function hasCycle(head) { if (!head || !head.next) return false; let slow = head; let fast = head.next; while (slow !== fast) { if (!fast || !fast.next) return false; // Einde bereikt, geen cyclus slow = slow.next; fast = fast.next.next; } return true; // Aanwijzers ontmoetten elkaar, cyclus bestaat } // Voorbeeld: 3->2->0->-4 waarbij -4 terugwijst naar 2. hasCycle retourneert true.

Object.create Implementeren

Implementeer een functie die het gedrag van Object.create(proto) nabootst.

Uitleg: Object.create creëert een nieuw object, waarbij een bestaand object wordt gebruikt als het prototype van het nieuw gecreëerde object. Je kunt dit bereiken door een tijdelijke constructorfunctie te maken, het prototype ervan in te stellen op de invoer proto, en vervolgens een nieuwe instantie ervan te retourneren.

function myObjectCreate(proto) { if (typeof proto !== 'object' && typeof proto !== 'function') { if (proto !== null) { throw new TypeError('Object prototype may only be an Object or null: ' + proto); } } function F() {} F.prototype = proto; return new F(); } const person = { isHuman: false, printIntroduction: function() { console.log(`Mijn naam is ${this.name}. Ben ik menselijk? ${this.isHuman}`); } }; const me = myObjectCreate(person); me.name = 'Matthew'; me.isHuman = true; me.printIntroduction(); // Mijn naam is Matthew. Ben ik menselijk? true console.log(Object.getPrototypeOf(me) === person); // true

Wat is Hoisting?

Leg hoisting uit in JavaScript en geef een voorbeeld.

Uitleg: Hoisting is het standaardgedrag van JavaScript om declaraties naar de top van de huidige scope (globaal of functie) te verplaatsen vóór code-uitvoering. Variabeledeclaraties (var) worden gehoist en geïnitialiseerd met undefined. Functiedeclaraties worden volledig gehoist (zowel naam als body). let en const worden gehoist maar niet geïnitialiseerd, wat leidt tot een Temporal Dead Zone.

console.log(myVar); // undefined (var is gehoist en geïnitialiseerd met undefined) // console.log(myLet); // ReferenceError: Kan 'myLet' niet benaderen vóór initialisatie (TDZ) myFunc(); // 'Hallo!' (Functie declaratie is volledig gehoist) var myVar = 'Ik ben een var'; let myLet = 'Ik ben een let'; function myFunc() { console.log('Hallo!'); }

Leg Event Bubbling en Capturing uit

Leg Event Bubbling en Capturing uit in de context van het DOM.

Uitleg: Dit zijn twee fasen van gebeurtenispropagatie in het HTML DOM. Capturing Phase: De gebeurtenis reist van het window naar het doelelement. Bubbling Phase: De gebeurtenis reist van het doelelement terug naar het window. Standaard worden de meeste gebeurtenishandlers geregistreerd tijdens de bubbling fase. Je kunt addEventListener(type, listener, useCapture) gebruiken met useCapture = true om gebeurtenissen tijdens de capturing fase af te handelen.

// In een HTML-structuur: <div><p><span>Klik Mij</span></p></div> // Als je op de <span> klikt: // Capturing: window -> document -> html -> body -> div -> p -> span // Bubbling: span -> p -> div -> body -> html -> document -> window /* JS Voorbeeld div.addEventListener('click', () => console.log('Div geklikt'), true); // Capturing p.addEventListener('click', () => console.log('P geklikt'), false); // Bubbling span.addEventListener('click', () => console.log('Span geklikt'), false); // Bubbling // Uitvoer zou zijn: Div geklikt, Span geklikt, P geklikt */

JSON.parse handmatig implementeren (basis)

Probeer een zeer eenvoudige versie van JSON.parse te implementeren (verwerk eenvoudige objecten, arrays, strings, nummers).

Uitleg: Dit is een zeer complexe taak in zijn geheel, maar voor een live-codeersessie kan je gevraagd worden om een zeer vereenvoudigde subset te verwerken. Je zou de string moeten parsen, objectgrenzen {} en arraygrenzen [] moeten identificeren, sleutel-waarde-paren (key: value), en basisgegevenstypen. eval of new Function kunnen dit cheaten, maar zijn gevaarlijk. Een echte parser zou een lexer/tokenizer en parser gebruiken.

function basicJsonParse(jsonString) { // WAARSCHUWING: Het gebruik van new Function is onveilig, net als eval. // Dit is een vereenvoudigd, ONVEILIG voorbeeld alleen ter demonstratie. // Een echte implementatie vereist een juiste parser. try { return (new Function('return ' + jsonString))(); } catch (e) { throw new SyntaxError('Ongeldige JSON: ' + e.message); } } console.log(basicJsonParse('{"a": 1, "b": ["x", "y"]}')); // { a: 1, b: ['x', 'y'] }

Een Diep Geneste Array Afvlakken

Schrijf een functie om een diep geneste array af te vlakken.

Uitleg: In tegenstelling tot de vorige flatten (één niveau), moet deze elk niveau van nesting verwerken. Recursie is een natuurlijke pasvorm. Itereer door de array. Als een element een array is, roep dan recursief flatten aan en concateneer het resultaat. Anders, voeg het element toe.

function deepFlatten(arr) { let flattened = []; arr.forEach(item => { if (Array.isArray(item)) { flattened = flattened.concat(deepFlatten(item)); } else { flattened.push(item); } }); return flattened; // ES2019+ biedt een veel eenvoudigere manier: // return arr.flat(Infinity); } console.log(deepFlatten([1, [2, [3, 4], 5], 6])); // [1, 2, 3, 4, 5, 6]

Een Trie Implementeren (Prefix Boom)

Implementeer een Trie-datastructuur met de methoden insert, search en startsWith.

Uitleg: Een Trie is een boomachtige datastructuur die wordt gebruikt om efficiënt sleutels op te slaan en op te halen in een dataset van strings. Elk knooppunt representeert een karakter, en paden vanaf de root representeren woorden of voorvoegsels.

class TrieNode { constructor() { this.children = {}; this.isEndOfWord = false; } } class Trie { constructor() { this.root = new TrieNode(); } insert(word) { let node = this.root; for (const char of word) { if (!node.children[char]) { node.children[char] = new TrieNode(); } node = node.children[char]; } node.isEndOfWord = true; } search(word) { let node = this.root; for (const char of word) { if (!node.children[char]) return false; node = node.children[char]; } return node.isEndOfWord; } startsWith(prefix) { let node = this.root; for (const char of prefix) { if (!node.children[char]) return false; node = node.children[char]; } return true; } } const trie = new Trie(); trie.insert('appel'); console.log(trie.search('appel')); // true console.log(trie.search('app')); // false console.log(trie.startsWith('app')); // true

Een Array Herschikken (Fisher-Yates)

Schrijf een functie om een array ter plaatse te herschikken met behulp van het Fisher-Yates (of Knuth) algoritme.

Uitleg: Het Fisher-Yates algoritme doorloopt een array van het laatste element naar het eerste. In elke iteratie verwisselt het het huidige element met een willekeurig gekozen element van het begin van de array tot het huidige element (inclusief).

function shuffleArray(arr) { for (let i = arr.length - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); [arr[i], arr[j]] = [arr[j], arr[i]]; // Verwissel elementen } return arr; } console.log(shuffleArray([1, 2, 3, 4, 5])); // bijv. [3, 5, 1, 2, 4]

Functies Componeren

Implementeer een compose-functie die meerdere functies aanneemt en een nieuwe functie retourneert die ze van rechts naar links toepast.

Uitleg: Functiecompositie (f ∘ g)(x) = f(g(x)) past de ene functie toe op het resultaat van de andere. Een algemene compose(f, g, h) zou f(g(h(x))) betekenen. Gebruik reduceRight voor een elegante implementatie.

function compose(...funcs) { return function(initialArg) { return funcs.reduceRight((acc, func) => func(acc), initialArg); }; } const add5 = x => x + 5; const multiply3 = x => x * 3; const subtract2 = x => x - 2; const composedFunc = compose(subtract2, multiply3, add5); console.log(composedFunc(10)); // (10 + 5) * 3 - 2 = 15 * 3 - 2 = 45 - 2 = 43

Functies Pijpen

Implementeer een pipe-functie die meerdere functies aanneemt en een nieuwe functie retourneert die ze van links naar rechts toepast.

Uitleg: Vergelijkbaar met compose, maar de toepassingsvolgorde is omgekeerd: pipe(f, g, h) betekent h(g(f(x))). Gebruik reduce voor implementatie.

function pipe(...funcs) { return function(initialArg) { return funcs.reduce((acc, func) => func(acc), initialArg); }; } const add5 = x => x + 5; const multiply3 = x => x * 3; const subtract2 = x => x - 2; const pipedFunc = pipe(add5, multiply3, subtract2); console.log(pipedFunc(10)); // (10 + 5) * 3 - 2 = 15 * 3 - 2 = 45 - 2 = 43

Muntwissel Probleem

Gegeven een array van muntwaarden en een bedrag, zoek het minimum aantal munten om dat bedrag te maken. Ga uit van een oneindige voorraad van elke munt.

Uitleg: Dit is een klassiek dynamisch programmeerprobleem. Maak een array dp aan waarbij dp[i] het minimum aantal benodigde munten voor bedrag i opslaat. dp[i] = min(dp[i - munt]) + 1 voor alle munten.

function coinChange(coins, amount) { const dp = new Array(amount + 1).fill(Infinity); dp[0] = 0; for (let i = 1; i <= amount; i++) { for (const coin of coins) { if (i - coin >= 0) { dp[i] = Math.min(dp[i], dp[i - coin] + 1); } } } return dp[amount] === Infinity ? -1 : dp[amount]; } console.log(coinChange([1, 2, 5], 11)); // 3 (5 + 5 + 1) console.log(coinChange([2], 3)); // -1

Laagst Gemeenschappelijke Voorouder (BST)

Gegeven een binaire zoekboom (BST), vind de laagst gemeenschappelijke voorouder (LCA) van twee gegeven knooppunten in de BST.

Uitleg: De LCA is het diepst gelegen knooppunt dat beide gegeven knooppunten als afstammelingen heeft. In een BST kun je deze vinden door vanaf de root te traverseren. Als beide knooppunten kleiner zijn dan het huidige knooppunt, ga dan naar links. Als beide groter zijn, ga dan naar rechts. Als de ene kleiner is en de andere groter (of een van de knooppunten het huidige knooppunt is), dan is het huidige knooppunt de LCA.

class Node { constructor(val) { this.val = val; this.left = this.right = null; } } function lowestCommonAncestor(root, p, q) { let current = root; while (current) { if (p.val > current.val && q.val > current.val) { current = current.right; } else if (p.val < current.val && q.val < current.val) { current = current.left; } else { return current; // LCA gevonden } } return null; // Zou niet mogen gebeuren in een geldige BST met p en q } // Voorbeeld: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 8 => LCA is 6

Binaire Boom Serialiseren en Deserialiseren

Ontwerp een algoritme om een binaire boom te serialiseren en te deserialiseren.

Uitleg: Serialisatie converteert een boom naar een string of array. Deserialisatie reconstrueert de boom. Een veelgebruikte manier is Pre-order Traversal (DFS). Gebruik een speciale markering (bijv. '#') voor null-knooppunten om de structuur te behouden.

class Node { constructor(val) { this.val = val; this.left = this.right = null; } } function serialize(root) { const values = []; function dfs(node) { if (!node) { values.push('#'); return; } values.push(node.val); dfs(node.left); dfs(node.right); } dfs(root); return values.join(','); } function deserialize(data) { const values = data.split(','); let index = 0; function buildTree() { if (values[index] === '#') { index++; return null; } const node = new Node(parseInt(values[index])); index++; node.left = buildTree(); node.right = buildTree(); return node; } return buildTree(); } // Voorbeeld: 1 -> [2, 3] -> [null, null, 4, 5] // Geserialiseerd: '1,2,#,#,3,4,#,#,5,#,#'

SetInterval Implementeren met SetTimeout

Implementeer een functie mySetInterval die setInterval nabootst, maar recursief setTimeout gebruikt.

Uitleg: setInterval voert een functie herhaaldelijk uit elke N milliseconden. Je kunt dit bereiken door een functie zichzelf te laten aanroepen met setTimeout na elke uitvoering. Je hebt ook een manier nodig om het te wissen (myClearInterval).

function mySetInterval(callback, delay, ...args) { const interval = { timerId: null }; function run() { interval.timerId = setTimeout(() => { callback.apply(this, args); run(); // Plan de volgende oproep }, delay); } run(); return interval; // Retourneer een object om wissen mogelijk te maken } function myClearInterval(interval) { clearTimeout(interval.timerId); } // Voorbeeld van gebruik: let count = 0; const intervalId = mySetInterval(() => { console.log(`Hallo: ${++count}`); if (count === 3) myClearInterval(intervalId); }, 500);

Graaf Traversal (BFS & DFS)

Implementeer Breadth-First Search (BFS) en Depth-First Search (DFS) voor een gegeven graaf (aangrenzendelijst-representatie).

Uitleg: BFS verkent buren eerst (met behulp van een wachtrij), terwijl DFS zo ver mogelijk langs elke tak verkent (met behulp van een stack of recursie). Houd bezochte knooppunten bij om cycli en overbodig werk te voorkomen.

const graph = { A: ['B', 'C'], B: ['D'], C: ['E'], D: [], E: ['F'], F: [] }; function bfs(graph, startNode) { const queue = [startNode]; const visited = new Set(); const result = []; visited.add(startNode); while (queue.length > 0) { const node = queue.shift(); result.push(node); for (const neighbor of graph[node]) { if (!visited.has(neighbor)) { visited.add(neighbor); queue.push(neighbor); } } } return result; } function dfs(graph, startNode) { const stack = [startNode]; const visited = new Set(); const result = []; while (stack.length > 0) { const node = stack.pop(); if (!visited.has(node)) { visited.add(node); result.push(node); // Voeg buren in omgekeerde volgorde toe om ze later in volgorde te verwerken (optioneel) for (let i = graph[node].length - 1; i >= 0; i--) { stack.push(graph[node][i]); } } } return result; } console.log('BFS:', bfs(graph, 'A')); // ['A', 'B', 'C', 'D', 'E', 'F'] console.log('DFS:', dfs(graph, 'A')); // ['A', 'B', 'D', 'C', 'E', 'F']

Afbeelding Roteren (Matrix)

Je krijgt een n x n 2D-matrix die een afbeelding voorstelt. Draai de afbeelding 90 graden (met de klok mee) ter plaatse.

Uitleg: Een veelvoorkomende manier om dit te bereiken is door eerst de matrix te transponeren (matrix[i][j] verwisselen met matrix[j][i]) en vervolgens elke rij om te keren.

function rotate(matrix) { const n = matrix.length; // Transponeren for (let i = 0; i < n; i++) { for (let j = i; j < n; j++) { [matrix[i][j], matrix[j][i]] = [matrix[j][i], matrix[i][j]]; } } // Keer elke rij om for (let i = 0; i < n; i++) { matrix[i].reverse(); } return matrix; } const matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]; console.log(rotate(matrix)); // [[7, 4, 1], [8, 5, 2], [9, 6, 3]]

Spiraalmatrix Doorloop

Gegeven een m x n matrix, retourneer alle elementen van de matrix in spiraalvormige volgorde.

Uitleg: Gebruik vier aanwijzers om de grenzen te definiëren: top, bottom, left, right. Doorloop de bovenste rij, dan de rechterkolom, dan de onderste rij, dan de linkerkolom, waarbij de grenzen na elke doorloop worden verkleind, totdat left right kruist of top bottom kruist.

function spiralOrder(matrix) { if (!matrix || matrix.length === 0) return []; const rows = matrix.length, cols = matrix[0].length; const result = []; let top = 0, bottom = rows - 1, left = 0, right = cols - 1; while (top <= bottom && left <= right) { for (let i = left; i <= right; i++) result.push(matrix[top][i]); top++; for (let i = top; i <= bottom; i++) result.push(matrix[i][right]); right--; if (top <= bottom) { for (let i = right; i >= left; i--) result.push(matrix[bottom][i]); bottom--; } if (left <= right) { for (let i = bottom; i >= top; i--) result.push(matrix[i][left]); left++; } } return result; } const matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]; console.log(spiralOrder(matrix)); // [1, 2, 3, 6, 9, 8, 7, 4, 5]

Zet Matrix Nullen

Gegeven een m x n matrix, als een element 0 is, zet dan de hele rij en kolom op 0. Doe dit ter plaatse.

Uitleg: Een naïeve benadering vereist O(m*n) extra ruimte. Om het in O(1) ruimte (of O(m+n)) te doen, kunt u de eerste rij en eerste kolom gebruiken om informatie op te slaan over welke rijen/kolommen moeten worden genuld. U hebt afzonderlijke vlaggen nodig voor of de eerste rij/kolom zelf moeten worden genuld.

function setZeroes(matrix) { const rows = matrix.length, cols = matrix[0].length; let firstColZero = false; for (let r = 0; r < rows; r++) { if (matrix[r][0] === 0) firstColZero = true; for (let c = 1; c < cols; c++) { if (matrix[r][c] === 0) { matrix[r][0] = 0; matrix[0][c] = 0; } } } for (let r = rows - 1; r >= 0; r--) { for (let c = cols - 1; c >= 1; c--) { if (matrix[r][0] === 0 || matrix[0][c] === 0) { matrix[r][c] = 0; } } if (firstColZero) matrix[r][0] = 0; } return matrix; } // Voorbeeld: [[1,1,1],[1,0,1],[1,1,1]] => [[1,0,1],[0,0,0],[1,0,1]]

Implementeer Promise.race

Implementeer een functie die zich gedraagt als Promise.race.

Uitleg: Promise.race neemt een array van promises en retourneert een enkele promise. Deze geretourneerde promise wordt afgehandeld (resolved of rejected) zodra een van de invoerpromises is afgehandeld, met de waarde of reden van die promise.

function myPromiseRace(promises) { return new Promise((resolve, reject) => { if (!promises || promises.length === 0) { return; // Of resolve/reject afhankelijk van specificatie } promises.forEach(promise => { Promise.resolve(promise).then(resolve, reject); }); }); } const p1 = new Promise((resolve) => setTimeout(resolve, 500, 'one')); const p2 = new Promise((resolve) => setTimeout(resolve, 100, 'two')); myPromiseRace([p1, p2]).then(value => console.log(value)); // 'two'

Singleton Patroon

Implementeer het Singleton ontwerppatroon in JavaScript.

Uitleg: Het Singleton patroon zorgt ervoor dat een klasse slechts één instantie heeft en biedt een globaal toegangspunt daartoe. Dit kan worden bereikt met behulp van een closure om de instantie vast te houden.

const Singleton = (function() { let instance; function createInstance() { // Privé methoden en variabelen const privateVar = 'Ik ben privé'; function privateMethod() { console.log('Privé'); } return { // Publieke methoden en variabelen publicVar: 'Ik ben openbaar', publicMethod: function() { console.log('Openbaar'); }, getPrivate: function() { return privateVar; } }; } return { getInstance: function() { if (!instance) { instance = createInstance(); } return instance; } }; })(); const instance1 = Singleton.getInstance(); const instance2 = Singleton.getInstance(); console.log(instance1 === instance2); // true console.log(instance1.getPrivate()); // 'Ik ben privé'

Valideer IP-adres

Schrijf een functie om te controleren of een gegeven string een geldig IPv4- of IPv6-adres is.

Uitleg: Controleer voor IPv4 op 4 getallen (0-255) gescheiden door punten, zonder voorloopnullen (behalve voor '0' zelf). Controleer voor IPv6 op 8 groepen van 1-4 hexadecimale cijfers gescheiden door dubbele punten (behandel variaties zoals '::'). Regex kan worden gebruikt, maar handmatige parsing is vaak duidelijker voor interviews.

function validIPAddress(IP) { function isIPv4(s) { const parts = s.split('.'); if (parts.length !== 4) return false; for (const part of parts) { if (!/^[0-9]+$/.test(part)) return false; if (part.length > 1 && part.startsWith('0')) return false; const num = parseInt(part); if (num < 0 || num > 255) return false; } return true; } function isIPv6(s) { const parts = s.split(':'); if (parts.length !== 8) return false; // Vereenvoudigd: Geen '::' for (const part of parts) { if (part.length < 1 || part.length > 4) return false; if (!/^[0-9a-fA-F]+$/.test(part)) return false; } return true; } if (IP.includes('.')) return isIPv4(IP) ? 'IPv4' : 'Geen van beide'; if (IP.includes(':')) return isIPv6(IP) ? 'IPv6' : 'Geen van beide'; return 'Geen van beide'; } console.log(validIPAddress('172.16.254.1')); // IPv4 console.log(validIPAddress('2001:0db8:85a3:0000:0000:8a2e:0370:7334')); // IPv6 (Vereenvoudigd) console.log(validIPAddress('256.256.256.256')); // Geen van beide

Zoek Piekelement

Een piekelement is een element dat strikt groter is dan zijn buren. Gegeven een invoerarray nums, zoek een piekelement en retourneer de index ervan.

Uitleg: Aangezien elke piek volstaat, en nums[-1] en nums[n] als -Infinity worden beschouwd, kunt u een gewijzigde binaire zoekopdracht gebruiken. Als nums[mid] kleiner is dan nums[mid+1], moet er een piek rechts bestaan. Anders moet er een piek links bestaan (of mid zelf is een piek).

function findPeakElement(nums) { let left = 0; let right = nums.length - 1; while (left < right) { let mid = Math.floor((left + right) / 2); if (nums[mid] < nums[mid + 1]) { left = mid + 1; // Piek is naar rechts } else { right = mid; // Piek is mid of naar links } } return left; // 'left' zal de index van een piek zijn } console.log(findPeakElement([1, 2, 3, 1])); // 2 (index van 3) console.log(findPeakElement([1, 2, 1, 3, 5, 6, 4])); // 5 (index van 6) of 1 (index van 2)

Bits tellen

Gegeven een geheel getal n, retourneer een array ans van lengte n + 1 zodanig dat voor elke i (0 <= i <= n), ans[i] het aantal 1'en is in de binaire representatie van i.

Uitleg: U kunt dit oplossen met dynamisch programmeren. Let op de relatie: dp[i] = dp[i >> 1] + (i & 1). Het aantal 1'en in i is het aantal 1'en in i rechts geschoven (d.w.z. i/2), plus 1 als i oneven is.

function countBits(n) { const dp = new Array(n + 1).fill(0); for (let i = 1; i <= n; i++) { dp[i] = dp[i >> 1] + (i & 1); // Of: dp[i] = dp[i & (i - 1)] + 1; (Verwijdert de laatst gezette bit) } return dp; } console.log(countBits(5)); // [0, 1, 1, 2, 1, 2]

Macht van Twee

Gegeven een geheel getal n, retourneer true als het een macht van twee is. Anders, retourneer false.

Uitleg: Een macht van twee heeft in binaire representatie precies één '1' bit (bijv. 1=1, 2=10, 4=100, 8=1000). Een slimme bitwise truc is om te controleren of n > 0 en (n & (n - 1)) === 0. Als n een macht van twee is, zullen alle bits onder die '1' in n-1 op '1' staan. Het uitvoeren van een bitwise AND daarop resulteert in 0.

function isPowerOfTwo(n) { return n > 0 && (n & (n - 1)) === 0; } console.log(isPowerOfTwo(16)); // true console.log(isPowerOfTwo(1)); // true console.log(isPowerOfTwo(18)); // false

Intervallen samenvoegen

Gegeven een array van intervallen waar intervals[i] = [starti, endi], voeg alle overlappende intervallen samen en retourneer een array van de niet-overlappende intervallen.

Uitleg: Sorteer eerst de intervallen op hun starttijden. Loop vervolgens door de gesorteerde intervallen. Als het huidige interval overlapt met het laatste interval in de resultaatlijst, voeg ze dan samen door de eindtijd van het laatste interval bij te werken. Anders voegt u het huidige interval toe aan de resultaatlijst.

function mergeIntervals(intervals) { if (intervals.length === 0) return []; intervals.sort((a, b) => a[0] - b[0]); const merged = [intervals[0]]; for (let i = 1; i < intervals.length; i++) { const last = merged[merged.length - 1]; const current = intervals[i]; if (current[0] <= last[1]) { // Overlap: samenvoegen last[1] = Math.max(last[1], current[1]); } else { // Geen overlap: nieuw interval toevoegen merged.push(current); } } return merged; } console.log(mergeIntervals([[1, 3], [2, 6], [8, 10], [15, 18]])); // [[1, 6], [8, 10], [15, 18]]

Woordafbreking

Gegeven een string s en een woordenboek van strings wordDict, retourneer true als s kan worden gesegmenteerd in een door spaties gescheiden reeks van één of meer woorden uit het woordenboek.

Uitleg: Gebruik dynamisch programmeren. Maak een booleaanse array dp waarbij dp[i] true is als de substring s[0...i-1] kan worden gesegmenteerd. dp[i] is true als er een j < i bestaat zodanig dat dp[j] true is en de substring s[j...i-1] zich in het woordenboek bevindt.

function wordBreak(s, wordDict) { const wordSet = new Set(wordDict); const n = s.length; const dp = new Array(n + 1).fill(false); dp[0] = true; // Basiscases: lege string for (let i = 1; i <= n; i++) { for (let j = 0; j < i; j++) { if (dp[j] && wordSet.has(s.substring(j, i))) { dp[i] = true; break; } } } return dp[n]; } console.log(wordBreak('leetcode', ['leet', 'code'])); // true console.log(wordBreak('applepenapple', ['apple', 'pen'])); // true console.log(wordBreak('catsandog', ['cats', 'dog', 'sand', 'and', 'cat'])); // false

Implementeer Array.prototype.flat

Implementeer uw eigen versie van Array.prototype.flat, die een nieuwe array maakt met alle sub-array-elementen recursief samengevoegd tot een gespecificeerde diepte.

Uitleg: Gebruik recursie. Loop door de array. Als een element een array is en de huidige diepte minder is dan de gespecificeerde diepte, roept u er recursief flatten op aan. Anders duwt u het element. Behandel de standaarddiepte van 1.

function myFlat(arr, depth = 1) { let flattened = []; arr.forEach(item => { if (Array.isArray(item) && depth > 0) { flattened.push(...myFlat(item, depth - 1)); } else { flattened.push(item); } }); return flattened; } console.log(myFlat([1, [2, [3, 4], 5], 6])); // [1, 2, [3, 4], 5, 6] console.log(myFlat([1, [2, [3, 4], 5], 6], 2)); // [1, 2, 3, 4, 5, 6]

Woorden in een string omkeren

Gegeven een invoerstring s, keer de volgorde van de woorden om.

Uitleg: Een 'woord' wordt gedefinieerd als een reeks niet-spatiekarakters. Trim de string, splits deze op één of meer spaties, filter eventuele lege strings eruit die voortvloeien uit meerdere spaties, keer de array om en voeg deze weer samen met enkele spaties.

function reverseWords(s) { return s.trim().split(/\s+/).reverse().join(' '); } console.log(reverseWords('the sky is blue')); // 'blue is sky the' console.log(reverseWords(' hello world ')); // 'world hello' console.log(reverseWords('a good example')); // 'example good a'

Query String Parser

Schrijf een functie om een URL-query string te parsen in een object.

Uitleg: Splits de string op &. Voor elk deel, splits op = om de sleutel en waarde te krijgen. Decodeer URI-componenten voor zowel sleutel als waarde. Behandel gevallen waarin een sleutel meerdere keren kan voorkomen (maak een array) of geen waarde heeft.

function parseQueryString(query) { if (query.startsWith('?')) { query = query.substring(1); } const result = {}; if (!query) return result; query.split('&').forEach(pair => { const [key, value] = pair.split('='); const decodedKey = decodeURIComponent(key); const decodedValue = value !== undefined ? decodeURIComponent(value) : true; if (result.hasOwnProperty(decodedKey)) { if (Array.isArray(result[decodedKey])) { result[decodedKey].push(decodedValue); } else { result[decodedKey] = [result[decodedKey], decodedValue]; } } else { result[decodedKey] = decodedValue; } }); return result; } console.log(parseQueryString('?foo=bar&baz=qux&baz=quux&corge')); // { foo: 'bar', baz: ['qux', 'quux'], corge: true }

Gebruik Proxy voor Validatie

Demonstreer hoe u een Proxy-object kunt gebruiken voor validatie van objecteigenschappen.

Uitleg: Een Proxy stelt u in staat om bewerkingen op een object te onderscheppen en aan te passen. Gebruik de set-trap om waarden te valideren voordat u ze aan een eigenschap toewijst. Werp een fout als de validatie mislukt.

const validator = { set: function(obj, prop, value) { if (prop === 'age') { if (!Number.isInteger(value)) { throw new TypeError('De leeftijd is geen geheel getal'); } if (value < 0 || value > 150) { throw new RangeError('De leeftijd lijkt ongeldig'); } } // Standaardgedrag: stel de eigenschap in obj[prop] = value; return true; } }; const person = {}; const personProxy = new Proxy(person, validator); personProxy.age = 30; // OK console.log(personProxy.age); // 30 // personProxy.age = 'dertig'; // Werpt TypeError // personProxy.age = 200; // Werpt RangeError

Vergaderzalen

Gegeven een array van vergadertijdsintervallen [[start1, end1], [start2, end2], ...], bepaal of een persoon alle vergaderingen zou kunnen bijwonen.

Uitleg: Als een persoon alle vergaderingen kan bijwonen, betekent dit dat er geen twee vergaderingen overlappen. Om dit te controleren, sorteert u de intervallen op hun starttijden. Loop vervolgens door de gesorteerde intervallen en controleer of de starttijd van de huidige vergadering vóór de eindtijd van de vorige vergadering ligt. Als dat zo is, is er een overlap en kan de persoon niet alle vergaderingen bijwonen.

function canAttendMeetings(intervals) { if (intervals.length < 2) return true; intervals.sort((a, b) => a[0] - b[0]); for (let i = 1; i < intervals.length; i++) { if (intervals[i][0] < intervals[i - 1][1]) { return false; // Overlap gedetecteerd } } return true; } console.log(canAttendMeetings([[0, 30], [5, 10], [15, 20]])); // false (5<30) console.log(canAttendMeetings([[7, 10], [2, 4]])); // true

Implementeer Promise.any

Implementeer een functie die zich gedraagt als Promise.any.

Uitleg: Promise.any neemt een array van promises en retourneert een enkele promise. Deze promise wordt vervuld zodra een van de invoerpromises wordt vervuld. Als alle invoerpromises worden afgewezen, wordt deze afgewezen met een AggregateError die alle afwijzingsredenen bevat.

function myPromiseAny(promises) { return new Promise((resolve, reject) => { const numPromises = promises.length; if (numPromises === 0) { reject(new AggregateError([], 'Alle promises zijn afgewezen')); return; } let rejectionCount = 0; const errors = []; promises.forEach((promise, index) => { Promise.resolve(promise) .then(resolve) // Oplossen zodra er een wordt vervuld .catch(error => { errors[index] = error; rejectionCount++; if (rejectionCount === numPromises) { reject(new AggregateError(errors, 'Alle promises zijn afgewezen')); } }); }); }); } // Voorbeeld: myPromiseAny([Promise.reject('err1'), Promise.resolve('ok')]) -> 'ok' // Voorbeeld: myPromiseAny([Promise.reject('err1'), Promise.reject('err2')]) -> AggregateError

Waarnemer Patroon

Implementeer het Waarnemer (Pub/Sub) ontwerppatroon in JavaScript.

Uitleg: Het Waarnemer patroon definieert een één-op-veel afhankelijkheid tussen objecten. Wanneer één object (Onderwerp) van staat verandert, worden al zijn afhankelijke objecten (Waarnemers) automatisch op de hoogte gebracht en bijgewerkt. Het Onderwerp onderhoudt een lijst van waarnemers en biedt methoden om ze toe te voegen, te verwijderen en te informeren.

class Subject { constructor() { this.observers = []; } subscribe(observer) { this.observers.push(observer); } unsubscribe(observer) { this.observers = this.observers.filter(obs => obs !== observer); } notify(data) { this.observers.forEach(observer => observer.update(data)); } } class Observer { constructor(name) { this.name = name; } update(data) { console.log(`${this.name} ontvangen: ${data}`); } } const subject = new Subject(); const obs1 = new Observer('Waarnemer 1'); const obs2 = new Observer('Waarnemer 2'); subject.subscribe(obs1); subject.subscribe(obs2); subject.notify('Er is iets gebeurd!'); // Waarnemer 1 ontvangen: Er is iets gebeurd! // Waarnemer 2 ontvangen: Er is iets gebeurd!

Dubbel nummer zoeken

Gegeven een array van integers nums die n + 1 integers bevatten waarbij elke integer in het bereik [1, n] inclusief is, zoek het ene herhaalde nummer.

Uitleg: Aangezien getallen in [1, n] zijn en de arraygrootte n+1 is, garandeert dit ten minste één duplicaat. Dit kan worden toegewezen aan een 'Zoek cyclus in gekoppelde lijst' probleem (Floyd's Tortoise and Hare). Behandel de arraywaarden als aanwijzers: index -> nums[index].

function findDuplicate(nums) { let tortoise = nums[0]; let hare = nums[0]; // Fase 1: Zoek het snijpunt do { tortoise = nums[tortoise]; hare = nums[nums[hare]]; } while (tortoise !== hare); // Fase 2: Zoek de ingang naar de cyclus let ptr1 = nums[0]; let ptr2 = tortoise; while (ptr1 !== ptr2) { ptr1 = nums[ptr1]; ptr2 = nums[ptr2]; } return ptr1; } console.log(findDuplicate([1, 3, 4, 2, 2])); // 2 console.log(findDuplicate([3, 1, 3, 4, 2])); // 3

Basis HTML Sanitizer

Schrijf een basisfunctie om een HTML-string te saneren, waarbij potentieel schadelijke tags (zoals script) worden verwijderd, terwijl veilige tags (zoals p, b) behouden blijven.

Uitleg: Dit is een complex onderwerp en echte wereld sanitizers zijn geavanceerd. Voor een interview kan een basisbenadering het gebruik van Regex inhouden om specifieke tags te verwijderen of alleen een whitelist van tags toe te staan. Dit is niet veilig voor productie.

function basicSanitize(html) { // WAARSCHUWING: Dit is een zeer basis en onveilig voorbeeld. // Gebruik dit NIET in productie. Gebruik een bibliotheek zoals DOMPurify. // Verwijder script tags let sanitized = html.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, ''); // Voorbeeld: Verwijder onclick attributen (zeer basis) sanitized = sanitized.replace(/\s(on\w+)=['"]?[^'"]*['"]?/gi, ''); return sanitized; } console.log(basicSanitize('<p>Hallo <script>alert("XSS")</script> <b onclick="danger()">Wereld</b></p>')); // <p>Hallo <b >Wereld</b></p>

Bewerkingsafstand

Gegeven twee strings word1 en word2, retourneer het minimale aantal bewerkingen (invoegen, verwijderen of vervangen) dat nodig is om word1 om te zetten in word2.

Uitleg: Dit is een klassiek dynamisch programmeringsprobleem (Levenshtein-afstand). Maak een 2D-array dp waarbij dp[i][j] de bewerkingsafstand is tussen de eerste i karakters van word1 en de eerste j karakters van word2. De recursierelatie omvat het overwegen van de kosten van invoegen, verwijderen en vervangen.

function minDistance(word1, word2) { const m = word1.length, n = word2.length; const dp = Array(m + 1).fill(null).map(() => Array(n + 1).fill(0)); for (let i = 0; i <= m; i++) dp[i][0] = i; for (let j = 0; j <= n; j++) dp[0][j] = j; for (let i = 1; i <= m; i++) { for (let j = 1; j <= n; j++) { const cost = word1[i - 1] === word2[j - 1] ? 0 : 1; dp[i][j] = Math.min( dp[i - 1][j] + 1, // Verwijdering dp[i][j - 1] + 1, // Invoeging dp[i - 1][j - 1] + cost // Vervanging/Overeenkomst ); } } return dp[m][n]; } console.log(minDistance('horse', 'ros')); // 3 console.log(minDistance('intention', 'execution')); // 5

Langste stijgende reeks (LIS)

Gegeven een integer array nums, retourneer de lengte van de langste strikt stijgende reeks.

Uitleg: Gebruik dynamisch programmeren. Laat dp[i] de lengte zijn van de LIS eindigend op index i. Om dp[i] te berekenen, zoek alle j < i zodanig dat nums[j] < nums[i], en neem dp[i] = 1 + max(dp[j]). Het uiteindelijke antwoord is de maximale waarde in de dp-array.

function lengthOfLIS(nums) { if (nums.length === 0) return 0; const n = nums.length; const dp = new Array(n).fill(1); let maxLength = 1; for (let i = 1; i < n; i++) { for (let j = 0; j < i; j++) { if (nums[i] > nums[j]) { dp[i] = Math.max(dp[i], dp[j] + 1); } } maxLength = Math.max(maxLength, dp[i]); } return maxLength; } console.log(lengthOfLIS([10, 9, 2, 5, 3, 7, 101, 18])); // 4 (2, 3, 7, 101)

N-Queens Probleem

De N-Queens puzzel is het probleem om N schaakdames op een N×N schaakbord te plaatsen zodat geen twee dames elkaar bedreigen. Gegeven N, retourneer één geldige oplossing (of alle).

Uitleg: Gebruik backtracking. Plaats dames rij voor rij. Voor elke rij, probeer een dame in elke kolom te plaatsen. Controleer vóór het plaatsen of de voorgestelde positie veilig is (niet aangevallen door dames in eerdere rijen). Indien veilig, plaats deze en herhaal voor de volgende rij. Als recursie mislukt, backtrack (verwijder de dame) en probeer de volgende kolom.

function solveNQueens(n) { const results = []; const board = Array(n).fill('.').map(() => Array(n).fill('.')); function isSafe(row, col) { for (let i = 0; i < row; i++) if (board[i][col] === 'Q') return false; // Controleer kolom for (let i = row - 1, j = col - 1; i >= 0 && j >= 0; i--, j--) if (board[i][j] === 'Q') return false; // Controleer diagonaal linksboven for (let i = row - 1, j = col + 1; i >= 0 && j < n; i--, j++) if (board[i][j] === 'Q') return false; // Controleer diagonaal rechtsboven return true; } function backtrack(row) { if (row === n) { results.push(board.map(r => r.join(''))); return; } for (let col = 0; col < n; col++) { if (isSafe(row, col)) { board[row][col] = 'Q'; backtrack(row + 1); board[row][col] = '.'; // Backtrack } } } backtrack(0); return results; } console.log(solveNQueens(4)); // [ [ '.Q..', '...Q', 'Q...', '..Q.' ], [ '..Q.', 'Q...', '...Q', '.Q..' ] ]

Gebruik WeakMap voor privégegevens

Demonstreer hoe u WeakMap kunt gebruiken om privégegevens voor klasse-instanties op te slaan.

Uitleg: WeakMap stelt u in staat om gegevens te koppelen aan een object op een manier die garbage collection niet verhindert als het object niet langer wordt verwezen. Dit is handig voor het maken van 'private' leden in JavaScript-klassen voordat native private fields breed beschikbaar waren of voor specifieke gebruikssituaties.

const privateData = new WeakMap(); class Person { constructor(name, age) { privateData.set(this, { name: name, age: age }); } getName() { return privateData.get(this).name; } getAge() { return privateData.get(this).age; } celebrateBirthday() { privateData.get(this).age++; } } const person = new Person('Alice', 30); console.log(person.getName()); // Alice person.celebrateBirthday(); console.log(person.getAge()); // 31 // console.log(person.name); // undefined - 'name' is geen publieke eigenschap // console.log(privateData.get(person)); // Toegankelijk, maar niet direct via 'person.'

Implementeer Promise.allSettled

Implementeer een functie die zich gedraagt als Promise.allSettled.

Uitleg: Promise.allSettled neemt een array van promises en retourneert een enkele promise. Deze promise wordt vervuld wanneer alle invoerpromises zijn afgehandeld (vervuld of afgewezen). De vervullingswaarde is een array van objecten, die elk het resultaat van één promise beschrijven ({status: 'fulfilled', value: ...} of {status: 'rejected', reason: ...}).

function myPromiseAllSettled(promises) { return new Promise((resolve) => { const numPromises = promises.length; const results = []; let settledCount = 0; if (numPromises === 0) { resolve([]); return; } promises.forEach((promise, index) => { Promise.resolve(promise) .then(value => { results[index] = { status: 'fulfilled', value: value }; }) .catch(reason => { results[index] = { status: 'rejected', reason: reason }; }) .finally(() => { settledCount++; if (settledCount === numPromises) { resolve(results); } }); }); }); } // Voorbeeld: myPromiseAllSettled([Promise.resolve(1), Promise.reject('err')]) // -> [{status: 'fulfilled', value: 1}, {status: 'rejected', reason: 'err'}]

Zoek de mediaan van twee gesorteerde arrays

Gegeven twee gesorteerde arrays nums1 en nums2 van respectievelijk grootte m en n, retourneer de mediaan van de twee gesorteerde arrays.

Uitleg: Dit is een moeilijk probleem dat vaak wordt opgelost met een binaire zoekmethode op de kleinere array. Het doel is om beide arrays zo te partitioneren dat alle elementen aan de linkerkant kleiner zijn dan of gelijk zijn aan alle elementen aan de rechterkant, en het aantal elementen aan de linkerkant gelijk is aan (of één meer is dan) de rechterkant. De mediaan kan dan worden berekend uit de grenselementen.

function findMedianSortedArrays(nums1, nums2) { if (nums1.length > nums2.length) [nums1, nums2] = [nums2, nums1]; // Zorg ervoor dat nums1 kleiner is const m = nums1.length, n = nums2.length; let low = 0, high = m; while (low <= high) { const partitionX = Math.floor((low + high) / 2); const partitionY = Math.floor((m + n + 1) / 2) - partitionX; const maxX = (partitionX === 0) ? -Infinity : nums1[partitionX - 1]; const minX = (partitionX === m) ? Infinity : nums1[partitionX]; const maxY = (partitionY === 0) ? -Infinity : nums2[partitionY - 1]; const minY = (partitionY === n) ? Infinity : nums2[partitionY]; if (maxX <= minY && maxY <= minX) { if ((m + n) % 2 === 0) { return (Math.max(maxX, maxY) + Math.min(minX, minY)) / 2; } else { return Math.max(maxX, maxY); } } else if (maxX > minY) { high = partitionX - 1; } else { low = partitionX + 1; } } throw new Error('Invoerarrays zijn niet gesorteerd'); } console.log(findMedianSortedArrays([1, 3], [2])); // 2.0 console.log(findMedianSortedArrays([1, 2], [3, 4])); // 2.5

Sudoku-oplosser

Schrijf een programma om een Sudoku-puzzel op te lossen door de lege cellen in te vullen.

Uitleg: Gebruik backtracking. Zoek een lege cel. Probeer deze in te vullen met de getallen 1 tot en met 9. Controleer voor elk getal of het geldig is (schendt geen Sudoku-regels). Indien geldig, roep de oplosser recursief aan. Als de recursie true retourneert, is een oplossing gevonden. Zo niet, backtrack (reset de cel) en probeer het volgende getal.

function solveSudoku(board) { function isValid(row, col, numStr) { for (let i = 0; i < 9; i++) { if (board[row][i] === numStr) return false; // Controleer rij if (board[i][col] === numStr) return false; // Controleer kolom const boxRow = 3 * Math.floor(row / 3) + Math.floor(i / 3); const boxCol = 3 * Math.floor(col / 3) + i % 3; if (board[boxRow][boxCol] === numStr) return false; // Controleer vak } return true; } function backtrack() { for (let r = 0; r < 9; r++) { for (let c = 0; c < 9; c++) { if (board[r][c] === '.') { for (let n = 1; n <= 9; n++) { const numStr = String(n); if (isValid(r, c, numStr)) { board[r][c] = numStr; if (backtrack()) return true; board[r][c] = '.'; // Backtrack } } return false; // Geen geldig nummer gevonden } } } return true; // Bord opgelost } backtrack(); return board; } // Voorbeeld vereist een 9x9 bord met '.' voor lege cellen.

Implementeer een basis middleware patroon

Implementeer een eenvoudig middleware patroon dat vaak wordt gezien in webframeworks.

Uitleg: Middleware-functies verwerken een verzoek voordat het de uiteindelijke handler bereikt. Elke middleware kan het verzoek/antwoord wijzigen of de controle doorgeven aan de next middleware. Maak een klasse of object dat een lijst met middleware-functies beheert en deze opeenvolgend uitvoert.

class App { constructor() { this.middlewares = []; } use(fn) { this.middlewares.push(fn); } handle(request) { let index = 0; const next = () => { if (index < this.middlewares.length) { const middleware = this.middlewares[index++]; middleware(request, next); } }; next(); } } const app = new App(); app.use((req, next) => { console.log('1: Loggen...'); req.logged = true; next(); }); app.use((req, next) => { console.log('2: Authenticeren...'); req.authed = true; next(); }); app.use((req, next) => { console.log('3: Definitieve handler:', req); }); app.handle({ data: 'enkele aanvraag' }); // 1: Loggen... // 2: Authenticeren... // 3: Definitieve handler: { data: 'enkele aanvraag', logged: true, authed: true }

Cyclus detecteren in gerichte grafiek

Gegeven een gerichte grafiek, schrijf een functie om te bepalen of deze een cyclus bevat.

Uitleg: Gebruik Diepte Eerste Zoek (DFS). Handhaaf twee sets: visiting (knopen die momenteel in de recursiestack staan) en visited (knopen die volledig zijn verkend). Als u tijdens DFS een knoop in de visiting set tegenkomt, hebt u een achterwaartse rand gevonden, wat betekent dat er een cyclus is.

function hasCycleDirected(graph) { const visiting = new Set(); const visited = new Set(); const nodes = Object.keys(graph); function dfs(node) { visiting.add(node); for (const neighbor of graph[node]) { if (visiting.has(neighbor)) return true; // Cyclus gedetecteerd if (!visited.has(neighbor)) { if (dfs(neighbor)) return true; } } visiting.delete(node); visited.add(node); return false; } for (const node of nodes) { if (!visited.has(node)) { if (dfs(node)) return true; } } return false; } const cyclicGraph = { A: ['B'], B: ['C'], C: ['A'] }; const acyclicGraph = { A: ['B', 'C'], B: ['D'], C: ['E'], D: [], E: [] }; console.log(hasCycleDirected(cyclicGraph)); // true console.log(hasCycleDirected(acyclicGraph)); // false

Implementeer Object.freeze gedrag

Leg Object.freeze uit en implementeer een (ondiepe) versie.

Uitleg: Object.freeze voorkomt het toevoegen van nieuwe eigenschappen, het verwijderen van bestaande eigenschappen en het wijzigen van bestaande eigenschappen of hun opsombaarheid, configureerbaarheid of schrijfbaarheid. Het maakt een object onveranderlijk (ondiep). U kunt het implementeren met behulp van Object.preventExtensions en Object.defineProperty om bestaande eigenschappen niet-schrijfbaar en niet-configureerbaar te maken.

function myFreeze(obj) { Object.preventExtensions(obj); // Voorkom het toevoegen van nieuwe eigenschappen Object.keys(obj).forEach(key => { Object.defineProperty(obj, key, { writable: false, configurable: false }); }); return obj; } const obj = { a: 1, b: 2 }; myFreeze(obj); // obj.c = 3; // Faalt stilzwijgend (of werpt een fout in strikte modus) // delete obj.a; // Faalt stilzwijgend (of werpt een fout in strikte modus) // obj.a = 10; // Faalt stilzwijgend (of werpt een fout in strikte modus) console.log(obj); // { a: 1, b: 2 }

Gebruik requestAnimationFrame voor vloeiende animatie

Leg requestAnimationFrame uit en laat zien hoe het te gebruiken voor een eenvoudige animatieloop.

Uitleg: requestAnimationFrame (rAF) vertelt de browser dat u een animatie wilt uitvoeren en vraagt de browser om een gespecificeerde functie aan te roepen om een animatie bij te werken vóór de volgende repaint. Het is efficiënter en vloeiender dan het gebruik van setTimeout of setInterval voor animaties, omdat het synchroniseert met de verversingsfrequentie van de browser en pauzeert wanneer het tabblad niet zichtbaar is.

// HTML: <div id='box' style='width: 50px; height: 50px; background: red; position: absolute; left: 0px;'></div> const box = document.getElementById('box'); let position = 0; let startTime = null; function animate(currentTime) { if (!startTime) startTime = currentTime; const elapsedTime = currentTime - startTime; // Beweeg 100px in 2 seconden (50px/sec) position = (elapsedTime / 2000) * 100; if (position < 200) { // Blijf animeren totdat het 200px bereikt box.style.left = position + 'px'; requestAnimationFrame(animate); } else { box.style.left = '200px'; } } // Start de animatie // requestAnimationFrame(animate); // Uncomment om in browser uit te voeren console.log('Gebruik requestAnimationFrame voor browseranimaties.');

Implementeer Array.prototype.some

Implementeer uw eigen versie van Array.prototype.some.

Uitleg: some test of ten minste één element in de array voldoet aan de test die is geïmplementeerd door de meegeleverde functie. Het retourneert true als het een element vindt waarvoor de callback true retourneert; anders retourneert het false.

function mySome(arr, callback, thisArg) { for (let i = 0; i < arr.length; i++) { if (i in arr) { // Behandel sparse arrays if (callback.call(thisArg, arr[i], i, arr)) { return true; } } } return false; } const arr = [1, 2, 3, 4, 5]; const hasEven = mySome(arr, x => x % 2 === 0); console.log(hasEven); // true

Implementeer Array.prototype.every

Implementeer uw eigen versie van Array.prototype.every.

Uitleg: every test of alle elementen in de array voldoen aan de test die is geïmplementeerd door de meegeleverde functie. Het retourneert true als alle elementen voldoen (of als de array leeg is), en false als een element niet voldoet.

function myEvery(arr, callback, thisArg) { for (let i = 0; i < arr.length; i++) { if (i in arr) { if (!callback.call(thisArg, arr[i], i, arr)) { return false; } } } return true; } const arr1 = [1, 30, 39, 29, 10, 13]; const isBelow40 = myEvery(arr1, x => x < 40); console.log(isBelow40); // true const arr2 = [1, 30, 39, 29, 10, 45]; const isBelow40_2 = myEvery(arr2, x => x < 40); console.log(isBelow40_2); // false

Implementeer Array.prototype.findIndex

Implementeer uw eigen versie van Array.prototype.findIndex.

Uitleg: findIndex retourneert de index van het eerste element in de array dat voldoet aan de meegeleverde testfunctie. Anders retourneert het -1, wat aangeeft dat geen enkel element de test heeft doorstaan.

function myFindIndex(arr, callback, thisArg) { for (let i = 0; i < arr.length; i++) { if (i in arr) { if (callback.call(thisArg, arr[i], i, arr)) { return i; } } } return -1; } const arr = [5, 12, 8, 130, 44]; const isLargeNumber = (element) => element > 13; console.log(myFindIndex(arr, isLargeNumber)); // 3

Wat zijn Web Workers?

Leg uit wat Web Workers zijn en hun primaire gebruiksgeval.

Uitleg: Web Workers zijn een middel voor webinhoud om scripts in achtergrondthreads uit te voeren. Het belangrijkste gebruiksgeval is het ontlasten van langlopende of computationeel intensieve taken van de hoofdthread (die UI-updates en gebruikersinteracties afhandelt), waardoor de UI niet onresponsief wordt of 'bevriest'. Workers communiceren met de hoofdthread via message passing (postMessage en onmessage).

// main.js /* if (window.Worker) { const myWorker = new Worker('worker.js'); myWorker.postMessage([5, 3]); // Verzend gegevens naar worker myWorker.onmessage = function(e) { console.log('Resultaat van worker:', e.data); } } else { console.log('Uw browser ondersteunt geen Web Workers.'); } */ // worker.js /* self.onmessage = function(e) { console.log('Bericht ontvangen van hoofdscript'); const result = e.data[0] * e.data[1]; console.log('Resultaat terugsturen naar hoofdscript'); self.postMessage(result); } */ console.log('Web Workers voeren scripts uit in achtergrondthreads.');

Decodeer Manieren

Een bericht met letters van A-Z kan worden gecodeerd in getallen met behulp van de toewijzing 'A' -> 1, 'B' -> 2, ..., 'Z' -> 26. Gegeven een string s die alleen cijfers bevat, retourneer het aantal manieren om deze te decoderen.

Uitleg: Gebruik dynamisch programmeren. Laat dp[i] het aantal manieren zijn om s[0...i-1] te decoderen. dp[i] hangt af van dp[i-1] (als s[i-1] een geldige 1-cijferige code is) en dp[i-2] (als s[i-2...i-1] een geldige 2-cijferige code is).

function numDecodings(s) { if (!s || s[0] === '0') return 0; const n = s.length; const dp = new Array(n + 1).fill(0); dp[0] = 1; dp[1] = 1; for (let i = 2; i <= n; i++) { const oneDigit = parseInt(s.substring(i - 1, i)); const twoDigits = parseInt(s.substring(i - 2, i)); if (oneDigit >= 1) { dp[i] += dp[i - 1]; } if (twoDigits >= 10 && twoDigits <= 26) { dp[i] += dp[i - 2]; } } return dp[n]; } console.log(numDecodings('12')); // 2 ('AB' of 'L') console.log(numDecodings('226')); // 3 ('BBF', 'BZ', 'VF') console.log(numDecodings('06')); // 0

Bitwise optellen (zonder +)

Schrijf een functie om twee gehele getallen op te tellen zonder de + operator te gebruiken.

Uitleg: Gebruik bitwise operators. De som kan worden berekend als a ^ b (som zonder carry), en de carry kan worden berekend als (a & b) << 1. Herhaal dit proces totdat de carry 0 wordt.

function getSum(a, b) { while (b !== 0) { const carry = (a & b) << 1; // Bereken carry a = a ^ b; // Bereken som zonder carry b = carry; // Carry wordt de nieuwe 'b' } return a; } console.log(getSum(3, 5)); // 8 console.log(getSum(-2, 3)); // 1

Controleer op Palindroom (nummer)

Gegeven een geheel getal x, retourneer true als x een palindroom is, en false anders.

Uitleg: Een negatief getal is geen palindroom. Converteer het getal naar een string en controleer of de string een palindroom is (leest hetzelfde voorwaarts en achterwaarts). Alternatief, keer het getal wiskundig om en vergelijk.

function isPalindromeNumber(x) { if (x < 0) return false; // String benadering // return String(x) === String(x).split('').reverse().join(''); // Wiskundige benadering let original = x; let reversed = 0; while (x > 0) { reversed = reversed * 10 + (x % 10); x = Math.floor(x / 10); } return original === reversed; } console.log(isPalindromeNumber(121)); // true console.log(isPalindromeNumber(-121)); // false console.log(isPalindromeNumber(10)); // false

Fabriekspatroon

Implementeer het Fabriek ontwerppatroon in JavaScript.

Uitleg: Het Fabriekspatroon biedt een interface voor het maken van objecten in een superklasse, maar laat subclasses het type objecten dat zal worden gemaakt wijzigen. Het is handig voor het maken van objecten zonder de exacte klasse te specificeren.

class Car { constructor(options) { this.type = 'Auto'; this.doors = options.doors || 4; } } class Truck { constructor(options) { this.type = 'Vrachtwagen'; this.bedSize = options.bedSize || 'lang'; } } class VehicleFactory { createVehicle(options) { if (options.vehicleType === 'car') { return new Car(options); } else if (options.vehicleType === 'truck') { return new Truck(options); } else { throw new Error('Onbekend voertuigtype'); } } } const factory = new VehicleFactory(); const car = factory.createVehicle({ vehicleType: 'car', doors: 2 }); const truck = factory.createVehicle({ vehicleType: 'truck', bedSize: 'short' }); console.log(car); // Car { type: 'Auto', doors: 2 } console.log(truck); // Truck { type: 'Vrachtwagen', bedSize: 'short' }

Leg Symbol uit en een gebruiksgeval

Leg uit wat Symbol is in JavaScript en geef een gebruiksgeval, zoals het maken van private eigenschappen of unieke objectsleutels.

Uitleg: Symbol is een primitief datatype dat in ES6 is geïntroduceerd. De instanties zijn uniek en onveranderlijk. Ze worden vaak gebruikt als sleutels voor objecteigenschappen om naamconflicten tussen verschillende bibliotheken te voorkomen of om pseudo-private eigenschappen te maken (hoewel niet echt privé, zijn ze niet toegankelijk via typische iteratie of Object.keys).

const _privateName = Symbol('name'); const _privateMethod = Symbol('greet'); class Person { constructor(name) { this[_privateName] = name; } [_privateMethod]() { console.log(`Hallo, mijn naam is ${this[_privateName]}`); } introduce() { this[_privateMethod](); } } const p = new Person('Bob'); p.introduce(); // Hallo, mijn naam is Bob console.log(Object.keys(p)); // [] - Symbolen zijn niet inbegrepen console.log(p[_privateName]); // Bob (Toegankelijk als u het Symbol heeft)

Converteer arguments naar een echte array

Hoe kunt u het arguments object (beschikbaar in niet-pijlfuncties) converteren naar een echte JavaScript-array?

Uitleg: Het arguments object lijkt op een array, maar is er geen; het mist methoden zoals map, filter, etc. U kunt het converteren met:

  1. Array.from(arguments) (ES6)
  2. [...arguments] (ES6 Spread Syntax)
  3. Array.prototype.slice.call(arguments) (Oudere manier)
function sumAll() { // 1. Array.from const args1 = Array.from(arguments); console.log('Gebruikmakend van Array.from:', args1.reduce((a, b) => a + b, 0)); // 2. Spread Syntax const args2 = [...arguments]; console.log('Gebruikmakend van Spread:', args2.reduce((a, b) => a + b, 0)); // 3. Slice const args3 = Array.prototype.slice.call(arguments); console.log('Gebruikmakend van Slice:', args3.reduce((a, b) => a + b, 0)); } sumAll(1, 2, 3, 4); // Gebruikmakend van Array.from: 10 // Gebruikmakend van Spread: 10 // Gebruikmakend van Slice: 10