DEV Community

Discussion on: Book Club: Eloquent JavaScript - Chapter 3

Collapse
peerreynders profile image
peerreynders

like let x = 42;

Nitpick: let is a statement. Assignment/binding is an expression. That is why let x = y = 42; works. x = 42 evaluates to 42 as does x = y = 42
.


Global scope is like setting some variables at the top of your file.

  • There is a global object that is available via the "global scope" but that is pretty much the extent of the "global scope". No new global names can be created. It just seems that way because properties on the global object can be directly accessed without referencing them through the global object - that is why they seem to exist in the "global scope". So if you need to access new names globally then you need to add them to the global object as properties.
  • Global objects: window (browser window), self (browser window and web worker), global (Node.js), globalThis (evergreen browser windows, web workers and Node.js (12.0+)).
  • The issue is that script level (outside of functions) var declarations inside of inline scripts (type="text/javascript", not type="module") attach themselves as properties to the global object. That doesn't happen inside functions, module scripts and ECMAScript modules.
  • import names are completely hoisted throughout that module ("module-global") but that doesn't affect other modules.

Then, once the the function has finished executing, they cease to exist.

While true in your particular example that isn't the full story as you explore in your Chapter 3 bonus.

Given the right circumstances the local variables of that particular invocation will continue to exist inside the closure.

const [a, r] = scheduleAppointment(12);
console.log(a()); // 12
r(10);
console.log(a()); // 10

function scheduleAppointment(initial) {
  let time = initial;
  return [appointment, reschedule];

  function appointment() {
    return time;
  }

  function reschedule(other) {
    time = other;
  }
}

Enter fullscreen mode Exit fullscreen mode

Compare this to

const o = scheduleAppointment(12);
console.log(o.a()); // 12
o.r(10);
console.log(o.a()); // 10

function scheduleAppointment(initial) {
  return {
    time: initial,
    a: appointment,
    r: reschedule,
  };

  function appointment() {
    return this.time;
  }

  function reschedule(other) {
    this.time = other;
  }
}
Enter fullscreen mode Exit fullscreen mode

If you squint you can see the similarities. Hence quote:

  • Objects are merely a poor man's closures.
  • Closures are a poor man's object.

You can use a closure like an implicit object.


There is another difference

That difference (their this being bound to the this of their site of creation rather than being dynamically set at run time based on the manner of invocation) is the reason why arrow functions were introduced in the first place.


Again, this is not the ultimate solution as it will only assign the parameters in order.

You can use a default parameter by deliberately passing undefined.

ages(undefined, 22); // 23, 22

function ages(person1 = 23, person2 = 99) {
  console.log(person1, person2);
}
Enter fullscreen mode Exit fullscreen mode

null doesn't work as it is considered an intentional value.


As we've seen, functions can be split into two types.

The two types that people actually get hot and bothered about are "synchronous" vs. "asynchronous" (i.e. returns a promise)


Functions that execute other functions or side effects, and functions that have return values.

Professor Frisby's Mostly Adequate Guide to Functional Programming - Chapter 03: Pure Happiness with Pure Functions:

A pure function is a function that, given the same input, will always return the same output and does not have any observable side effect.

A pure function is free to call other pure functions.

Functional Geekery Episode 08 - Jessica Kerr isolation:

When the only information a function has about the external world is passed into it via arguments.


relying on global variables whose values might change

Probably was supposed to be

not relying on global variables whose values might change


always returning/producing the same value

more like

the value returned only depends on the arguments supplied and will always be the same for the same arguments.

This property is referred to as referential transparency.

can easily be replaced with a simple value:

… so that a function invocation with a specific set of arguments can be simply replaced with its return value.


I'd say it's fine to have a mixture of both.

See Functional Core - Imperative Shell (related to Hexagonal Architecture).


outside the scope of the filter method

Is that what you mean?

filter is a method of the Array instance (Array.prototype.filter()). But it doesn't use searchQuery. filter is a higher order function (HOF) - i.e. it is passed a function value as an argument, in this case (post) => post.includes(searchQuery). It's that argument (function value) that accesses searchQuery via the closure it (the function value) was created in.

The fact that searchQuery is outside the scope of filter is irrelevant as filter only uses the passed function value. It's the passed function value that accesses searchQuery outside of its own function scope but inside its closure.


You can use the declared function name directly for filter - no need to create another intermediate arrow function.

const blogPosts = [
  'Just Graduated a Boot Camp!',
  'What is a Stack?',
  'Growth Mindsets: A Fever Dream?',
  'Working at FAANG! An Ex-FAANG Tale',
  'Working at a Startup! An Ex-CEO Tale',
];
const searchQuery = 'Ex';
console.log(blogPosts.filter(findPostByQuery));
/* ["Working at FAANG! An Ex-FAANG Tale",
  "Working at a Startup! An Ex-CEO Tale"];
*/

function findPostByQuery(post) {
  return post.includes(searchQuery);
}
Enter fullscreen mode Exit fullscreen mode