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)
– forNaN
checking
Dialogs
alert(message)
– Modal dialog, OK button.result = prompt(title, [default])
– modal, with input field with default value and OK/Cancel.Result === null
if canceled.Result = confirm(question)
– modal, OK/Cancel. Result istrue
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
. Ifthis
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
orobj - obj
alert(obj)
The conversion algorithm is:
- Call
obj[Symbol.toPrimitive](hint)
if the method exists, - Otherwise if hint is "string"
- try
obj.toString()
andobj.valueOf()
, whatever exists.
- try
- Otherwise if hint is "number" or "default"
- try
obj.valueOf()
andobj.toString()
, whatever exists.
- try
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
},
};