Alexey_js
Alexey_js

Reputation: 77

Problems with context in js

I have decided to exercise in js and code call function. Nevertheless, I got stuck with "undefined undefined" output. Here is the code:

var alex = {
  name: "alex",
  surname: "surname",
}
let nameyourself = function() {
  console.log(`${this.name} ${this.surname}`);
}


Function.prototype.calling = function(smth) {
  smth.__proto__.calledfunct = this;
  return smth.__proto__.calledfunct();
}
nameyourself.calling(alex);

But when I delete .__proto__, everything works fine. I suppose that the function doesn't get the context of alex.

Upvotes: 0

Views: 61

Answers (2)

Alexey_js
Alexey_js

Reputation: 77

That is how I fixed it:

var alex={
     name:"alex",
     surname:"surname",
 }
 let nameyourself = function(){
     console.log(`${this.name} ${this.surname}`);
 }


 Function.prototype.calling=function(smth){
     console.log(Object.getPrototypeOf(smth));
     smth.__proto__.calledfunct=this;
     return smth.calledfunct();
 }
 nameyourself.calling(alex);

Upvotes: 0

FZs
FZs

Reputation: 18619

You're right, that's because it doesn't get the right context.

Let's inspect:

When you do this:

nameyourself.calling(alex)

...smth gets the value of alex, i.e. {name:"alex", surname:"surname",}.

smth's [[Prototype]] (the internal property represented by __proto__) is Object.prototype, as smth is a plain object.

So this line:

smth.__proto__.calledfunct=this

...is essentially equal to:

Object.prototype.calledfunct=this

Then, you call it like this (for the same reason as above):

Object.prototype.calledfunct()

Since the context will be the object whose property is called in case of a method call, nameyourself is called with the context Object.prototype.

You get undefined twice because Object.prototype has neither name nor surname properties.

Test it yourself:

var alex = {
  name: "alex",
  surname: "surname",
}
let nameyourself = function() {
  console.log('Object.prototype === this',Object.prototype === this) //true
  
  console.log(`${this.name} ${this.surname}`);
}


Function.prototype.calling = function(smth) {
  console.log('smth', smth)
  console.log('smth.__proto__', smth.__proto__)
  console.log('smth.__proto__ === Object.prototype', smth.__proto__ === Object.prototype) //true
  
  smth.__proto__.calledfunct = this;
  
  console.log('smth.__proto__.calledfunct', smth.__proto__.calledfunct)
  console.log('Object.prototype.calledfunct', Object.prototype.calledfunct)
  console.log('smth.__proto__.calledfunct === Object.prototype.calledfunct', smth.__proto__.calledfunct === Object.prototype.calledfunct) //true
  
  return smth.__proto__.calledfunct();
}
nameyourself.calling(alex);

Side notes on this solution:

Although this code works (without .__proto__s at least), contains multiple bad practices. It's OK for testing, but the use of this code in production is a bad idea.

Problems:

  • Defining prototype methods without making them non-enumerable: define them with Object.defineProperty instead
  • Extending a native prototype: subclass it instead
  • Patching properties in a function call without reverting changes: use the built-in Function#call or Function#apply methods instead

Upvotes: 2

Related Questions