panzerdp / dmitripavlutin.com-comments

7 stars 0 forks source link

javascript-closures-interview-questions/ #106

Open utterances-bot opened 3 years ago

utterances-bot commented 3 years ago

7 Interview Questions on JavaScript Closures. Can You Answer Them?

Can you answer these 7 interview questions on the closure concept in JavaScript?

https://dmitripavlutin.com/javascript-closures-interview-questions/

noidar commented 3 years ago

Questions 5

for log function to return need incremented value we need to put message variable in the body of log function

 function log() {
     let message = `Count is ${count}`;
    console.log(message);
}

so on every (log) function call a new message variable will be created with latest modification of count variable from outer scope.

nodm commented 3 years ago

Question 4

for (var i = 0; i < 3; i++) {
  setTimeout(function log(j) {
    console.log(j); // What is logged?
  }, 1000, i);
}

Thanks a lot for such useful posts!

panzerdp commented 3 years ago

Questions 5

for log function to return need incremented value we need to put message variable in the body of log function

That's the solution @noidar. Thanks for sharing.

panzerdp commented 3 years ago

Question 4

for (var i = 0; i < 3; i++) {
  setTimeout(function log(j) {
    console.log(j); // What is logged?
  }, 1000, i);
}

Yes, that's a good fix.

Thanks a lot for such useful posts!

You're welcome @nodm!

kanybekd commented 3 years ago

for(let i=0; ....) will resolve scoping issue

panzerdp commented 3 years ago

for(let i=0; ....) will resolve scoping issue

Yes, that's the right fix.

AliN11 commented 3 years ago

7

In Javascript, an argument can hold a value of another argument. So it can be done like this:

function multiply(num1, num2 = num1) {
  return num1 * num2;
}
panzerdp commented 3 years ago

7

In Javascript, an argument can hold a value of another argument. So it can be done like this:

function multiply(num1, num2 = num1) {
  return num1 * num2;
}

Nope, it won't work.

AliN11 commented 3 years ago

7

In Javascript, an argument can hold a value of another argument. So it can be done like this:

function multiply(num1, num2 = num1) {
  return num1 * num2;
}

Nope, it won't work.

Sorry. I didn't read the question well :)) Just wanted to give some solutions.

anediaz commented 3 years ago

Question 5 : rewrite log method

function log() { console.log(Count is ${count}); }

mmejiadeveloper commented 3 years ago

question 5

set count as parameter, we know clousres "remembers" parameters

function createIncrement(count = 0) {
  function increment() {
    count++;
  }

  function log() {
    console.log(`Count is ${count}`);
  }

  return [increment, log];
}

const [increment, log] = createIncrement();
increment();
increment();
increment();
log(); // What is logged?
mmejiadeveloper commented 3 years ago

question 6:

There is no way to directly modify items array because this approach below is exposing only an API to modify an initial array exisiting with const stack = createStack(); so right after this you can only use push or pop. if you want to modify the items directly you would need to create another "instance" or scope with createStack and that's another "row".

function createStack (items= []) {
  function push (par) {
    items.push(par);
  }

  function pop () {
    items.pop();
  }

  function checkItems(){
    console.log(`items status: ${items}`)
  }

  return { push, pop, checkItems}
}

const stack = createStack();

stack.push(10);
stack.push(5);
stack.pop();

stack.checkItems()
Danny-Engelman commented 3 years ago

5

const createIncrement=(c=0)=>[_=>c++,_=>console.log("Count is "+c)];

const [increment, log] = createIncrement();
increment(); 
increment(); 
increment(); 
log(); // What is logged?
mmejiadeveloper commented 3 years ago

no second param? ok then return a closure remembering the first argument and expecting to multiply with a second one the second call. to avoid NaN value just default to 1 if there is not num2 param... again...

function multiply(num1, num2= null) {
  if (!num2) return num2 => num1 * (num2 || 1)
  return num1 * num2
}

const n1 = multiply(2, 2);
const n2 = multiply(3)

console.log(n1, n2(5))

thanks a lot for this post, didnt think i would get nailed into this. greetings from Colombia :colombia:

Danny-Engelman commented 3 years ago

7

const multiply = (a , b = c => a * c ) => typeof b == 'function' ? b : a * b;

multiply(4,5)
multiply(3,3)
const double = multiply(2);
double(5);
double(11);
Danny-Engelman commented 3 years ago

6

class saveArray extends Array {
  get items() {
    console.log("You can't access the items!");
  }
  set items(value){
    console.log("I said! You can't access the items! And use modern ES code!");
  }
}

const stack = new saveArray();
stack.push(10);
stack.push(5);
stack.pop(); // => 5
stack.items; // => undefined
panzerdp commented 3 years ago

question 5

set count as parameter, we know clousres "remembers" parameters

function createIncrement(count = 0) {
  function increment() {
    count++;
  }

  function log() {
    console.log(`Count is ${count}`);
  }

  return [increment, log];
}

const [increment, log] = createIncrement();
increment();
increment();
increment();
log(); // What is logged?

There's no need to make count a parameter: that's not the main problem. Either way, using console.log(`Count is ${count}`); is a good solution too.

panzerdp commented 3 years ago

question 6:

There is no way to directly modify items array because this approach below is exposing only an API to modify an initial array exisiting with const stack = createStack(); so right after this you can only use push or pop. if you want to modify the items directly you would need to create another "instance" or scope with createStack and that's another "row".

function createStack (items= []) {
  function push (par) {
    items.push(par);
  }

  function pop () {
    items.pop();
  }

  function checkItems(){
    console.log(`items status: ${items}`)
  }

  return { push, pop, checkItems}
}

const stack = createStack();

stack.push(10);
stack.push(5);
stack.pop();

stack.checkItems()

Yes, your solution doesn't expose the stack.items.

However, your implementation still has a broken encapsulation: I can modify items array through the argument. For example:

function createStack(items = []) {
  function push(par) {
    items.push(par);
  }

  function pop() {
    return items.pop();
  }

  function checkItems() {
    console.log(`items status: ${items}`)
  }

  return { push, pop, checkItems }
}

const items = [];
const stack = createStack(items);

stack.push(10);
stack.push(5);

// Hack the items! Broken encapsulation!
items.length = 1;

console.log(stack.pop()); // returns 10 instead of 5

stack.checkItems()

I can still modify items directly, for example I cut the number of items items.length = 1, thus breaking the encapsulation of the stack.

panzerdp commented 3 years ago

no second param? ok then return a closure remembering the first argument and expecting to multiply with a second one the second call. to avoid NaN value just default to 1 if there is not num2 param... again...

function multiply(num1, num2= null) {
  if (!num2) return num2 => num1 * (num2 || 1)
  return num1 * num2
}

const n1 = multiply(2, 2);
const n2 = multiply(3)

console.log(n1, n2(5))

Your solution doesn't work if the second argument is 0:

function multiply(num1, num2 = null) {
   if (!num2) return num2 => num1 * (num2 || 1)
   return num1 * num2
}

console.log(multiply(5, 0)); // logs a function instead of 0

thanks a lot for this post, didnt think i would get nailed into this. greetings from Colombia

You're welcome!

mmejiadeveloper commented 3 years ago

no second param? ok then return a closure remembering the first argument and expecting to multiply with a second one the second call. to avoid NaN value just default to 1 if there is not num2 param... again...

function multiply(num1, num2= null) {
  if (!num2) return num2 => num1 * (num2 || 1)
  return num1 * num2
}

const n1 = multiply(2, 2);
const n2 = multiply(3)

console.log(n1, n2(5))

Your solution doesn't work if the second argument is 0:

function multiply(num1, num2 = null) {
   if (!num2) return num2 => num1 * (num2 || 1)
   return num1 * num2
}

console.log(multiply(5, 0)); // logs a function instead of 0

thanks a lot for this post, didnt think i would get nailed into this. greetings from Colombia

You're welcome!

just forgot 0 evaluate to fasly

function multiply(num1, num2= null) {
  if (!num2 && num2 !== 0) return num2 => num1 * (num2 || 1)
  return num1 * num2
}
mmejiadeveloper commented 3 years ago

question 6: There is no way to directly modify items array because this approach below is exposing only an API to modify an initial array exisiting with const stack = createStack(); so right after this you can only use push or pop. if you want to modify the items directly you would need to create another "instance" or scope with createStack and that's another "row".

function createStack (items= []) {
  function push (par) {
    items.push(par);
  }

  function pop () {
    items.pop();
  }

  function checkItems(){
    console.log(`items status: ${items}`)
  }

  return { push, pop, checkItems}
}

const stack = createStack();

stack.push(10);
stack.push(5);
stack.pop();

stack.checkItems()

Yes, your solution doesn't expose the stack.items.

However, your implementation still has a broken encapsulation: I can modify items array through the argument. For example:

function createStack(items = []) {
  function push(par) {
    items.push(par);
  }

  function pop() {
    return items.pop();
  }

  function checkItems() {
    console.log(`items status: ${items}`)
  }

  return { push, pop, checkItems }
}

const items = [];
const stack = createStack(items);

stack.push(10);
stack.push(5);

// Hack the items! Broken encapsulation!
items.length = 1;

console.log(stack.pop()); // returns 10 instead of 5

stack.checkItems()

I can still modify items directly, for example I cut the number of items items.length = 1, thus breaking the encapsulation of the stack.


function createStack(items = []) {
const noRefItems = [...items];
function push(par) {
noRefItems.push(par);
}

function pop() { return noRefItems.pop(); }

function checkItems() { console.log(items status: ${noRefItems}) }

return { push, pop, checkItems } }

const items = []; const stack = createStack(items);

stack.push(10); stack.push(5);

// Hack the items! Broken encapsulation! items.length = 1;

console.log(stack.pop()); // returns 10 instead of 5

stack.checkItems()



there you go. the problem: modification by reference, solution: mutation avoidance by spread only value cloning
indrajeetydv commented 3 years ago

Question:4->

for(let i=0;i<3;i++){ setTimeout(function log(){ console.log(i) },1000); }

omachala commented 3 years ago

First solution of #4 that came to my mind:


for (var i = 0; i < 3; i++) {
  setTimeout((function log(x) {
    console.log(x);
  })(i), 1000);
}
panzerdp commented 3 years ago

First solution of #4 that came to my mind:

for (var i = 0; i < 3; i++) {
  setTimeout((function log(x) {
    console.log(x);
  })(i), 1000);
}

@omachala Your solution logs the numbers right away, but the numbers should be logged with a delay of 1 second.

orl0 commented 3 years ago

Thank you for your challenges.


Questions 4: Tricky closure

It's better to use let but it was challenging to do it grandpa-style.

for (var i = 0; i < 3; i++) {
  (function (x) {
    setTimeout(function () {
      console.log(x); // What is logged?
    }, 1000);
  })(i);
  setTimeout(
    (function (r) {
      return function () {
        console.log(r); // What is logged?
      };
    })(i),
    1000
  );
}

Questions 5: Right or wrong message

Too obvious.

Questions 6: Restore encapsulation

Quite obvious too. And the best solution has been described already:

console.log("I said! You can't access the items! And use modern ES code!")

Questions 7: Smart multiplication

const multiply = (num1, num2) => (num2 !== undefined ? num1 * num2 : (n2) => num1 * n2);
Adash commented 3 years ago

"Questions 5 for log function to return need incremented value we need to put message variable in the body of log function That's the solution @noidar. Thanks for sharing." Are you sure @panzerdp ? I've tested it and it outputs : 'Count is 3' 'Count is 3' 'Count is 3'

Adash commented 3 years ago

Sorry, wrong question... please ignore my message above

webdev-sulfur commented 3 years ago

Hi, thanks about this useful article.

I use bind method for multiply:

function multiply(num1, num2){ return num2 !== undefined ? num1 * num2 : multiply.bind(null, num1); }

panzerdp commented 3 years ago

Hi, thanks about this useful article.

Thanks @webdev-sulfur.

I use bind method for multiply:

function multiply(num1, num2){ return num2 !== undefined ? num1 * num2 : multiply.bind(null, num1); }

Looks good!

linyuwei commented 3 years ago

5

I removed message variable that won't store the count value at the beginning, instead directly read the count variable.

function createIncrement() {
  let count = 0
  function increment() {
    count++;
  }

  function log() {
    console.log(`Count is ${count}`);
  }

  return [increment, log];
}

const [increment, log] = createIncrement();
increment();
increment();
increment();
log(); // What is logged?
Beej126 commented 3 years ago

Question #4

no new tricks but let's cut it even thinner =)

for (var i = 0; i < 3; i++) setTimeout(console.log, 1000, i);

or to round out other valid interview responses if candidate didn't remember setTimeout's full signature (i sure didn't =)

for (var i = 0; i < 3; i++) ((j) => setTimeout(() => console.log(j), 1000))(i);
G1anpierre commented 3 years ago

Solution 5 :

function createIncrement() {
  let count = 0;
  let message = '';
  function increment() {
    count++;
    message = `Count is ${count}`;
  }

  function log() {
    console.log(message);
  }

  return [increment, log];
}

kadiryetisen commented 2 years ago

FOR 4. QUESTION

var i = 0;
for (; i < 3; i++) {
    setTimeout(
        (function log(j) {
            console.log(j); // What is logged?
        })(i),
        1000
    );
}
WORMSS commented 2 years ago

FOR 4. QUESTION var i = 0; for (; i < 3; i++) { setTimeout( (function log(j) { console.log(j); // What is logged? })(i), 1000 ); }

I.... don't think this is doing what you think this is doing... You are actually doing

setTimeout(undefined, 1000);

3 times..

kadiryetisen commented 2 years ago

@WORMSS Didn't he want that ? app.js:4 0 app.js:4 1 app.js:4 2

WORMSS commented 2 years ago

@WORMSS Didn't he want that ? app.js:4 0 app.js:4 1 app.js:4 2

Up your timeout to 60000 (eg, 1 minute) and see how quickly your code does the console logs to see the problem with your code.

panzerdp commented 2 years ago

@WORMSS Didn't he want that ? app.js:4 0 app.js:4 1 app.js:4 2

@kadiryetisen Sorry, the question is a bit confusing. You have to log 0, 1, 2 with a delay of 1 second.

I have updated the question in the post to be more accurate.

kadiryetisen commented 2 years ago

@panzerdp @WORMSS oohh ok i've just understood. for (var i = 0; i < 3; i++) { function close(i) { setTimeout(() => { console.log(i); }, i * 1000); } close(i); } Is it true ?

WORMSS commented 2 years ago

@kadiryetisen that would work. by the way, you can use ```js my code ``` and you will keep formatting and highlighting

kadiryetisen commented 2 years ago

@WORMSS oh I got it , thanks :))

pietow commented 2 years ago

Questions 4: Tricky closure

for (var i = 0; i < 3; i++) {
    ;(lockedInIndex => {
        setTimeout(function log() {
            console.log(lockedInIndex) // What is logged?
        }, 1000)
    })(i) //used an IIFE inspired by http://benalman.com/news/2010/11/immediately-invoked-function-expression/
}
WORMSS commented 2 years ago

@pietow interesting that you mixed arrow functions with standard functions in such a small code snippet. Any reason you felt you had to do that?

WORMSS commented 2 years ago

As much as I love the setTimeout(func,time,arguments) as mentioned above.. There is also the simple fix of just doing

for (var i = 0; i < 3; i++) {
  const j = i;
  setTimeout(function log() {
    console.log(j); // What is logged?
  }, 1000);
}
mjfung1 commented 2 years ago

^^^^@WORMSS or better yet. Use let instead of var.

for (let i = 0; i < 3; i++) {
  setTimeout(function log() {
    console.log(i); // What is logged?
  }, 1000);
}
bindumanjunatha commented 2 years ago

Can anyone explain how does the below code work? I'm able to modify the item array even if it is private!

function createStack() {
  const items = [];
  return {
    push(item) {
      items.push(item);
    },
    pop() {
      return items.pop();
    }
  };
}
const stack = createStack();
stack.push(10);
stack.push(5);
stack.pop(); // => 5
console.log(stack.items) //undefined
stack.items = [12,13]
console.log(stack.items) // prints [12,13]
WORMSS commented 2 years ago

You are adding a new property to the object that has the { push() pop() } methods on.

PuffCab commented 2 years ago

Begginer here ...

Anyone care to explain why in Question 4) for(let i=0; ....) would resolve the problem?

I guet that Let is blockscoped, but if the log() is scheduled for execution after the 1000ms, and the for() cycle is completed by then, shouldn't read i = 3?

When will log() read 0 and 1?

Thanks in advance ! (and thanks for the article!)

mjfung1 commented 2 years ago

Hey PuffCab!, Look into closures, that will give an insight of what is really going on under the hood of javascript... essentially you're passing a FUNCTION ( console.log() ) ENCLOSED/BUNLED-TOGETHER WITH ITS SURROUNDING STATE ( i ) aka LEXICAL ENVIRONMENT. This can only be achieved with let and const ( local-scoped ) vs var ( global-scoped ).

Hope this didnt confuse you, Just trying to help,

This is a great video about closures. Little long tho...but you will learn a lot. https://www.youtube.com/watch?v=ZVXrJ4dnUxM&t=5594s

aekryz1993 commented 2 years ago

Questions 4:

for (var i = 0; i < 3; i++) { setTimeout(log(i), 1000); }

function log(i) { console.log(i); // What is logged? }

WORMSS commented 2 years ago

Questions 4:

for (var i = 0; i < 3; i++) { setTimeout(log(i), 1000); }

function log(i) { console.log(i); // What is logged? }

try that again with 30000 (30 seconds) notice they appear immediately.