i'm working with a piece of code that sets a name property as a function
name = n;
this.name = function(n){
if( n ) name=n;
return name;
}
full constructor is:
function Warrior(n){
name = n;
this.name = function(n){
if( n ) name=n;
return name;
}
}
I don't understand how this is different than this.name = n; and why I can't use that in a toString function like
Person.prototype.toString = function(){
return "Hi! my name's "+this.name;
}
What you created is a getter/setter for name:
A getter is a function which returns a value associated with an object property.
A setter is a function which updates the value associated with an object property.
Getters and setters are useful for creating “derived properties” (i.e. properties which are derived from other properties). For example:
function Person(firstname, lastname) {
this.firstname = firstname;
this.lastname = lastname;
}
Person.prototype.fullname = function (fullname) {
if (arguments.length < 1) return this.firstname + " " + this.lastname;
var newname = fullname.split(" ");
this.firstname = newname[0];
this.lastname = newname[1];
return fullname;
};
var person = new Person("Aadit", "Shah");
alert(person.firstname);
alert(person.lastname);
alert(person.fullname());
person.fullname("Chuck Norris");
alert(person.firstname);
alert(person.lastname);
alert(person.fullname());
In the above code fullname is a derived property. It is derived from firstname and lastname. As you can see, when I update fullname, I am also updating firstname and lastname. Hence getters and setters are useful in this case.
However a lot of people misuse getters and setters. For example:
function Warrior(name) {
this.name = function (newName) {
if (arguments.length >= 1)
name = newName;
return name;
}
}
In this case there's no good reason to create a getter/setter for name because it is not a derived property. It's a misuse of getters/setters and all it does is make the code slower. Instead it should be implemented as:
function Warrior(name) {
this.name = name;
}
Plain and simple.
Now consider:
Person.prototype.toString = function () {
return "Hi! my name's " + this.name;
};
The reason this doesn't work is because this.name is a getter function. Hence you need to call it to get the actual value. Thus, it should be:
Person.prototype.toString = function () {
return "Hi! my name's " + this.name();
};
Finally, newer versions of JavaScript have built in support for getters and setters:
function Person(firstname, lastname) {
this.firstname = firstname;
this.lastname = lastname;
}
Object.defineProperty(Person.prototype, "fullname", {
get: function () {
return this.firstname + " " + this.lastname;
},
set: function (fullname) {
var newname = fullname.split(" ");
this.firstname = newname[0];
this.lastname = newname[1];
}
});
var person = new Person("Aadit", "Shah");
alert(person.firstname);
alert(person.lastname);
alert(person.fullname);
person.fullname = "Chuck Norris";
alert(person.firstname);
alert(person.lastname);
alert(person.fullname);
This makes getters and setters look like normal properties. Such properties are called accessor properties.
Here your constructor function is Warrior and you can add new method toString to an existing prototype Warrior. Like this you can add.
Warrior.prototype.toString = function(){ // code goes here }
this.name is a function .so
Warrior.prototype.toString = function(){
return "Hi! my name's "+this.name();
}
Using new keyword to create new objects from the same prototype
Warrior.prototype.toString = function(){
return "Hi! my name's "+this.name();
}
myname = new Warrior("ur name");
console(myname.toString());
Related
I had a chance to met this question but unable to find an answer.
var arr = [];
Why does typeof Array returns "function" and typeof arr returns an "Object"?
Can anyone explain please.
When you write typeof Array, it means that you are getting type of constructor function. As class is under the hood is constructor function. Let me show an example:
class Person {
constructor(firstName, lastName, address) {
this.firstName= firstName;
this.lastName = lastName;
this.address= address;
}
getFullName () {
return this.firstName + " " + this.lastName ;
}
}
and to create an instance of the class:
let car = new Person ("Jon", "Freeman", "New York");
In the above code, we've created a variable car which references to function construtor defined in the class:
function Person (firstName, lastName, address) {
this.firstName = firstName,
this.lastName = lastName,
this.address = address,
this.getFullName = function () {
return this.firstName+ " " + this.lastName;
}
}
So this is a reason why typeof Array returns function.
Consider the following code:
const defclass = prototype => {
const constructor = prototype.constructor;
constructor.prototype = prototype;
return constructor;
};
const Person = defclass({
constructor: function Person(firstname, lastname) {
this.firstname = firstname;
this.lastname = lastname;
},
get fullname() {
delete this.fullname; // doesn't delete on instances
return this.fullname = this.firstname + " " + this.lastname;
}
});
const john = new Person("John", "Doe");
const jane = new Person("Jane", "Doe");
console.log(john.fullname); // John Doe
console.log(jane.fullname); // Jane Doe
This works because the property assignment on this shadows the non-existent setter.
Now, consider the same code using ES6 classes:
class Person {
constructor(firstname, lastname) {
this.firstname = firstname;
this.lastname = lastname;
}
get fullname() {
delete this.fullname; // doesn't delete on instances
return this.fullname = this.firstname + " " + this.lastname;
}
}
const john = new Person("John", "Doe");
const jane = new Person("Jane", "Doe");
console.log(john.fullname); // throws an error because there is no setter
console.log(jane.fullname);
The reason why it doesn't work is explained in the this answer. It's because we find the property in the prototype chain and it doesn't have a setter. So, why isn't the same error thrown when we use regular prototypes?
Note: You can delete the line with the delete keyword without affecting the behavior of the code.
I do get the same error with the first code:
const defclass = prototype => {
const constructor = prototype.constructor;
constructor.prototype = prototype;
return constructor;
};
const Person = defclass({
constructor: function Person(firstname, lastname) {
this.firstname = firstname;
this.lastname = lastname;
},
get fullname() {
"use strict";
// ^^^^^^^^^^^^
return this.fullname = this.firstname + " " + this.lastname;
}
});
const john = new Person("John", "Doe");
const jane = new Person("Jane", "Doe");
console.log(john.fullname); // John Doe
console.log(jane.fullname); // Jane Doe
It's just that class code is in strict mode by default.
In sloppy mode, the assignment doesn't work but is ignored and the right hand side value is returned from the getter. Accessing .fullname again would run the getter again.
I would like to have a click listener which binds the object values itself and a data attribute of the element which has been clicked.
Example:
var Person = function (firstName) {
this.firstName = firstName;
$(window).on("click", ".myclass", sayHello).bind(this);
};
Person.prototype.sayHello = function() {
console.log("Hello, I'm " + this.firstName + data-url);
};
var person1 = new Person("Alice");
HTML:
<div class="wrapper">
Test
Test
Test
</div>
I don't know how to get the data attribute within the sayHello function. But I need the firstName and the data-url within the sayHello function.
Any suggestions?
I think this is what you're looking for (Not tested but it will help you):
$(a).click(function(){
data_val = $(this).data('url');//will give you data attribute value of clicked anchor element
var person1 = new Person("Alice", data_val);
});
var Person = function (firstName, val) {
this.firstName = firstName;
this.val = val;
};
Person.prototype.sayHello = function() {
console.log("Hello, I'm " + this.firstName + this.val);
};
Update the sayHello method to accept additional params and create a link to this of the Person object:
var Person = function (firstName) {
var self = this;
this.firstName = firstName;
$(window).on("click", ".myclass", function() {
self.sayHello(this);
});
};
Person.prototype.sayHello = function(element) {
console.log("Hello, I'm " + this.firstName + $(element).data('url'));
};
var person1 = new Person("Alice");
use getAttribute javascript function, try
console.log("Hello, I'm " + this.firstName + this.getAttribute('data-url'));
I'm trying to learn how to use Object.defineProperties(). I'm using the following code:
var Person = function(firstName, lastName)
{
this.firstName = firstName;
this.lastName = lastName;
};
Object.defineProperties(Person, {
sayHi : {
get : function() {return "Hello";},
enumerable : true
},
sayBye : {
get : function() {return "Bye";},
enumerable : true
}
});
var john = new Person('John', 'Doe');
console.log(john.sayHi());
But I keep getting:
TypeError: john.sayHi is not a function
console.log(john.sayHi());
Can someone tell me what is wrong with this code?
Thanks
Well, you are not defining sayHi as a function. This is how to define it as a function:
var Person = function(firstName, lastName)
{
this.firstName = firstName;
this.lastName = lastName;
};
// Define the properties on the prototype, not the Person object itself
Object.defineProperties(Person.prototype, {
sayHi : {
get : function() {
return function() {
return "Hello, I am " + this.firstName + " " + this.lastName;
};
},
enumerable : true
},
sayBye : {
get : function() {
return function() {
return "Bye";
};
},
enumerable : true
}
});
var john = new Person('John', 'Doe');
console.log(john.sayHi());
console.log(john.sayBye());
To be precise: in your code, john.sayHi returns the "Hello" string, which is a string primitive, and therefore definitely not a function ;-)
The get function for the property must return a function in order to achieve what you want.
To give you a longer answer, see this following other implementation, taking fully advantage of two things: first the new features from ES5 (Object.create()) and ES6 (Object.defineProperties()) and the prototypal nature of JS (no use of new operator, prototypal inheritance):
var Person = {
init: function(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
};
Object.defineProperties(Person, {
sayHi : {
get : function() {return function() {return "Hello, I am " + this.firstName + " " + this.lastName;}},
enumerable : true
},
sayBye : {
get : function() {return function() {return "Bye";};},
enumerable : true
}
});
var Employee = Object.create(Person); // Employee inherits from Person
Employee.init = function(firstName, lastName, position) {
this.firstName = firstName;
this.lastName = lastName;
this.position = position;
};
Object.defineProperties(Employee, {
introduce : {
get : function() {return function() {
return this.sayHi() + ", " + this.position;
}},
enumerable : true
},
farewell : {
get: function() {return function() {
return this.sayBye() + ", it was a pleasure to meet you";
}},
enumerable: true
}
});
var john = Object.create(Employee); // john inherits from Employee
john.init('John', 'Doe', 'Manager');
console.log(john.sayHi()); // Inherited from Person
console.log(john.introduce()); // Inherited from Employee
console.log(john.sayBye()); // Inherited from Person
console.log(john.farewell()); // Inherited from Employee
JSFIddle demo
Replace
get : function() {return "Hello";} with
get : function() {return function() {return "Hello";};}
I'm still struggling with this concept. I have two different Person objects, very simply:
;Person1 = (function() {
function P (fname, lname) {
P.FirstName = fname;
P.LastName = lname;
return P;
}
P.FirstName = '';
P.LastName = '';
var prName = 'private';
P.showPrivate = function() { alert(prName); };
return P;
})();
;Person2 = (function() {
var prName = 'private';
this.FirstName = '';
this.LastName = '';
this.showPrivate = function() { alert(prName); };
return function(fname, lname) {
this.FirstName = fname;
this.LastName = lname;
}
})();
And let's say I invoke them like this:
var s = new Array();
//Person1
s.push(new Person1("sal", "smith"));
s.push(new Person1("bill", "wonk"));
alert(s[0].FirstName);
alert(s[1].FirstName);
s[1].showPrivate();
//Person2
s.push(new Person2("sal", "smith"));
s.push(new Person2("bill", "wonk"));
alert(s[2].FirstName);
alert(s[3].FirstName);
s[3].showPrivate();
The Person1 set alerts "bill" twice, then alerts "private" once -- so it recognizes the showPrivate function, but the local FirstName variable gets overwritten.
The second Person2 set alerts "sal", then "bill", but it fails when the showPrivate function is called. The new keyword here works as I'd expect, but showPrivate (which I thought was a publicly exposed function within the closure) is apparently not public.
How can I ensure that my closure is a reusable object with publicly exposed methods? I am using the (function() { //object code })(); syntax to ensure my variables are scoped only to the object being created. Thanks!
this is using the technique from "JavaScript: The Good Parts" by Douglas Crockford
http://oreilly.com/catalog/9780596517748
Chapter 5: Inheritance - section 'Functional'
var personConstructor = function (spec) {
var that = {};
that.fullName = function () {
return spec.first_name + " " + spec.last_name;
};
that.setFirstName = function (firstName) {
spec.first_name = firstName;
};
return that;
};
var john = personConstructor({first_name: "John", last_name: "Doe"});
var jane = personConstructor({first_name: "Jane", last_name: "Doe"});
alert(john.fullName()); // John Doe
alert(jane.fullName()); // Jane Doe
john.first_name = "OVERWRITE";
alert(john.fullName()); // John Doe
john.setFirstName("OVERWRITE-VIA-SETTER");
alert(john.fullName()); // OVERWRITE-VIA-SETTER Doe
put this together on jsfiddle, link below if you want to test drive it,
watch out for the 4 alerts when the page loads
http://jsfiddle.net/SecKg/
I modified Person2 to declare the showPrivate function within the constructor -- ensuring the function was public:
;Person2 = (function() {
var prName = 'private';
return function(fname, lname) {
this.FirstName = fname;
this.LastName = lname;
this.showPrivate = function() { alert(prName); };
}
})();
You want to be using the prototype method of creating Person objects.
;(function(){
function Person(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
function showPrivate() {
alert(this.firstName);
}
}
Person.prototype.getName = function() {
return this.firstName + ' ' + this.lastName;
}
})();
showPrivate will be private as it's creating inside the constructor. The getName function is public, and can be invoked from outside the Person object.
I'm not entirely sure what you're trying to accomplish here. Your question is a little confusing. Perhaps try to reword?
//this would allow for instance:
// john.setName('first', 'Johnny');
// jane.setName('last', 'Smith');
var personConstructor = function (spec) {
return {
fullName: function () {
return spec.first_name + " " + spec.last_name;
},
setName: function (nameType, name) {
spec[nameType+'_name'] = name;
}
};
};