# Javascript
- JavaScript/Ecmasript is a standard.
- Ecmasript 2015 (ES6) is currently implemented in all browsers.
- Implementation of standards
- Google's v8 engine (written in C++)
- Node uses v8 & adds extra stuffs like filesystem, http, etc
- Npm, Yarn are package managers to fetch 3rd party js library.
- JavaScript Core is related to v8 engine.
- But JavaScript DOM is related to browsers.
- This is why we have JavaScript Core in Node but not Javascript DOM.
TIP
"use strict"
Always "use strict". Helps understand, optimize & avoid old JavaScript pitfalls.
Object.getOwnPropertyNames(obj)
- Lists both enumerable & non-enumerable properties for obj.
- Help us know if its own property or inherited.
function foo() {}
console.log(foo); // foo() {}
console.log(Object.getOwnPropertyNames(foo)); // ["length", "name", "arguments", "caller", "prototype"]
# Prototype Model
# Execution Context
- Everytime we run javascript code a
global
execution context is created by the js engine. - This
global
context has various features likewindow
object in browser. Similarly other features in nodejs. - This context consists of 2 phases.
- Memory allocation
- Variables - gets value as
undefined
. - Functions - store the defination of that function
- Variables - gets value as
- Code execution (synchronously and single thread)
- Variables - gets value as assigned in the code
- Functions - If invoked will create another
local
execution context.
global
& alllocal
contexts are loaded in acallstack
. Each new context will have it's previous context variables & functions in it's scope. It is calledscope chain
- When the execution context is done running it gets destroyed.
# <script>
# In <body>
<!-- at the end of <body> -->
<script src="path/to/foo.js"></script>
# In <head>
<!-- defer/async - Download & Run -->
<!-- defer - Run after html is loaded - order is maintained -->
<script defer src="..."></script>
<!-- async - Runs immediately - order is not maintained -->
<script async src="..."></script>
# Examples
<!-- crossorigin - if using cdn -->
<script crossorigin src="..."></script>
<script
crossorigin
src="https://unpkg.com/react@17/umd/react.production.min.js"
></script>
<script
src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/js/bootstrap.bundle.min.js"
integrity="sha384-ygbV9kiqUc6oa4msXn9868pTtWMgiQaeYH7/t7LECLbyPA2x65Kgf80OJFdroafW"
crossorigin="anonymous"
></script>
# <link>
<!-- prefetch/preload - Download & Cache -->
<!-- Both js/css -->
<!-- Advantage - No DOM blocking -->
<!-- prefetch - downloads in idle time by browser (maybe needed in future) -->
<!-- preload - downloads asap (needed on same page in future) -->
<link rel="prefetch" href="..." />
<link rel="preload" href="..." />
# Basics
# Console
console.clear(); // clear history
let c = (...s) => console.log(...s); // shortcut
c("hello", "world"); // hello world
c(3, 2); // 3 2
# Syntax
"hello", 23; // Literals
"foo" + "bar"; // Expression
let foo, _foo, $foo; // Identifiers
let foo, Foo; // case-sensitive
let fooBar; // camelCase
let foo; // declaration
foo = 23; // assignment
let foo = 23; // declare & assignment
c(2 == "2"); // true - Loose comparison - NEVER USE
c(2 === "2"); // false - Strict comparison
# "use strict"
- Always
"use strict"
. Helps understand, optimize & avoid old JavaScript pitfalls. - Modules always use strict automatically.
- Strict and non-strict modes can coexist.
- Both syntax & runtime are affected.
- Scope - Either whole script or a function.
function foo() {
"use strict";
c(this);
}
let f1 = foo.bind(this);
let f2 = foo.bind({ x: 2 });
foo(); // undefined
f1(); // window
f2(); // {x: 2}
# Values & Types
Values
- Primitive - Immutable value - (passed by Value)
- Object - Mutable value - (passed by Reference)
Types - There are total 9 types in javascript
- Primitive -
undefined, null, string, number, boolean, symbol, bigint
- Non-primitive -
object, function
// Primitive
typeof undefined; // undefined
typeof null; // object (historical bug in js)
typeof true; // boolean
typeof "hello"; // string
typeof 23; // number
typeof BigInt(4566); // bigint
typeof Symbol("id"); // symbol
// Non Primitives
typeof {}; // object
typeof []; // object (array is object)
typeof (() => {}); // function (all functions are objects. But all objects are not functions.)
Separate values - Two object, function, symbol
- are never same.
c(Symbol("id") === Symbol("id")); // false
c((() => {}) === (() => {})); // false
c({} === {}); // false
// But - weird numbers (rarely useful)
c(NaN === NaN); // false (anything comapred to NaN is always false)
c(0 === -0); // true
Object.is()
Not much useful. Use ===
instead.
# Runtime
Everthing is Object in JSEngine (runtime)
Primitive - JavaScript takes primitive syntax and creates wrapper object over it's value.
It's like "6/9" depends on your perspective.
But yes syntax is not objects. It's a way of creating object description & behaviour. { }, [ ], for, if, etc
Ex:
- We can use
"hello".toUpperCase()
- JavaScript wraps
"hello"
temporarily asnew String("hello")
object. Work on it. Destroy on completion. - It will be -
{ 0: "h", 1: "e", 2: "l", 3: "l", 4: "o", length: 5, __proto__: String }
- Primitive is Immutable -
"hello"
&"HELLO"
are different values.
Ex:
// Js internally creates a wrapper object around primitives datatypes
// This wrapper obj is temp and discarded later.
"use strict";
let s = "hi"; // wrapper obj will be created by javascript just before runtime (obj is read-only for user)
let so = new String("hi"); // obj created by user (obj is read/write for user)
// compare
c(s, so); // hi --- String {"hi"}
c(s == so); // true - value is same
c(s === so); // false - string & object
c(s[1] == so[1]); // true - "i"
// mutation (create new property)
s[5] = "abc"; // Error - Cannot create property '5' on string 'hi'
so[5] = "abc"; // works (user has read/write access.)
// mutation (existing property)
s[1] = "abc"; // Error - Cannot assign to read only property '1' of string 'hi'
so[1] = "abc"; // Error - Cannot assign to read only property '1' of object '[object String]'
// Conclusion
// ---- Javascript is owner of wrapper obj for "s" & user can only read. This is as expected.
// ---- But, User is the owner of object "so" with read/write access.
// ---- Then why one prop is writable and other not writable ?
so[1] = "abc"; // error. why? - Because this prop is created by Javascript using String() constructor.
so[5] = "abc"; // works. Why? - Because this prop is created by User directly.
// Hidden Property Flags - Reason for above Conclusion
// ---- User created property always has all 3 flags = true
c(Object.getOwnPropertyDescriptor(so, "1")); // {value: "i", writable: false, enumerable: true, configurable: false}
c(Object.getOwnPropertyDescriptor(so, "5")); // {value: "abc", writable: true, enumerable: true, configurable: true}
// What about functions ?
// ---- They are objects too. They have property access using dot notation.
c(String.prototype); // works
c(Symbol.prototype); // works
c(Object.prototype); // works
c(Function.prototype); // works
// Weird.....but correct
c(myObject instanceof MyConstructor); // meaning - MyConstructor.prototype must be somewhere in the __proto__ chain of myObject
c(Function instanceof Object); // true - // Object.prototype is present in the __proto__ chain of Function - (Object.prototype === Function.__proto__.__proto__)
c(Object instanceof Function); // true - // Function.prototype is present in the __proto__ chain of Object - (Function.prototype === Object.__proto__)
// Root Object in Javascript
c(Object.prototype); // {constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ, __lookupGetter__: ƒ, …}
c(Object.prototype.__proto__); // null
Deep Rabbit Hole - It never ends.
Focus on using JavaScript Engine. Not much on how it works underhood. Just basics is enough.
It's like driving a car. Not creating a one.
# Operator & Precedence
# Operators
// Chaining operator
o.prop // dot
o["my-prop"]
o.prop?.name // optional chain property
o.method?.() // optional chain method
// assignment
= -= +=
[a, b] = [1, 2] // array destructuring
{a, b} = {a:1, b:2} // object destructuring
// Arithmatic
+ - * / % ** -- ++
typeof foo // typeof
obj instanceof Foo // instanceof Class
delete obj.prop //// delete property of object
// Comparison
== != // Loose (value) [never use]
=== !== // Strict (value & type)
< > <= >=
(cond) ? true : false // Ternary operator
?? // nullish coalescing (nullish means null or undefined)
// Logical
&& || ! // And Or Not
// Bitwise (rarely used)
& | ~ ^ // And Or Not Xor
<< >> // Shift left/right
# Precedence
MDN - Operator_Precedence (opens new window)
Precedence | Operator | Info |
---|---|---|
20 | ( ) | (a + b) |
19 | . [ ] ( ) | a.foo a['foo'] Foo() |
18 | new | new Foo() |
17 | ++ / -- | ++foo |
16 | ++ / -- | foo++ |
15 | ** | 10**2 = 100 ( ES7 ) |
14 | * / % | |
13 | + - | |
12 | << >> | |
11 | <= >= < > in instanceof | |
10 | == === != !== | |
9 - 4 | & ^ | && || ?: | Bitwise Logical |
3 | = += -= | Assignment |
# Events
- Events are fired by Browser or User
- Ex:
onclick onchange onmouseover onmouseout onkeydown onload
<button onclick="foo()"></button>
// Maynot need this in frameworks
document.addEventListener("click", callback);
# Automatic Type conversion
- Sometimes, JavaScript will automatically convert the data-type.
false , undefined , 0 , -0 , "" , null , NaN // always false
// string -> number (- * / etc)
c(3 - '2') // 1
c(2 < "12") // true
// number -> string ( + is concat)
c(3 + '2') // 32 (concat not add)
// This is not automatic type conversion. Both are strings.
if("2" < "12") // false -- "2" is greater than "1"
# Let/Const & Scope
let
andconst
are block level only ie inside curly braces{ .... }
- Scope
- It's an object just like global window object.
- But there is no way to access this object. Only javascript engine can access it.
- Garbage collector will not destroy this object if it's properties are still referenced by someone.(Closures working.)
- It forms scope chain just like prototypes chain
# Scope
// Local
// ----------
function foo() {
let x;
// use x
}
// Global
// ----------
let x;
function foo() {
// use x
let y;
}
// use x
// NO y
Global vs Local
// Ex 1
let x = 10;
{
c("Inside :", x); // Inside : 10
}
c("Outside :", x); // Outside : 10
// Ex 2
let x = 10;
{
c("Inside :", x); // Error - cannot use before initializing - (No hoisting for let/const)
let x = 5;
}
Const
vs let
// declaration
let x;
const y; // error
// declare & assignment
let x = 3;
const y = 3;
// reassignment
x = 33;
y = 33; // error
// objects are mutable even though they are const
const obj = { x: 3 };
obj = { x: 33 }; // error
obj.x = 33; // mutable
Performance Tip
// store in variable - Avoid repeat access/calculations
let x = document.getElementById("foo");
x.innerHTML("....");
# Hoisting
- Move variable to top of it's scope
- Only
var
NOTlet/const
- First Intialize & then use. Can Declare anytime
- Only declarations are hoisted (not initialization values)
// 'var' is hoisted
x = 23;
c(x);
var x; // x is local now
// functions are hoisted
c(Foo()); // 3
function Foo() {
return 3;
}
// let/const are not hoisted
c(f()); // no access before initialization
c(foo()); // not defined
let f = function foo() {
return 3;
};
# String
let s = "Hello";
// Template string
let s = `
<div>
Hello ${anyVariable}
</div>
`;
# Methods
// returns index "number" or "-1" if not found
s.indexOf("foo", startPosition);
s.lastIndexOf("foo");
// same as indexOf() but no startPosition
s.search("foo");
s.search(regularExpression);
// returns new string
s.slice(start, end); // -ve is allowed
s.slice(start); // rest of the string
// similar to slice()
s.substring(start, end); // -ve is not allowed
s.substring(start);
// similar to slice()
s.substr(start, length); // -ve is allowed
// return new string
s.replace("foo", "newFoo"); // only 1st match is replaced NOT ALL
s.replace(regularExpression, "newFoo");
// returns new string
s.toUpperCase();
s.toLowerCase();
// removes whitespaces on both sides
s.trim();
// returns character
s.charAt(index);
// returns Array
var s = "a,b,c";
s.split(","); // ["a", "b", "c"]
// New ones
s.include("R");
s.startsWith("R");
s.endsWith("R");
# Numbers
- No
int, float, short, long
- only 64-bit floating point.
// automatic type conversion
var x = "100",
y = 10;
x / y; // 10
x * y; // 1000
x - y; // 90
x + y; // "10010" - concat string
// **NaN**
var a = 100 / "one";
isNaN(a); // true
Methods
var n = 9.656;
n.toString(); // To String
n.toFixed(2); // 9.66
n.toFixed(4); // 9.6600
n.toPrecision(2); // 9.7
parseFloat("10.33"); // 10.33 (string to float)
parseInt(10.33); // 10 (float to int)
Precision Error
Good precision over 15-digits
c(0.1 + 0.2); // 0.30000000000000004 (17th digit)
# Math Object
Math.PI();
Math.random(); // between 0 - 1
Math.round(4.7); // 5
Math.round(4.3); // 4
Math.round(4.5); // 5
Math.pow(8, 2); // 64
Math.sqrt(64); // 8
Math.abs(-4); // 4
Math.ceil(4.4); // 5
Math.floor(4.7); // 4
Math.min(5, 4, 8, 12, 35, 45); // 4
Math.max(5, 4, 8, 12, 35, 45); // 45
# Arrays
Lodash
- Use Lodash because it's mostly optimized for fast run time than inbuilt.
- Can use
Set()
too.
typeof []; // Object
foo[1]; // get
foo[1] = "baz"; // set
Props & Methods :
// To String
// a = ["a", "b"];
a.toString(); // a, b
a.join("*"); // a*b
// length
a.length;
// add/remove
a.pop(); // remove last
a.push("c"); // add at last
a.shift(); // remove first
a.unshift("c"); // add at first
a[a.length] = "c"; // add at last
// Alter
a.splice(start, itemsToRemove, "add-1", "add-2");
a.splice(0, 1); // removes 1st
a.splice(3, 0, "newfoo", "newBar"); // adds without removing at position 3
// get index of first matched
a.indexOf("foo");
a.lastIndexOf("foo");
// sort
c([2, 3, 1].sort()); // [1,2,3] (can use callback too)
c([2, 3, 1].reverse()); //[1,3,2]
// Iterations :
a.forEach(function(value, index, array){...});
a.map(function(value, index, array){...}); // MAP - to modify each value
a.filter(function(value, index, array){...}); // FILTER - to accept/reject values
# Date Object
- Use 3rd party library.
# Condition / Loop
# If / Switch
if(cond) {......}
switch(n){
case a:
// code
break;
case b:
// code
break;
case x:
case y:
// code
break;
default:
// code
}
# For-in vs For-of
for in
- Enumerable properties- Use
Object.hasOwnProperty()
to avoid inherited props/methods
- Use
for of
- Iterable properties- Built-in iterables -
String, Array, TypedArray, Map, Set, arguments
- Built-in iterables -
// Normal loop
for (var i = 0; i < 5; i++) {}
// For Object
// -------------
let foo = {
x: 3,
y: 6,
};
for (v in foo) {
console.log(v); // x / y
}
for (v of foo) {
console.log(v); // error not iterable
}
// For Array
// -------------
let foo = [3, 5];
for (v in foo) {
console.log(v); // 0 / 1
}
for (v of foo) {
console.log(v); // 3 / 5
}
# while / do while
while (cond) {}
do {} while (cond);
break; // stops loop
continue; // stops 1 iteration in loop
# Objects vs Functions
Object
- A collection of Property -
{ key: value }
- Types of objects
- Literal -
{...}
- Function - Just a literal with some built-in properties like
name, arguments, etc
makes it callable. - Regex
- JSX
- Literal -
Root Object & Root Function
// Root object
// ---- Object (function) & Object.prototype (literal) are 2 different objects
let rootObj = Object.prototype;
c(rootObj); // {constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ, __lookupGetter__: ƒ, …}
c(rootObj.__proto__); // null (no parent)
// Root function (child of Root object)
// ---- Function (function) & Function.prototype (literal) are 2 different objects
let rootFunc = Function.prototype;
c(rootFunc); // ƒ () { [native code] }
c(rootFunc.__proto__); // Root object
// "Object" is a function and so a child of Root function
c(Object.__proto__); // Root function
# Prototype
Yes it can be done in multiple ways. We can go round & round & round or simply follow popular conventions.
Old Inheritance Convention
__proto__
will be deprecated.
- Literal -> literal -
childObj.__proto__ = parentObj
- Constructor -> Constructor (Extend) -
Child.prototype.__proto__ = Parent.prototype
- Get -
obj.__proto__
- Set -
obj.__proto__ = obj2
Modern Inheritance Convention
- Literal -> literal -
childObj = Object.create(parentObj)
- Constructor -> instance -
new Foo()
- Constructor -> Constructor (Extend)
Parent.call(this, args)
Child.prototype = Object.create(Parent.prototype)
Child.prototype.constructor = Child
- Get -
Object.getPrototypeOf(obj)
- Set -
Object.setPrototypeOf(obj)
- Use Classes - Modern & easier syntactic sugar for inheritance.
Avoid deep inheritance
Never go more than 1 - 2 levels of inheritance. It will create problems.
Word proto
means nearest ancestor.
What is ?
__proto__
- Get/Set for internal property[[Prototype]]
(WE WILL REFER AS__proto__
ONLY)prototype
- Just a normal property of object.
Why this naming convention ?
__proto__
- By creators of browsers. Can't change.prototype
- By creators of javascript. Used in built-in constructors likeObject, Array, etc
. (User constructors can have any name but then it's convention & we must follow.)
What is it's value ?
__proto__
- object or null (Other values are discarded by js)prototype
- anything (convention is object or null since__proto__
will discard other values)
Who has ?
__proto__
- All objects (compulsory)prototype
- Only Constructors. (Conventions. Any object can have it. But literals don't make sense.)
Who can set ?
__proto__
- user or JavaScript. (default is javascript)prototype
- user
Who is child, parent ?
- parent - Constructors. (Conventions. Any object can have
prototype
and be parent.) - child - Constructors & Instances. (Conventions. All objects are children of rootObj directly/indirectly.)
Behaviour ?
__proto__
- inherited behaviourprototype
- Constructor's own behaviour.
See Examples - /oop/#inheritance
# Functions
- Hoisting is allowed.
- Types of invoking (calling)
- Normal invoke
- Event invoke
- Self invoke
Create
function foo(){...}; // named
let foo = function(){...}; // Anonymous - stored in variable
(function(){...})(); // self-invoking
function foo(a, b = 23) {...} // b has a default value
Object.getOwnPropertyNames()
// ["length", "name", "arguments", "caller", "constructor", "apply", "bind", "call", "toString"]
c(Object.getOwnPropertyNames(Function.prototype));
// ["length", "name", "prototype"]
c(Object.getOwnPropertyNames(Function));
// ["length", "name", "arguments", "caller", "prototype"]
c(Object.getOwnPropertyNames(function() {}));
// ["length", "name"]
c(Object.getOwnPropertyNames(() => {}));
# Arrows Functions
- No access to
this, arguments, super, new.target
// ES6 functions
const x = () => {...};
const x = a => {...};
const x = (a.b) => {...};
const x = a => "single line return.";
# Function vs Constructor
- Both are one and the same.
- Difference is in conventions which developers developed for themselves.
- Constructor - Create set of objects with similar behaviour. (First letter is capital)
- Function - Create single functionality ie take input give output.
function
- Do single task.
function sayHi(name) {
return "hi" + name;
}
sayHi("umesh");
constructor
- Create set of objects.
function Person(name) {
this.name = name;
this.sayHi = function() {
return "hi " + this.name;
};
}
let p1 = new Person("p1");
let p2 = new Person("p2");
# Closures
- It closes a variable from outer scope. And any function inside the scope can use this variable.
- A function which returns a function.
- Solves counter dilemma.
- It a combination of 2 objects -
scope
&function
- Use it only if it's necessary. It will hamper performance.
# Lexical scope
- AKA Lexical Environment
- Even if function
innerFoo()
is called from outer scope, the value ofx
depends on the scope where the function is defined ie. insidefoo()
.
let x = "outer";
function foo() {
let x = "inner";
return function() {
c(x);
};
}
let innerFoo = foo();
innerFoo(); // inner;
Example 1 :
let add = (function() {
let c = 0; // c is private member
return () => {
return (c += 1);
};
})();
console.log(add()); // 1
console.log(add()); // 2
Example 2 :
function multiplier(m) {
return (n) => {
return n * m;
};
}
let twice = multiplier(2);
let thrice = multiplier(3);
console.log(twice(5)); // 10
console.log(thrice(5)); // 15
# Destructuring
let [a, b] = [1, 2]; // array destructuring
let { a, b } = { a: 1, b: 2 }; // object destructuring
# Try / Catch / Finally / Throw
Not allowed in async code.(Except throw
)
try {
if( x=="" ) { throw "x is empty"; }
if( x < 5 ) { throw "Too low value"; }
if( isNaN(x) ) { throw "Not a number"; }
} catch (err) {
// err.name
// err.message
console.log(err);
}finally{...}
Example :
// Range error
// Reference error
// Syntax error
// Type error
# Recursion
- Function code calls itself.
- Recusion is SLOWER than For loop
function power(n, pow) {
if (pow == 0) return 1;
return n * power(n, pow - 1);
}
power(2, 3); // 8
# Modules - Import/Export
- Modules always use
strict
mode automatically defer
is automatically applied.- Always use bundler like Webpack & not directly in browser.
# In Browser
- Can run in browser but the convention is to use Webpack to bundle all modules into one(or few) js files.
- Defer - Module Scripts are always deferred.
<!-- use localhost -->
<script type="module">
// console.log(this); // this is always undefined
</script>
<script type="module" src="foo.js"></script>
# Official Standard
// Export
export let bar = () => {};
export { foo, bar };
export { bar as barrr };
export default { count: 0 };
export default class Foo {....}; // classname Foo is optional
// Import
import _ from "Lodash"; // FOr default exports
import { bar } from "./Foo.js"; // only bar - name must be same // helps in tree-shaking
import { bar as barrr } from "./Foo.js"; // just rename
import * as Foo from "./Foo.js"; // all (like a namespace)
// Dynamic import
import("./module.js").then((mod) => {
c(mod.foo);
});
# Multiple Imports
- Modules are executed only once.
- If same file is imported in different files then it's imported & evaluated only once. Then same copy/instance is given to other files.
- It helps in initial configuration.
- It must be an object. Other values are READONLY.
// common.js
export let common = { name: "umesh" };
// import1.js
import { common } from "./common.js";
console.log("From import-1:", common.name);
common.name = "newName";
// import2.js
import { common } from "./common.js";
console.log("From import-2:", common.name); // newName
// index.js
import "./import1";
import "./import2";
# Module Aggregation
- Collect sub-modules to form one single module.
// animals.js = cat.js + dog.js
export Cat from './cat.js`
export Dog from './dog.js`
//main.js
import {Cat, Dog} from "./animals.js"
# CommonJs
- Used for Nodejs but now it's old. Use offcial modules.
module.exports = {}; // export
const foo = require("./FooJs"); // import
V8's Recommendations
- Use
.mjs
for modules. - But tools like Typescript may never support it.
Using _module.js, _foo.js, _bar.js
could be better.
# Rest and Spread operators
- Both rest & spread uses same syntax - three dots
...
Rest : args -> array
function foo(p1, ...params) {
c(params); // an array - [3, 5, 8]
}
foo(2, 3, 5, 8);
Spread : array -> args
let params = [2, 3, 5, 8];
c(1, ...params); // list of args - (1, 2, 3, 5, 8)
//NOTE - must be last argument
function foo(...numbers, x); // wrong
function foo(x, y, ...numbers); // right
Shallow copy
let foo = { x: 2 };
let bar = { ...foo, y: 3 }; // { x:2, y: 3 }
# Map/Set
# Map
Map
is iterable whileObject
is not.hasOwnProperty
is not needed since native & prototype properties don't showup on iteration- A Hashtable.
- Keys in
Object
- strings or symbolsMap
- can be anything
- Use
Map
if- key/value pairs are of same type.
- keys are unknown but added in runtime
let m = new Map();
m.set(key, value);
m.get(key);
m.has(key);
m.delete(key);
m.clear();
m.size;
for ([key, value] of m) {
// Insertion order is maintained
}
Map vs Object
Maps are usually faster than Object
Use Object if --> Keys are strings only + Write is less + Read is more. Else Use Map
. - stackoverflow (opens new window)
# Set
- Values in
Array
- not uniqueSet
- unique(no duplicate) with not index
- Use
Set
if- Search/add/delete are faster than array stackoverflow (opens new window)
- no duplicates
let s = new Set();
s.add(value);
s.has(value);
s.delete(value);
s.clear();
s.size;
for (value of s) {
// Insertion order is maintained
}
// array --> set
new Set([2, 3, 3, 4]); // [2,3,4] - unique values
# Symbol
- A symbol is unique identifier.
- It's always accessed using variable.
- Hidden/Skipped by -
for in
,Object.keys
but not by cloning(Object.assign(dest, [src1, src2...]))
// Description is obtional
let id = Symbol("This is description & has no side-effects on anything.");
let id = Symbol();
id.description; // read description
c(Symbol("id") === Symbol("id")); // false - Symbols are always unique
// To use variable as property name we use symbols.
// This avoids conflict with other properties of same name
let id = Symbol();
let obj = {
id: "property",
[id]: "Symbol variable as property",
};
c(obj.id); // "property",
c(obj[id]); // "Symbol variable as property"
# Classes
- todo
# Composition
- todo