So I created the greeting method, but I am having trouble trying to return the string with the this method. All help would be greatly appreciated please.
function exerciseTwo(userObj){
// Exercise Two: You will be given an object called 'userObj'
// userObject will already have a key on it called 'name'
// Add a method to userObj, called 'greeting'.
// Using the keyword 'this', the greeting method should return the following string:
// 'Hi, my name is ' and the users name.
// eg: If userObj has a name: 'Dan', greeting should return: Hi, my name is Dan'
// NOTE: DO NOT create a new object.
// NOTE: DO NOT create a key called name the key is already on the object.
userObj.greeting = 'Hi, my name is ' + this.name;
return userObj;
}
If you run the function without creating any new objects and call the function, this will have the scope of where you call the function from, which will be window, so you can either create a var name with the value 'users name' or set a property on window directly with window.name = 'users name';
this in JavaScript is a bit loopy.
var name = 'Dan';
console.log(window.name);
function exerciseTwo(userObj){
userObj.greeting = 'Hi, my name is ' + this.name;
return userObj;
}
console.log(exerciseTwo({}));
In the browser DOM you have a window object that is the namespace for everything the page does, so calling exerciseTwo when you are not in an object, this will resolve to window. All variables you create without a namespace will be attached to the window object.
var userObj = { // An object with the name property as Dan
name: 'Dan'
}
userObj.greeting = function () { // Adding a function to the object called greeting
return 'Hi, my name is ' + this.name;
}
console.log(userObj.greeting());
In this instance, when the function is called this has the scope of the object userObj.
Related
I am new at Javascript. I am currently looking at the keyword this and methods and how to return strings.
I am struggling to return a string using the keyword this.
I have successfully created code that returns the string, but the problem is that it shows the error that "the object is already defined".
Here is the exercise I am working on and also the code I have tried to create which fails to return the correct results:
function exerciseTwo(userObj) {
// Exercise Two: You will be given an object called 'userObj'
// userObject will already have a key on it called 'name'
// Add a method to userObj, called 'greeting'.
// Using the keyword 'this', the greeting method should return the following string:
// 'Hi, my name is ' and the users name.
// eg: If userObj has a name: 'Dan', greeting should return: Hi, my name is Dan'
// NOTE: DO NOT create a new object.
// NOTE: DO NOT create a key called name the key is already on the object.
let userObj = {
name: "Lisa",
greeting: function() {
return "Hi, my name is " + this.name;
},
};
console.log(userObj.greeting());
}
//In the first line of code it shows a error which says that "userObj" is already defined. So I do not know how to return the string without creating a new object and creating a key called name.
//Here is another option I tried but it also did not work out:
function greeting() {
this.name = "Lisa";
let result = "Hi, my name is " + this.name;
return result;
},
userObj.greeting();
}
//The exercise should add a greeting method to userObject object.
So if userObj has a name: 'Lisa', greeting should return: 'Hi, my name is Lisa'
The problem is that your local variable has the same name as the function parameter. You're supposed to add a method to the existing variable, not create a new variable. The instructions specifically say "DO NOT create a new object", yet that is what you did.
function exerciseTwo(userObj) {
// Exercise Two: You will be given an object called 'userObj'
// userObject will already have a key on it called 'name'
// Add a method to userObj, called 'greeting'.
// Using the keyword 'this', the greeting method should return the following string:
// 'Hi, my name is ' and the users name.
// eg: If userObj has a name: 'Dan', greeting should return: Hi, my name is Dan'
// NOTE: DO NOT create a new object.
// NOTE: DO NOT create a key called name the key is already on the object.
userObj.greeting = function() {
return "Hi, my name is " + this.name;
};
console.log(userObj.greeting());
}
let obj = {
name: "Joe"
};
exerciseTwo(obj);
function exerciseTwo(userObj){ // The argument for this "exerciseTwo" function has been declared as "userObj"
let userObj = { // Here you are trying to declare another variable "userObj"
name: "Lisa",
greeting: function() {
return "Hi, my name is " + this.name;
}
};
console.log(userObj.greeting());
}
To solve your issue,
- Declare the let userObj = { ... } block outside the "exerciseTwo" function and pass it in as a variable
let lisa = {
name: "Lisa"
};
function exerciseTwo(userObj){ // Whatever variable you pass into this function will be synonymous to `userObj` within this function
userObj.greeting = function () {
return "Hi, my name is " + this.name;
}
console.log(userObj.greeting());
}
exerciseTwo(lisa) // lisa will take the position of `userObj` above
As the exercise says you only have to add the greeting function to the users object. Like this:
let userObj = { name: "Lisa" };
function exercise2(userObj) {
userObj.greetings = function () {
return "Hi, my name is " + this.name;
}
}
exercise2(userObj);
console.log(userObj.greetings());
function user(){
user.name ="Amine";
user.lastName ='Meziane';
document.write(user.name);
};
user();
When executing it writes "user" only and not the name "Amine"
user refers to the user function, which comes with an existing, unassignable name property initialized to the function name. The user.name ="Amine"; assignment is ignored.
As user2357112 pointed out, your Code tries to modify the name property of the function user. This property isn't modifieable. And so it doesn't change. The name property of the function user, contains the name of the function user, which is "user" :-). And this name is what your code prints out.
You can write:
function user(){
var user = {};
user.name = "Amine";
user.lastName = "Meziane";
document.write(user.name);
};
user();
Here user(.name) will not refer to the function user, but to the local variable (var) user, which is initialized with an object literal ({}).
Maybe you wanted to write an constructor function. Then you would add properties to this.
function User(name, lastname){
this.name = name;
this.lastName = lastname;
return this; //optional
};
var userAmine = new User("Amine", "Meziane"); // don't forget "new"
document.write(userAmine.name);
Maybe you can read JavaScript Patterns by Stoyan Stefanov for deeper understanding of JavaScript.
Because the name of the function is read-only. Refer to documentation: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/name
window.onload = function() {
function user(){
var json = '{"name":"Amine","lastName":"Meziane"}',
obj = JSON.parse(json);
document.body.innerHTML=obj.name+' '+obj.lastName;
}
user();
};
//or
function user(){
var user = {};
user.name ="Amine";
user.lastName ='Meziane';
document.write(user.name);
};
user();
I came up with the situation below:
function Dog () {
"use strict";
this.age = 1;
var name = "Fido";
this.getName = function () { return name; }
}
And now I'm creating a new instance of the "Dog" class and printing the variable's values.
var d = new Dog;
document.write('<strong>Dog age:</strong> ' +d.age); \\Outputs "1" as expected
document.write('<br/>');
document.write('<strong>Dog name:</strong> ' +d.name); \\Outputs "undefined" as expected, 'cause it's a private variable.
document.write('<br/>');
document.write('<strong>Get Dog name:</strong> ' +d.getName()); \\Outputs "Fido", as expected.
But let's say I want to change the Dog's name, like this:
d.name = "Stinky";
document.write('<br/>');
document.write('<strong>Dog name Again:</strong> ' +d.name);
document.write('<br/>');
document.write('<strong>Get Dog name Again:</strong> ' +d.getName());
Based on this, I got a couple of questions:
Why on earth "d.name" didn't showed me "undefined"? Isn't "name" a private variable? I suppose you can't change private variables values, am I right? I wonder if this process have created a new variable, but this time, a public one, and with the same name. If so, is there a way to prevent the creation of new variables every time I try to assign a new property with the same name? Is there a way to throw a "type error" or something (well, this was what I expected).
And at last: Why "getName" printed the original value, "Fido", even after I assigned a new value to it ?
Any ideas?
Here's a fid to make things easier.
http://fiddle.jshell.net/yZpfg/2/
d.name = "Stinky"; is adding a new (public) property to to the d object, which is an instanceOf Dog.
the getter still references the (private) variable with the value Fido.
If you want to allow a consumer to change a private variable, you need a setter as well:
function Dog () {
"use strict";
this.age = 1;
var name = "Fido";
this.getName = function () { return name; }
this.setName = function (value) { name = value; }
}
var d = new Dog();
d.name; // undefined because there is no name public property
d.getName() // returns the internal private, "Fido"
d.setName('Stinky'); // the internal private is now "Stinky"
You're confusing local variables with instance properties. These are totally separate.
1) You set d.name, an instance property, and it's this you're calling, not the private variable.
2) Fido is the valeu of the private var, which is what your method returns, not the instance property, so the method will always say Fido.
Your original code should probably look like this:
function Dog () {
"use strict";
this.age = 1;
this.name = "Fido";
}
Dog.prototype.getName = function() { return this.name; }
Note I add the method to the prototype rather than explicitly adding it to each instance. This way, the instance inherits it. This is better practice; reusable code should be on the prototype, and it's better performing than adding it each time to every instance.
var name inside your constructor and someObj.name are never ever going to be the same thing. 2 different values that can be set to 2 different things. Instead, you need a setter function that works like your getter:
this.setName = function(newName) { name = newName; };
There is no way to raise an error when setting a property that has the same name as a private variable. They are 2 completely different things, and there is no callback when a property is set that you could even intercept.
var name = 'Mike';
var person = {
name: 'John',
welcome: function(){
var name = 'Mary';
return 'Hi ' + this.name;
}
}
//person.welcome();
// output is
// Hi John
// I was expecting output to be
// Hi Mary
person.welcome.call();
// output is
// Hi Mike
// In this case since no argument is passed to call so this is window and
// I get that window.name is Mike
var name = 'Mike';
var person = {
name: 'John',
welcome: function(){
var name = 'Mary';
return 'Hi ' + this.name;
}
}
this.name refers to the object property "name"
name refers to the variable "name"
You would get the expected result with return 'Hi ' + name;
Why were you expecting Hi Mary in the first case?
var name = 'Mary';
Doesn't overwrite this.name, but rather creates a local variable named name in the function.
In the second case, you are using call, which takes a this argument, and:
Determines the value of this inside
fun. If thisArg is null or undefined,
this will be the global object.
From here.
this always refers to the object you are calling the function from. In most simple cases this would be whatever is in front of the .. For example, in the case of person.welcome() this now refers to person. If you call person.welcome.call() this refers to the window, because you did not specify anything as a parameter to call().
If you are waiting the output to be Hi Mary then you do not need to use this in the welcome function. This should do it:
var name = 'Mike';
var person = {
name: 'John',
welcome: function(){
var name = 'Mary';
return 'Hi ' + name;
}
}
When you do person.welcome() the this keyword references person, so on the welcome function this.name would become person.name which is John.
I'm reading "Pro JavaScript Techniques" by John Resig, and I'm confused with an example. This is the code:
// Create a new user object that accepts an object of properties
function User( properties ) {
// Iterate through the properties of the object, and make sure
// that it's properly scoped (as discussed previously)
for ( var i in properties ) { (function(){
// Create a new getter for the property
this[ "get" + i ] = function() {
return properties[i];
};
// Create a new setter for the property
this[ "set" + i ] = function(val) {
properties[i] = val;
};
})(); }
}
// Create a new user object instance and pass in an object of
// properties to seed it with
var user = new User({
name: "Bob",
age: 44
});
// Just note that the name property does not exist, as it's private
// within the properties object
alert( user.name == null );
// However, we're able to access its value using the new getname()
// method, that was dynamically generated
alert( user.getname() == "Bob" );
// Finally, we can see that it's possible to set and get the age using
// the newly generated functions
user.setage( 22 );
alert( user.getage() == 22 );
Now running that in the Firebug console (on Firefox 3) throws that user.getname() is not a function. I tried doing this:
var other = User
other()
window.getname() // --> This works!
And it worked!
Why?
Doing:
var me = this;
seems to work a bit better, but when executing "getname()" it returns '44' (the second property)...
Also I find it strange that it worked on the window object without modification...
And a third question, what's the difference between PEZ solution and the original? (He doesn't use an anonymous function.)
I think it's best not to use the new keyword at all when working in JavaScript.
This is because if you then instantiate the object without using the new keyword (ex: var user = User()) by mistake, *very bad things will happen...*reason being that in the function (if instantiated without the new keyword), the this will refer to the global object, ie the window...
So therefore, I suggest a better way on how to use class-like objects.
Consider the following example :
var user = function (props) {
var pObject = {};
for (p in props) {
(function (pc) {
pObject['set' + pc] = function (v) {
props[pc] = v;
return pObject;
}
pObject['get' + pc] = function () {
return props[pc];
}
})(p);
}
return pObject;
}
In the above example, I am creating a new object inside of the function, and then attaching getters and setters to this newly created object.
Finally, I am returning this newly created object. Note that the the this keyword is not used anywhere
Then, to 'instantiate' a user, I would do the following:
var john = user({name : 'Andreas', age : 21});
john.getname(); //returns 'Andreas'
john.setage(19).getage(); //returns 19
The best way to avoid falling into pitfalls is by not creating them in the first place...In the above example, I am avoiding the new keyword pitfall (as i said, not using the new keyword when it's supposed to be used will cause bad things to happen) by not using new at all.
Adapting Jason's answer, it works:
We need to make a closure for the values. Here's one way:
function bindAccessors(o, property, value) {
var _value = value;
o["get" + property] = function() {
return _value;
};
o["set" + property] = function(v) {
_value = v;
};
}
Then the User constructor looks like this:
function User( properties ) {
for (var i in properties ) {
bindAccessors(this, i, properties[i]);
}
}
You probably want something like this, which is more readable (closures are easy to learn once you get some practice):
function User( properties ) {
// Helper function to create closures based on passed-in arguments:
var bindGetterSetter = function(obj, p, properties)
{
obj["get" + p] = function() { return properties[p]; }
obj["set" + p] = function(val) { properties[p]=val; return this; }
};
for (var p in properties)
bindGetterSetter(this, p, properties);
}
I also added "return this;", so you can do:
u = new User({a: 1, b:77, c:48});
u.seta(3).setb(20).setc(400)
I started this post with the sole purpose of learning why that things happened, and I finally did. So in case there's someone else interested in the "whys", here they are:
Why does 'this' changes inside the anonymous function?
A new function, even if it is an anonymous, declared inside an object or another function, always changes the scope, in this case returning to the global scope (window).
Solution: all stated in the post, I think the clearer is executing the anonymous function with .call(this).
Why does getname() always return the age?
While the anonymous function gets executed right away, the getters/setters get executed for the first time when they are called. In that moment, the value of i will always be the last, because it has already iterated for all the properties... and it will always return properties[i] which is the last value, in this case the age.
Solution: save the i value in a variable like this
for ( i in properties ) { (function(){
var j = i
// From now on, use properties[j]
As written in the OP, this in the loop is not referring to the User object as it should be. If you capture that variable outside the loop, you can make it work:
function User( properties ) {
// Iterate through the properties of the object, and make sure
// that it's properly scoped (as discussed previously)
var me = this;
for ( i in properties ) { (function(){
// Create a new getter for the property
me[ "get" + i ] = function() {
return properties[i];
};
// Create a new setter for the property
me[ "set" + i ] = function(val) {
properties[i] = val;
};
// etc
I just modified the code a bit like this.. This one should work.. This is same as setting me=this; But a closure is required to set the value of each property properly, else the last value will be assigned to all properties.
// Create a new user object that accepts an object of properties
var User = function( properties ) {
// Iterate through the properties of the object, and make sure
// that it's properly scoped (as discussed previously)
var THIS = this;
for ( var i in properties ) { (function(i){
// Create a new getter for the property
THIS[ "get" + i ] = function() {
return properties[i];
};
// Create a new setter for the property
THIS[ "set" + i ] = function(val) {
properties[i] = val;
};
})(i); }
}
// Create a new user object instance and pass in an object of
// properties to seed it with
var user = new User({
name: "Bob",
age: 44
});
// Just note that the name property does not exist, as it's private
// within the properties object
alert( user.name == null );
// However, we're able to access its value using the new getname()
// method, that was dynamically generated
alert( user.getname() == "Bob" );
// Finally, we can see that it's possible to set and get the age using
// the newly generated functions
user.setage( 22 );
alert( user.getage() == 22 );
Maybe the variable i is "closured" with the last value in the iteration ("age")? Then all getters and setters will access properties["age"].
I found something that seems to be the answer; it’s all about context. Using the anonymous function inside the for loop, changes the context, making 'this' refer to the window object. Strange isn't it?
So:
function User(properties) {
for (var i in properties) {
// Here this == User Object
(function(){
// Inside this anonymous function, this == window object
this["get" + i] = function() {
return properties[i];
};
this["set" + i] = function(val) {
properties[i] = val;
};
})();
}
}
I don't know why that function changes the context of execution, and I'm not sure it should do that. Anyway, you can test it running the code there and trying window.getname(). It magically works! :S
The solution, as stated before, is changing the context. It can be done like J Cooper said, passing the variable 'me' and making the function a closure or you can do this:
(function(){
// Inside this anonymous function this == User
// because we called it with 'call'
this[ "get" + i ] = function() {
return properties[i];
};
this["set" + i] = function(val) {
properties[i] = val;
};
}).call(this);
Anyway, I'm still getting 44 when running 'getname'... What could it be?