This question already has answers here:
Self-references in object literals / initializers
(30 answers)
Closed 10 months ago.
I am trying to call an object method from an object (the same object) property definition to no avail.
var objectName = {
method : function() {
return "boop";
},
property : this.method()
};
In this example I want to assign the return value of objectName.method ("boop") to objectName.property.
I have tried objectName.method(), method(), window.objectName.method(), along with the bracket notation variants of all those as well, ex. this["method"], with no luck.
At initialization this does not refer to the object holding the property method (which is not yet initialized) but to the curent context - and since this has no method property you will get a TypeError.
If it is a custom getter you want, then you might look into using getters and setters in javascript - they are not supported by ECMAscript prior to ES5, but many engines support them nonetheless.
I can see no reason why you would want to do this?
Why not just use a getter, if you don't want to use the method name.
var objectName = {
method : function() {
return "boop";
},
property : function () {
return this.method();
}
};
/* overwrites the `property` function with a the set value
* the first time it's called. From then on it's just a
* property
*/
var objectName = {
method: function(){ return 'boo'; },
property: function(){
var returnValue = this.method();
this.property = returnValue;
return returnValue;
}
};
/* or */
var objectName = {
property: ( function(){ return 'boo'; }() );
};
/* this evaluates the anonymous function within
* the parenthetical BEFORE the definition of
* objectName leaving only the returned value
* to be set to property
*/
/* or */
var objectName = {
method: function(){
this.property = 'boop';
return this.property;
}
}
/* calling the method to create/set the property
* for objectName to the evaluated value
*/
/* or */
var objectName = {
data: {
property: 'boop'
},
property: function( value ){
if ( value ) this.data.property = value;
return this.data.property;
}
};
/* the last one, I believe, is a neat way of handling
* both set and get stealing the concept from my use
* with JQuery.
* set = objectName.property( 'boo' );
* get = objectName.property();
*/
One more way of doing this:
var objectName = {
method : function() {
return "boop";
}
};
$.extend(objectName, {
property : objectName.method()
})
objectName already initialized at the time of calling 'method'.
It worked for me as follows:
var objectName = {
method : function() {
return "boop";
},
property : objectName.method()
};
Wouldn't you just go:
var objectName = {
method : function() { return "boop"; },
property : function() { return this.method(); }
};
Related
Please note this question is not answered by
Self-references in object literals / initializers
as that question addresses defining properties in terms of other properites, not methods.
Also, How does the "this" keyword in Javascript act within an object literal? is too high-level a decription of the subject for me to be able to solve my use case.
In Python, I can do this:
class Test:
def __init__(self):
self.id = self.get_id()
def get_id(self):
return 10
t = Test()
print(t.id)
Meaning an object property can be defined in terms of a method of the same object.
In JavaScript, it doesn't work:
var Test = {
id : this.getId(),
getId : function() {
return 10;
}
};
Gives script.js:47 Uncaught TypeError: this.getId is not a function
I've tried defining the id after the method definition but that didn't work.
How do I do this in JavaScript please?
The other answer (Evgeny Yudin) is simpler - but this is an alternative using classes that may add something to your understanding.
class Test {
constructor() {
this.id = this.getId();
}
getId() {
return 10;
}
}
console.log((new Test()).id); //outputs 10
var Test = {
get id() {
return 10
}
}
console.log(Test.id)
https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Functions/get
or
var Test = {
get id () {
return this.getId()
},
getId: function () {
return 10
}
}
console.log(Test.id)
You can only use this inside a method. There's no way to reference the current object in an object literal.
But you can refer to it after the object is created.
var Test = {
getId : function() {
return 10;
}
};
Test.id = Test.getId()
I need to add new objects to the list (Filters.list)
* Filters.prop - is a default prop
* list items also have prop - list[name].prop (equal default prop)
Chage default Filters.prop -> [not] change item list[name].prop
Where is the mistake?
function Filters() {
this.prop = 'some';
this.list = { };
this.add = function (name) {
this.list[name] = {
prop: this.prop,
};
}
}
let attempt = new Filters();
attempt.add('first');
attempt.prop = 'other';
document.writeln(attempt.prop);
document.writeln(attempt.list['first'].prop);
After run snippet output: other some
But I need: other other
I thought the property would be saved by reference. But this is not so, it does not change. When I change Filters.prop I expected that too it will change list[name].prop
The problem with this is that, as you noticed yourself, the value is passed the way you're doing it instead of the reference.
Thanks to JavaScript get, you can return the value of prop of the surrounding object within a function which behaves like an attribute.
function Filters() {
this.prop = 'some';
this.list = { };
this.add = function(name) {
let self = this;
this.list[name] = {
get prop() {
return self.prop;
},
};
}
}
let attempt = new Filters();
attempt.add('first');
attempt.prop = 'other';
document.writeln(attempt.prop)
document.writeln(attempt.list['first'].prop)
Side note: I use the variable self here because using this.prop within get prop() would reference the wrong object and hence cause a recursion.
I have all non - enumerable properties in object, I want to clone that object.
My problem non-enumerable properties are not getting cloned.
Take below example
Object.defineProperty(this, 'prop', {
get: function () {
return prop;
},
set: function (value) {
prop= value;
}
});
Object.defineProperty(this, 'newprop', {
get: function () {
return newprop;
},
set: function (value) {
newprop= value;
}
});
For example I have above two properties in my object doing clone using following methods, my properties are not getting cloned I believe it is because they are non - enumerable.
var newObject = $.extend({},oldObject);
var newObject= Object.assign({},oldobject);
How do I copy non-enumerable properties in javascript.
If one or more properties aren't enumerable, how do you want to auto-magically enumerate them?
Since you know their names, you should do something like this:
var sourceObj = this;
var targetObj = {};
["prop", "otherProperty"].forEach(function(property) {
targetObj[property] = sourceObj[property];
});
Or you can build the whole property name array whenever you define a non-enumerable property:
var propertyNames = [];
Object.defineProperty(this, 'newprop', {
get: function () {
return newprop;
},
set: function (value) {
newprop= value;
}
});
propertyNames.push("newprop"); // <---
Object.defineProperty(this, 'newprop2', {
get: function () {
return newprop;
},
set: function (value) {
newprop= value;
}
});
propertyNames.push("newprop2"); // <---
propertyNames.forEach(function(property) {
targetObj[property] = sourceObj[property];
});
Alternate approach: Object.getOwnPropertyNames
The Object.getOwnPropertyNames() method returns an array of all
properties (enumerable or not) found directly upon a given object.
Maybe this is the best approach. Object.getOwnPropertyNames gets the name of own object's properties either if they're enumerable or not. That is, you can avoid building the whole propertyNames array, and this approach should be fine with you because you said that all properties aren't enumerable:
var sourceObj = this;
var targetObj = {};
Object.getOwnPropertyNames(sourceObj).forEach(function(property) {
targetObj[property] = sourceObj[property];
});
You can use Object.defineProperties and Object.getOwnPropertyDescriptors() function
The Object.defineProperties() method defines new or modifies existing properties directly on an object, returning the object.
The Object.getOwnPropertyDescriptors() method returns all own property descriptors of a given object.
So, you can add the properties of the original object into an empty object, as follow
var source_obj = this
var cloned_obj = Object.defineProperties({}, Object.getOwnPropertyDescriptors(source_obj));
I have a similar question in How to implement Ruby's extend module in JavaScript.
And because I don't wanna use iterate attach each method to new object, so the final best way I can do is like that, welcome give me any idea and suggestion, thanks.
class Test {
constructor(name) {
this.name = name;
}
test1(){
return this.name + "test1"
}
}
const Other = {
test2: function(){
return this.name + "test2"
},
test3: function(){
return this.name + "test3"
},
test4: function(){
return this.name + "test4"
}
}
var person = new Test("HKE")
Object.assign(person,Other) //this like object.extend(Module) in ruby
console.log(person.test1()) // HKEtest1
console.log(person.test2()) // HKEtest2
console.log(person.test3()) // HKEtest3
console.log(person.test4()) // HKEtest4
var house = new Test("Tsao")
console.log(house.test1())
console.log(house.test2()) // TypeError: house.test2 is not a function
console.log(house.test3()) // TypeError: house.test3 is not a function
console.log(house.test4()) // TypeError: house.test4 is not a function
I am trying to create a UserDon object, and trying to generate the get and set methods programmatically ( based on Pro Javascript book by John Resig page 37 ), and am testing this on Firefox 3.5
The problem is: in function UserDon, "this" refers to the window object instead of the UserDon object.
So after calling var userdon = new UserDon(...) I got setname and getname methods created on the window object (also setage and getage).
How can I fix this?
function UserDon( properties ) {
for( var i in properties ) {
(function(){
this[ "get" + i ] = function() {
return properties[i];
};
this[ "set" + i ] = function(val) {
properties[i] = val;
};
})();
}
}
var userdon = new UserDon( {
name: "Bob",
age: 44
});
The this value you are using belongs to the auto-invoking function expression you have inside the loop, and when you invoke a function in this way, this will always refer to the global object.
Edit: I missed the fact that the function expression is trying to make variable capturing to handle the getter/setter creation inside the loop, but the looping variable i, needs to be passed as an argument in order to do it and since the function expression is there, context (the outer this) should be preserved:
function UserDon( properties ) {
var instance = this; // <-- store reference to instance
for( var i in properties ) {
(function (i) { // <-- capture looping variable
instance[ "get" + i ] = function() {
return properties[i];
};
instance[ "set" + i ] = function(val) {
properties[i] = val;
};
})(i); // <-- pass the variable
}
}
var userdon = new UserDon( {
name: "Bob",
age: 44
});
userdon.getname(); // "Bob"
userdon.getage(); // 44
You can also use the call method to invoke the function expression, preserving the context (the value of this) and introducing the looping variable to the new scope in a single step:
function UserDon( properties ) {
for( var i in properties ) {
(function (i) { // <-- looping variable introduced
this[ "get" + i ] = function() {
return properties[i];
};
this[ "set" + i ] = function(val) {
properties[i] = val;
};
}).call(this, i); // <-- preserve context and capture variable
}
}
I would also recommend to use an if (properties.hasOwnProperty(i)) { ... } inside the for...in loop to avoid iterating over user extended properties inherited from Object.prototype.
You can also use the lesser-known
__defineGetter__("varName", function(){});
and __defineSetter__("varName", function(val){});
Although they are nonstandard [like x-html-replace content-type] they are supported by a majority of the non-ie browsers out there [chrome, firefox]
Syntax would be:
benjamin = new object();
benjamin.__defineGetter__("age", function(){
return 21;
});
Or you can approach this with prototyping
benjamin = {
get age()
{
return 21;
}
}
It could be a better idea to get a generic function with 2 parameters : the name of the property and the value (or only the name for the getter).
And this function would check the presence of a speial function for this property and if there isn't any, it would juste change the property's value (or return it's value for getter).
Here is how I would code it:-
function UserDon( properties ) {
var self = this;
for( var i in properties ) {
(function(prop){
self[ "get" + prop ] = function() {
return properties[prop];
};
self[ "set" + prop ] = function(val) {
properties[prop] = val;
};
})(i);
}
}
Please look at my required JavaScript.
var someVariable = new SomeDataType();
// I can directly access value of its property.
someVariable.someProperty = "test";
alert(someVariable.someProperty); // <- this command must should "test"
// However, I have some methods in above property
// I want to validate all rule in this property.
someVariable.someProperty.isValid(); // <- this method will return true/false
Is it possible for doing this in current version of JavaScript?
UPDATE
Please look as my answer!
Yes, you can assign Javascript functions as properties like this:
someVariable.someProperty = function (arg1, arg2) {
// function code goes here
};
This is the method using function literals.
Another method is to use function instances like this:
someVariable.someProperty = new Function (arg1, arg2, code);
Note that in the second method, the code goes in as the last parameter and the Function keyword has a capitalized 'F' as against method 1 where the 'f' is small.
Further, creating a function instance inside a loop etc. will create an entire new instance to assign which is inefficient in memory. This problem does not arise while using the function literal method.
You can't (and probably shouldn't) treat objects like that in JavaScript. As someone else mentioned, you can override the toString() method to get half of the functionality (the read portion), but you cannot use the assignment operator on an object like that without overwriting the object.
You should choose a different approach, like using nested objects (as CMS suggested).
Its possible, but with the below change in your code
function SomeDataType(){
var localProperty="";
this.someProperty = function(txt){
if (arguments.length==0)
return localProperty;
else
localProperty=txt;
}
this.someProperty.isValid = function(){
return (localProperty!="") ? true : false;
};
}
instead of defining someProperty as a property, define this as function which sets value to the local property if any value is passed or it ll return that property value if no argument is given.
var someVariable = new SomeDataType();
someVariable.someProperty("test");
alert(someVariable.someProperty());
var isValid = someVariable.someProperty.isValid();
this is how you need to access the SomeDataType object.
someVariable.someProperty = [ test, anotherFunc, yetAnotherFunc];
someVariable.somePropertyAllValid= function() {
for(var prop in someVariable.someProperty) {
if(!prop()) return false;
}
return true;
};
someVariable.somePropertyAllValid();
I just found the answer. It's very simple & clean.
function createProperty(value, defaultValue, ruleCollection)
{
this.value = value;
this.defaultValue = defaultValue;
this.ruleCollection = ruleCollection;
}
createProperty.prototype.toString = function()
{
return this.value;
};
var someVariable =
{
someProperty: new createProperty
(
'currentValue',
'defaultValue',
null
)
};
For testing, you can use something like my following code.
var test = ">>" + someVariable.someProperty + "<<";
// this alert must shows ">> currentValue <<"
alert(test);
someVariable =
{
someProperty: new createProperty
(
7,
5,
null
)
};
test = someVariable.someProperty + 3;
// This alert must shows "10"
alert(test);
I just test it on FF 3.5 & IE 8. It works fine!
Update
Oops! I forget it. Because this technique returns object reference for this property. So, it's impossible to directly set property data. It isn't my final answer.
Perhaps this would be of help:
var SomeVar = {
someProperty : {
value : 7,
add : function (val) {
this.value += parseInt(val, 10);
return this;
},
toString : function () {
return this.value;
}
}
}
alert(SomeVar.someProperty.add(3));