Declaration Object constructor syntax const person = new Object(); person.firstName = "John"; person.lastName = "Doe"; let person1 = new Object({ name: 'Chris', age: 38, greeting: function() { alert('Hi! I\'m ' + this.name + '.'); } }); create() method let person2 = Object.create(person1); person2.name; // 'Chris' person2.greeting(); // Hi! I'm Chris. Object literal syntax let user = { // "user" object has 3 properties: 1st property has the name "name" and the value "John" name: "John", // by key/name/identifier "name" the value "John" is stored age: 30, // by key "age" the value 30 is stored "likes birds": true, // multi word property name must be quoted // can use trailing comma a: {b: 666}, }; Constructor function purpose is to implement reusable object creation can be done using constructor functions and the "new" operator function named starts from the capital letter executed with "new" operator constructors usually do not have a return statement. their task is to write all necessary stuff into 'this', and it automatically becomes the result. function User(name) { this.name = name this.isAdmin = false this.sayHi = function() { alert( "My name is: " + this.name ) } } user1 = new User // {name: undefined, isAdmin: false, sayHi: ƒ} user2 = new User() // {name: undefined, isAdmin: false, sayHi: ƒ} user3 = new User('John') // {name: 'John', isAdmin: false, sayHi: ƒ} // same as function User(name) { const obj = {} obj.name = name obj.isAdmin = false obj.sayHi = function() { alert( "My name is: " + obj.name ) } return obj } Set value user.name = "John"; user["likes birds"] = true; Get property values user.name; // John user.age; // 30 user["likes birds"]; // true user['a']['b']; // 666 Optional chaining ?. safe way to access nested object properties, even if a property doesn’t exist safe reading and deleting, but not writing no error if key does not exist ?. stops the evaluation & returns undefined if the value before ?. is undefined or null variable before ?. must be declared works for .key , ?.() , ?.[] let user = {car: "volvo"} user?.car // volvo user?.address // undefined user?.address?.street // undefined user?.address.street // error // same as user.address ? user.address.street : undefined // no error if method does not exist let userGuest = {}; let userAdmin = { admin() { alert("I am admin"); } }; userAdmin.admin?.(); // I am admin userGuest.admin?.(); // nothing (no such method) // ?.() let userAdmin = { admin() { alert("admin"); } } let userGuest = {}; userAdmin.admin(); // admin userAdmin.admin?.(); // admin userGuest.admin(); // error userGuest.admin?.(); // nothing (no such method) // ?.[] let key = "firstName"; let user1 = { firstName: "John" }; let user2 = null; user1?.[key] // John user2?.[key] // undefined // read, delete, but not write let user = {} delete user?.name; // delete user.name if user exists user?.name = "John"; // Error, doesn't w ause it evaluates to undefined = "John" Remove property delete user.age; delete user["likes birds"]; Dynamic key with square brackets let key = "likes birds"; user[key] = true; let fruit = "apple"; let bag = { [fruit]: 5, // the name of the property is taken from the var "fruit" }; let fruit = 'apple'; let bag = { [fruit + 'Computers']: 5 // bag.appleComputers = 5 }; Property value shorthand function makeUser(name, age) { return { name, // same as name: name age, // same as age: age // ... }; } let user = makeUser("John", 30); // {name: "John", age: 30} Property existence, “in” operator let user = { name: "John", age: 30 }; "age" in user // true "blabla" in user // false let key = 'age'; key in user // true For…in loop let obj = { name: "John", age: 30, isAdmin: true }; // iterates over properties of an object for (let key in obj) { console.log( key, obj[key] ); // name John, age 30, isAdmin true } Order Integer properties are sorted, others appear in creation order let obj = { "49": "Germany", "41": "Switzerland", "44": "Great Britain", "1": "USA" }; for (let code in obj) { alert(code); // 1, 41, 44, 49 } Object references Objects are stored and copied “by reference”. On object copy, the reference is copied, but object itself is not duplicated. let user = { name: 'John' }; let admin = user; admin.name = 'Pete'; // changed by the "admin" reference user.name; // 'Pete', changes are seen from the "user" reference user === admin; // true Two objects are not equal, even though they look alike. let a = {}; let b = {}; a == b ; // false Object declared as const can be modified. const user = { name: "John" }; user.name = "Pete"; user.name; // Pete Cloning Shallow copy let obj = { name: "John", age: 30 }; let clone = Object.assign({}, user); // or clone = { ...obj } Nested cloning use existing library _.cloneDeep(obj) from the library lodash // npm i lodash.clonedeep const obj = [{ 'a': 1 }, { 'b': 2 }]; const deep = _.cloneDeep(obj); Object.keys, values, entries let user = { name: "John", age: 30 }; Object.keys(user) // ["name", "age"] // real array, not an iterator Object.values(user) // ["John", 30] Object.entries(user) // [ ["name","John"], ["age",30] ] for (let value of Object.values(user)) { alert(value); // John, then 30 } // loop over keys-and-values for (let [key, value] of Object.entries(obj)) { alert(`${key}:${value}`); // name:John, then age:30 } Array into Object Object.fromEntries() let arr = [ ['banana', 1], ['orange', 2], ['meat', 4] ] let obj = Object.fromEntries(arr); // {banana: 1, orange: 2, meat: 4} Map over object let prices = { banana: 1, orange: 2, meat: 4, }; // convert to array, map, and then fromEntries gives back the object let doublePrices = Object.fromEntries( Object.entries(prices).map(([key, value]) => [key, value * 2]) ) doublePrices // {banana: 2, orange: 4, meat: 8} Object & 'this' this is an object means "current object" // example 1 { let user = { name: "John", age: 30, showThisName() { alert(this.name) }, }; user.showThisName(); // "John" // same as alert(user.name); } // example 2 { let user = { name: "John" }; let admin = { name: "Admin" }; function sayHi() { alert( this.name ); } // use the same function in two objects user.f = sayHi; // assign method to object admin.f = sayHi; user.f(); // John (this == user) admin.f(); // Admin (this == admin) } // "this" in function function x() { alert( this ); } x() // [object Window] // arrow function has no “this”, it is taken from the outer “normal” function { let user = { name: "John", sayHi() { let arrow = () => alert(this.name); arrow(); } }; user.sayHi(); // John } Chainable methods Just return this let ladder = { step: 0, up() { this.step++; return this; }, down() { this.step--; return this; }, showStep: function() { // shows the current step alert( this.step ); } } ladder.up().up().down().showStep(); // 1 Global object The global object holds variables available everywhere In a browser it is named window In Node.js it is named global Can be accessed by globalThis or window var gVar = 5 window.gVar // 5 (became a property of the global object) window.currentUser = { name: "John"}; window.currentUser.name // John Primitive as an object Primitives also have methods, but how? they are not objects A special object is created that has useful methods, like toUpperCase() , runs and destroyed null & undefined have no methods "Hello".toUpperCase(); // HELLO Property flags and descriptors Object properties, have 3 attributes (flags): "writable" – if true, the value can be changed, otherwise it’s read-only "enumerable" – if true, then listed in loops, otherwise not listed "configurable" – if true, the property can be deleted and flags can be modified, otherwise not by default they are all true Object.getOwnPropertyDescriptor() Object.defineProperty(obj, prop, descriptor) Can work with flags using descriptors methods: Object.getOwnPropertyDescriptor(obj, propertyName) - returns “property descriptor” object: it contains the value and all the flags. Object.defineProperty(obj, prop, descriptor) - defines a new property on object or modifies an existing ones & returns the object Object.defineProperties(obj, props) - defines new or modifies existing properties directly on an object, returning the object. Sets many properties at once. Object.getOwnPropertyDescriptors(obj) - returns object containing all own property descriptors of an object Object.preventExtensions(obj) - forbids the addition of new properties to the object Object.seal(obj) - forbids adding/removing of properties. Sets 'configurable' to false for all existing properties. Object.freeze(obj) - forbids adding/removing/changing of properties. Sets 'configurable' to false , 'writable' to false for all existing properties. Object.isExtensible(obj) - returns false if adding properties is forbidden, otherwise true . Object.isSealed(obj) - returns true if adding/removing properties is forbidden, and all existing properties have configurable: false . Object.isFrozen(obj) - returns true if adding/removing/changing properties is forbidden, and all current properties are configurable: false , writable: false . // getOwnPropertyDescriptor let user = { name: "John" } let descriptor = Object.getOwnPropertyDescriptor(user, 'name') descriptor // {value: 'John', writable: true, enumerable: true, configurable: true} // defineProperty let user = { name: "John", toString() { return this.name } } for (let key in user) alert(key) // name, toString Object.defineProperty(user, "name", { value: "John", writable: false, // won't be able to change user.name or its flags configurable: false, // delete user.name; // Error // can not change even flags // enumerable: false, // for (let key in user) alert(key); // toString ONLY!!! // + there are many more property settings }) user.name = "Anton" user.name // "John" delete user.name // false // non-enumerable properties are also excluded from Object.keys // making a property non-configurable is a one-way road, cannot change it back with defineProperty // “configurable: false” prevents change of flags and its deletion, while allowing to change its value // defineProperties // method defines new or modifies existing properties directly on an object, returning the object // we can set many properties at once. Object.defineProperties(user, { name: { value: "John", writable: false }, surname: { value: "Smith", writable: false }, }); // getOwnPropertyDescriptors // method defines new or modifies existing properties directly on an object, returning the object // we can set many properties at once. Object.getOwnPropertyDescriptors(user) /* { name: {value: 'John', writable: false, enumerable: false, configurable: false} surname: {value: 'Smith', writable: false, enumerable: false, configurable: false} toString: {writable: true, enumerable: true, configurable: true, value: ƒ} } */ Getters & setters In object literal they are denoted by get and set keywords let user = { name: "John", surname: "Smith", get fullName() { return `${this.name} ${this.surname}`; }, set fullName(value) { [this.name, this.surname] = value.split(" "); } } user.fullName // John Smith user.fullName = "Jane Musk" // not a method with parenthesis, but a property user.name // Jane user.surname // Musk Widely known convention is to keep value in a separate property, which starts with underscore & it is accessed via getter and setter. _propName let user = { get name() { return this._name }, set name(value) { if (value.length < 4) { alert("Name is too short, need at least 4 characters") return } this._name = value } } user.name = "Pete"; // "Pete" user.name = ""; // Name is too short... In accessor descriptors they are denoted by get() and set() methods let user = { name: "John", surname: "Smith" }; Object.defineProperty(user, 'fullName', { get() { return `${this.name} ${this.surname}`; }, set(value) { [this.name, this.surname] = value.split(" "); } }); user.fullName; // "John Smith" // property can be either an accessor (has get/set methods) or a data property (has a value), not both // If we try to supply both get and value in the same descriptor, there will be an error Can be used in constructor functions function User(name, birthday) { this.name = name this.birthday = birthday // age is calculated from the current date and stored birthday Object.defineProperty(this, "age", { get() { let todayYear = new Date().getFullYear() return todayYear - this.birthday.getFullYear() } }) } let john = new User("John", new Date(1992, 6, 1)) john.birthday // Wed Jul 01 1992 00:00:00 GMT+0300 (Eastern European Summer Time) john.age // 29 Iterable object Iterable has for..of loop functionality // strings are iterables for (let char of "test") { console.log(char) // t, e, s, t } To make an iterable we need to add Symbol.iterator & next() method. let range = { from: 1, to: 5, [Symbol.iterator]() { this.current = this.from; return this; }, next() { if (this.current <= this.to) { return { done: false, value: this.current++ }; } else { return { done: true }; } } }; for (let num of range) alert(num); // 1, then 2, 3, 4, 5 Array - like object Array-likes are objects that have indexes and length, so they look like arrays // strings are array-like objects "test".length // 4 "test"[2] // 's' // here’s the object that is array-like, but not iterable: let arrayLike = { // has indexes and length => array-like 0: "Hello", 1: "World", length: 2 }; for (let item of arrayLike) {} // Error (no Symbol.iterator) From iterables & array-like object into array iterables & array-likes are not arrays, they don’t have push, pop etc. methods Array.from() makes a “real” array let arrayLike = { 0: "Hello", 1: "World", length: 2 }; let arr = Array.from(arrayLike); arr.pop(); // World (method works) // optional args Array.from(obj[, mapFn, thisArg]) let arr = Array.from(arrayLike, str => " - " + str); // [" - Hello", " - World"] // string into array let str = "Hello" let strArr1 = Array.from(str) let strArr2 = str.split("") console.log(strArr1, strArr2) // ["H", "e", "l", "l", "o"] ["H", "e", "l", "l", "o"] // we can convert jQuery collection into array with such method Objects are not equal From MDN In JavaScript, objects are a reference type. Two distinct objects are never equal, even if they have the same properties. Only comparing the same object reference with itself yields true. Primitives 'abc' === 'abc' // true 123 === 123 // true false === false // true null === null // true undefined === undefined // true Symbol("Sym") === Symbol("Sym") // false Objects {} === {} // false [] === [] // false (() => 0) === (() => 0) // false Conditionally add a property into an object const didIPassExam = true const study = { monday : 'writing', tuesday : 'reading', ...(didIPassExam && {wednesday : 'sleep happily'}) } Without parentheses also works (but looks weird imho) const obj = { key1: 'value 1', key2: 'value 2', ...true && { key3: 'value 3' } }