Why compiler doesn't translate "this" link into context-agnostic variable? - javascript

Suppose I have a class (very simple scenario)
class Student
{
name = "John";
sayHello()
{
console.log("Hi, I'm " + this.name);
}
}
It's compiled by TypeScript compiler to:
var Student = (function () {
function Student() {
this.name = "John";
}
Student.prototype.sayHello = function () {
console.log("Hi, I'm " + this.name); //here is the problem. Accessing name via this
};
return Student;
})();
Now if I create an object and call a method, everything works fine.
var student = new Student();
student.sayHello(); //prints Hi, I'm John
But if I invoke that method from callback, it breaks (this is referencing a Window as expected)
setTimeout(student.sayHello); //prints Hi, I'm
I'm aware of the difference between this in JavaScript and C# or Java. I'm also aware, that TypeScript tries to address this difference. For example this code:
class Student
{
name = "John";
sayHelloTo(other)
{
other(() => this.name);
}
}
Would have been compiled to
var Student = (function () {
function Student() {
this.name = "John";
}
Student.prototype.sayHelloTo = function (other) {
//note, the compiler solves the problem by capturing this into local variable
var _this = this;
other(function () {
return _this.name;
});
};
return Student;
})();
Why isn't the compiler creates something like _this variable in the first scenario for class members? I would expect to see something along next code (not a real output and this code is not correct either, just to show my intention)
var Student = (function () {
var _this;
function Student() {
_this = this; //solves the problem of setTimeout(student.sayHello)
_this.name = "John";
}
Student.prototype.sayHello = function () {
console.log("Hi, I'm " + _this.name);
};
return Student;
})();
I've used the TypeScript v0.9.7 compiler

You might want to change the sayHello function like below to make it generate to code you want. Notice the sayHello = () => { }
This will still work with multiple students which is not the case with your example.
class Student
{
name = "John";
sayHello = () =>
{
console.log("Hi, I'm " + this.name);
}
}
It will generate code like this:
function Student() {
var _this = this;
this.name = "John";
this.sayHello = function () {
console.log("Hi, I'm " + _this.name);
};
}
Another possibility is to change the call to setTimeout like this
setTimeout(() => { student.sayHello() });

The only thing that the compiler could do would be to make sure each constructed object had a bound copy of the prototype functions. That would involve a very significant semantic change, so it can't really do that.
The translated code returns a function that has access to a closure, it's true. However, in your suggested alternative, there's only one _this that would be shared by all instances created by the constructor. The closure is in that function that is called to create the "Student" constructor; that function only runs once, when the constructor is made, and then never again. Thus each call to new Student() would update that single, shared variable _this. (In the example, the way that would cause a problem would be for the "name" property to change on a Student instance. If they all are named "John" it doesn't matter :)
The fundamental issue is that in JavaScript, there is no intrinsic relationship between a function and any object. When you call
setTimeout(student.sayHello, 100);
the first parameter expression evaluates to a plain reference to that "sayHello" function. The fact that the reference came from the object is lost. I suppose another alternative for Typescript would be to catch those sorts of expressions and create a bound function at that point. That is, the class code itself would remain the same, but the setTimeout() call would be translated as
setTimeout(student.sayHello.bind(student), 100);
What sort of ramifications that would have on everything I can't say. I also don't know how hard it would be for the compiler to know that it should do that transformation; there might be times at which it doesn't make sense.

Related

Can I bind a constructor's prototype methods to constructed instances while keeping concerns separated?

Say I have an object constructor and a prototype method, like:
function Human(name) {
this.name = name;
}
Human.prototype.sayName = function(){
console.log('my name' + this.name);
};
Elsewhere in my code, I've defined an instance of human:
let jeff = new Human('jeff');
and lastly I want to pass jeff.sayName as a callback to some other function, like (for a particularly trivial example)
function callFunction(callback) {
callback();
}
callFunction(jeff.sayName);
When I call callFunction(jeff.sayName) above, the context needs to be bound to jeff itself, like
callFunction(jeff.sayName.bind(jeff)). But this is clunky, and I'd rather not have to worry about this sort of logic every time I use this method as a callback.
An alternative would be to replace Human.prototype.sayName with something like
Human.prototype.createSayName = function(context){
return function() {
console.log(context.name);
};
};
and then define the function with
jeff.sayName = jeff.createSayName(jeff)
but this is a bit awkward, and I'd like to keep jeff agnostic to the methods it inherits from Human.prototype.
So ideally I'd like to handle this logic on Human.prototype itself, something like
Human.prototype.sayName.bind(WhateverObjectHoldsThisMethod)
but I'm not aware javascript has a way of doing this.
TL;DR I would like a way of binding an object's prototype method to whatever object inherits it, without having to do so every time I pass that method as an argument or every time I define a new object. Sorry if this makes little sense. Thanks!
Due to the way lexical environments, scope resolution, prototypal inheritance, and environment records work in JavaScript, what you're asking for is not possible without modifying the function which calls the callback function.
However, you could—instead of passing the Human#sayName reference as the call back—use an arrow function that in turn calls the Human#sayName reference you wish to call.
It's not perfect, but it's simple, clean, and readable.
function Human(name) {
this.name = name;
}
Human.prototype.sayName = function(){
console.log('my name' + this.name);
};
let jeff = new Human('jeff');
function callFunction(callback) {
callback();
}
callFunction(_ => jeff.sayName());
For a better understanding of those fancy words I referenced earlier, and how they work in JavaScript, I would recommend reading section 8.1 of the ECMAScript 2017 Language Specification. Subsection 8.1.1.3 has the specific information you're looking for, but the rest of the section up to that point is necessary to understand that subsection.
Basically, when you pass Human#sayName to callFunction, you're passing the reference to the original sayName function, so you might as well be doing this: (pardon the pun)
function callFunction(callback) {
callback();
}
callFunction(function(){
console.log('my name' + this.name);
});
The content of the function is not evaluated until it is executed, which means by the time it is executed, the value of this has already changed. To add to the debacle, the original function has no knowledge of which instance you requested it through. It never actually exists on the jeff object. It exists in the prototype of the the function object, and when you perform the object property look up, the JavaScript engine searches the prototype chain to find that function.
You very well could get the behavior you're asking for, but not under the constraints you have laid out. For example, if the function does not have to exist on the prototype chain, and can instead exist on the instance (keep in mind that this creates a new function object for each instance, so it will increase cost), you could define the function in the constructor, then store a reference to the correct this using an identifier that will not be overwritten:
function Human(name) {
const _this = this;
this.name = name;
this.sayName = function(){
console.log('my name' + _this.name);
};
}
let jeff = new Human('jeff');
function callFunction(callback) {
const _this = { name: 'hello' }; // does not affect output
callback();
callback.call(_this); // does not affect output
}
callFunction(jeff.sayName);
This would be a safer option, because you know that _this will always refer to the object you're expecting it to refer to within the context of the constructor, all function objects defined within that function object will inherit the identifiers of their parent scope, and those identifiers will not be affected by the calling context.
Or, you could go one step further, and not rely on the value of this at all:
function Human(name) {
const sayName = function(){
console.log('my name' + name);
};
Object.assign(this, { name, sayName });
}
let jeff = new Human('jeff');
function callFunction(callback) {
const name = 'hello'; // does not affect output
callback();
callback.call({ name: 'world' }); // does not affect output
}
callFunction(jeff.sayName);
This has the advantages of:
Being easier to read,
Less code,
Allowing you explicit about the properties and methods being exposed through the object, and
Never having to worry about what the value of this will be.
I suppose you may want to achieve this
function Human(name) {
this.name = name;
}
Human.prototype.sayName = function() {
console.log('my name' + this.name);
};
let jeff = new Human('jeff');
function callFunction(callback) {
callback();
}
callFunction(function() {
jeff.sayName()
});
another guess, a prototype bound to an instance, it works but has anti-patten
function Human(name) {
this.name = name;
}
const jeff = new Human('jeff');
Human.prototype.sayName = function() {
console.log('my name' + jeff.name);
};
function callFunction(callback) {
callback();
}
callFunction(jeff.sayName);
another guess, relection
function Human(name) {
this.name = name;
}
Human.prototype.sayName = function() {
console.log('my name' + this.name);
};
Human.prototype.reflectName = function(item) {
this.sayName = () => item.sayName()
};
const jeff = new Human('jeff');
const tod = new Human('tod');
tod.reflectName(jeff)
tod.sayName()
Expanding on TinyGiant answer, you can use an arrow function, but if you combine it with a getter, you can define it as a method of the prototype and not bother with defining your callback as a arrow function, which may be more flexible depending on your needs. Like this:
function Human(name) {
this.name = name;
}
Object.defineProperty(Human.prototype, "sayName", {
get: function() {
return () => {
console.log("my name is", this.name);
}
}
});
function callfunction(callback) {
callback();
}
let jeff = new Human('jeff');
callfunction(jeff.sayName);
// just to show it works even as a regular function
jeff.sayName();
// in fact it overrides every context you bind
jeff.sayName.bind(window)()

Some basic questions about OOP in JS

currently I´m learning about objects and I´m not sure about the terminology of some words and descriptions. I´m sure some of you can help me out here :)
Code example:
function Person(name) {
this.name = name,
this.introduce = function() {
console.log("My name is " + this.name);
}
}
var jon = new Person("Jon")
jon.introduce();
My questions:
Is there actually a difference between the code above and the following code:
var Person = function(name) {
this.name = name,
this.introduce = function() {
console.log("My name is " + this.name);
}
}
var jon = new Person("Jon")
jon.introduce();
Which one is better practice? I guess the first code snippet, since it´s less code.
Now the terminology.
2.1 Am I right, given the code example at the beginning, that you call the following snippet the Prototype?
function Person(name) {
this.name = name,
this.introduce = function() {
console.log("My name is " + this.name);
}
}
2.2 Am I right, given the code example at the beginning, that you call the following snippet the constructor(-function)?
var jon = new Person("Jon")
Thanks and happy eastern! :)
Point 1: The big Words
function Person(name) {
this.name = name,
this.introduce = function() {
console.log("My name is " + this.name);
}
}
var jon = new Person("Jon")
jon.introduce();
This function has a name 'Person'. This is called a Function statement.
var Person = function(name) {
this.name = name,
this.introduce = function() {
console.log("My name is " + this.name);
}
}
var jon = new Person("Jon")
jon.introduce();
This funciton is anonymous and does not have a name. We can assign a name but
it is not required since the variable can be used to execute the function. This is called a function expression.
You can read more about Function statements and expressions here:
https://javascriptweblog.wordpress.com/2010/07/06/function-declarations-vs-function-expressions/
Point 2: Execution (Hoisting)
Functions assigned to variable must be called after the function is defined because of the way hoisting works.
So for the first case the function can be called either below or above it is defined.
But for the second case the function must be invoked after it. Since it is stored in a variable. Invoking it before the function will return undefined. It won't give error. The variable is there in memory space but it is not defined at that point.
You can read more about variable and function hoisting here:
http://adripofjavascript.com/blog/drips/variable-and-function-hoisting
Point 3: Function constructor:
In your case the term to use for function is "Function Constructor" since you are essentially using function as a constructor for the Person object to define it's properties.
It's simply the difference between a function expression vs. declaration. I would go with the seemingly simpler declaration instead of the unnamed anonymous function assigned to a variable.
That snippet shows the constructor function also returned by jon.constructor. The prototype of a person constructed by that constructor function - which you can access via Object.getPrototypeOf(jon) or Person.prototype - is pretty much empty. Assigning this.name = name doesn't add a name attribute to the prototype but the currently created object.
It's the new operator applied to a constructor function returning a newly constructed object.

the 'this' key word with IIFE in javascript

I was practicing java script from one of the book's examples and encounter following
Code one: here i learnt 'this' key word in javascript references the object that owns the code where 'this' keyword is.
function Vehicle1(theYear, theMake, theModel) {
var year = theYear;
var make = theMake;
var model = theModel;
this.getYear = function () { return year; };
this.getMake = function () { return make; };
this.getModel = function () { return model; };
}
Vehicle1.prototype.getInfo = function () {
return 'Vehicle1: ' + this.getYear() + ' ' + this.getMake() + ' ' + this.getModel();
}
Code two: here i was learning about cretaing namespace using IIFE (Immediately Invoked Function Expression).
(function () {
this.myApp = this.myApp || {};
var ns = this.myApp;
var vehicleCount = 5;
var vehicles = new Array();
ns.Car = function () { };
ns.Truck = function () { };
var repair = {
description: 'changed spark plugs',
cost: 100
};
} ());
I was supposed to execute above codes individually to understand the concepts that the author was trying to explain. But i ended up executing both codes in single file, and i am getting error message in code one stating
Uncaught TypeError: undefined is not a function Vehicle1.getInfo.myApp
Question is: Why or how the IIFE function is trying to put or finding myApp namespace in code one ?
if i execute above 2 codes individually all works as expected.
EDIT
Here is full code just copy past it in html's head section using script tags. I run it in chrome and look in console for error details
function Vehicle1(theYear, theMake, theModel) {
var year = theYear;
var make = theMake;
var model = theModel;
this.getYear = function () { return year; };
this.getMake = function () { return make; };
this.getModel = function () { return model; };
};
Vehicle1.prototype.getInfo = function () {
return 'Vehicle1: ' + this.getYear() + ' ' + this.getMake() + ' ' + this.getModel();
}
(function () {
this.myApp = this.myApp || {};
var ns = this.myApp;
var vehicleCount = 5;
var vehicles = new Array();
ns.Car = function () { };
ns.Truck = function () { };
var repair = {
description: 'changed spark plugs',
cost: 100
};
} ());
i learned 'this' key word in javascript references the object that owns the code where 'this' keyword is.
That is not the case, but it seems kind of like it is.
Warning - the following will sound stupid. That is because javascript this is a really bad language feature (and people should stop using it).
Here's the deal - there's not really such a thing as this. At least not in the way you think it is. It's really a function parameter just like any other.
Let me show you.
let's say you have a function like this
function sayHi(firstName, lastName) {
console.log("Hi", firstName, " ", lastName);
}
Here's two different but pretty much identical ways to invoke it:
sayHi("Fred", "Flintstone");
sayHi.call(null, "Fred", "Flintstone");
this is because all functions have a call method. If you want to, you can write all your function invocations by using .call.
But what's that first null parameter? Well, that parameter is what this will be set to. So if you have:
function sayHi(lastName) {
console.log("Hi", this, " ", lastName);
}
you can say
sayHi.call("Fred", "Flintstone");
So now let me ask you a question. If you can always write all functions using .call, and if .call takes this as a parameter right next to the others, how exactly is this different from any other parameter? Just one that you don't get to name.
But what does the sayHi(...) format do with this then? Since we're not specifying it directly it must come from somewhere. Well, in this form - which is really just a facade for call - javascript takes a guess at what you want it to be. The rules aren't terribly complex, but it's still pretty confusing.
If you're invoking it as an object
var fred = "Fred";
fred.sayHi = sayHi;
fred.sayHi("Flintstone");
is equivalent to
fred.sayHi.call(fred, "Flintstone");
In the case of invoking it directly:
sayHi("Flintstone");
the rules dictate that it will guess that this is the global window object. Unless you're in strict mode, in which case it will be undefined (I think, it might be null).
sayHi.call(window, "Flintstone");
This is what is going on with an IIFE.
Basically, this is a feature that was tacked on to make it look more like Java and is really not necessary for the language at all. Some libraries use it, and you don't have a choice, but I always consult people that when they have control of it they don't use this or new (which is even more confusing) at all.
There is an error because of a missing semicolon after the first code, causing the combined file to be interpreted in a very different way than intended.
Let's simplify the code to see the structure. You have something like:
getInfo = function () {
this.getYear();
}
(function () {
this.myApp = {};
} ());
Without the semicolon after the first function, the parenthesis around the second function are treated as giving the parameters to call the first function:
getInfo = function () {
this.getYear();
}(function () {
this.myApp = {};
} ());
So it's basically as if you wrote:
function getInfo() {
this.getYear();
}
getInfo(function () {
this.myApp = {};
}());
this in both these cases refers to the Window (or the global object) because the function is not being called on an instance. You're calling the second function, which will set Window.myApp, then passing its return value (undefined) to getInfo. getInfo will access this.getYear which doesn't exist so you get the type error.
The error I get here, and with your original code is:
TypeError: this.getYear is not a function
Because this is the global object, then this.getYear is going to be undefined (not a function).
The this keyword in JavaScript is whatever is calling or executing the function. In your example, for a IIFE, this would be the global Window object AKA window
Your question seems to be missing code. Are Car and Truck supposed to extend Vehicle1 or something?
And you have no code calling Vehicle1.getInfo.myApp, so I'm not sure how you could be getting that message.

Do functions attached to the prototype property not have closure

I am trying to figure out how I can add methods to a constructor after I have created it.
In my code below, I cannot use Person's prototype property to add a new public method which has access to Person's vars. (Do the functions attached to the prototype property not close over the vars in the main function).
Unlike the first way, the second way works - Person 2. seems like these are called privileged methods -http://www.crockford.com/javascript/private.html.
function Person(name, age){}
Person.prototype.details = function(){
return "name: "+name+", age: "+age;
};
function Person2(name, age){
this.details = function(){
return "name: "+name+", age: "+age;};
}
var per1 = new Person("jim", 22);
var per2 = new Person2("jack", 28);
per1.details();
//=> ReferenceError: age is not defined
per2.details();
//=> "name: jack, age: 28"
No, they do not have closure over the constructor functions vars. They are in a different scope.
// This function is in one scope.
function Person(name, age) {
}
// This statement is in the parent scope, which
// doesn't have access to child scopes.
Person.prototype.details = function(){
return "name: "+name+", age: "+age;
};
That's the way that "public" functions work in JavaScript. You could make details a privileged function by defining it within the constructor:
function Person(name, age) {
this.details = function() {
return "name: "+name+", age: "+age;
};
}
Of course, that means that each instance of Person gets it's own copy of the details function.
You could also, as #Chuck suggests, make name and age public members, in which you would have access to them in a prototype function:
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.details = function(){
return "name: " + this.name + ", age: " + this.age;
};
No. Typically you would either use the second approach, or set this._name = name; in the constructor and reference it that way in the other method.
Of course not, the function was declared in a scope, different to the scope where the arguments/variables were declared, so JS wouldn't know which variables you're on about. Suppose you had a second closure, or better (well, worse actually) yet: a global variable called name. Which one would JS pick?
Here's an example for you:
function MyObject(name)
{
var localVar = 'foobar';
this.evilMethod = (function(localVar)
{
return function()
{
console.log('localVar = '+localVar);//=== name
};
})(name);
this.badMethod = function()
{
console.log('localVar = '+ localVar);// === 'foobar'
};
}
var name = 'Global Name';
var anotherClosure = (function(name)
{
var localVar = name.toLowerCase();
return function()
{
console.log(name);
console.log(localVar);
}
})('Bobby');
MyObject.prototype.closureVars = function()
{
console.log(name);//Global name
console.log(localVar);//undefined
};
Now first off: this is terrible code, but you get the point: you can have hundreds of variables with the same name, which one JS has to use, might not always be clear.
Giving prototypes access to instance closure variables has other implications, too: you could, for instance change their values, which defeats the point of having a closure in the first place. But the biggest problem by a country mile would be: multiple instances! If you create a constructor, odds are you're going to instantiate more than 1 object with it. How would that work, if they all share the same prototype?
Just assign the arguments/variables you want to access in the prototype to a property, like FishBasketGordo's example does

Forcing the context

I have this class where I have a private property and a public method for access:
Person = function () {
this.Name = "asd";
var _public = new Object();
_public.Name = function (value) {
if (value == undefined) { //Get
return this.Name
} else {
this.Name = value; //Set
}
};
return _public;
};
I want to force the context in _public.Name for access a this.Name.
I know the technique of closure, but I want to see if I can force a context.
I found a technique to do it, extend object Function:
Function.prototype.setScope = function (scope) {
var f = this;
return function () {
f().apply(scope);
}
}
And my class becomes:
Person = function () {
this.Name = "asd";
var _public = new Object();
_public.Name = function (value) {
if (value == undefined) {
return this.Name
} else {
this.Name = value;
}
}.setScope(this);
return _public;
};
So I can force correctly the context, but I can not pass value and can not, however, return this.Name.
Not
f().apply(scope);
just
f.apply(scope);
(No () after f.) You want to use the apply function on the function f object, not call the function f and access apply on its return value.
To also pass on the arguments that your function in setScope receives, add this:
f.apply(scope, arguments);
arguments is an implicit argument to all functions, which is a pseudo-array of the actual arguments passed to the function at runtime. apply accepts any array-like thing as its second parameter to specify the arguments to use when calling the underlying function.
I'd also have it return the return value:
return f.apply(scope, arguments);
So setScope becomes:
Function.prototype.setScope = function (scope) {
var f = this;
return function () {
return f.apply(scope, arguments);
}
}
Live example
Note that the usual name for this function, and the name it has in the new ECMAScript5 standard, is bind (Section 15.3.4.5; ECMAScript5's bind also lets you curry arguments, which isn't done by this implementation). setScope is a particularly unfortunate name, because it doesn't set the scope, it sets the context.
Having said all that, there's no reason you need setScope in your Person constructor. You can just do this:
Person = function () {
var self = this;
this.Name = "asd";
var _public = new Object();
_public.Name = function (value) {
if (value == undefined) {
return self.Name;
} else {
self.Name = value;
}
};
return _public;
};
Live example
But using bind (aka setScope) can be useful in places where you don't want a new closure over the context in which you're doing it.
Off-topic: The way you're specifying Person will break certain things people might expect to work, such as:
var p = new Person();
alert(p instanceof Person); // Expect "true", but in your case will be "false"
...because you're replacing the object new created for you, but returning a different object out of your constructor (which overrides the default).
Rather than creating a new object and returning that in your constructor, allow the object constructed for you by new to be the object (and thus the Person relationship is maintained), but you can still get truly private variables and use accessors:
function Person() {
// Private variable
var name = "asd";
// Accessor function
this.Name = function(value) {
if (typeof value === "undefined") {
return name;
}
name = value;
};
}
Live example
As you can see, this is dramatically simpler, and it preserves the instanceof relationship. Note that we're not qualifying our references to name within Name at all, and so we're using the local variable in the constructor call in which our Name function, which closes over it, was created.
I've also taken the liberty there of giving the constructor function a name, because I'm not a fan of anonymous functions. I should have given the accessor a name as well:
function Person() {
// Private variable
var name = "asd";
// Accessor function
this.Name = Person_Name;
function Person_Name(value) {
if (typeof value === "undefined") {
return name;
}
name = value;
}
}
Off-topic 2: The overwhelming convention in JavaScript code is to use initial caps on function names only for constructor functions (like Person), and not on other kinds of functions (like Name). You're free to do whatever you like, of course, but I thought I'd mention the convention, as it makes it easier for other people to read your code.
Worth noting: All of these techniques result in every single Person object having its own copy of the accessor function. If there are going to be a lot of these objects, that could be a memory issue. If there are only going to be a few, that's fine.
First thing, I think the correct way to go about this is the "closure" method, as the syntax is easier and simpler to understand and makes more sense and most object oriented code written in Javascript is written that way. Another thing to note is that in your method, the "private" member can be accessed from outside by accessing Person.Name (instead of (new Person()).Name).
That being said, it seems that you want something like Prototype.JS's bind method, which allows you to bind a function reference as a method call to a specific object, and also passes all the arguments correctly (including allowing preloaded arguments).
Look at Prototype.JS source for the complete implementation, but a simple implementation of this semantic might look like this:
Function.prototype.bind = function(context) {
var callee = this;
var args = Array.prototype.slice.call(arguments,1);
return function() {
var newargs = args.concat(Array.prototype.slice.call(arguments,0));
return callee.apply(context, newargs);
};
};
It is difficult to understand what you are trying to achieve. But if I guess that you are trying to create a Person class with a name method to get/set the person's name, here is my suggestion:
function Person() {
this._name = undefined; // not required but is better than assigning a fake name
return this;
}
Person.prototype.name = function( _name ) {
if ( _name === undefined ) return this._name; // get
return this._name = _name; // set
}
Note that I have defined the name function with a lower case first letter. This is standard practice in JavaScript where only constructors are usually capitalized. To use this class you do:
person = new Person();
person.name( "Ermes Enea Colella" );
alert( person.name ); // displays "Ermes Enea Colella"
There is no need to bind any context with this method, so you may be looking for something else. If you can clarify your need, I'll be happy to edit my answer.
I hope this helps.

Categories

Resources