I`m trying to figure out what is getters and setters in JavaScript.
Here is my object
function User(fullName) {
this.fullName = fullName;
Object.defineProperties(this,{
firstName :{
get: function(){
return this.fullName.split(" ")[0];
},
set :function(value){
this.firstName = value;
}
},
lastName:{
get: function(){
this.lastName = this.fullName.split(" ")[1];
},
set: function(value){
this.lastName = value;
}
},
fullName :{
set: function(value){
this.fullName = value;
}
}
});
}
Then creates a new user:
var user = new User("New User");
But when I`m trying to get the firstName property like
alert( user.firstName )
it throw an error "Cannot read property 'split' of undefined".
What may cause the problem? It looks like 'this' is not visible inside get function, but as I understand it should. Thanks!
You don't need a setter for fullName as direct assignment will work.
function User(fullName) {
this.fullName = fullName || '';
Object.defineProperties(this, {
firstName: {
get: function() {
return this.fullName.split(" ")[0];
},
set: function(value) {
this.firstName = value; // NOTE: This will throw an error
}
},
lastName: {
get: function() {
return this.fullName.split(" ")[1];
},
set: function(value) {
this.lastName = value; // NOTE: This will throw an error
}
}
});
}
var joe = new User('John Doe');
var jane = new User('Jane Dane');
jane.fullName = 'Jane Doe';
document.write(
'<pre>' + joe.firstName + '</pre>' +
'<pre>' + jane.lastName + '</pre>'
);
However, as noted in the code comments above you can't set a property on this to the same name as a defined property with a setter. For example:
// defining `firstName`
firstName: {
...
set: function(value) {
this.firstName = value; // NOTE: This will throw an error
}
This operation will cause a recursion stack error as it will continuously try to update firstName since this.firstName is a setter.
To avoid this you could use local scoped variables inside the constructor function and do something like:
function User(fullName) {
var firstName;
var lastName;
Object.defineProperties(this, {
firstName: {
get: function() {
return firstName;
},
set: function(value) {
return (firstName = value);
}
},
lastName: {
get: function() {
return lastName;
},
set: function(value) {
return (lastName = value);
}
},
fullName: {
get: function() {
return firstName + ' ' + lastName;
},
set: function(value) {
var names = value && value.split(' ');
firstName = names[0];
lastName = names[1];
}
}
});
if (fullName) {
this.fullName = fullName;
}
}
var joe = new User('John Doe');
var jane = new User('Jane Dane');
jane.lastName = 'Doe';
document.write(
'<pre>' + joe.firstName + '</pre>' +
'<pre>' + jane.lastName + '</pre>'
);
Some issues/changes needed to your code:
this.fullName.split(" ")[0]; => will try to invoke the getFullName since fullName is defined as a property. Since you have not defined getFullName this results in an error
Say you go ahead and define a getter for fullName:
get: function() {
return this.fullName;
}
This will throw a stackoverflow since this.fullName ends up recursively calling getFullName()
The right way to use it would be (of course update the setters to do something useful):
function User(fullName) {
this.fullName = fullName;
Object.defineProperties(this, {
firstName: {
get: function () {
return this.fullName.split(" ")[0];
},
set: function (value) {
this.firstName = value;
}
},
lastName: {
get: function () {
return this.fullName.split(" ")[1];
},
set: function (value) {
this.lastName = value;
}
}
});
}
var user = new User("New User");
alert( user.firstName );
fullName does not have a getter so it return undefined
get
A function which serves as a getter for the property, or undefined if there is no getter. The function return will be used as the value of property.
Defaults to undefined.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty#Description
Related
I have little issue accessing object property in typscript. this works fine in js but doesn't in ts
let getUserName = {
firstname : "timi",
lastname: "oluwayomi",
middlename: "ola",
full : () => { return this.firstname + this.lastname + this.middlename } ,
first: () => { return this.firstname }
}
console.log(getUserName.first())
in javascript output: timi
but ts throws error. is there a different way to access it in ts ?
just refer to the same object in the object, this is a hack IMO
let getUserName = {
firstname : "timi",
lastname: "oluwayomi",
middlename: "ola",
full : () => { return getUserName.firstname + getUserName.lastname + getUserName.middlename } ,
first: () => { return getUserName.firstname }
}
console.log(getUserName.first())
this inside arrow function refers to the global object, so its giving error, you can try with traditional functions
let getUserName = {
firstname : "timi",
lastname: "oluwayomi",
middlename: "ola",
full : function() { return this.firstname + this.lastname + this.middlename } ,
first: function() { return this.firstname }
}
console.log(getUserName.first())
i was working on an component based project "Angular"
so i had to initialize first
getUserName : any;
getUserName = {
firstname : "timi",
lastname: "oluwayomi",
middlename: "ola",
full : () => { return getUserName.firstname + getUserName.lastname + getUserName.middlename } ,
first: () => { return getUserName.firstname }
}
console.log(getUserName.first())
also thanks to Dean Van Greunen
You are accessing this within an arrow function. Inside an arrow function, this refers to the global object, not the execution context. See You Don't Know JS Yet - Chapter 3 - this Keyword for more info on this.
So, to start with, use this:
let getUserName = {
firstname : "timi",
lastname: "oluwayomi",
middlename: "ola",
full() { return this.firstname + this.lastname + this.middlename },
first() { return this.firstname },
}
As this is of type any in this case, as the TypeScript compiler cannot infer the type of your object for you, you will need to type the object yourself (usually a good idea anyway):
type GetUserName = {
firstname: string;
lastname: string;
middlename: string;
full(): string;
first(): string;
}
let getUserName: GetUserName = {
firstname: "timi",
lastname: "oluwayomi",
middlename: "ola",
full(this: GetUserName) {
return this.firstname + this.lastname + this.middlename;
},
first(this: GetUserName) {
return this.firstname;
},
};
i have defined an object containing an array of objects and functions and called the methods as below:
var Person = {
people: [{
name: "Max",
age: 41,
},
{
name: "John",
age: 25,
},
{
name: "Doe",
age: 67,
},
],
greeting: function() {
return "Hello, my name is" + " " + this.name;
}
};
function searchByName(namePerson) {
var result = Person.people.find(Obj => Obj.name === namePerson);
return result;
}
var max = searchByName('Max');
max.greeting();
Is something wrong with my function definition? On running it says "greeting" is not a function.
You could change your Person into an actual class that you could instantiate with new Person()
//make the person class
function Person ( person ) {
this.name = person.name;
this.age = person.age;
}
//add the greeting method to the person class
Person.prototype.greeting = function () {
return "Hello, my name is" + " " + this.name;
};
//build your people
var people = [
new Person( { name: "Max", age: 41 } ),
new Person( { name: "John", age: 25 } ),
new Person( { name: "Doe", age: 67 } )
];
function searchByName(namePerson) {
var result = people.find(person => person.name === namePerson);
return result;
}
var max = searchByName('Max');
console.log( max.greeting() );
Your code doesn't make much sense, your greeting function is on the outer Person object but you are using it as if it were a property of each person inside the array.
You need a constructor for persons so you can instantiate three Persons with a greeting method
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.greeting = function() {
return "Hello, my name is " + this.name + " and I am " + this.age + " years old";
}
const people = [
new Person("Max", 41),
new Person("JOhn", 25),
new Person("Doe", 67),
];
function searchByName(namePerson) {
var result = people.find(obj => obj.name === namePerson);
return result;
}
var max = searchByName('Max');
console.log(max.greeting());
You're returning an object, which does not have a greeting function.
A potential different solution with a little bit of a different structure is this.
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.greeting = function() {
return "Hello, my name is " + this.name;
}
function People(personArr) {
this.people = personArr;
}
People.prototype.searchByName = function(name) {
return this.people.find( person => person.name === name);
}
var people = new People([new Person("Max", 41), new Person("John", 25), new Person("Doe", 67)]);
var max = people.searchByName("Max");
console.log(max.greeting());
var student={};
Object.defineProperty(student,"name",{
get:function(){
this.name;
},
set:function(){
this.name=name;
}
});
I am assuming that if i assign student.name="xyz",a name property will get add to student object.I in confusion when and how to use accessor properties.
Object.defineProperty is used to work on properties like configurable, enumerable, writable, get, set, etc. With get and set in your code, I dont see any point of doing what. You might want to use them when you for ex. mutate data by writing it in or getting it back.
More obvious benefits of changing those properties you can see with for ex. writable:
var obj = { name: 'foo' }
obj.name // 'foo'
obj.name = 'bar'
obj.name // 'bar'
Object.defineProperty(obj, 'name', {writable: false}
obj.name = 'foo'
obj.name // 'bar'
Or enumerable:
var o = {name: 'foo', lastName: 'bar'}
Object.defineProperty(o, 'name', {enumerable: false})
for(var prop in o) { console.log(prop)} // lastName
A good usecase would be modifying the object when one property is set, e.g:
const person = {
firstName: "Jonas",
lastName: "W.",
get fullName(){ return this.firstName + " " + this.lastName;},
set fullName(name){
const [first, last] = name.split(" ");
this.firstName = first;
this.lastName = last;
}
};
So one can do:
person.fullName = "Max Mustermann";
console.log(person.firstName, person.lastName);
A use case for property accessors is when you need to perform some data validation on the property being set.
const Vehicle = function(){};
Object.defineProperty(Vehicle.prototype, "_wheels", {
enumerable: false,
writable: true,
});
const Bike = function() {}
Bike.prototype = Object.create(Vehicle.prototype);
Object.defineProperty(Bike.prototype, "wheels", {
get: function() {
return this._wheels;
},
set: function(numWeels) {
if (numWeels > 2) {
throw new Error('Sorry, too many wheels!');
return;
}
this._wheels = numWeels;
}
});
myBike = new Bike();
myBike.wheels = 3;
console.log(myBike.wheels);
So I was wondering whether this is the right way to add functions to an object created through object literals.
var person = {
firstname: "default",
lastname: "default",
greet: function () {
return "hi " + this.firstname;
}
}
var me = Object.create(person);
me.myFunction = function() {
return console.log("meow");
};
console.log(me.myFunction());
However it returns an undefined after meow, is there any reason why it would do so?
When you write
return console.log("meow");
you don't return "meow", but the return value of console.log, which is undefined. Modify the code like this:
me.myFunction = function() {
return "meow";
};
console.log(me.myFunction());
console.log() doesn't return any value, so the "fallback" value of the function is undefined.
Since you're returning the return value of console.log and log that again, you get undefined.
All of this has nothing to do with modifying an object or a prototype.
You should return meow within myFunction:
var person = {
firstname: "default",
lastname: "default",
greet: function () {
return "hi " + this.firstname;
}
}
var me = Object.create(person);
me.myFunction = function() {
return "meow";
};
document.write(me.myFunction());
var person = {
firstname: "default",
lastname: "default",
greet: function () {
return "hi " + this.firstname;
}
}
var me = Object.create(person);
me.myFunction = function() {
return console.log("meow");
};
console.log(me.myFunction());
why you return console.log() it's return nothing
I'm playing around with a javascript object that defines some getters and setters using the Object.defineProperty method.
function User() {
var _username;
var _id;
Object.defineProperty(User, 'id', {
get: function() {
return _username;
}
});
Object.defineProperty(User, 'username', {
get: function() {
return _username;
},
set: function(username) {
this._username = username;
}
});
}
For one of the properties (id), I only want a getter. Originally I had a typo and it was returning the value of _username, but I quickly realized that the above did not work. Just for curiosity sake though, I'm trying to understand why it didn't work as expected. If I did the following:
var u = new User();
u.username = 'bob';
alert(u.username);
alert(u.id);
the last statement would alert undefined instead of bob. Why is that? And is there a way to get it to return another property?
You must define the properties on this instead of the constructor function
function User(params) {
var _username;
Object.defineProperty(this, 'id', {
get: function() {
return _username;
}
});
Object.defineProperty(this, 'username', {
get: function() {
return _username;
},
set: function(username) {
_username = username;
}
});
if (params && params.username) {
this.username = params.username;
}
}
User.prototype.stringify = function () {
return JSON.stringify({ username: this.username});
}