console.log(typeof {}); // object
console.log(Object.prototype.toString.call({})); // [object Object]
console.log(Object.getPrototypeOf({}) === Object.prototype); // true
console.log({}.constructor === Object); // true
console.log({} instanceof Object); // true
console.log({}.prototype); // undefined

[[Prototype]]
This is an internal property of an object, which is a reference to constructor's prototype object.
Each object has an internal link to another object called its prototype (which means [[Prototype]] , not prototype properties). The notation someObject.[[Prototype]] is used to designate the prototype of someObject .
When a property is accessed on an object, and it's not found in the object itself, JavaScript will look up this [[Prototype]] chain until it either finds the property or reaches an object with a null [[Prototype]]. By definition, null has no prototype and acts as the final link in this prototype chain. It is possible to mutate any member of the prototype chain or even swap out the prototype at runtime, so concepts like static dispatching do not exist in JavaScript.
const o = {
a: 1,
b: 2,
// __proto__ sets the [[Prototype]]. It's specified here
// as another object literal.
__proto__: {
b: 3,
c: 4,
},
};
// o.[[Prototype]] has properties b and c.
// o.[[Prototype]].[[Prototype]] is Object.prototype (we will explain
// what that means later).
// Finally, o.[[Prototype]].[[Prototype]].[[Prototype]] is null.
// This is the end of the prototype chain, as null,
// by definition, has no [[Prototype]].
// Thus, the full prototype chain looks like:
// { a: 1, b: 2 } ---> { b: 3, c: 4 } ---> Object.prototype ---> null
console.log(o.a); // 1
// Is there an 'a' own property on o? Yes, and its value is 1.
console.log(o.b); // 2
// Is there a 'b' own property on o? Yes, and its value is 2.
// The prototype also has a 'b' property, but it's not visited.
// This is called Property Shadowing
console.log(o.c); // 4
// Is there a 'c' own property on o? No, check its prototype.
// Is there a 'c' own property on o.[[Prototype]]? Yes, its value is 4.
console.log(o.d); // undefined
// Is there a 'd' own property on o? No, check its prototype.
// Is there a 'd' own property on o.[[Prototype]]? No, check its prototype.
// o.[[Prototype]].[[Prototype]] is Object.prototype and
// there is no 'd' property by default, check its prototype.
// o.[[Prototype]].[[Prototype]].[[Prototype]] is null, stop searching,
// no property found, return undefined.
You can't directly access [[Prototype]], but you can interact with it using Object.getPrototypeOf(obj) and Object.setPrototypeOf(obj, newProto).
It used to be __proto__ , a getter/setter that exposes the internal [[Prototype]] of an object. It's been deprecated and isn't supported in all environments.
let obj = {};
console.log(obj.__proto__ === Object.prototype); // true
console.log(Object.getPrototypeOf(obj) === Object.prototype); // true
constructor
function Foo() {}, Foo.prototype.constructor is Foo.instanceof operator can check object’s belonging constructor.toString()/toLocaleString(): This method returns a string representing the object. By default, it returns [object type]. It can be overwrited by own implementation.
console.log({}.toString()); // [object Object]
valueOf(): For objects, the same object is returned. For primitive constructor (wrapper objects) like Number, String, Boolean, it returns the primitive value.
console.log(false); // false
console.log(false.valueOf()); // false
console.log(new Boolean(false)); // Boolean {}
console.log(new Boolean(false).valueOf()); // false
hasOwnProperty(prop): This method returns a boolean indicating whether the object has the specified property as its own property (as opposed to inheriting it).
let obj = {prop: 'value'};
console.log(obj.hasOwnProperty('prop')); // true
console.log(obj.hasOwnProperty('toString')); // false
propertyIsEnumerable() This method returns a Boolean indicating whether the specified property is enumerable. A property is enumerable if it can be accessed in a for...in loop (unless the property's key is a Symbol).
let obj = { prop: 'value' };
console.log(obj.propertyIsEnumerable('prop')); // true
// non-enumerable property
Object.defineProperty(obj, 'nonEnum', { enumerable: false, value: 'non-enumerable value' });
console.log(obj.propertyIsEnumerable('nonEnum')); // false
The nonEnum is defined as a non-enumerable property.
isPrototypeOf(object): This method checks whether an object exists in another object's prototype chain.
// Define two objects, parent and child
const parent = {
name: "Parent Object",
};
// child is an object that inherits from parent
const child = Object.create(parent); // const child = { __proto__: parent};
// Using isPrototypeOf to check if parent is the prototype of child
if (parent.isPrototypeOf(child)) {
console.log("parent is the prototype of child");
} else {
console.log("parent is not the prototype of child");
}
console.log(Object.prototype.isPrototypeOf({})); // true\\
console.log(typeof new Object()); // object
console.log(Object.prototype.toString.call(new Object())); // [object Object]
console.log(Object.getPrototypeOf(new Object()) === Object.prototype); // true
console.log(new Object().constructor === Object); // true
console.log(new Object() instanceof Object); // true
console.log(new Object().prototype); // undefined

Flexibility: Both approaches are functionally equivalent for creating empty objects. However, the literal syntax is more versatile for initialising objects with properties.
// Creating an object with properties using object literal syntax
const person = {
name: "John Doe",
age: 30,
greet() {
console.log("Hello, my name is " + this.name);
}
};
// Creating an object with properties using the Object constructor
const person = new Object();
person.name = "John Doe";
person.age = 30;
person.greet = function() {
console.log("Hello, my name is " + this.name);
};