Skip to content

karissastew/phase-1-context-lab

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

21 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

JavaScript Advanced Functions: Context

Learning Goals

  • Define execution context
  • Define this
  • Access implicitly-set global object in a JavaScript engine
  • Access implicitly-set global object in a function call
  • Prevent implicitly setting in function calls with use strict
  • Access implicitly-set parent object in a contained function expression
  • Access implicitly-set new object in object-oriented programming constructor
  • Explicitly override context with call and apply
  • Explicitly lock context for a function with bind

Introduction

In the previous lesson we provided definitions of:

  • "Execution Context"
  • this
  • call
  • apply
  • bind

but then built an application that didn't use them. However, with a record-oriented application built, we will have a shared context to understand these challenging concepts in JavaScript (there's a pun in there, but you won't get it until the end of this lesson).

Define Execution Context

When a function in JavaScript is called, it is provided an execution context.

The execution context is a JavaScript Object that is either implicitly or explicitly passed at the time of the function's call.

The implicit way of passing a context with a function is something we have to memorize and accept as part of the nature of JavaScript.

The tools for explicitly passing a context at function call-time are the methods call, apply, and bind.

Define this

The JavaScript keyword this returns the current execution context. Whether that context was passed explicitly or implicitly, this returns it.

We'll now identify places where the execution context is implicitly set.

Access Implicitly-Set Global Object in a JavaScript Engine

this //=> Window (the class of the object we're "inside;" explained below)
typeof this //=> "object"
this === window //=> true

In a brand new Chrome console, try the code above. All the code you write in a JavaScript file can be thought of as "one big function" whose context is set to the window object. Thus this === window is true. When you type this, JavaScript tells you you're in a context that, itself, is of type Window.

We can try the same thing out in NodeJS. If you have node installed on your computer, open the Node REPL by typing node.

this === global //=> true

Node calls the default execution context global, but otherwise it's a global object like window.

Access Implicitly-Set Global Object in a Function Call

All function declarations and function expressions you have called to this point have also had an execution context, you just didn't know to ask for it with this.

let contextReturner = function() {
  return this
}

contextReturner() //=> window
contextReturner() === window //=> true

When you invoked a function, the global context was implicitly passed.

JavaScript does this so that by default our functions can access any properties on that most-global object, window (or global in Node). Consider:

let locationReturner = function() {
  return this.location.host
}

locationReturner() //=> URL host serving this page e.g. developer.mozilla.org

Doesn't that seem nice and handy to do? In an effort to be nice to us programmers, JavaScript's implementers made the global object available inside all functions.

This rule is very, very, very important and is at the root of a vital bug we'll explore in just a few lessons.

It's worth noting that even in a function inside of another function, the inner function's default context is still the global object:

function a() {
  return function b() {
    return this;
  }
}

a()() === window //=> true

> **NEUROLINGUISTIC/ANTHROPOLOGICAL ASIDE**: Some people think that `this` is a
> strange thing to call such an important concept. But pronouns like "this,"
> "he," or "here" all refer to a _context_. At a concert, if I say (scream)
> "It's noisy here," you don't think "Here in the Milky Way galaxy? I disagree.
> Space has little oxygen as a sound medium and is therefore quite quiet."
> Instead, you recognize the most-relevant context is at this significant and
> unusual event with giant speakers and guitar players and therefore infer that
> "here" refers to "this concert." JavaScript thought the best pronoun to use
> was `this`, and it seems sensible to us.

### Prevent Implicitly Setting in Function Calls With `use strict`

We wish we could say that the default context was **always** the global object.
It'd make things simple.

However, in JavaScript, if the engine sees the `String` "use strict" inside a
function, it will _stop_ passing the implicit _execution context_ of the global
object.  If JavaScript sees `"use strict"` at the top of a JavaScript code
file, it will apply this rule (and other strict behaviors) to _all functions_.

```js
function looseyGoosey() {
  return this
}

function noInferringAllowed() {
  "use strict"
  return this
}

looseyGoosey() === window; //=> true
noInferringAllowed() === undefined //=> true

There are really no guidelines as to which you'll see more. Some programmers think strict stops confusing bugs (seems wise!); others think it's an obvious rule of the language and squelching it is against the language's love of functions (a decent argument!). Generally, we advise you to think of the "default mode" as the one that permits an implicit presumption of context. For more on strict-mode, see the Resources.

Access Implicitly-Set Parent Object in a Contained Function Expression

This (no pun intended) case is simple, thankfully: functions inside Objects have this set to the thing to the left of the .. This lets them point access other sibling properties and functions:

let byronPoodle = {
  name: "Byron",
  sonicAttack: "ear-rupturing atomic bark",
  mostHatedThing: "noises in the apartment hallway",
  warn: function() {
    console.log(`${this.name} issues an ${this.sonicAttack} when he hears ${this.mostHatedThing}`)
  }
}
byronPoodle.warn()
// LOG: Byron issues an ear-rupturing atomic bark when he hears noises in the apartment hallway

After all that subtlety around strict mode, it's nice to have an easy example!

Access Implicitly-Set New Object in Object-Oriented Programming Constructor

This lesson covers how this works, all in one place. An important place where this is implicitly set is when new instances of classes are created. Class definition and instance creation are hallmarks of object-oriented ("OO") programming, a style you might not be familiar with in JavaScript. Rather than ignore this important case until later, we're going to cover it now, even though you might not be familiar with OO programming.

If you're not familiar with OO in JavaScript (or anywhere for that matter!), that's OK, just remember this rule for later. When you see this inside of a class definition in JavaScript, come back and make sure you understand this rule.

That said, this rule is a lot like the previous one, it's for convenience and feels "natural" from a linguistic point of view: "The thing we're setting up should be the default context for work during its construction in its own function that's called constructor; so, JavaScript, make it so."

class Poodle{
  constructor(name, pronoun){
    this.name = name;
    this.pronoun = pronoun
    this.sonicAttack = "ear-rupturing atomic bark"
    this.mostHatedThing = "noises in the apartment hallway"
  }

  warn() {
    console.log(`${this.name} issues an ${this.sonicAttack} when ${this.pronoun} hears ${this.mostHatedThing}`)
  }
}
let b = new Poodle("Byron", "he")
b.warn() //=> Byron issues an ear-rupturing atomic bark when he hears noises in the apartment hallway

To sum up the previous discussion:

  1. Execution context is set at function call-time, implicilty or explicitly.
  2. Execution context defaults to the global object unless prevented by "use strict".
  3. Execution context defaults to the containing Object for function expressions that are properties within that Object
  4. Execution context defaults to the new object in a class's constructor

So let's learn to explicitly set the context of a function call.

Explicitly Override Context with call and apply

The methods on functions called call and apply allow us to override the execution context.

Let's think back to the previous lesson and recall working with records.

let asgardianBrothers = [
  {
    firstName: "Thor",
    familyName: "Odinsson"
  },
  {
    firstName: "Loki",
    familyName: "Laufeysson-Odinsson"
  }
]

let intro = function(person, line) {
  return `${person.firstName} ${person.familyName} says: ${line}`
}

let introWithContext = function(line){
  return `${this.firstName} ${this.familyName} says: ${line}`
}

let phrase = "I like this brown drink very much, bring me another!"
intro(asgardianBrothers[0], phrase) //=> Thor Odinsson says: I like this brown drink very much, bring me another!
intro(asgardianBrothers[0], phrase) === introWithContext.call(asgardianBrothers[0], phrase) //=> true
intro(asgardianBrothers[0], phrase) === introWithContext.apply(asgardianBrothers[0], [phrase]) //=> true

let complaint = "I was falling for thirty minutes!"
intro(asgardianBrothers[1], complaint) === introWithContext.call(asgardianBrothers[1], complaint) //=> true
intro(asgardianBrothers[1], complaint) === introWithContext.apply(asgardianBrothers[1], [complaint]) //=> true

In the previous lesson, we wrote functions in the style of intro. They took the record as an argument. In fact, if we look at the solution for the previous lesson we'll see that multiple functions have the same, first parameter: employee, the record.

let createTimeInEvent = function(employee, dateStamp){ /* */ }
let createTimeOutEvent = function(employee, dateStamp){ /* */ }
let hoursWorkedOnDate = function(employee, soughtDate){ /* */ }

What if we told JavaScript that instead of the record being a parameter (in addition to a phrase), it could be assumed as a context and thus accessible via this. That's what we're doing with the function introWithContext as invoked with either call or apply.

The introWithContext function expects only a catchphrase as an argument. Both call and apply take a thisArg argument as their first argument (see their documentation for further clarification): that argument becomes the this inside the function. In the case of call, anything after the thisArg gets passed to the function like arguments inside of a (). In the case of apply, the contents in the Array get destructured and passed to the function like arguments inside of a ().

ES6 ALERT: Some might wonder: if we have destructuring of Arrays, why do we need both call and apply since a destructured Array, as required by apply could simply be destructured and fed to call. Destructuring is a relatively new arrival to JavaScript, so before then JavaScript had two separate methods.

Explicitly Lock Context For a Function With bind

Let's suppose that we wanted to create the introWithContext function, but have it permanently bound to asgardianBrothers[0]. As the adjective "bound" suggests, we use bind:

let asgardianBrothers = [
  {
    firstName: "Thor",
    familyName: "Odinsson"
  },
  {
    firstName: "Loki",
    familyName: "Laufeysson-Odinsson"
  }
]
let introWithContext = function(line){
  return `${this.firstName} ${this.familyName} says: ${line}`
}
thorIntro = introWithContext.bind(asgardianBrothers[0])
thorIntro("Hi, Jane") //=> Thor Odinsson says: Hi, Jane
thorIntro("I love snakes") //=> Thor Odinsson says: I love snakes

The bind method returns a function that needs to be called, but wherever the function that bind was called on had a this reference, the this is "hard set" to what was passed into bind.

To sum up the explicit overrides:

  1. Execution context is set in a function by invoking call on the function and passing, as the first argument, a thisArg which is accessed via this in the function. Additional parameters to the function are listed after ,
  2. Execution context is set in a function by invoking apply on the function and passing, as first argument, a thisArg which is accessed via this in the function. Additional parameters to the function are stored in the second argument: an Array containing arguments to the function.
  3. Execution context can be locked in a function by invoking bind on it and passing it a thisArg. The bind function makes a copy of the functionality of its function but with all the this stuff locked in place and returns that function. That new function can have arguments passed to it during its call with () as usual.

Lab

In this lab, we're going to build the time-card and payroll application using the record-oriented approach again. This lab will feature the same topic and area of work as the previous lab; however, how we call and use functions will change with our new knowledge. While the code will stay mostly the same, you're going to need to use this a lot more.

The tests guide you to implementing a time card system: when someone enters the company's state of the art technical office, the employee has to insert their card in a time-clock which will record the time they came in. When it's time to leave, the employee will "punch out."

For simplicity's sake, we'll make these assumptions:

  1. Assume that employees always check in and check out
  2. Assume employees always check in and our on the hour
  3. The time is represented on a 24-hour clock (1300 is 1:00 pm); this keeps the math easier and is the standard in most of the world
  4. When timestamps are needed, they will be provided as Strings in the form: "YYYY-MM-DD 800" or "YYYY-MM-DD 1800" e.g. "2018-01-01 2300"
  5. Employees will never work across days i.e. in at 2200 and out at 0400 the next day.

The lab tests will guide you toward a solution. Keep in mind, the goal is to understand how to "grow" an application in "record-oriented" fashion in JavaScript, as well as pass the lab. Make sure you're learning about this app design while you pass the solutions.

As before, if you find yourself having extra time, use the guidance in the previous lab to make your application more robust.

Take advantage of your collection-processing strengths that you trained up over the last few lessons.

Put your code in index.js.

A Mystery on the Horizon

You'll notice that in this lab we give you the implementation of allWagesFor. As part of writing this challenge, we ran right smack into one of the most famous bugs in JavaScript land: the lost context bug. Because we've not taught you to deal with it, we've "given" you this function. We think you can solve the other tests with this little piece having been given to you.

If you find yourself having extra time, try researching this topic on your own. We'll tell you all about it in our next lesson, though.

Conclusion

This is one of the hardest topics in JavaScript. But you have hands-on experience with the why and motivations of it! You're so much better off than most JavaScript hackers who never quite get the hang of it. It's been a lot of growth, but this hard-won knowledge is going to help you do staggeringly cool things. Here's a summary of execution context all in one place. Come back whenever you need! We do!

  1. Execution context is set at function call-time, implicitly or explicitly.
  2. Execution context defaults to the global object unless prevented by "use strict".
  3. Execution context defaults to the containing Object for function expressions that are properties within that Object
  4. Execution context defaults to the new object in a class's constructor***
  5. Execution context is set in a function by invoking call on the function and passing, as first argument, a thisArg which is accessed via this in the function. Additional parameters to the function are listed after ,
  6. Execution context is set in a function by invoking apply on the function and passing, as first argument, a thisArg which is accessed via this in the function. Additional parameters to the function are stored in the second argument: an Array containing arguments to the function.
  7. Execution context can be locked in a function by invoking bind on it and passing it a thisArg. The bind function makes a copy of the functionality of its function but with all the this stuff hard coded and returns that function. That new function can have arguments passed to it during its call with () as usual.

Resources

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • JavaScript 98.1%
  • HTML 1.9%