Skip to content

Language 02

JS Comparisons 1

When comparing different types, JS converts values to numbers:

1
2
3
0 == false  // true
'' == false // true
'01' == 1   // true

Problems:

1
2
3
Boolean(0)      // false
Boolean("0")    // true
0 == "0"        // true

JS Comparison 2

null and undefined

1
2
null === undefined // false, different types
null == undefined // true

In equality:

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

JS Comparisons 3

1
2
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

1
2
3
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

1
2
3
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

1
2
3
4
5
6
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.

    1
    2
    3
    4
    5
    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 [].

    1
    2
    3
    4
    let fruit = 'apple';
    let bag = {
        [fruit + 'Computers']: 5
    };
    

Objects - Basics 2

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

    1
    2
    3
    4
    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.

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

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

Objects - Basics 3

  • Property existance test

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

    1
    2
    3
    "age" in user 
    let key = "age" 
    key in user
    
  • Edge caselet

    1
    2
    3
    x = {name: undefined};  // property exists 
    x.test === undefined    // true – but property is there?
    "name" in x             // true – correct, property exists
    

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.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
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

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

Objects - Basics 7

  • Object copying – BY REFERENCE
  • Shallow copy
1
2
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).
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
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.

1
2
3
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.

1
2
3
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
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
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.

1
2
3
4
5
6
7
8
9
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 objSymbol.toPrimitive 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.
1
2
3
4
5
6
7
8
9
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.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
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.

1
2
3
4
5
6
7
8
9
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
1
2
3
4
5
6
let arr = ["Foo", "Bar"];

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

Destructuring objects

  • Also works with objects:

    1
    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

    1
    2
        let title, width, height; 
        ({title, width, height} = {title: "Menu", width: 200, height: 100});
    
    1
    2
    3
    4
    5
    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

    1
    2
    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

    1
    2
    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

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

    1
    2
    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.

1
2
3
4
5
6
7
8
9
let obj = {
    get propName() {
        // getter, the code executed on getting obj.propName
    },

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