let inside a block is valid only within the block: block-scope local variablelet declaration has global scope
let a = "a"; // global vs local?
b = "b"; // global vs local?
function f()
{
c = "c"; // global vs local?
let d = "d"; // global vs local?
}
let vs varlet was introduced in ES6let, var was used with the following difference
var has function scope (as opposed to block scope)var is hoisted (vs no hoisting)
let produces cleaner code. Use it!var Examplevar a = 10; // global vs local?
function f() {
b = 10; // global vs local?
console.log(b);
var b;
}
f();
console.log(b);
typeof returns function not objectfunction square(x) { return x*x; };
function myfunc(x, func) {
return func(x);
}
myfunc(10, square);
myfunc(10, function (x) { return x * 2; }); // anonymous function
myfunc.a = 20;
function outer_function() {
console.log("a")
function inner_function() {
console.log(1);
}
console.log("b");
inner_function();
}
outer_function(); // what will be printed?
function f() {
let a = 1;
let b = 2;
function g() {
console.log(b); // b = ?
b = 3;
}
if (a > 0) {
let b = 4;
g();
console.log(b); // b = ?
}
console.log(b); // b = ?
}
f(); // what will be printed?
function getFunc() {
function printUCLA() { console.log("UCLA"); }
return printUCLA;
}
let func = getFunc();
func();
Closure: When a nested function references non-local variables and is returned, it is “bundled together” with the referenced variables
function getFunc() {
let age = 10;
function printAge() { console.log(++age); }
return printAge;
}
let func = getFunc();
func(); func(); // what will be printed?
printAge() does not have its own local variable, but the returned printAge carries the age variable from its surrounding context!
function getFunc() {
let age = 10;
function printAge() { console.log(++age); }
return printAge;
}
let myFunc1 = getFunc();
myFunc1(); myFunc1();
let myFunc2 = getFunc();
myFunc2(); myFunc2();
(function() {
var count = 0;
function helper() {
console.log(`Help called ${++count} times!`);
}
helper();
// ...
helper();
})() // create an anonymous function and call it immediately
count and helper()In JavaScript, we often have to pass a function as a parameter
function ChangeColor(event) {
document.body.style.color = "red";
}
document.body.addEventListener("click", ChangeColor);
Polluting namespace can be avoided using anonymous functions
document.body.addEventListener("click", function(event) {
document.body.style.color = "red";
});
document.body.addEventListener("click", (event) => {
document.body.style.color = "red";
});
(param1, ...) => expression
(param1, ...) => { statements; }
() => expression returns the value of expression() => { statements; } should return a value explicitlylet o = { x: 10 };
o.multiply = function (v) { this.x *= v; }
o.multiply(5);
console.log(o.x);
this points to the object itselfclass Shape {
constructor(color) { // constructor() is class constructor
this.color = color;
}
info() { return "color: " + this.color; }
whoami() {
console.log("I am a Shape with " + this.info());
}
};
s = new Shape('blue');
s.whoami();
class Rectangle extends Shape {
constructor(color, x, y) {
super(color); // super refers to the parent class
this.x = x;
this.y = y;
}
info() {
return `${super.info()}, x: ${this.x}, y: ${this.y}`;
}
};
let r = new Rectangle("red", 2, 3);
r.whoami();
prototype object
If a vaiable is undefined or null, we get an error
let obj;
console.log(obj.name); // Error: obj is undefined!
Checking for the error is ugly
let obj;
console.log(obj ? obj.name : undefined);
Instead of throwing an error, optional chaining operator returns undefined:
let obj;
console.log(obj?.name); // returns undefined
thisthis is a source of great confusion and bug in JavaScriptthis
this = called object/classthis = DOM element to which event handler was setthis = the global objectthis in Event Handling Callthis binds to the DOM element where the handler is set<body id="body_id">
...
</body>
<script>
document.body.addEventListener("click", function (event) {
console.log(this.id); // what does this bind to?
});
</script>
this in Other Placesthis is used in other than class method or event handler, this binds to the global objectglobalThis in ES2020)
window objectglobal objectthis Binding() => {}) does not provide its own this binding
this binding of the enclosing lexical context<body id="body_id">
...
</body>
<script>
document.body.addEventListener("click", (event) => {
console.log(this.id); // what does this bind to?
});
</script>
thisx = 10;
function_printx = function() { console.log(this.x); };
arrow_printx = () => { console.log(this.x); };
o = { x: 20 };
o.printx_f = function_printx;
o.printx_a = arrow_printx;
// What will be printed?
console.log(this.x);
function_printx();
arrow_printx();
o.printx_f();
o.printx_a();
thisthis changes dynamically depending on how a function is called
this confusing and hard to understand, leading to many bugsthis only in object/class methodreverse, sort, push, pop, shift, unshift, spliceconcat, slice, filter, maplet a = [1, 2, 3, 4];
let b = a;
console.log(b);
a[1] = 5;
console.log(b);
a = [1, 2, 3];
console.log(b);
let a = [1, 2, 3];
let b = a.reverse(); // reverse is a mutator
console.log(b);
a[1] = 6;
console.log(b);
a = [1, 2, 3];
b = a.concat([4, 5]); // concat is an accessor
console.log(b);
a[1] = 6;
console.log(b);
let o = { userid: 10, password: "secret" };
const { userid, password, email = "default_email" } = o;
// userid = 10, password = "secret", email = "default_email"
let a = [1, 2, 3, 4];
let [a1, a2, ...rest] = a;
// a1 = 1, a2 = 2, rest = [3, 4]
exportexported entities can be imported and used by other JavaScript code//------ lib.js ------
export function square(x) {
return x * x;
}
export function dist(x, y) {
return Math.sqrt(square(x) + square(y));
}
//------ main.js ------
import { square } from './lib.js';
square(11);
//----- main2.js ------
import * as mylib from './lib.js';
mylib.dist(4, 3);
//------ lib.js ------
export default function () { ... }
//------ main1.js ------
import myFunc from './lib.js';
myFunc();
{ } to import default export{ } to import named export (even if we import just one)