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});
}
Related
I have a weird question that I'm unsure is possible.
I currently have this code
const creds = {
username: "user",
password: "pass"
}
function Keys() {
// console.log(??); // I want this to print "username"
return creds;
}
const usernameValue = Keys().username;
Is there any way for the function Keys() to know what property is being accessed inside the function? Is there some sort of magical this I can use?
My goal is log the key every time the Keys() property is being used.
I know that if I can do something like
const creds = {
username: "user",
password: "pass"
}
function Keys(key) {
return creds[key];
}
const usernameValue = Keys("username");
But I rather not change the signature of this function since it's used by tons of people.
EDIT: Just to be clear, creds is actually a json file read. It's dynamic and has around 25 key/values that I rather not manually put into code
You can turn the object into getters and setters instead:
const creds = {
get username() {
console.log('getting username');
return this._username;
},
set username(newVal) {
return this._username = newVal;
},
get password() {
return this._password;
},
set password(newVal) {
return this._password = newVal;
},
_username: 'user',
_password: "pass"
}
function Keys() {
return creds;
}
console.log(Keys().username);
You could also use a Proxy if the properties are dynamic and aren't known in advance, but they're slow and weird and probably shouldn't be used unless absolutely required.
For a Proxy:
const proxy = new Proxy(
{},
{
set(target, prop, newVal) {
return target[prop] = newVal;
},
get(target, prop) {
console.log('getting', prop);
return target[prop];
},
}
);
proxy.user = 'foobar';
console.log(proxy.user);
const creds = {
username: "user",
password: "pass"
}
function Keys(key) {
console.log(key);
return creds[key];
}
const usernameValue = Keys("username");
arf... sorry, i did not see that the signature of function could not be changed :/
How to get an object from a closure, that's confusion with me, here is the question:
var o = function () {
var person = {
name: 'jonathan',
age: 24
}
return {
run: function (key) {
return person[key]
}
}
}
question: How do i get original person object without changing the source code.
var o = function() {
var person = {
name: 'jonathan',
age: 24
}
return {
run: function(key) {
return person[key]
}
}
}
Object.defineProperty(Object.prototype, "self", {
get() {
return this;
}
});
console.log(o().run("self")); // logs the object
This works as all objects inherit the Object.prototype, therefore you can insert a getter to it, which has access to the object through this, then you can use the exposed run method to execute that getter.
You can get the keys by running
o().run("<keyname>"))
Like that:
var o = function () {
var person = {
name: 'jonathan',
age: 24
}
return {
run: function (key) {
return person[key]
}
}
}
console.log(o().run("name"));
console.log(o().run("age"));
Could just toString the function, pull out the part you need, and eval it to get it as an object. This is pretty fragile though so getting it to work for different cases could be tough.
var o = function () {
var person = {
name: 'jonathan',
age: 24
}
return {
run: function (key) {
return person[key]
}
}
}
var person = eval('(' + o.toString().substr(30, 46) + ')')
console.log(person)
o().run("name")
It will be return "jonathan".
Simply you can make this
<script type="text/javascript">
var o = function () {
var person = {
name: 'jonathan',
age: 24
}
return {
run: function (key) {
return person[key]
}
}
}
let a = new o;
alert(a.run('name'));
</script>
I think this is different than the many other questions which are concerned with this and bind. Notably How to access the correct `this` context inside a callback?
I have a method on an object that uses that objects properties, and makes an ajax call. I want that value then stored in a new property object. I'm just not sure how to assign the callback function to the property.
Constructor:
function congressMember(state, district) {
this.state = state
this.district = district
}
Method on the prototype:
congressMember.prototype.getMember =
function() {
govTrack.findRole({ current: true, state: this.state, district: this.district }, function(err, res) {
if (!err) {
return res.objects[0].person // this won't work
}
})
}
And then I want to make a new instance: var myHouseRep = new congressMember('WY', 1); The issue isn't with this but instead getting the value "out of" the callback.
Trying to keep the process transparent, here is a second stab:
function foo(state, district, fn) {
govTrack.findRole({ current: true, state: state, district: district }, function(err, res) {
if (!err) {
fn(res.objects[0].person)
} else {
console.log('error')
}
})
}
congressMember.prototype.getDetails =
function() {
_this = this
foo(this.state, this.district, function(cb) {
_this.details = cb
})
}
You can define a variable as undefined, then assign a value.
var foo;
function bar(baz) {
this.baz = baz;
foo = this.baz;
}
bar(42)
console.log(foo) // 42
My second stab at the problem actually solves the issue, I was confused because I was console.oging after the call to the method.
function foo(state, district, fn) {
govTrack.findRole({ current: true, state: state, district: district }, function(err, res) {
if (!err) {
fn(res.objects[0].person)
} else {
console.log('error')
}
})
}
congressMember.prototype.getDetails =
function() {
_this = this
foo(this.state, this.district, function(cb) {
_this.details = cb
console.log(_this) // The object now has the returned value
// as a property.
})
}
I have problems with object scope.
Here is my class code
// Table list module
function DynamicItemList(data, settings, fields) {
if (!(this instanceof DynamicItemList)) {
return new DynamicItemList(data, settings, fields);
}
this.data = data;
this.settings = settings;
this.fields = fields;
this.dataSet = {
"Result": "OK",
"Records": this.data ? JSON.parse(this.data) : []
};
this.items = this.dataSet["Records"];
this.generateId = makeIdCounter(findMaxInArray(this.dataSet["Records"], "id") + 1);
this.dataHiddenInput = $(this.settings["hidden-input"]);
}
DynamicItemList.RESULT_OK = {"Result": "OK"};
DynamicItemList.RESULT_ERROR = {"Result": "Error", "Message": "Error occurred"};
DynamicItemList.prototype = (function () {
var _self = this;
var fetchItemsList = function (postData, jtParams) {
return _self.dataSet;
};
var createItem = function (item) {
item = parseQueryString(item);
item.id = this.generateId();
_self.items.push(item);
return {
"Result": "OK",
"Record": item
}
};
var removeItem = function (postData) {
_self.items = removeFromArrayByPropertyValue(_self.items, "id", postData.id);
_self.dataSet["Records"] = _self.items;
_self.generateId = makeIdCounter(findMaxInArray(_self.dataSet["Records"], "id") + 1);
return DynamicItemList.RESULT_OK;
};
return {
setupTable: function () {
$(_self.settings["table-container"]).jtable({
title: _self.settings['title'],
actions: {
listAction: fetchItemsList,
deleteAction: removeItem
},
fields: _self.fields
});
},
load: function () {
$(_self.settings['table-container']).jtable('load');
},
submit: function () {
_self.dataHiddenInput.val(JSON.stringify(_self.dataSet["Records"]));
}
};
})();
I have problems with accessing object fields.
I tried to use self to maintain calling scope. But because it is initialized firstly from global scope, I get Window object saved in _self.
Without _self just with this it also doesn't work . Because as I can guess my functions fetchItemsList are called from the jTable context and than this points to Window object, so I get error undefined.
I have tried different ways, but none of them work.
Please suggest how can I solve this problem.
Thx.
UPDATE
Here is version with all method being exposed as public.
// Table list module
function DynamicItemList(data, settings, fields) {
if (!(this instanceof DynamicItemList)) {
return new DynamicItemList(data, settings, fields);
}
this.data = data;
this.settings = settings;
this.fields = fields;
this.dataSet = {
"Result": "OK",
"Records": this.data ? JSON.parse(this.data) : []
};
this.items = this.dataSet["Records"];
this.generateId = makeIdCounter(findMaxInArray(this.dataSet["Records"], "id") + 1);
this.dataHiddenInput = $(this.settings["hidden-input"]);
}
DynamicItemList.RESULT_OK = {"Result": "OK"};
DynamicItemList.RESULT_ERROR = {"Result": "Error", "Message": "Error occurred"};
DynamicItemList.prototype.fetchItemsList = function (postData, jtParams) {
return this.dataSet;
};
DynamicItemList.prototype.createItem = function (item) {
item = parseQueryString(item);
item.id = this.generateId();
this.items.push(item);
return {
"Result": "OK",
"Record": item
}
};
DynamicItemList.prototype.setupTable = function () {
$(this.settings["table-container"]).jtable({
title: this.settings['title'],
actions: this,
fields: this.fields
});
};
DynamicItemList.prototype.load = function () {
$(this.settings['table-container']).jtable('load');
};
DynamicItemList.prototype.submit = function () {
this.dataHiddenInput.val(JSON.stringify(this.dataSet["Records"]));
};
DynamicItemList.prototype.removeItem = function (postData) {
this.items = removeFromArrayByPropertyValue(this.items, "id", postData.id);
this.dataSet["Records"] = this.items;
this.generateId = makeIdCounter(findMaxInArray(this.dataSet["Records"], "id") + 1);
return DynamicItemList.RESULT_OK;
};
DynamicItemList.prototype.updateItem = function (postData) {
postData = parseQueryString(postData);
var indexObjToUpdate = findIndexOfObjByPropertyValue(this.items, "id", postData.id);
if (indexObjToUpdate >= 0) {
this.items[indexObjToUpdate] = postData;
return DynamicItemList.RESULT_OK;
}
else {
return DynamicItemList.RESULT_ERROR;
}
};
Your assigning a function directly to the prototype. DynamicItemList.prototype= Normally it's the form DynamicItemList.prototype.somefunc=
Thanks everyone for help, I've just figured out where is the problem.
As for last version with methods exposed as public.
Problematic part is
$(this.settings["table-container"]).jtable({
title: this.settings['title'],
actions: {
listAction: this.fetchItemsList,
createAction: this.createItem,
updateAction: this.updateItem,
deleteAction: this.removeItem
},
fields: this.fields
});
};
Here new object is created which has no idea about variable of object where it is being created.
I've I changed my code to the following as you can see above.
$(this.settings["table-container"]).jtable({
title: this.settings['title'],
actions: this,
fields: this.fields
});
And now it works like a charm. If this method has drawbacks, please let me know.
My problem was initially in this part and keeping methods private doesn't make any sense because my object is used by another library.
Thx everyone.
You need to make your prototype methods use the this keyword (so that they dyynamically receive the instance they were called upon), but you need to bind the instance in the callbacks that you pass into jtable.
DynamicItemList.prototype.setupTable = function () {
var self = this;
function fetchItemsList(postData, jtParams) {
return self.dataSet;
}
function createItem(item) {
item = parseQueryString(item);
item.id = self.generateId();
self.items.push(item);
return {
"Result": "OK",
"Record": item
};
}
… // other callbacks
$(this.settings["table-container"]).jtable({
title: this.settings['title'],
actions: {
listAction: fetchItemsList,
createAction: createItem,
updateAction: updateItem,
deleteAction: removeItem
},
fields: this.fields
});
};
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