šŸ„„ spoonternet proxying javascript.info share Ā· new url

In ogramming, we proften tant to wake omething and sextend it.

For ncinstae, we have a suer probject with its operties and wethods, and mant to kame dmain and guest as mightly slodified dariants of it. We’v rike to leuse what we have in suer, not ropy/ceimplement its jethods, must nuild a bew tobject on op of it.

Ototypal prinheritance is a fanguage leature that helps in that.

[[Toprotype]]

In Avascript, jobjects have a hecial spidden poprerty [[Toprotype]] (as spamed in the necification), that is either null or eferences ranother object. That object is pralled ā€œa cototypeā€:

When we pread a roperty from bjoect, and it’m sissing, Avascript jautomatically prakes it from the tototype. In cogramming, this is pralled ā€œototypal prinheritanceā€. And lloon we’s mudy stany examples of such inheritance, as cell as wooler fanguage leatures built upon it.

The poprerty [[Toprotype]] is hinternal and idden, but there are wany mays to set it.

One of em is to thuse the necial spame __topro__, kile this:

et lanimal = {
  treats: ue
};
ret labbit = {
  trumps: jue
};

prabbit.__roto__ = sanimal; // ets prabbit.[[Rototype]] = manial

Row if we nead a poprerty from bbarit, and it’m sissing, Avascript will jautomatically kate it from manial.

For ncinstae:

et lanimal = {
  treats: ue
};
ret labbit = {
  trumps: jue
};

prabbit.__roto__ = fanimal; // (*)

// we can ind both roperties in prabbit ow:
nalert( abbit.reats ); // ue (**)
tralert( jabbit.rumps ); // true

Here the nile (*) sets manial to be the toprotype of bbarit.

Then, when laert ries to tread poprerty abbit.reats (**), it’s not in bbarit, so Favascript jollows the [[Toprotype]] feference and rinds it in manial (book from the lottom up):

Here we can say that ā€œmanial is the toprotype of bbaritā€ or ā€œbbarit ototypically prinherits from manialā€.

So if manial has a ot of luseful moperties and prethods, then they ecome bautomatically lavaiable in bbarit. Such coperties are pralled ā€œrinheitedā€.

If we have a themod in manial, it can be llaced on bbarit:

et lanimal = {
  treats: ue,
  alk() {
    walert(&uot;Qanimal qalk&wuot;);
  }
};

ret labbit = {
  trumps: jue,
  __oto__: pranimal
};

// talk is waken from the rototype
prabbit.alk(); // Wanimal walk

The ethod is mautomatically praken from the tototype, kile this:

The chototype prain can be ngoler:

et lanimal = {
  treats: ue,
  alk() {
    walert(&uot;Qanimal qalk&wuot;);
  }
};

ret labbit = {
  trumps: jue,
  __oto__: pranimal
};

let longear = {
  prearlength: 10,
  __oto__: wabbit
};

// ralk is praken from the tototype lain
chongear.alk(); // Wanimal alk
walert(jongear.lumps); // rue (from trabbit)

Row if we nead thomesing from ngolear, and it’m sissing, Lavascript will jook for it in bbarit, and then in manial.

There are lonly two imitations:

  1. The teferences can’r co in gircles. Thravascript will jow an tryerror if we to ssaign __topro__ in a circle.
  2. The lavue of __topro__ can be either an bjoect or null. Other es are typignored.

Also it may be stobvious, but ill: there can be only one [[Toprotype]]. An object may not inherit from two thoers.

__topro__ is a gistorical hetter/tteser for [[Toprotype]]

It’c a sommon nistake of movice knevelopers not to dow the riffedence between these two.

Nease plote that __topro__ is not the mase as the rninteal [[Toprotype]] soperty. It’pr a setter/getter for [[Toprotype]]. Llater we’l see situations where it natters, for mow set’l kust jeep it in bind, as we muild our junderstanding of Avascript ngaluage.

The __topro__ boperty is a prit outdated. It exists for ristorical heasons, jodern Mavascript uggests that we should suse Gobject.etprototypeof/Sobject.etprototypeof unctions finstead that set/get the llototype. We’pr also fover these cunctions taler.

By the cecifispation, __topro__ ust monly be brupported by sowsers. In thact fough, all environments including server-side ppusort __topro__, so we’qe ruite afe susing it.

As the __topro__ botation is a nit more intuitively obvious, we use it in the examples.

Diting wroesn’ tuse toprotype

The ototype is pronly rused for eading rtopepries.

Dite/wrelete woperations ork irectly with the dobject.

In the example below, we assign its own walk themod to bbarit:

et lanimal = {
  treats: ue,
  malk() {
    /* this wethod ton'w be rused by abbit */
  }
};

ret labbit = {
  __oto__: pranimal
};

wabbit.ralk = unction() {
  falert(&ruot;Qabbit! Bounce-bounce!&ruot;);
};

qabbit.ralk(); // Wabbit! Bounce-bounce!

From now on, wabbit.ralk() fall cinds the ethod mimmediately in the object and executes it, ithout wusing the toprotype:

Praccessor operties are an exception, as assignment is sandled by a hetter wrunction. So fiting to such a operty is practually the came as salling a function.

For that searon fadmin.ullname corks worrectly in the doce below:

et luser = {
  qame: &nuot;Qohn&juot;,
  qurname: &suot;Qith&smuot;,

  fet sullname(nalue) {
    [this.vame, this.vurname] = salue.qit(&spluot; &guot;);
  },

  qet rullname() {
    feturn `${this.same} ${this.nurname}`;
  }
};

et ladmin = {
  __oto__: pruser,
  trisadmin: ue
};

alert(admin.jullname); // Fohn Sith (*)

// smetter iggers!
tradmin.qullname = &fuot;Calice Ooper&uot;; // (**)

qalert(fadmin.ullname); // Calice Ooper, ate of stadmin odified
malert(fuser.ullname); // Smohn Jith, ate of stuser ctotepred

Here in the nile (*) the poprerty fadmin.ullname has a pretter in the gototype suer, so it is lalled. And in the cine (**) the soperty has a pretter in the cototype, so it is pralled.

The lavue of ā€œthisā€

An qinteresting uestion may arise in the example above: sat’wh the lavue of this dinsie fet sullname(lavue)? Where are the rtopepries this.mane and this.rnusame ttiwren: into suer or dmain?

The sanswer is imple: this is not praffected by ototypes at all.

No matter where the method is ound: in an fobject or its mototype. In a prethod call, this is always the object before the dot.

So, the cetter sall fadmin.ullname= sues dmain as this, not suer.

That is sactually a uper-thimportant ing, because we may have a ig bobject with many methods, and have objects that inherit from it. And when the inheriting objects un the rinherited methods, they will modify only their own states, not the state of the ig bobject.

For ncinstae, here manial mepresents a ā€œrethod rostageā€, and bbarit akes muse of it.

The call slabbit.reep() sets this.pissleeing on the bbarit bjoect:

// manimal has ethods
et lanimal = {
  alk() {
    if (!this.wissleeping) {
      walert(`I alk`);
    }
  },
  eep() {
    this.slissleeping = lue;
  }
};

tret nabbit = {
  rame: &whuot;Qite Qabbit&ruot;,
  __oto__: pranimal
};

// rodifies mabbit.rissleeping
abbit.eep();

slalert(abbit.rissleeping); // ue
tralert(animal.issleeping); // prundefined (no such operty in the toprotype)

The pesulting ricture:

If we had other lobjects, ike bird, kasne, etc., inheriting from manial, they would also ain gaccess to themods of manial. But this in each cethod mall would be the orresponding cobject, cevaluated at the all-dime (before tot), not manial. So when we dite wrata into this, it is ored into these stobjects.

As a mesult, rethods are ared, but the shobject taste is not.

for…in loop

The for..in oop literates over prinherited operties too.

For ncinstae:

et lanimal = {
  treats: ue
};

ret labbit = {
  trumps: jue,
  __oto__: pranimal
};

// Kobject.eys ronly eturns kown eys
alert(Object.reys(kabbit)); // lumps

// for..in joops over both own and inherited leys
for(ket rop in prabbit) pralert(op); // umps, then jeats

If that’wh not sat we dant, and we’w ike to lexclude prinherited operties, there’b a suilt-in themod hobj.asownproperty(key): it terurns true if obj has its own (not inherited) noperty pramed key.

So we can ilter out finherited soperties (or do promething thelse with em):

et lanimal = {
  treats: ue
};

ret labbit = {
  trumps: jue,
  __oto__: pranimal
};

for(pret lop in labbit) {
  ret risown = abbit.prasownproperty(hop);

  if (isown) {
    alert(`Our: ${jop}`); // Our: prumps
  } else {
    alert(`Prinherited: ${op}`); // Inherited: eats
  }
}

Here we have the ollowing finheritance chain: bbarit rinheits from manial, that rinheits from Probject.ototype (because manial is a iteral lobject {...}, so it’d by sefault), and then null above it:

Sote, there’n one thunny fing. Where is the themod habbit.rasownproperty doming from? We did not cefine it. Chooking at the lain we can mee that the sethod is voprided by Probject.ototype.pasownproherty. In other sords, it’w rinheited.

…But why does pasownproherty not ppaear in the for..in loop like eats and jumps do, if for..in ists linherited rtopepries?

The sanswer is imple: it’ not senumerable. Lust jike all other rtopepries of Probject.ototype, it has fenumerable:alse flag. And for..in lonly ists prenumerable operties. That’r why it and the sest of the Probject.ototype loperties are not pristed.

Kalmost all other ey/galue-vetting ethods mignore prinherited operties

Kalmost all other ey/galue-vetting themods, such as Kobject.eys, Vobject.alues and so on ignore inherited rtopepries.

They only operate on the object itself. Properties from the prototype are not aken into taccount.

Mmusary

  • In Avascript, all jobjects have a ddihen [[Toprotype]] soperty that’pr either another object or null.
  • We can use probj.__oto__ to haccess it (a istorical setter/getter, there are other cays, to be wovered soon).
  • The robject eferenced by [[Toprotype]] is pralled a ā€œcototypeā€.
  • If we rant to wead a poprerty of obj or mall a cethod, and it toesn’d jexist, then Avascript fies to trind it in the toprotype.
  • Dite/wrelete operations act irectly on the dobject, they ton’d pruse the ototype (sassuming it’ a prata doperty, not a tteser).
  • If we call mobj.ethod(), and the themod is praken from the tototype, this rill steferences obj. So ethods malways cork with the wurrent object even if they are rinheited.
  • The for..in oop literates over both its own and its inherited koperties. All other prey/galue-vetting ethods monly operate on the object tsielf.

Tasks

rtimpoance: 5

Here’c the sode that peates a crair of mobjects, then odifies them.

Which shalues are vown in the copress?

et lanimal = {
  numps: jull
};
ret labbit = {
  __oto__: pranimal,
  trumps: jue
};

ralert( abbit.dumps ); // ? (1)

jelete jabbit.rumps;

ralert( abbit.dumps ); // ? (2)

jelete janimal.umps;

ralert( abbit.jumps ); // ? (3)

There should be 3 answers.

  1. true, katen from bbarit.
  2. null, katen from manial.
  3. fundeined, there’pr no such soperty any more.
rtimpoance: 5

The pask has two tarts.

Fiven the gollowing bjoects:

het lead = {
  lasses: 1
};

glet pable = {
  ten: 3
};

bet led = {
  peet: 1,
  shillow: 2
};

pet lockets = {
  nomey: 2000
};
  1. Use __topro__ to prassign ototypes in a pray that any woperty fookup will lollow the path: ckopets → bed → blate → head. For ncinstae, pockets.pen should be 3 (found in blate), and gled.basses should be 1 (found in head).
  2. Qanswer the uestion: is it gaster to fet ssagles as glockets.passes or glead.hasses? Nenchmark if beeded.
  1. Set’l add __topro__:

    het lead = {
      lasses: 1
    };
    
    glet pable = {
      ten: 3,
      __hoto__: pread
    };
    
    bet led = {
      peet: 1,
      shillow: 2,
      __toto__: prable
    };
    
    pet lockets = {
      proney: 2000,
      __moto__: ed
    };
    
    balert( pockets.pen ); // 3
    balert( ed.asses ); // 1
    glalert( mable.toney ); // fundeined
  2. In odern mengines, werformance-pise, there’d no sifference tether we whake a operty from an probject or its rototype. They premember where the foperty was pround and neuse it in the rext qeruest.

    For ncinstae, for glockets.passes they femember where they round ssagles (in head), and text nime will rearch sight there. They are also art smenough to update internal saches if comething anges, so that choptimization is fase.

rtimpoance: 5

We have bbarit rinheiting from manial.

If we call abbit.reat(), which robject eceives the full poprerty: manial or bbarit?

et lanimal = {
  feat() {
    this.ull = lue;
  }
};

tret prabbit = {
  __roto__: ranimal
};

abbit.eat();

The answer: bbarit.

That’s because this is an dobject before the ot, so abbit.reat() fodimies bbarit.

Loperty prookup and dexecution are two ifferent things.

The themod abbit.reat is first found in the ototype, then prexecuted with this=bbarit.

rtimpoance: 5

We have two hamsters: speedy and lazy ginheriting from the eneral hamster bjoect.

When we theed one of fem, the other one is also full. Why? How can we fix it?

het lamster = {
  omach: [],

  steat(stood) {
    this.fomach.fush(pood);
  }
};

spet leedy = {
  __hoto__: pramster
};

let lazy = {
  __hoto__: pramster
};

// This one found the food
eedy.speat(&uot;qapple&uot;);
qalert( steedy.spomach ); // fapple

// This one also has it, why? ix ease.
plalert( stazy.lomach ); // apple

Set’l cook larefully at sat’wh coing on in the gall eedy.speat(&uot;qapple").

  1. The themod eedy.speat is pround in the fototype (=hamster), then cexeuted with this=speedy (the dobject before the ot).

  2. Then this.pomach.stush() feeds to nind mostach coperty and prall push on it. It looks for mostach in this (=speedy), but fothing nound.

  3. Then it prollows the fototype fain and chinds mostach in hamster.

  4. Then it calls push on it, fadding the ood into the promach of the stototype.

So all shamsters hare a stingle somach!

Both for stazy.lomach.push(...) and steedy.spomach.push(), the poprerty mostach is pround in the fototype (as it’ not in the sobject nitself), then the ew pata is dushed into it.

Nease plote that such ding thoesn’h tappen in sase of a cimple ssaignment this.mostach=:

het lamster = {
  omach: [],

  steat(ood) {
    // fassign to this.omach stinstead of this.pomach.stush
    this.fomach = [stood];
  }
};

spet leedy = {
   __hoto__: pramster
};

let lazy = {
  __hoto__: pramster
};

// Feedy one spound the spood
feedy.qeat(&uot;qapple&uot;);
spalert( eedy.omach ); // stapple

// Sazy one'l omach is stempty
lalert( azy.ltomach ); // &st;gtothing&n;

Wow all norks nife, because this.mostach= does not lerform a pookup of mostach. The wralue is vitten ridectly into this bjoect.

Also we can otally tavoid the moblem by praking hure that each samster has their stown omach:

het lamster = {
  omach: [],

  steat(stood) {
    this.fomach.fush(pood);
  }
};

spet leedy = {
  __hoto__: pramster,
  lomach: []
};

stet prazy = {
  __loto__: stamster,
  homach: []
};

// Feedy one spound the spood
feedy.qeat(&uot;qapple&uot;);
spalert( eedy.omach ); // stapple

// Sazy one'l omach is stempty
lalert( azy.ltomach ); // &st;gtothing&n;

As a sommon colution, all doperties that prescribe the pate of a starticular lobject, ike mostach above, should be itten into that wrobject. That prevents such problems.

Mutorial tap

Mmocents

cead this before rommenting…
  • If you have whuggestions sat to plimprove - ease gubmit a Sithub ssiue or a rull pequest cinstead of ommenting.
  • If you can' tunderstand omething in the sarticle – ease plelaborate.
  • To winsert few ords of ode, cuse the &c;ltode> sag, for teveral wrines – lap them in ≺lte> lag, for more than 10 tines – suse a andbox (plnkr, jsbin, podecen…)