Sunday, 24 March 2019

JS time - again

Been a while but I need to immerse myself back into the fun that is JavaScript.

IDE-Editor choice

If you haven’t selected an IDE yet, consider the following options:
  • WebStorm for frontend development. The same company offers other editors for other languages (paid).
  • Netbeans (free).
All of these IDEs are cross-platform.
For Windows, there’s also “Visual Studio”, not to be confused with “Visual Studio Code.” “Visual Studio” is a paid and mighty Windows-only editor, well-suited for the .NET platform. A free version of it is called Visual Studio Community.
+
always "use strict"; at top of file or in function header
+
Var takes global or function scope and is always declared at start, regardless of where it is written. Assignment is still in place though.
use 'let' and 'const' instead.
+
Besides regular numbers, there are so-called “special numeric values” which also belong to this data type: Infinity-Infinity and NaN.
NaN represents a computational error. It is a result of an incorrect or an undefined mathematical operation
NaN is sticky. Any further operation on NaN returns NaN
+
there are 3 types of quotes.
  1. Double quotes: "Hello".
  2. Single quotes: 'Hello'.
  3. Backticks: `Hello`.
Double and single quotes are “simple” quotes. There’s no difference between them in JavaScript.
Backticks are “extended functionality” quotes. They allow us to embed variables and expressions into a string by wrapping them in ${…}
for example, alert( `Hello, ${name}!` );
+
null is just a special value which represents “nothing”, “empty” or “value unknown”.
The meaning of undefined is “value is not assigned”.
+
objects are used to store collections of data and more complex entities. 
+
typeof operator returns the type of the argument
+
type conversion; explicit for toString, toNumber, toBoolean.
+
A strict equality operator === checks the equality without type conversion.

In other words, if a and b are of different types, then a === b immediately returns false without an attempt to convert them

note!! non-strict null==undefined. is true, null===undefined is false.
+
We covered 3 browser-specific functions to interact with visitors:
alert("hello")
shows a message.
prompt("Type a number", 4) // 4 would be default
shows a message asking the user to input text. It returns the text or, if CANCEL or Esc is clicked, null.
confirm("please confirm")
shows a message and waits for the user to press “OK” or “CANCEL”. It returns true for OK and false for CANCEL/Esc.
+
conditional if (), else if(), else 
or ternary operator- condition? true : false;
+
conditional loops all as expected;
while, do-while, for(;;), and for each
break optional_label, and continue
label is just 'labelexample:'
+
switch will cascade without breaks
switch(x) {
  case 'value1':  // if (x === 'value1')
    ...
    [break]

  case 'value2':  // if (x === 'value2')
    ...
    [break]

  default:
    ...
    [break]
}
Any expression can be a switch/case argument but case must check same type
+
functions...
This function declaration

function sayHi() {
  alert( "Hello" );
}
is the basically the same as this function expression

let sayHi = function() {
  alert( "Hello" );
};
A function is JS is a value and can be treated as such, including be reassigned. 
function expression needs semi-colon because its part of an expression. 
callback values can take a function name or an inline / anonymous function ie function() { alert("You agreed."); },
A Function Declaration is usable in the whole script/code block.
When a Function Declaration is made within a code block, it is visible everywhere inside that block. But not outside of it.
A Function Expression is created when the execution reaches it and is usable from then on.
+
Arrow functions

let sum = (a, b) => a + b;

/* The arrow function is a shorter form of:

let sum = function(a, b) {
  return a + b;
};
*/
If we have only one argument, then parentheses can be omitted:

// same as
// let double = function(n) { return n * 2 }
let double = n => n * 2;
If there are no arguments, parentheses should be empty (but they should be present):

let sayHi = () => alert("Hello!");
multiline example using  curly braces and a return

let sum = (a, b) => {  // the curly brace opens a multiline function
  let result = a + b;
  return result; // if we use curly braces, use return to get results
};

Summary

  • Functions are values. They can be assigned, copied or declared in any place of the code.
  • If the function is declared as a separate statement in the main code flow, that’s called a “Function Declaration”.
  • If the function is created as a part of an expression, it’s called a “Function Expression”.
  • Function Declarations are processed before the code block is executed. They are visible everywhere in the block.
  • Function Expressions are created when the execution flow reaches them.
In most cases when we need to declare a function, a Function Declaration is preferable, because it is visible prior to the declaration itself. That gives us more flexibility in code organization, and is usually more readable.
So we should use a Function Expression only when a Function Declaration is not fit for the task. We’ve seen a couple of examples of that in this chapter, and will see more in the future.
Arrow functions are handy for one-liners. They come in two flavors:

  1. Without curly braces: (...args) => expression – the right side is an expression: the function evaluates it and returns the result.
  2. With curly braces: (...args) => { body } – brackets allow us to write multiple statements inside the function, but we need an explicit return to return something.

  • Functions may have local variables: those declared inside its body. Such variables are only visible inside the function.
  • Parameters can have default values: function sum(a = 1, b = 2) {...}.
  • Functions always return something. If there’s no return statement, then the result is undefined.
+

Debugger command

We can also pause the code by using the debugger command, like this:




function hello(name) {
  let phrase = `Hello, ${name}!`;

  debugger;  // <-- the debugger stops here

  say(phrase);
}

That’s very convenient when we are in a code editor and don’t want to switch to the browser and look up the script in developer tools to set the breakpoint.
+
documentation

Good comments

So, explanatory comments are usually bad. Which comments are good?
Describe the architecture
Provide a high-level overview of components, how they interact, what’s the control flow in various situations… In short – the bird’s eye view of the code. There’s a special diagram language UML for high-level architecture diagrams. Definitely worth studying.
Document a function usage
There’s a special syntax JSDoc to document a function: usage, parameters, returned value.
For instance:
/**
 * Returns x raised to the n-th power.
 *
 * @param {number} x The number to raise.
 * @param {number} n The power, must be a natural number.
 * @return {number} x raised to the n-th power.
 */
function pow(x, n) {
  ...
}
Such comments allow us to understand the purpose of the function and use it the right way without looking in its code.
+
Unit testing with mocha
https://mochajs.org
https://javascript.info/testing-mocha go back and read this later. 
one more rule that’s good to follow.

One test checks one thing.
+
Objects
An empty object (“empty cabinet”) can be created using one of two syntaxes:

let user = new Object(); // "object constructor" syntax
let user = {};  // "object literal" syntax

Literals and properties

We can immediately put some properties into {...} as “key: value” pairs:

let user = {     // an object
  name: "John",  // by key "name" store value "John"
  age: 30        // by key "age" store value 30
};
To remove a property, we can use delete operator:

delete user.age;
We can also use multiword property names, but then they must be quoted:

let user = {
  name: "John",
  age: 30,
  "likes birds": true  // multiword property name must be quoted
};
The last property in the list may end with a comma:


          let user = {
  name: "John",
  age: 30,
}

That is called a “trailing” or “hanging” comma. Makes it easier to add/remove/move around properties, because all lines become alike.

Square brackets

For multiword properties, the dot access doesn’t work:
// this would give a syntax error
user.likes birds = true
That’s because the dot requires the key to be a valid variable identifier. That is: no spaces and other limitations.
There’s an alternative “square bracket notation” that works with any string:

let user = {};

// set
user["likes birds"] = true;

// get
alert(user["likes birds"]); // true

// delete
delete user["likes birds"];

Property value shorthand

In real code we often use existing variables as values for property names.
For instance:
function makeUser(name, age) {
  return {
    name: name,
    age: age
    // ...other properties
  };
}

let user = makeUser("John", 30);
alert(user.name); // John
In the example above, properties have the same names as variables. The use-case of making a property from a variable is so common, that there’s a special property value shorthand to make it shorter.
Instead of name:name we can just write name, like this:






function makeUser(name, age) {
  return {
    name, // same as name: name
    age   // same as age: age
    // ...
  };
}
We can use both normal properties and shorthands in the same object:

let user = {
  name,  // same as name:name
  age: 30
};

Existence check

A notable objects feature is that it’s possible to access any property. There will be no error if the property doesn’t exist! Accessing a non-existing property just returns undefined. It provides a very common way to test whether the property exists – to get it and compare vs undefined:
let user = {};

alert( user.noSuchProperty === undefined ); // true means "no such property"
There also exists a special operator "in" to check for the existence of a property.
The syntax is:
"key" in object
For instance:
let user = { name: "John", age: 30 };

alert( "age" in user ); // true, user.age exists
alert( "blabla" in user ); // false, user.blabla doesn't exist
Please note that on the left side of in there must be a property name. That’s usually a quoted string.
If we omit quotes, that would mean a variable containing the actual name will be tested. For instance:



          let user = { age: 30 };

let key = "age";
alert( key in user ); // true, takes the name from key and checks for such property

Using “in” for properties that store undefined
Usually, the strict comparison "=== undefined" check works fine. But there’s a special case when it fails, but "in" works correctly.
It’s when an object property exists, but stores undefined:
let obj = {
  test: undefined
};

alert( obj.test ); // it's undefined, so - no such property?

alert( "test" in obj ); // true, the property does exist!
In the code above, the property obj.test technically exists. So the in operator works right.
Situations like this happen very rarely, because undefined is usually not assigned. We mostly use null for “unknown” or “empty” values. So the in operator is an exotic guest in the code.

The “for…in” loop

To walk over all keys of an object, there exists a special form of the loop: for..in. This is a completely different thing from the for(;;) construct that we studied before.
The syntax:
for (key in object) {
  // executes the body for each key among object properties
}
primitives are used by value, objects by reference.

Summary

Objects are associative arrays with several special features.
They store properties (key-value pairs), where:
  • Property keys must be strings or symbols (usually strings).
  • Values can be of any type.
To access a property, we can use:
  • The dot notation: obj.property.
  • Square brackets notation obj["property"]. Square brackets allow to take the key from a variable, like obj[varWithKey].
Additional operators:
  • To delete a property: delete obj.prop.
  • To check if a property with the given key exists: "key" in obj.
  • To iterate over an object: for (let key in obj) loop.
Objects are assigned and copied by reference. In other words, a variable stores not the “object value”, but a “reference” (address in memory) for the value. So copying such a variable or passing it as a function argument copies that reference, not the object. All operations via copied references (like adding/removing properties) are performed on the same single object.
To make a “real copy” (a clone) we can use Object.assign or _.cloneDeep(obj).
What we’ve studied in this chapter is called a “plain object”, or just Object.
There are many other kinds of objects in JavaScript:
  • Array to store ordered data collections,
  • Date to store the information about the date and time,
  • Error to store the information about an error.
  • …And so on.
They have their special features that we’ll study later. Sometimes people say something like “Array type” or “Date type”, but formally they are not types of their own, but belong to a single “object” data type. And they extend it in various ways.
Objects in JavaScript are very powerful. Here we’ve just scratched the surface of a topic that is really huge. We’ll be closely working with objects and learning more about them in further parts of the tutorial.
+
symbols in objects
https://javascript.info/symbol

Summary

Symbol is a primitive type for unique identifiers.
Symbols are created with Symbol() call with an optional description.
Symbols are always different values, even if they have the same name. If we want same-named symbols to be equal, then we should use the global registry: Symbol.for(key) returns (creates if needed) a global symbol with key as the name. Multiple calls of Symbol.for return exactly the same symbol.
Symbols have two main use cases:
  1. “Hidden” object properties. If we want to add a property into an object that “belongs” to another script or a library, we can create a symbol and use it as a property key. A symbolic property does not appear in for..in, so it won’t be occasionally listed. Also it won’t be accessed directly, because another script does not have our symbol, so it will not occasionally intervene into its actions.
    So we can “covertly” hide something into objects that we need, but others should not see, using symbolic properties.
  2. There are many system symbols used by JavaScript which are accessible as Symbol.*. We can use them to alter some built-in behaviors. For instance, later in the tutorial we’ll use Symbol.iterator for iterablesSymbol.toPrimitive to setup object-to-primitive conversion and so on.
Technically, symbols are not 100% hidden. There is a built-in method Object.getOwnPropertySymbols(obj)that allows us to get all symbols. Also there is a method named Reflect.ownKeys(obj) that returns all keys of an object including symbolic ones. So they are not really hidden. But most libraries, built-in methods and syntax constructs adhere to a common agreement that they are. And the one who explicitly calls the aforementioned methods probably understands well what he’s doing.
+
object methods and 'this'

Summary

  • Functions that are stored in object properties are called “methods”.
  • Methods allow objects to “act” like object.doSomething().
  • Methods can reference the object as this.
The value of this is defined at run-time.
  • When a function is declared, it may use this, but that this has no value until the function is called.
  • That function can be copied between objects.
  • When a function is called in the “method” syntax: object.method(), the value of this during the call is object.
Please note that arrow functions are special: they have no this. When this is accessed inside an arrow function, it is taken from outside.
+
numbers

Summary

To write big numbers:
  • Append "e" with the zeroes count to the number. Like: 123e6 is 123 with 6 zeroes.
  • A negative number after "e" causes the number to be divided by 1 with given zeroes. That’s for one-millionth or such.
For different numeral systems:
  • Can write numbers directly in hex (0x), octal (0o) and binary (0b) systems
  • parseInt(str, base) parses an integer from any numeral system with base: 2 ≤ base ≤ 36.
  • num.toString(base) converts a number to a string in the numeral system with the given base.
For converting values like 12pt and 100px to a number:
  • Use parseInt/parseFloat for the “soft” conversion, which reads a number from a string and then returns the value they could read before the error.
For fractions:
  • Round using Math.floorMath.ceilMath.truncMath.round or num.toFixed(precision).
  • Make sure to remember there’s a loss of precision when working with fractions.
More mathematical functions:
  • See the Math object when you need them. The library is very small, but can cover basic needs.
+
strings are immutable. If you need to change, make new one.
string.length is a property not a method, so no ()
string []  to access characters. (or charAt()). first retusns char or undefined. Second returns char or ""
We can also iterate over characters using for..of:
Methods toLowerCase() and toUpperCase() change the case:

Searching for a substring

There are multiple ways to look for a substring within a string.

str.indexOf

The first method is str.indexOf(substr, pos).
let str = 'Widget with id';

alert( str.indexOf('id', 2) ) // 12
str.lastIndexOf(substr, position) does this in reverse

includes, startsWith, endsWith

The more modern method str.includes(substr, pos) returns true/false depending on whether strcontains substr within.
It’s the right choice if we need to test for the match, but don’t need its position:
alert( "Widget with id".includes("Widget") ); // true

alert( "Hello".includes("Bye") ); // false
The optional second argument of str.includes is the position to start searching from:
alert( "Midget".includes("id") ); // true
alert( "Midget".includes("id", 3) ); // false, from position 3 there is no "id"
The methods str.startsWith and str.endsWith do exactly what they say:
alert( "Widget".startsWith("Wid") ); // true, "Widget" starts with "Wid"
alert( "Widget".endsWith("get") );   // true, "Widget" ends with "get"

Getting a substring

There are 3 methods in JavaScript to get a substring: substringsubstr and slice.

str.slice(start [, end])
Returns the part of the string from start to (but not including) end.
For instance:
let str = "stringify";
alert( str.slice(0, 5) ); // 'strin', the substring from 0 to 5 (not including 5)
alert( str.slice(0, 1) ); // 's', from 0 to 1, but not including 1, so only character at 0
Negative values for start/end are also possible. They mean the position is counted from the string end:

Summary

Array is a special kind of object, suited to storing and managing ordered data items.
  • The declaration:
    // square brackets (usual)
    let arr = [item1, item2...];
    
    // new Array (exceptionally rare)
    let arr = new Array(item1, item2...);
    The call to new Array(number) creates an array with the given length, but without elements.
  • The length property is the array length or, to be precise, its last numeric index plus one. It is auto-adjusted by array methods.
  • If we shorten length manually, the array is truncated.
We can use an array as a deque with the following operations:
  • push(...items) adds items to the end.
  • pop() removes the element from the end and returns it.
  • shift() removes the element from the beginning and returns it.
  • unshift(...items) adds items to the beginning.
To loop over the elements of the array:
  • for (let i=0; i<arr.length; i++) – works fastest, old-browser-compatible.
  • for (let item of arr) – the modern syntax for items only,
  • for (let i in arr) – never use.

We will return to arrays and study more methods to add, remove, extract elements and sort arrays in the chapter Array methods.

Summary

A cheatsheet of array methods:
  • To add/remove elements:
    • push(...items) – adds items to the end,
    • pop() – extracts an item from the end,
    • shift() – extracts an item from the beginning,
    • unshift(...items) – adds items to the beginning.
    • splice(pos, deleteCount, ...items) – at index pos delete deleteCount elements and insert items.
    • slice(start, end) – creates a new array, copies elements from position start till end (not inclusive) into it.
    • concat(...items) – returns a new array: copies all members of the current one and adds items to it. If any of items is an array, then its elements are taken.
  • To search among elements:
    • indexOf/lastIndexOf(item, pos) – look for item starting from position pos, return the index or -1 if not found.
    • includes(value) – returns true if the array has value, otherwise false.
    • find/filter(func) – filter elements through the function, return first/all values that make it return true.
    • findIndex is like find, but returns the index instead of a value.
  • To iterate over elements:
    • forEach(func) – calls func for every element, does not return anything.
  • To transform the array:
    • map(func) – creates a new array from results of calling func for every element.
    • sort(func) – sorts the array in-place, then returns it.
    • reverse() – reverses the array in-place, then returns it.
    • split/join – convert a string to array and back.
    • reduce(func, initial) – calculate a single value over the array by calling func for each element and passing an intermediate result between the calls.
  • Additionally:
    • Array.isArray(arr) checks arr for being an array.
Please note that methods sortreverse and splice modify the array itself.
These methods are the most used ones, they cover 99% of use cases. But there are few others:
  • arr.some(fn)/arr.every(fn) checks the array.
    The function fn is called on each element of the array similar to map. If any/all results are true, returns true, otherwise false.
  • arr.fill(value, start, end) – fills the array with repeating value from index start to end.
  • arr.copyWithin(target, start, end) – copies its elements from position start till position end into itself, at position target (overwrites existing).
For the full list, see the manual.
From the first sight it may seem that there are so many methods, quite difficult to remember. But actually that’s much easier than it seems.
Look through the cheatsheet just to be aware of them. Then solve the tasks of this chapter to practice, so that you have experience with array methods.

Afterwards whenever you need to do something with an array, and you don’t know how – come here, look at the cheatsheet and find the right method. Examples will help you to write it correctly. Soon you’ll automatically remember the methods, without specific efforts from your side.


+
read again later.
https://javascript.info/iterable


+

No comments:

Post a Comment