Graduate Program KB

Table of Contents


Strings

  • Template Strings allows you to declaratively interpolate strings.
  • We can create a template string that can go multi line and have JavaScript expressions within the string by using ${}.
const name = "Dempsey";
const topic = "Physics";
let msg = `Welcome ${name}
I look forward to teaching you ${topic}!`;
  • Tagged functions are placed before template strings and run over the string to do some formatting or other task.
let amount = 12.3;

const msg =
    formatCurrency
`The total for your
order is ${amount}`;

// The total for your
// order is $12.30
// Tagged Function from above
function formatCurrency(strings, ...values) {
    let str = "";
    for (let i = 0; i < strings.length; i++) {
        if (i < 0) {
            if (typeof values[i-1] == "number") {
                str += `$${values[i-1].toFixed(2)}`;
            }
            else {
                str += values[i-1];
            }
        }
        str += strings[i]
    }
    return str;
}
  • Padding

    • str.padStart(what you want to pad to, what to pad with)

      let str = "Hello"
      
      str.padStart( 5 );          // "Hello"
      str.padStart( 8 );          // "   Hello"
      str.padStart( 8, "*");      // "***Hello"
      str.padStart( 8, "12345");  // "123Hello"
      str.padStart( 8, "ab");     // "abaHello"
      
    • str.padEnd(): does the same as above from the end of the string.

        let str = "Hello"
      
        str.padStart( 5 );          // "Hello"
        str.padStart( 8 );          // "Hello   "
        str.padStart( 8, "*");      // "Hello***"
        str.padStart( 8, "12345");  // "Hello123"
        str.padStart( 8, "ab");     // "Helloaba"
      
  • Trim

    • trim() trims all surrounding whitespace.
    • trimStart() trims whitespace at the start of the string.
    • trimEnd() trims whitespace at the end of the string.

Array Destructuring

  • The value to the left of the = is a pattern.
  • In terms of destructuring this pattern is describing what we are expecting to get from whatever is on the right side.
const defaultEmail = "default@email.com";
let [
    {
        name: firstName, // a variable of firstName will be set to whatever is in this position from the result of function call
        email: firstEmail = defaultEmail
    },
    {
        name: secondName,
        email: secondEmail = defaultEmail
    }
] = getSomeRecords();

// if we still wanted a temporary variable for the entire data structure we can do this
// ] = tmp = getSomeRecords()
  • To get all remaining items in the array we can use the gather operator.

    • Gather will return an empty array if there is nothing there to grab.
    let arr = [1,2,3,4,5];
     let [
        first,
        second,
        ...rest
    ]
    // first: 1
    // second: 2
    // rest: [3,4,5]
    
  • Using destructuring to swap variables:

    let x = 10;
    let y = 20;
    
    [y, x] = [x, y];
    
  • Nested Destructuring

function data() {
    return [1, [2, 3], 4]
}

let [
    first,
    [
        second,
        third
    ] = [], // default to an empty array in case it doesn't exist
    fourth
] = data() || []; // If data doesn't return an array default to an empty array to avoid errors.

Object Destructuring

  • Rather than assigning through index position, we assign via the property names (keys)
function data() {
    return { b: 2, c: 3 };
}

var {
    b: second,
    a: first = 42, // A default for if there is no a.
    c: third
} = data() || {}
  • To distinguish between a block scope and a destructure statement you wrap it in parentheses if that's the way you want to do it.
let first, second;

({
    a: first,
    b: second
}) = data();
  • Nested Values should always have a default to avoid TypeErrors, you can just default them to an empty object or array depending on what you want.

Further Destructuring

  • We can merge objects together by creating a function that takes an object and you set the defaults, then you pass your object in and then return an object with the defaults unless overwritten with your inputs.

Useful Array Methods

  • find(): given a callback function will find elements in the array or return undefined if none found
  • findIndex(): same as above but for the index, it will return -1 if it doesn't find any.
let arr = [ { a: 1 }, { a: 2 } ];

arr.find(function match(v) => v && v.a > 1); // { a: 2 }
arr.find(function match(v) => v && v.a > 10); // undefined
arr.findIndex(function match(v) => v && v.a > 10); // -1
  • includes(): returns true or false depending on whether or not what you pass it exists in the array.

  • Array.flat(): essentially brings nested items out to a specified level.

        let nestedValues = [ 1, [2, 3], [[]], [4, [5] ], 6 ];
    
        nestedValues.flat(0) // [ 1, [ 2, 3 ], [[]], [ 4, [5] ], 6 ]
        // default value is 1 => flat()
        nestedValues.flat(1) // [ 1, 2, 3, [], 4, [ 5 ], 6 ]
        nestedValues.flat(2) // [ 1, 2, 3, 4, 5, 6 ]
    
  • Array.flatMap(): flattens while we are mapping rather than mapping and then flattening.

        [1,2,3,4,5,6].flatMap(function doubleEvens(v) {
            if (v % 2 === 0) {
                return [v, v*2];
            } else {
                return []
            }
        });
        // [2, 4, 4, 8, 6, 12]
    

Iterators and Generators

  • for of loop: takes in iterables that can be iterated over and gives you the iterated value of each iteration.
let str = "Hello";
let iter = str[Symbol.iterator](); // not required because strings are iterable, but this is how you make an iterable.

for (let v of iter) {
    console.log(v); // "H" "e" "l" "l" "o"
}

// alternatively we could just use spread
let letters = [...str];
  • Objects are not iterable by default.
  • We can however iterate over an object via a generator
function *main() {
    yield 1;
    yield 2;
    yield 3;
    return 4;
}

let iter = main();

iter.next(); // { value: 1, done: false }
iter.next(); // { value: 2, done: false }
iter.next(); // { value: 3, done: false }
iter.next(); // { value: 4,done: true }

[...main()];
//[1,2,3]
  • Below is how we make a declarative iterator for an object:
let obj = {
    a: 1,
    b: 2,
    c: 3,
    *[Symbol.iterator](){
        for (let key of Object.keys(this)) {
            yield this[key];
        }
    }
};

[...obj];
// [1,2,3]

Regular Expressions

  • Look Ahead: Finds a match and then only actually wants the match if whats after the match, matches the look ahead.

    • For example I only want this thing if there is a full stop after it.
    let msg ="Hello World";
    
    // Assertions
    msg.match(/(l.)/g);     // ["ll", "ld"]
    msg.match(/(l.)$/g);    // ["ld"] $ => End of string assertion
    
    // Look Ahead
    msg.match(/(l.)(?=o)/g) // ["ll"]           (?=o) => We only want the match if what is after the match is an "o"
    msg.match(/(l.)(?!o)/g) // ["lo", "ld"]     (?!o) => Negates above, any match that isn't followed by an o
    
  • Look Behind: Finds a match when it is preceded by a particular value

    msg.match(/(?<=e)(l.)/g);   // ["ll"]
    msg.match(/(?<!e)(l.)/g);   // ["lo", "ld"]
    
  • Capture Groups: Is a way to have a sub part of the pattern pulled out on top of the full pattern.

    msg.match(/.(l.)/); // ["ell", "ll"]
    
  • Named Capture Groups: Allow us to give meaningful names to groups and make code much more readable.

    msg.match(/(?<cap>l.)/).groups
    // {cap: "ll"}
    

Async Await

  • Provides a nice readable, syntactic way to write code that we need to wait for essentially.
  • Requires you to declare a function with async which then allows you to use the async keyword.
async function getData() {
    return await fetch(url);
}

Potential Issues:

  • The await keyword only knows how to deal with Promises and Thenable.
  • Scheduling happens in the microtask queue, which essentially means these promises skip the line in the event loop.
    • This can in some cases create a loop that will cut off anything else from running, as the promises will keep cutting in front infinitely.
    • This is also referred to as starvation.
  • External Cancellation (Kyle Article)
    • You want a way to be able to externally cancel, so you don't get stuck forever.
    • Kyle developed a utility called CAF which does this.

Async Generators:

  • We can create async generators to await results and push them out as they come in.
  • We can then use an await for loop in a async function to wait for these values to be pushed out, async iteration

Return