Created by
var i = 999;
if (true){
let i = 111;
console.log(`if block i=${i}`);
}
console.log(`main i=${i}`);
for (let i = 0; i < 3; i++) {
console.log(`for block i=${i}`);
}
console.log(`main i=${i}`);
let x;
let x = 2; // Uncaught SyntaxError: Identifier 'x' has already been declared
// next example will not through error, as second let declaration is in other scope
let x;
{
let x = 2;
}
const account = {
'balance' : 200
};
// TypeError: Assignment to constant variable.
account = {};
// no error - the object is mutable and const did not prevent that
account.balance = 300;
console.log(account.balance);
// 300
const pi = 3.14;
// function expression syntax:
let circleAreaExp = function(r){
return r*r*pi;
}
// arrow function syntax:
let circleAreaArrow = r=>r*r*pi;
(param1, param2, …, paramN) => { statements }
const pi = 3.14;
let circleArea = (r)=>{return r*r*pi};
console.log(`circleArea(2) = ${circleArea(2)}`);
In next slides we'll see how we can shorten the circleArea
definition
singleParam => { statements }
const pi = 3.14;
let circleArea = r=>{return r*r*pi};
console.log(`circleArea(2) = ${circleArea(2)}`);
(param1, param2, …, paramN) => expression
let circleArea = r=>r*r*pi;
console.log(`circleArea(2) = ${circleArea(2)}`);
return
is not implied.(param1, param2, …, paramN) => expression
(param1, param2, …, paramN) => {expression}
const pi = 3.14;
// no return is implied, so the return value will be 'undefined':
let circleArea = r=>{r*r*pi};
console.log(`circleArea(2) = ${circleArea(2)}`);
// undefined
call/apply or bind
methods!
const obj = {
'id': 1,
'exp': function(){
console.log(this.id);
},
'arr': ()=>{console.log(this.id)}
}
obj.exp(); // 1
obj.arr(); // undefined
lexicalThis = this;
var obj = {
'exp': function(){
console.log(this === lexicalThis);
},
'arr': ()=>{console.log(this === lexicalThis)}
}
obj.exp(); // false
obj.arr(); // true
const Person = function(name){
this.name = name;
this.greet = function(name){
console.log(`Hi ${name}, I'm ${this.name}`)
};
this.greetArr = name=>{
console.log(`Hi ${name}, I'm ${this.name}`)
};
}
const pesho = new Person('Pesho');
const friends = ['George', 'Ana'];
friends.forEach(pesho.greet)
friends.forEach(pesho.greetArr)
// OUTPUT:
// Hi George, I'm undefined
// Hi Ana, I'm undefined
// Hi George, I'm Pesho
// Hi Ana, I'm Pesho
arguments
object in arrow function is reference to the arguments object in the enclosing scope
const logArgs = ()=>{
for (let i = 0; i < arguments.length; i++) {
console.log(arguments[i]);
}
}
logArgs(1,2,3);
Before ES6
function f(x, y, z){
var x = x || 1;
var y = y || 2;
var z = z || 3;
console.log(x, y, z); //6,7,3
}
f(6, 7);
After ES6
function f(x=1, y=2, z=3){
console.log(x, y, z); //6,7,3
}
f(6, 7);
myFunction(...iterableObj);
function foo(a,b,c){
console.log(`a=${a}, b=${b}, c=${c}`)
}
let arr = [1,2,3];
// pass foo three arguments with spread operator (ES6 way):
foo(...arr); // a=1, b=2, c=3
// pass foo three arguments with the apply() method (ES5 way):
foo.apply(this, arr); //a=1, b=2, c=3
[...iterableObj, 4, 5, 6];
let arr = [1,2,3];
let str = 'abc';
console.log( [...arr, 9] ); // [1, 2, 3, 9]
console.log( [9, ...arr] ); // [9, 1, 2, 3]
console.log( [...arr, ...str] ); // [1, 2, 3, "a", "b", "c"]
arguments
object. The difference is that the ...rest
parameter is an array type
function foo(a, b, ...args){
// a = 1, b = 2, args = [3,4,5]
console.log(args); //"3, 4, 5"
}
foo(1, 2, 3, 4, 5);
function bar(a,...args, b){
console.log(args);
}
// SyntaxError: Rest parameter must be last formal parameter
// assign variables from array:
let [a, b] = [1,2];
console.log(`a = ${a}; b = ${b}`); // a = 1; b = 2
// we can first declare variables, and then destrucure:
let a,b;
[a,b] = [1,2]
console.log(`a = ${a}; b = ${b}`); // a = 1; b = 2
// same as above - no matter that we pass more elements:
let [a, b] = [1,2,3,4,5];
console.log(`a = ${a}; b = ${b}`); // a = 1; b = 2
// assign variables from array in conjunction with rest syntax:
let [a, ...rest] = [1,2,3];
console.log(a); // 1
console.log(rest); // [2, 3]
let a = 1, b = 2;
// we do the swap without tmp variable, just with one line:
[a,b] = [b,a];
console.log(`a = ${a}; b = ${b}`); // a = 2; b = 1
let obj = {p1: 1, p2: 2};
// assign obj properties to variables with same name:
let {p1, p2} = obj;
console.log(p1); // 1
console.log(p2); // 2
let obj = {p1: 1, p2: 2};
let p1,p2;
// assign obj properties to variables with same name (note the braces):
({p1, p2} = obj);
console.log(p1); // 1
console.log(p2); // 2
let obj = {'a':1,'b':2,'c':3}
// 'c' will go into c, and the rest of obj into obj2
let {c,...obj2} = obj
console.log(c);
console.log(obj2);
const userData = {
id: 1,
name:'Ada',
age: 23,
}
function greet( name, age ){
console.log(`Hello ${name}. You are ${age} years old!`);
}
greet(userData.name,userData.age);
// note the curly braces in params declaration
function greet( {name,age} ){
console.log(`Hello ${name}. You are ${age} years old!`);
}
greet(userData);
Note, that such object destructuring (in function call) is a very common pattern used in React!
let userName = 'pesho';
let userAge = 23;
// ES6 way:
let p1 = {userName, userAge};
// ES5 way:
// let p1 = {userName:userName, userAge:userAge};
console.log(p1); // { userName: 'pesho', userAge: 23 }
let p1 = {
name: 'Pesho',
greet(){
console.log(`Hi, I'm ${this.name}`);
}
}
p1.greet(); // Hi, I'm Pesho
let source = {a:1, b:2}
let target = {a:2, c:3}
let new_object = Object.assign(target, source);
console.log(target); // { a: 1, c: 3, b: 2 }
console.log(new_object); // { a: 1, c: 3, b: 2 }
let p1 = {
name: 'Pesho',
address: {
town: 'Sofia',
zip: 1504
}
}
// shallow copy the object:
let p2 = Object.assign({}, p1);
console.log(p2.address); // { town: 'Sofia', zip: 1504 }
// now change p1
p1.address.town = 'Plovdiv';
// check if the change is reflected into p2
console.log(p2.address); // { town: 'Plovdiv', zip: 1504 } (yes, so we have a shallow copy)
target = JSON.parse(JSON.stringify(source))
let p1 = {
name: 'Pesho',
address: {
town: 'Sofia',
zip: 1504
}
}
// create a deep copy
let p2 = JSON.parse(JSON.stringify(p1));
console.log(p2.address); // { town: 'Sofia', zip: 1504 }
// now change p1
p1.address.town = 'Plovdiv';
// check if the change is reflected into p2
console.log(p2.address); // { town: 'Sofia', zip: 1504 } (no, so we have a deep copy)
const obj1 = {
a:1,
b:2
}
const obj2 = {
c:3
}
// Variant 1: with spread operator:
const obj3 = {...obj1,...obj2};
// constiant 2: with Object.assign:
const obj4 = Object.assign({}, obj1, obj2)
console.log(obj3);
console.log(obj4);
// OUTPUT:
// { a: 1, b: 2, c: 3 }
// { a: 1, b: 2, c: 3 }
for...of
that creates a loop that iterates over iterable objects such as: Built-in Array, String, Map, Set, and Array-like objects such as arguments or NodeListfor..of
statement with for..in
statement
for (variable of iterable) {
// statements
}
let numbers = [1,2,3,4];
for (const num of numbers){
console.log(num);
}
const str = 'abc'
for(const l of str){
console.log(l);
}
// class declaration
class Person{
// method definitions
}
// class expression
let Person = class {
// method definitions
}
class Person{
constructor(name, age){
this.name = name;
this.age = age;
}
}
let p1 = new Person('Pesho', 23);
console.dir(p1);
class Person{
constructor(name, age){
this.name = name;
this.age = age;
}
greet(){
console.log(`I'm ${this.name}, ${this.age} years old.`);
}
}
let p1 = new Person('Pesho', 23);
console.dir(p1);
class Person{
constructor(name, age){
this.name = name;
this.age = age;
Person.counter();
}
// static method
static counter(){
Person.count+=1;
console.log(`${Person.count} objects were created.`);
}
greet(){
console.log(`I'm ${this.name}, ${this.age} years old.`);
}
}
// static (class) property
Person.count = 0;
let p1 = new Person('Pesho', 23);
let p2 = new Person('Maria', 28);
class Person{
name="Anonymous";
age;
constructor(name, age){
this.name = name;
this.age = age;
}
greet(){
console.log(`I'm ${this.name}, ${this.age} years old.`);
}
}
let p1 = new Person('Pesho', 23);
let p2 = new Person();
p1.greet();
p2.greet();
get
syntax binds an object property to a function that will be called when that property is looked up.set
syntax binds an object property to a function to be called when there is an attempt to set that property.
class Person{
constructor(name){
this._name = name || "Anonymous";
}
get name(){
console.log(`getting name at: ${new Date()}`);
return this._name;
}
set name(name){
console.log(`setting new name at: ${new Date()}`);
this._name = name;
}
greet(){
console.log(`I'm ${this.name}`); // here we use the getter
}
}
let p1 = new Person('Pesho', 23);
p1.name = 'Peter'; // here we use the setter
p1.greet();
extends
and super
keywordssupper
keyword is used to call the parent's constructor it must be used before the this keyword is used.super
keyword can be used to access parent properties as well (like calling parent methods), using the super.prop
or super[expr]
syntax
class Person{
constructor(name, age){
this.name = name;
this.age = age;
}
greet(){
console.log(`I'm ${this.name}, ${this.age} years old.`);
}
}
class Developer extends Person{
constructor(name, age, skills){
// call the parent constructor:
super(name, age)
this.skills = skills
}
greet(){
// call the parent greet() method
super.greet();
console.log(`My skills are: ${this.skills.join()}`);
}
}
let dev1 = new Developer('Pesho', 23, ['JS','React','Vue'])
dev1.greet()
These slides are based on
customised version of
framework