Understanding Contexts in Javascript - javascript

i'm struggling to understand how contexts work in JavaScript or specifically deriving the value of this. in my case i have Person constructor that returns an object. From my understanding the value of this depends on the way that the function is called. For instance calling the person function like this "Person()" .the value returned for this will be the window. How do i then ensure that the person values passed are exactly what is returned
function Person(personName,Title,Phone,Email,Id, picUrl, description){
var myusername = this.username;
var myTitle = this.Title;
var myPhone = this.Phone;
var myEmail = this.Email;
var myId = this.Id;
var mypicUrl = this.picUrl;
var mydescription = this.description;
return {
name: myusername,
Title: myTitle,
phoneNumber: myPhone,
Email: myEmail,
UserId: myId,
PictureUrl: mypicUrl,
Description: mydescription
}
}
function getallDetails (){
var PeopleCompleteList = [];
for (i=0; i< data.d.results.length; i++) {
if(data.d.results[i]['Name'] != null){
personName = data.d.results[i]['Name'].Name.split('|')[2];
userName = data.d.results[i]['Name']['Name'];
UserTitle = data.d.results[i]['Name']['Title'];
UserphoneNumber = data.d.results[i]['Name']['WorkPhone'];
UserEmail = data.d.results[i]['Name']['EMail'];
Id = data.d.results[i]['Name']['Id'];
picUrl= WebURL + '/_layouts/15/userphoto.aspx?size=L&accountname='+UserEmail;
description = data.d.results[i]['pozb'];
PeopleCompleteList.push(Person(personName, UserTitle, UserphoneNumber,UserEmail,Id, picUrl, description));
}
}
}//end function

As #CBroe and #Icepickle said in comments, you are not using the Person function as a constructor. You need to call your method with the new keyword. By doing so :
the this keyword inside your function will be a reference to the object you're willing to create (otherwise it would be a reference to the global object)
the function doesn't need to return anything as it implicitly returns the object referenced by this
I'd advise you read this article : MDN - the new operator.
Meanwhile, the snippet below shows what your code would look like if you used the Person function as a constructor
function Person(personName, title, phone, email, id, picUrl, description) {
// populating the object behind the 'this' reference with each argument
// if you call this method with the 'new' keyword, 'this' will be a reference to the instance you are creating
this.username = personName;
this.title = title;
this.phoneNumber = phone;
this.email = email;
this.userId = Id;
this.pictureUrl = picUrl;
this.description = description;
// if this function is called with the 'new' keyword, you don't have to return anything. the reference behind 'this' will be implicitly returned
}
function getallDetails() {
// local variable declarations are usually put on top of the function
var peopleCompleteList = [], personName, userName, userTitle, userphoneNumber, userEmail, id, picUrl, description;
for (i=0; i< data.d.results.length; i++) {
if (data.d.results[i]['Name'] != null) {
personName = data.d.results[i]['Name'].Name.split('|')[2];
userName = data.d.results[i]['Name']['Name'];
userTitle = data.d.results[i]['Name']['Title'];
userphoneNumber = data.d.results[i]['Name']['WorkPhone'];
userEmail = data.d.results[i]['Name']['EMail'];
id = data.d.results[i]['Name']['Id'];
picUrl= WebURL + '/_layouts/15/userphoto.aspx?size=L&accountname='+UserEmail;
description = data.d.results[i]['pozb'];
// adding the 'new' keyword before invoking the Person method
peopleCompleteList.push(new Person(personName, userTitle, userphoneNumber,userEmail,Id, picUrl, description));
}
}
}//end function
Couple side notes :
You forgot to use the var keyword when declaring variables in the getallDetails function. It's a bad practice as they will all be global variables and won't be cleared after the execution of the getallDetails function
Most of your variables declarations violate the JavaScript variable naming convention. I updated the snippet with proper variable names (using the camelCase notation).

In simple terms, You can say context is the place where this reside. For example:
<script>
function show() {
this.context = 'sudhir';
}
</script>
This is equivalent to
<script>
var context;
function show() {
this.context = 'sudhir';
}
</script>
Both functions are written on the global scope. So, this refers to global means scope or your context is global. Now let's see the interesting example:
<script>
function newContext() {
var a = function show() {
this.context = 'sudhir';
}
}
</script>
It's equivalent to write here like this:
<script>
function newContext() {
var context;
var a = function show() {
this.context = 'sudhir';
}
}
</script>
Here this refers to the function show which is written on the scope of newContext function. Hence, Context of this is newContext.

I will show you what I will have done.
First of all, if you create an object, you don't have to return a value.
function Person(personName,Title,Phone,Email,Id, picUrl, description){
this.personName = personName;
[...]
this.mydescription = description;
}
Then you instantiate your "class" like :
var person = new Person('David', ..., 'some description');
And access attribute like that :
console.log('Person name is', person.personName);
JavaScript "This" is about function scope, example :
function Person(personName,Title,Phone,Email,Id, picUrl, description){
this.personName = personName;
[...]
this.mydescription = description;
function someAnonymousFunction(){
console.log('Person name is', this.personName); // Person name is Undefined
// Because this is a reference to someAnonymousFunction
}
}
Javascript trick to preserve this is to stock the context in variable :
function Person(personName,Title,Phone,Email,Id, picUrl, description){
var self = this;
this.personName = personName;
[...]
this.mydescription = description;
function someAnonymousFunction(){
console.log('Person name is', self.personName); // Person name is David
// Because we use self variable instead of this here
}
}
But you have an easier way
ECMAScript 2015 includes classes syntax. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes
If you care about navigator compatibility, you must use TypeScript
https://www.typescriptlang.org/
Typescript allow you to write more readable JavaScript code, especially about classes. Then, you can "compile" your code in JavaScript by choosing with version of JavaScript you want in output files.
Hope it helps,
Mickael

Related

JavaScript Self Invoking function properties

Trying to understand JS better, have a couple of clarifications. Lets suppose we have the following method
var customer = function(){
var name = "Contoso";
return {
getName : function(){
return this.name;
},
setName : function(newName){
this.name = newName;
}
}
}();
why is name not visible outside ?, when we log (customer.name) its undefined, however if we remove the self initializing parenthesis on function and change the variable declaration to (this.name) & again when we log the same we are able to see the value. what am i missing in here.
You need to take in consideration that JavaScript doesn't really have native classes. With this said, the way you can create constructors in order to "mimic" a class and be able to use this you need to create something like so:
function fnc (string) {
this.string = string;
}
fnc.prototype.getString = function() {
return this.string;
}
var x = new fnc('bar');
console.log(x.getString()); //bar
This is called the Constructor Pattern.
What you're trying to do is use something called the Module Pattern which works something like so:
var fn = (function() {
var string = 'foo';
return {
getString() {
return string;
}
}
})();
console.log(fn.getString()); //foo
Here is a working example: https://repl.it/FCn7
Also, a good read: https://addyosmani.com/resources/essentialjsdesignpatterns/book/
Edit
Example using getString and setString with the Module Pattern
var fn = (function() {
var string = 'foo';
return {
getString() {
return string;
},
setString(str){
string = str;
}
}
})();
fn.setString('xyz');
console.log(fn.getString()); // xyz
var is creating variable inside function scope which is not available outside as function property. If you want to access properties like customer.name you need to initialize it as this.name as you noticed.
var in this example is like creating private variable which can be modified by functions, but not directly.
SO this will work:
var customer = function(){
var name = "Contoso";
return {
getName : function(){
return name;
},
setName : function(newName){
name = newName;
}
}
}();

Why use "this" in OO JavaScript?

I see many people using the "this" keyword in an object oriented context in JavaScript. Similar to how "self" is used in Python and some other languages. In an object oriented context, I have been able to avoid using "this" by using the "module pattern".
For example:
var robot = (function() {
// private variables
var status = "on";
var name;
// private function
function turnOff() {
status = "off";
}
// public function
function setName(new_name) {
name = new_name;
}
return {
setName: setName
};
})();
Or, this pattern:
var robot = function(robot_name) {
// private variables
var status = "on";
var name = robot_name;
// private function
function turnOff() {
status = "off";
}
// public function
function setName(new_name) {
name = new_name;
}
return {
setName: setName
};
};
var FooBot = new robot('Foo');
var BarBot = new robot('Bar');
Is using "this" just a preference? Or, am I missing out on something?
No, this is not a preference. this is useful to know on which object your function was called.
In your example you don't need this information, that's why this is not useful for you.
But consider a constructor with multiple instances, and each one has its own associated data:
class Robot {
constructor(name) {
this.status = "on";
this.setName(name);
}
turnOff() {
this.status = "off";
}
setName(new_name) {
this.name = new_name;
}
}
var robotFoo = new Robot('foo');
var robotBar = new Robot('bar');
How would you read and store the data of the desired instance without using this?
You could use static methods and pass the instance as an argument, but that would be uglier.

Why these closure based private variables in JavaScript not working

I am experimenting imitating OOP like behavior in JS. I am trying to have (private) variables: id and name in function Person. To this function I am passing arguments which are used to initialize (private) variables. Then I am returning object having getter and setter for name and only a getter for id, thus effectively making id read-only.
So id can be set only through constructor whereas name can be set and get anytime.
This is the code:
var Person = function (_id,_nm) {
var id, name;
this.id = _id;
this.name = _nm;
return {
setName: function (nm) {
name = nm;
},
getName: function () {
return name;
},
getId: function () {
return id;
},
print: function () {
document.writeln("Id: "+id+"<br />Name: "+name);
}
}
}
var person = new Person(123, "Mahesh");
person.print();
However when new Person(123,"Mahesh") executes, I dont understand it is actually setting id and name or not, since while debugging I can see values set appropriately when hovered over them but Locals panel does not show them initialized:
Or either while in print() is is not referring to the desired id and name variables:
Whats wrong here?
Working fiddle
The reason is because you are using public members for the Person.prototype. You don't need to add this references to these two. So delete:
this.id = _id;
this.name = _nm;
and simply use:
var id = _id,
name = _nm;
Now everything will work fine. The whole idea is to use var, and not this, otherwise a closure will not be created. Now you will not be able to access name and id directly, instead you will have to use setName(), getName(), setId(), getId() etc.
The two members, id and name, will now become closures as you want them to be.
Update
If you used this.id, then it wouldn't have been private and you could just do var p = new Person(1, "Mahesha"); and access p.name or p.id directly. They are supposed to be private so this is not what you want.
With the closure pattern, p.name and p.id are undefined and can only be accessed through p.getName(); and p.getId();. Read on how closures work. The idea is that because you are using that var name, a closure will be created to remember it's value.
Your getName and setName are using that closure to access the name property. There is no this.name, there is a value remembered through a higher - order closure.
this.id and var id are not the same. this.id is a property of the object. var id belongs to the local scope.
Either use new or return a value. Not both.
The problem is that you're creating a new instance of Person using the new keyword, but your constructor function is returning another object instead.
When you return something from a constructor function it returns that value, and not the instance of the function.
You see when you execute new Person(123, "Mahesh") a new instance of Person is created. This is accessible within the constructor function as this.
If you don't return anything from your constructor then JavaScript automatically returns this. However you're explicitly returning another object.
No wonder var person doesn't have id and name properties (which you only defined on this).
In addition print doesn't display the id and name because although you declared them (var id, name) you didn't give them any values. Hence they are undefined.
This is how I would rewrite your Person constructor:
function Person(id, name) {
this.getId = function () {
return id;
};
this.getName = function () {
return name;
};
this.setName = function (new_name) {
name = new_name;
};
this.print = function () {
document.writeln("Id: " + id + "<br/>Name: " + name);
};
}
I didn't set the id and name properties on this because it makes no sense to include them.
You've mixed up using locally scoped ("private") variables for _id and _nm and "public" instance properties (this.id and this.nm).
In this case you need the former, but you created both and only initialised the latter.
Note that since id is read-only you don't really need a separate local variable at all, you can just use the lexically scoped first parameter to the constructor:
var Person = function (id, _nm) {
var name = _nm;
...
};
Let me try to explain using the following:
// Scope 1
var Person = function (_id,_nm) {
// Scope 2
var id, name;
this.id = _id;
this.name = _nm;
return {
// Scope 3
setName: function (nm) {
name = nm;
},
getName: function () {
return name;
},
getId: function () {
return id;
},
print: function () {
$("#output").append("<p>Id: "+id+"; Name: "+name + "<p/>");
}
}
}
The print method will return undefined because you are referring to the var id, name; that is never set in your code. You set the _id and _name to the property id and name but you fail to return the object that you just created. Instead, you return a dictionary, that references the name and id variable you created in Scope 2 that you never set, hence the undefined output in the print() call.
Here is what you should have done:
var NewPerson = function (_id,_nm) {
var self = this;
this.id = _id;
this.name = _nm;
this.print = function () {
$("#output").append("<p>New Person Id: "+this.id+"; Name: "+ this.name + "<p/>");
};
return this;
};
var nperson = new NewPerson(456, "Marcos");
nperson.print();
This will output:
New Person Id: 456; Name: Marcos
In essence, new is creating a new object represented by this, and you must return this to have a reference to the object created. In fact, if you do not use the new before creating an object, you end up referencing a global instance of this. Try the following:
var nperson = new NewPerson(456, "Marcos");
this.name = "Hello";
nperson.print();
var nperson1 = NewPerson(456, "Marcos");
this.name = "Hello";
nperson1.print();
You will see that the first output will be as expected:
New Person Id: 456; Name: Marcos
But the second will pick up the change made to this.name:
New Person Id: 456; Name: Hello
Here is the JSFiddle.

how do i namespace pseudo-classical javascript

I have some simple OO code I've written that I'm playing with:
//define a constructor function
function person(name, sex) {
this.name = name;
this.sex = sex;
}
//now define some instance methods
person.prototype.returnName = function() {
alert(this.name);
}
person.prototype.returnSex = function() {
return this.sex;
}
person.prototype.talk = function(sentence) {
return this.name + ' says ' + sentence;
}
//another constructor
function worker(name, sex, job, skills) {
this.name = name;
this.sex = sex;
this.job = job;
this.skills = skills;
}
//now for some inheritance - inherit only the reusable methods in the person prototype
//Use a temporary constructor to stop any child overwriting the parent prototype
var f = function() {};
f.prototype = person.prototype;
worker.prototype = new f();
worker.prototype.constructor = worker;
var person = new person('james', 'male');
person.returnName();
var hrTeamMember = new worker('kate', 'female', 'human resources', 'talking');
hrTeamMember.returnName();
alert(hrTeamMember.talk('I like to take a lot'));
Now this is all well and good. But I'm confused. I want to include namespacing as part of my code writing practice. How can I namespace the above code. As it is now I have 2 functions defined in the global namespace.
The only way I can think to do this is to switch to object literal syntax. But then how do I implement the pseudo-classical style above with object literals.
You could for example do following:
var YourObject;
if (!YourObject) {
YourObject = {};
YourObject.Person = function(name, sex) {
// ...
}
YourObject.Person.prototype.returnName = function() {
// ...
}
// ...
}
You don't have to use object literals, at least, not exclusively.
Decide on the single global symbol you'd like to use.
Do all your declaration work in an anonymous function, and explicitly attach "public" methods as desired to your global object:
(function(global) {
// all that stuff
global.elduderino = {};
global.elduderino.person = person;
global.elduderino.worker = worker;
})(this);
I may not be completely understanding the nuances of your issue here, but the point I'm trying to make is that Javascript makes it possible for you to start with your symbols being "hidden" as locals in a function, but they can be selectively "exported" in various ways.

Encapsulation in javascript

I need to create simple reusable javascript object publishing several methods and parameterized constructor. After reading through several "OOP in JavaScript" guides I'm sitting here with an empty head. How on the Earth can I do this?
Here my last non-working code:
SomeClass = function(id) {
this._id = id;
}
(function() {
function intFun() {
return this._id;
}
SomeClass.prototype.extFun = function() {
return incFun();
}
})();
This is my usual approach:
MyClass = function(x, y, z) {
// This is the constructor. When you use it with "new MyClass(),"
// then "this" refers to the new object being constructed. So you can
// assign member variables to it.
this.x = x;
...
};
MyClass.prototype = {
doSomething: function() {
// Here we can use the member variable that
// we created in the constructor.
return this.x;
},
somethingElse: function(a) {
}
};
var myObj = new MyClass(1,2,3);
alert(myObj.doSomething()); // this will return the object's "x" member
alert(myObj.x); // this will do the same, by accessing the member directly
Normally the "this" keyword, when used in one of the object's methods, will refer to the object itself. When you use it in the constructor, it will refer to the new object that's being created. So in the above example, both alert statements will display "1".
An exception to this rule is when you pass one of your member functions somewhere else, and then call it. For example,
myDiv.onclick = myObj.doSomething;
In this case, JavaScript ignores the fact that "doSomething" belongs to "myObj". As a result, the "this" inside doSomething will point to another object, so the method won't work as expected. To get around this, you need to specify the object to which "this" should refer. You can do so with JavaScript's "call" function:
myDiv.onclick = function() {
myObj.doSomething.call(myObj);
}
It's weird, but you'll get used to it eventually. The bottom line is that, when passing around methods, you also need to pass around the object that they should be called on.
I usually don't worry too much about hiding the internals, although I do prefix them with underscores to mark them as not intended to be used outside the "class". Normally what I will do is:
var MyClass = function() {};
MyClass.prototype = {
_someVar : null,
_otherVar : null,
initialize: function( optionHash ) {
_someVar = optionsHash["varValue"];
_otherVar = optionsHash["otherValue"];
},
method: function( arg ) {
return _someVar + arg;
},
};
And use it as so...
var myClass = new MyClass( { varValue: -1, otherValue: 10 } );
var foo = myClass.method(6);
All vars are private:
SomeClass = function (id) {
var THIS = this; // unambiguous reference
THIS._id = id;
var intFun = function () { // private
return THIS._id;
}
this.extFun = function () { // public
return intFun();
}
}
Use THIS within private methods since this won't equal what you might expect.
From http://learn.jquery.com/code-organization/concepts/#the-module-pattern:
// The module pattern
var feature = (function() {
// private variables and functions
var privateThing = "secret";
var publicThing = "not secret";
var changePrivateThing = function() {
privateThing = "super secret";
};
var sayPrivateThing = function() {
console.log( privateThing );
changePrivateThing();
};
// public API
return {
publicThing: publicThing,
sayPrivateThing: sayPrivateThing
};
})();
feature.publicThing; // "not secret"
// logs "secret" and changes the value of privateThing
feature.sayPrivateThing();
So using returning an object that aliases its "methods" could be another way to do it.
I've read from http://www.amazon.com/Programming-Oracle-Press-Poornachandra-Sarang-ebook/dp/B0079GI6CW that it is always good practice to use getters and setters rather that accessing the variable directly from outside the object, so that would eliminate the need of returning variables by reference.
BTW you could just use this.variable to reference/declare a public variable and var variable to declare a private variable.
I know this is a late answer, but I hope it helps anyone who reads it in the future.

Categories

Resources