# Understanding By Example

## Functions:

````javascript
# Function Context Cheatsheet

## Types of Invocation
- Applies to only **named** and **unnamed** functions
- **Doesn't matter** for fat arrow functions

### Function-style invocation
- Context of the function will be global context **UNLESS** binded

### Method-style invocation
- Context of the function will be the object which the function is called on **UNLESS** binded

```javascript
const obj = {
  name: 'Example Object',
  unnamedFunc: function() {
    console.log(this.name);
  }
};

// Method-style invocation
obj.unnamedFunc(); // 'Example Object' 

// Function-style invocation
const unnamedFunc = obj.unnamedFunc;
unnamedFunc(); // Global context
````

## Types of Functions

### Named Function

* explicit `return` keyword required
* curly braces `{}` around function body
* **NOT** saved to a variable
* parameters must be surrounded by parentheses `()`
* context is defined by how it's invoked or called
  * function-style: global context
  * method-style: context is object that function is being called on
* calling `bind` on the function will return a function binded to the context of the `bind` argument

```javascript
function namedFunc(params) {
  return 'named function'
}
```

```javascript
// bindedNamedFunc will have the context of obj
const bindedNamedFunc = namedFunc.bind(obj);
```

### Unnamed Function

* explicit `return` keyword required
* curly braces `{}` around function body
* **MUST** be saved to a variable
* parameters must be surrounded by parentheses `()`
* context is defined by how it's invoked or called
  * function-style: global context
  * method-style: context is object that function is being called on
* calling `bind` on the function will return a function binded to the context of the `bind` argument

```javascript
const unnamedFunc = function(params) {
  return 'unnamed function'
}
```

```javascript
// bindedUnnamedFunc will have the context of obj
const bindedUnnamedFunc = unnamedFunc.bind(obj);
```

### Explicit Fat Arrow Function

* explicit `return` keyword required
* curly braces `{}` around function body
* **MUST** be saved to a variable
* parameters must be surrounded by parentheses `()` **IF more than one parameter**
* takes the context of where it's defined
* **CANNOT** be binded using `bind`

```javascript
const explicitFatArrow = params => {
  return 'explicit fat arrow function'
};
```

### Implicit Fat Arrow Function

* **NO** `return` keyword
* function body can only consist of what is being returned
* Optional parentheses `()` around function body

  > NOTE: Parentheses needs to be used if returning an object `ex: ({ key: value })`
* **MUST** be saved to a variable
* parameters must be surrounded by parentheses `()` **IF more than one parameter**
* takes the context of where it's defined
* **CANNOT** be binded using `bind`

```javascript
const implicitFatArrow = (params) => 'implicit fat arrow function';
```

```javascript
const implicitFatArrow = (params) => ('implicit fat arrow function');
```

```javascript
const implicitFatArrow = (params) => ({
  function: 'implicit fat arrow'
});
```

## Bind

* `bind` accepts multiple arguments
* first argument is the context that you want to bind the function to
* any arguments that come afterwards will be passed in when the bound function is called **BEFORE** the call time arguments

  \`\`\`

```javascript
// Arrow Functions
// 1. Syntax
// 2. Scoping with Arrow Functions

function logger(title, body) {
  console.log("\x1b[31m%s\x1b[0m", title);
  console.log('    ', body);
}

// Named Function
function sayHelloNamed(name) {
  return 'hello ' + name;
}

logger('Named Function', sayHelloNamed('Justin'));

// Unnamed Function
const sayHelloUnnamed = function(name) {
  return 'hello ' + name;
};

logger('Unnamed Function', sayHelloUnnamed('Soon-Mi'));







// Fat Arrow Function with EXPLICIT return
// curly braces around body of fat arrow functions need explicit return keyword
// const sayHelloExplicit = (name) => {
//   return 'hello ' + name;
// };

// No parentheses surrounding parameter needed if ONLY one parameter
const sayHelloExplicit = name => {
  return 'hello ' + name;
};

logger('Fat Arrow Function with Explicit Return', sayHelloExplicit('Gordon'));












// Fat Arrow Function with IMPLICIT return
// Also called a One-Liner Fat Arrow function
// should only use this when the function's body is NOT multi-line
const sayHelloImplicit = (name) => 'hello' + name;

// can use parentheses around function body as well
// const sayHelloImplicit = (name) => ({
//   hello: 'Angela'
// });

// notice the semicolon ; at the end

logger('Fat Arrow Function with Implicit Return', sayHelloImplicit('Angela'));






console.log('\n--------------\n');




// Example using fat arrow functions
const arr = [1, 2, 3];

const newArr = arr.map(el => {
  return el + 2;
});
// const newArr = arr.map(function(el) {
//   return el + 2;
// });

logger('Original Array, `arr`', arr);
logger('New Mapped Array, `newArr`', newArr);




console.log('\n--------------\n');






// Context using Fat Arrow functions
const pony = {
  name: 'Lucy',
  wrappedSayName: function() {
    console.log(this.name);
    return function() {
      console.log(this.name);
      console.log('Hello my name is ' + this.name);
    }
  },
  wrappedArrowSayName: function() {
    console.log(this.name);
    return () => {
      console.log('Hello my name is ' + this.name);
    }
  }
};

pony.wrappedSayName = pony.wrappedSayName.bind(pony);
let wrap = pony.wrappedSayName().bind(pony); // method-style invocation
wrap(); // function-style invocation

console.log('-----------');

wrap = pony.wrappedArrowSayName(); // method-style invocation
wrap(); 

console.log('-----------');

const arrowSayName = pony.wrappedArrowSayName; // not invoking
wrap = arrowSayName(); // function-style invocation
wrap();



// bound to the context of wherever it's defined






console.log('-----------');

const zoomMeeting = {
  students: ['Christian', 'Ronald','Wren'],
  listStudent: function(studentName) {
    console.log(this.students);
    // console.log(studentName);
  },
  listStudents: function() {
    const listStudent = this.listStudent;
    listStudent(); // function-style
    console.log('***');
    this.students.forEach(listStudent);
  }
};

zoomMeeting.listStudents();
```

## Scope:

```javascript
//  Global Scope

let cities = ["NYC", "SF"];

//console.log(cities);




// Local / Function Scope


function sayThings() {
  // let cities = ['Bogota', 'Madrid'];
  // console.log(cities);

  let word = 'dinosaur';
  console.log(word);
}

// console.log(cities); // prints NYC & SF
// console.log(word); // ReferenceError
//sayThings();










// Block Scope

if (true) {
  let cats = ['Roma', 'Luigi'];
  //console.log(cats); // prints Roma & Luigi
}

//console.log(cats) // ReferenceError

// This example was extended to show how scope within nested blocks works
// As Justin pointed out, it also leads directly into closures

let carrot = 'snake';

if (true){
  let carrot = 'doggie!';
  //console.log(carrot);
  if (true) {
    carrot = 'carrot';
    console.log(carrot); // prints carrot
  }
  for(let i = 1; i < 5; i++){
    console.log(`i is ${i}`);
  };
  //console.log(`i is ${i}`); // reference error
  console.log("line 57:" ,'george' ,carrot);
}

console.log(carrot); // prints snake
```

## Closure

```javascript
// Closure
// When an inner function uses, or changes, 
// variables defined in an outer scope.
// NOT for declaring a variable of the same name in an inner scope.
function sayHi() {
    let name = 'Bryan Guner';

    function greeting() {
        // here greeting function closes over, or captures, the name variable
        // to read it's value
        return "Hi there, " + name + "!";
    }
    // Here, we return the return value of the greeting function
    return greeting();
}
// console.log(sayHi());
function nameAndCity() {
    let person = {
        name: 'Sergey',
        city: 'Moscow'
    };

    function changeCity() {
        // here changeCity function closes over the person variable
        // and reassigns a value on an existing key
        person.city = 'Toronto';
    }
    changeCity();
    // the person variable will show the changes from the changeCity function
    return person;
}
// console.log(nameAndCity());
function smoothieMaker() {
    let ingredients = [];

    function addIngredient( ingredient ) {
        // Here addIngredient function closes over the ingredients variable
        // to push new elements into the ingredients variable.
        // We have created a private state where we cannot access 
        // the ingredients array from the outside and can only access
        // the variable from the inner function.
        ingredients.push( ingredient );
        return ingredients;
    }
    // Here the return value for smoothiemaker is the return value 
    // is the function addIngredient, NOT addIngredient's return value
    return addIngredient;
}
// Here we initialize we return a new addIngredient function
// which has closed over the ingredients array
const makeSmoothie = smoothieMaker();
console.log( makeSmoothie );
console.log( makeSmoothie( 'spinach' ) ); // prints [ spinach ]
console.log( makeSmoothie( 'turmeric' ) ); // prints [ spinach, turmeric ]
// let mySmoothie = makeSmoothie();
// Here we return a new and different addIngredient function
// which has closed over a new a different ingredients array
const makeSmoothie2 = smoothieMaker();
console.log( makeSmoothie2( 'kale' ) ); // prints [ kale ]  -- does not include spinach and turmeric
function createCounter() {
    let count = 0;
    return function () {
        count++;
        return count;
    }
}
let counter1 = createCounter();
let counter2 = createCounter();
// console.log(counter1());
// console.log(counter1());
// console.log(counter1());
// console.log(counter1());
// What will this print out?
// console.log(counter2());
// Brief talk of scope and redeclaring a const variable in a loop
for ( let i = 0; i < 5; i++ ) {
    const num = i + 2;
}


// In the following examples we will predict what will 
// be printed to the terminal






// 1

function dinerBreakfast(food) {
    let order = "I'd like cheesy scrambled eggs and ";

    function finishOrder() {
        return order + food;
    }

    return finishOrder();
}

// console.log(dinerBreakfast('green tea'));









// 2

function dinerBreakfast2(food) {
    let order = "I'd like a(n) " + food;

    function withEggs() {
        order = order + ' and cheesy scrambled eggs, please!'
    };

    withEggs();
    return order;
}

// console.log(dinerBreakfast2('avocado toast'));









// 3

function dinerBreakfast3() {
    let order = "I'd like cheesy scrambled eggs";

    return function (food) {
        order = order + " and " + food;
        return order;
    }
}

let breakfastOrder = dinerBreakfast3();

console.log(breakfastOrder);
console.log(breakfastOrder('cappuccino'));
console.log(breakfastOrder('pancakes'));
```

## Context:

```javascript
// In the following examples we will predict what will
// be printed to the terminal


function whatIsThis() {
    console.log(this);
}

const pony = {
    name: "Lucy",
    whatIsThis: function () {
        console.log(this);
    },
    sayName: function () {
        console.log('Hello my name is ' + this.name);
    },
    changeName: function (newName) {
        this.name = newName;
        this.sayName();
    }
}


// 1.
// whatIsThis(); 


// 2.
// pony.whatIsThis();


// 3.
// pony.sayName();


// 4.
// pony.changeName("Layla");


// 5. 
// const sayNameFunc = pony.sayName;
// sayNameFunc();


// 6.
// const boundSayName = pony.sayName.bind(pony);
// boundSayName();


// 7.
const bart = {
    name: 'Bart'
}

const boundToBart = pony.sayName.bind(bart);
// boundToBart();


// 8.
const changeBartsName = pony.changeName.bind(bart);
changeBartsName('Sergey');


// Context
// What does the keyword 'this' refer to?
// The context is determined by HOW a function is invoked


// Different ways of invoking a function
// function style
// context is set to the global object
// 'this' refers to the global object
// method style
// context is set to the object on which the method is called
// 'this' refers to the object on which the method is called


// How to ensure bind never change the context of a function 
// no matter how it is invoked.
// .bind()
// By adding .bind() to the end of a function we set the context
// to equal the argument passed to .bind() 
// 'this' will refer to the argument passed to .bind()


// Scope VS Context
// VERY DIFFERENT THINGS!!

// Scope: 
// Availability of variables at a line in your application
// Context:
// The value of this
// Determined by how a function has been invoked or the .bind() method




const cat = {
    name: 'Luigi',
    age: 2,
    whatIsThis: function () {
        console.log(this);
    },
    nameAndAge: function () {
        console.log(this.name + " is " + this.age + " years old.")
    }
};

// cat.whatIsThis();
// cat.nameAndAge();

const nameAndAgeFunc = cat.nameAndAge;
nameAndAgeFunc();

const boundNameAndAge = cat.nameAndAge.bind(cat);
// boundNameAndAge();








const cat2 = {
    name: 'Roma',
    age: 3
}

function nameAndAge() {
    console.log(this.name + " is " + this.age + " years old.");
}

const dog = {
    name: 'Napo',
    age: 5
}

const catNameAge = nameAndAge.bind(cat2);
const dogNameAge = nameAndAge.bind(dog);

catNameAge();
dogNameAge();





const obj = {
  name: 'Example Object',
  unnamedFunc: function() {
    console.log(this.name);
  }
};

// Method-style invocation
obj.unnamedFunc(); // 'Example Object'

// Function-style invocation
const unnamedFunc = obj.unnamedFunc;
unnamedFunc(); // `undefined` because Global context





console.log('-------------------');






// Unnamed Func
const dog = {
  name: 'Digby'
};

const boundFunc = obj.unnamedFunc.bind(dog);
boundFunc(); // Digby
obj.unnamedFunc(); // Example Object







console.log('-------------------');




// Bind Time and Call Time Arguments
// bind time arguments passed in first, then call time arguments
function printAge(...args) {
  const age = args[0];
  const year = args[1];
  console.log(args);
  console.log(this.name + ' is ' + age + ' years old. Born in ' + year);
}

const otherArgs = [2005];

const printDigby = printAge.bind(dog, 12, ...otherArgs);
printDigby(2000);
printDigby(2008);

// const printRealDigby = printAge.bind(dog, 4);
// printRealDigby(2002);
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://bryan-guner.gitbook.io/my-docs/mynotes/general-notes/understanding-by-example.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
