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";};}
Related
How to access a the prototype object from within a constructor function ?
In the following Javascript code I have a Person constructor with two getter/setter functions, and a hydrate function defined inside it's prototype:
+++++++++++++++++++++++++++++
+ Person +
+++++++++++++++++++++++++++++
+ setFirstname( firstname ) +
+ getFirstname() +
+ setLastname( lastname ) +
+ getLastname() +
+++++++++++++++++++++++++++++
+++++++++++++++++++++++++++++
+ Person.prototype +
+++++++++++++++++++++++++++++
+ hydrate( person ) +
+++++++++++++++++++++++++++++
I'd like to call the hydrate() function from within the Person constructor, even though it makes no sense to me, it's like calling a method before the object is fully created, right? correct me if I'm wrong.
Javascript code :
function Person( person ) {
var _firstname = '',
_lastname ='';
if ( person ) this.hydrate( person );
function setFirstname( firstname ) {
_firstname = firstname;
}
function getFirstname() {
return _firstname;
}
function setLastname( lastname ) {
_lastname = lastname;
}
function getLastname() {
return _lastname;
}
this.setFirstname = setFirstname;
this.getFirstname = getFirstname;
this.setLastname = setLastname;
this.getLastname = getLastname;
}
Person.prototype = {
hydrate: function( person ) {
this.setFirstname( person.firstname );
this.setLastname( person.lastname );
}
};
var john = new Person( {
firstname: "John",
lastname: "Doe"
} );
Result :
Error: Uncaught TypeError: this.setFirstname is not a function
Question: How am I supposed to do that ? is that even possible ?
In order to do that I should've changed the position of where the hydrate() function is called, and put it all the way at bottom [right after making setFirstname() and setLastname() publicly available ], as follows :
function Person( person ) {
var _firstname = '',
_lastname ='';
function setFirstname( firstname ) {
_firstname = firstname;
}
function getFirstname() {
return _firstname;
}
function setLastname( lastname ) {
_lastname = lastname;
}
function getLastname() {
return _lastname;
}
this.setFirstname = setFirstname;
this.getFirstname = getFirstname;
this.setLastname = setLastname;
this.getLastname = getLastname;
if ( person ) this.hydrate( person );
}
Person.prototype = {
hydrate: function( person ) {
this.setFirstname( person.firstname );
this.setLastname( person.lastname );
}
};
var john = new Person( {
firstname: "John",
lastname: "Doe"
} );
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'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());
I am wondering why when I make adjustments to the following code:
var product = {
firstname: "john",
lastname: "kcarl",
fullname : function() { return this.firstname + " " + this.lastname }
};
function write()
{
document.write(product.fullname());
};
write();
**** the following fullname becomes undefined
function write(method)
{
document.write(product.method());
};
write(fullname);
**** even when I try this it comes undefined
function write(method)
{
document.write(method());
};
write(product.fullname);
It's because of scope issue with javascript. Here is a better way to write this code:
var Product = function() {
var self = this;
this.firstname = "john";
this.lastname = "kcarl";
this.fullname = function() {
return self.firstname + " " + self.lastname;
}
};
var product = new Product();
var fullnameMethod = product.fullname;
document.write(fullnameMethod());
In the above example, I save the scope into a variable called self and I can feel free to call the fullname() method anytime
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;
}
};
};