Skip to main content

Language 02

JS Comparisons 1

When comparing different types, JS converts values to numbers:

0 == false; // true
"" == false; // true
"01" == 1; // true

Problems:

Boolean(0); // false
Boolean("0"); // true
0 == "0"; // true

JS Comparison 2

null and undefined

null === undefined; // false, different types
null == undefined; // true

In equality:

null > 0; // false
null == 0; // false - special case. Null and undefined equal only eachother
null >= 0; // true

JS Comparisons 3

undefined > 0; // false
undefined < 0; // false
undefined == 0; //false

undefined gets converted to NaN, NaN returns false for all comparisons. In equality check – undefined only equals null, undefined and no other value.

JS Comparisons 4

  • Comparison operators return a boolean value.
  • Strings are compared letter-by-letter in the "dictionary" order.
  • When values of different types are compared, they get converted to numbers (with the exclusion of a strict equality check).
  • The values null and undefined equal == each other and do not equal any other value.
  • Be careful when using comparisons like > or < with variables that can occasionally be null/undefined. Checking for null/undefined separately is a good idea.
  • isNaN(value) – for NaN checking

Dialogs

  • alert(message) – Modal dialog, OK button.
  • result = prompt(title, [default]) – modal, with input field with default value and OK/Cancel. Result === nullif canceled.
  • Result = confirm(question) – modal, OK/Cancel. Result is true for OK, false otherwise.

Switch

  • Mostly normal
  • When first match is found, execution continues to next break.
  • Any expression can be switch/case argument.
  • Switch uses strict equality checks (===).

Functions

function showMessage() {
alert("Hello everyone!");
}
  • Does not need return statement (returns undefined).
  • Variables declared inside function are local
  • Can access variables from outside
  • Outer variable is only used, if there is not a local one (shadowed)
  • Pass arbitrary data to functions using parameters
  • If value for parameter is not provided – it becomes undefined
  • Can use default values function showMessage(text = "greetings"){}
  • Default is evaluated when no parameter was presented function showMessage(text = getText() ){}

Function expressions

let sayHi = function () {
alert("Hell");
};
  • In JavaScript, a function is not a "magical language structure", but a special kind of value.
  • Can be passed around as any other value
  • typeof( Math.abs ) === "function" // true
  • A Function Expression is created when the execution reaches it and is usable only from that moment.
  • A Function Declaration can be called earlier than it is defined.
  • In strict mode, when a Function Declaration is within a code block, it’s visible everywhere inside that block. But not outside of it.

Arrow functions

Multiline arrow functions – you need return statement

let sum = (a, b) => {
// the curly rbance opens a multuline function
let result = a + b;
return result; // if we use curly braces, then we need explicit "return"
};

alert(sum(1, 2)); // 3

For future discussion:

  • Arrow functions have no this. If this is accessed, it’s taken from outside.
  • Arrow functions have no arguments variable
  • No super also

Objects - Basics 1

  • Objects are used to store keyed collections of various data and more complex entities

  • Can be created with or with new Object()

  • Properties can be acced and added two ways: dot notation and brackets.

    let user = {
    name: "FooBar",
    };
    user.name = "akaver";
    user["name"] = "akaver"; //can use multiword properties
  • Properties can be removed with delete operator. delete user.name;

  • Properties names can be computed in case of [].

    let fruit = "apple";
    let bag = {
    [fruit + "Computers"]: 5,
    };

Objects - Basics 2

  • If property name in object will be the same as variable – shorthand syntax

    let name = "fooBar";
    let person = {
    name, // same as name:name
    };
  • Property name limitations

  • Strings or symbols (will be covered later)

  • Other types are converted to strings.

    let x = {
    1: "foo",
    };
    x["1"] === "foo";
  • Can use keywords as names.

    let x = { return: "foobar" };
  • Special property proto (later)

Objects - Basics 3

  • Property existance test

    user.noSuchProperty === undefined; // true when not found
  • Special operator in – key in object

    "age" in user;
    let key = "age";
    key in user;
  • Edge case

let x = { name: undefined }; // property exists

console.log(x.name === undefined); // true – but property is there?
console.log("name" in x); // true
console.log(x.foo === undefined); // true
console.log("foo" in x); // false

Objects - Basics 4

  • To walk over all keys of an object, there exists a special form of the loop: for..in
  • Or use Object.keys(someObject) – returns array of property names.
  • Or use Object.values(someObject)– returns array of property values.
let user = {
name: "John",
age: 30,
isAdmin: true,
};

for (let key in user) {
// keys
alert(key); // name, age, isAdmin

// values of the keys
alert(user[key]); // JOhn, 30, true
}

Objects - Basics 5

  • Object properties – order
  • Integer properties are sorted, others appear in creation order.
  • The integer property - a string that can be converted to-and-from an integer without a change.

Objects - Basics 6

Comparison by reference

let a = {};
let b = {};
alert(a == b); // false

Objects - Basics 7

  • Object copying – BY REFERENCE
  • Shallow copy
Object.assign(dest, [src1, src2, src3...]);
let clone = Object.assign({}, user);
  • What to do when properties are other objects – deep copy
  • There’s a standard algorithm for deep cloning that more complex cases, called the Structured cloning algorithm. Do not to reinvent the wheel, use a working implementation of it from the JavaScript library lodash, _.cloneDeep(obj).
let user = {
name: "John",
age: 30,
isAdmin: true,
};

let clone = {}; // the new empty object

// let's copy all user properties into it

for (let key in user) {
clone[key] = user[key];
}

// now clone is a fully independent clone
clone.name = "Pete";

alert(user.name); // still John in the original object

Symbols 1

Somewhat advanced

Symbol is a primitive type for unique identifiers.

Symbols are created with Symbol() call with an optional description (name).

Symbols are always different values, even if they have the same name.

For same-named symbols to be equal, use the global registry: Symbol.for(key) returns (creates if needed) a global symbol with key as the name. Multiple calls of Symbol.for with the same key return exactly the same symbol.

Symbols 2

Symbols don’t auto-convert to strings.

let id = Symbol("id");
alert(id); // error
alert(id.toString()); // ok "Symbol(id)"

Symbols allow to create "hidden" properties of an object, that no other part of code can accidentally access or overwrite.

let id = Symbol("id");
user[id] = 1;
let x = { [id]: "foo" };

Symbols 3

  • Symbols are skipped by for…in
  • Object.keys(user) also ignores them
  • Built-in method Object.getOwnPropertySymbols(obj) - get all symbols.
  • Also there is a method named Reflect.ownKeys(obj) - returns all keys of an object including symbolic ones.
  • Object.assign copies symbols over to new object.

Object methods, this

  • Objects properties can contain anything – including functions.

  • Can be defined in several ways.

  • To access current instance of the object – use this

  • this is not bound in js (its defined in run-time)!

  • In non strict mode – this will finally be window object

  • In strict mode – if not inside object – this == undefined

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

user.sayHi = function () {
alert("Hello!");
};

user = {
sayHi: function () {
alert("Hello");
},
};

user = {
sayHi() {
alert("Hello");
},
};

Arrow functions and this

Arrow functions have no this

Arrow functions are special: they don’t have their "own" this. When referenced this from such a function, it’s taken from the outer "normal" function.

let user = {
firstName: "Toomas",
sayHi() {
let arrow = () => alert(this.firstName);
arrow();
},
};

user.sayHi(); // Toomas

Object - converting to primitives

Advanced topic, later

  • obj + obj or obj - obj
  • alert(obj)

The conversion algorithm is:

  • Call obj[Symbol.toPrimitive](hint) if the method exists,
  • Otherwise if hint is "string"
    • try obj.toString() and obj.valueOf(), whatever exists.
  • Otherwise if hint is "number" or "default"
    • try obj.valueOf() and obj.toString(), whatever exists.
let user = {
name: "John",
money: 1000,

[Symbol.toPrimitive](hint) {
alert(`hint: ${hint}`);
return hint == "string" ? `{name: "${this.name}"}` : this.money;
},
};

Constructor functions, operator new

Constructor functions – just regular functions, few conventions

  • Named with capital letters

  • Should only be executed with “new" operator

  • A new empty object is created and assigned to this.

  • The function body executes. Usually it modifies this, adds new properties to it.

  • The value of this is returned.

function User(name) {
// this = {}; (implicitly)

// add properties to this
this.name = name;
this.isAdmin = false;

// return this; (implicitly)
}

let user = new User("Jack");

Constructor functions 2

Inside a function, we can check whether it was called with new or without it, using a special new.target property.

function User(name) {
if (!new.target) {
// if you run me without new
return new User(name); // ...I will add new for you
}
this.name = name;
}

let john = User("John"); // redirects call to new User
alert(john.name); // John

Constructor functions, return

Usually, constructors do not have a return statement. Their task is to write all necessary stuff into this, and it automatically becomes the result.

But if there is a return statement:

  • If return is called with an object, then the object is returned instead of this.
  • If return is called with a primitive, it’s ignored.

Destructing assignment

  • Two most used data structures in JS – Array and Object
  • Objects allow us to create a single entity that stores data items by key, and arrays allow us to gather data items into an ordered collection.
  • You can ignore elements with extra commas
  • Works with any iterable on right hand side
  • Assign to anything in left hand side
  • Gather remaining values into new array with “…putRestHere"
  • Absent values – undefined, can use defaults
let arr = ["Foo", "Bar"];

// destructuring assignment
// sets firstName = arr[0]
// and surname = arr[1]
let [firstName, surname] = arr;

Destructuring objects

  • Also works with objects:

    let {var1, var2} = {var1:, var2:}
  • Ordering of variables does not matter, names have to match.

  • Names on the left can be changed { sourceProperty: targetVariable }

  • In case of pre-existing variables use parenthesis

    let title, width, height;
    ({ title, width, height } = { title: "Menu", width: 200, height: 100 });
    function showMenu({ title = "Menu", width = 100, height = 200 } = {}) {
    alert(`${title} ${width} ${height}`);
    }

    showMenu(); // Menu 100 200

Rest parameters

  • Any function can be called with any number of parameters

  • Use rest parameter to collect arguments into array

    function showName(firstName, lastName, ...titles) {}
  • Rest parameter must be the last one

  • There is older built-in array like arguments variable (but its not array)

  • Arrow functions do not have arguments variable

Spread syntax

  • Reverse of rest parameters

    let arr = [3, 5, 1];
    alert(Math.max(...arr)); // 5 (spread turns array into a list of arguments)
  • Can be used several times and mixed with normal parameters

  • Can be used to concatenate arrays

    let arr = [3, 5, 1];
    let arr2 = [8, 9, 15];
    let merged = [0, ...arr, 2, ...arr2];
  • Can be used on objects the same way

    const o1 = { a: "va", b: "vb" };
    const o2 = { c: "vc", ...o1 };

Object getters and setters

From outside, an accessor property looks like a regular one.

let obj = {
get propName() {
// getter, the code executed on getting obj.propName
},

set propName(value) {
// setter, the code executed on setting obj.propName = value
},
};