Objects in JavaScript | What You NEED to Know

1. Object methods & this

  • Methods are simply properties that hold function values

— Simple object method

var rabbit = {};
rabbit.speak = function(line) {
    console.log("The rabbit says '"+ line + "'");
};
rabbit.speak("I'm alive.")

has an output of

The rabbit says 'I'm alive.'

— Object method & this

  • When a function is called as a method — looked up as a property and immediately called, as in object.method() — the special variable this in its body will point to the object that was called on
function speak(line) {
    console.log("The "+ this.type + " rabbit says '" +
     line + "'");
};
var whiteRabbit = {type: "white", speak: speak};

whiteRabbit.speak("Oh my ears and whiskers, " + 
                "how late it's getting!");

has an output of

The white rabbit says 'Oh my ears and whiskers, how late it's getting!'

- apply & call

  • apply & call can be used for object.method()
  • apply & call methods both take a first argument that can be used to simulate method calls
  • The first argument is in fact used to give a value to this
function speak(line) {
    console.log("The "+ this.type + " rabbit says '" +
     line + "'");
};
var whiteRabbit = {type: "white", speak: speak};

speak.apply(whiteRabbit, ["Burp!"]);
speak.call({type: "old"}, "Oh my.");

has an output of

The white rabbit says 'Burp!'
The old rabbit says 'Oh my.

2. Prototypes

  • Almost all objects have a prototype
  • A prototype is another object that is used as a fallback source of properties
  • When an object gets a request for a property that is does not have, its prototype will be searched for the property, then the prototype’s prototype, and so on

— prototype of an empty object

  • prototype of the empty object is the great ancestral prototype, the entity behind almost all objects, Object.prototype
var empty = {};
console.log(empty.toString);
console.log(empty.toString());

has an output of

[Function: toString]
[object Object]

— default property of other objects (array, function …)

  • Many objects do not directly have Object.prototype as their prototype, but has its own default properties
  • Functions derive from Function.prototype & arrays derive from Array.prototype
console.log(Object.getPrototypeOf(isNaN) ==
            Function.prototype);
console.log(Object.getPrototypeOf([]) ==
Array.prototype);

has an output of

true
true

— Object.create to create an object with a specific prototype

  • The protoRabbit acts as a container for the properties that are shared by all rabbits
  • An individual rabbit object, like the killer rabbit, contains properties that apply only to itself — in this case its type — and derives shared properties from its prototype
var protoRabbit = {
    speak: function(line) {
        console.log("The " + this.type + " rabbit says '" + 
        line + "'");
    }
};
var killerRabbit = Object.create(protoRabbit);
killerRabbit.type = "killer";
killerRabbit.speak("SKREEE!");

has an output of

The killer rabbit says 'SKREEE!'

3. Constructors

— constructor prototype

  • A more convenient way to create object that derive from some shared prototype is to use a constructor
  • In JavaScript, calling a function with the new keyword in front of it causes it to be treated as a constructor
  • The constructor will have its this variable bound to a fresh object, and unless it explicitly returns another object value, this new object will be returned from the call
  • An object created with new is said to be an instance of its constructor
  • It’s a convention to capitalize the names of constructors so that they are easily distinguished from other functions
function Rabbit(type) {
    this.type = type;
}

var killerRabbit = new Rabbit("killer");
var blackRabbit = new Rabbit("black");
console.log(blackRabbit.type);

has an output of

black

— constructor has Object.prototype by default

  • Constructors (in fact, all functions) automatically get a property named prototype, which by default holds a plain, empty object that derives from Object.prototype
  • Every instance created with this constructor will have this object as its prototype
function Rabbit(type) {
    this.type = type;
}

var blackRabbit = new Rabbit("black");
Rabbit.prototype.speak = function(line) {
    console.log("The " + this.type + " rabbit says '" +
            line + "'");
};
blackRabbit.speak("Doom...");

has an output of

The black rabbit says 'Doom...'

4. Overriding Derived Properties

— same prototype name

  • If there is a property by the same name in the prototype, this property is not changed
  • The property is added to the object itself
  • console.log(blackRabbit.teeth) printssmall because blackRabbit object does not have teeth property and it inherits from Rabbit object’s own teeth property , which is small
function Rabbit(type) {
    this.type = type;
}
var blackRabbit = new Rabbit("black");
var killerRabbit = new Rabbit("killer");

Rabbit.prototype.teeth = "small";
console.log(killerRabbit.teeth);
// small
killerRabbit.teeth = "long, sharp, and bloody";
console.log(killerRabbit.teeth);
// long, sharp, and bloody
console.log(blackRabbit.teeth);
// small
console.log(Rabbit.prototype.teeth);
// small

5. Prototype Interference

— enumerable vs. nonenumerable

  • toString did not show up in the for/in loop but the in operator returned true for it
  • This is because JavaScript distinguishes between enumerable and nonenumerable properties
  • All properties that we create by simply assigning to them are enumerable
  • The standard properties in Object.prototype are all nonenuerable, which is why they do not show up in such a for/in loop
var map = {};
function storePhi(event, phi) {
    map[event] = phi;
}

storePhi("pizza", 0.069);
storePhi("touched tree", -0.081);

Object.prototype.nonsense = "hi";

for(var name in map) {
    console.log(name)
}

console.log("nonsense" in map);
console.log("toString" in map);

has an output of

pizza
touched tree
nonsense
true
true
  • It is possible to define our own nonenumerable properties by using the Object.defineproperty function, which allows us to control the type of property we are creating
  • In the example, the property is there map.hiddenNonsense but it won’t show up in a loop
var map = {};
function storePhi(event, phi) {
    map[event] = phi;
}

storePhi("pizza", 0.069);
storePhi("touched tree", -0.081);

Object.defineProperty(Object.prototype, "hiddenNonsense",
                {enumerable: false, value: "hi"});

for (var name in map) {
    console.log(name);
}

console.log(map.hiddenNonsense);

has an output of

pizza
touched tree
hi

— hasOwnProperty vs Object in

  • hasOwnProperty methods tells us whether the object itself has the property, without looking at its prototypes
  • This is often a more useful piece of information than what the in operator gives us
const map = {};
console.log("toString" in map);
console.log(map.hasOwnProperty("toString"));

has an output of

true
false
  • Thus, when you are worried that someone might have messed with have messed with the based object prototype, I recommend you write your for/in loop like this
for (var name in map) {
    if (map.hasOwnProperty(name)) {
        // ... this is an own property
    }
}

6. Prototype-less objects

  • Object.create function allows us to create an object with a spefici prototype
  • You are allowed to pass null as the prototype to create a fresh object with no prototype
  • Thus, we no longer need the hasOwnProperty because all the properties the object has are its own properties
  • Now we can safely use for/in loops, no matter what people have been doing to Object.prototype
var map = Object.create(null);
map["pizza"] = 0.069;
console.log("toString" in map);
// false
console.log("pizza" in map);
// true

Thank you for reading!

#javascript #programming #web development

Objects in JavaScript | What You NEED to Know
95.60 GEEK