Object.create is a great addition to JavaScript, because it adheres more to the prototypical nature of JS. However, I can't help but find the syntax of the 2nd parameter to the function to be too verbose, and a step back.
For example, if I want to create an object, and specify a new property in the derived object, I need to include that property value within a property object, regardless if I'm interested in the extra features or not.
So, something as simple as this:
o = Object.create({}, { p: 42 })
Now becomes:
o = Object.create({}, { p: { value: 42 } })
Obviously this is a simple example, but to me the verbosity is unnecessary, and should be optional.
Does anyone understand the decision to require a properties object? What is your opinion of the requirement of the new syntax?
Note: I understand there are easy solutions to overcome this requirement.
The syntax is done this way so that you can add parameters that control each property:
So, when you do this:
o = Object.create({}, { p: { value: 42 } })
you are saying that you want a property named p with a value of 42. The key here is that there are other parameters you can set for each property and if there wasn't this extra level of object hierarchy, you wouldn't have any way to pass those extra parameters.
So, for example, you could also do this:
o2 = Object.create({}, { p: { value: 42, writable: true, enumerable: true, configurable: true } });
Here's you are not just specifying the value of 42, but also some options for that property. If there wasn't the extra level of object hierarchy here, then you wouldn't have a place to put those extra options.
Yes, it does seem inconvenient when you only want the simple case. But, you could easily write yourself a helper function that made the simpler syntax work:
function myCreate(proto, props, enumerable, writable, configurable) {
// last three arguments are optional - default them to true if not present
if (typeof enumerable === "undefined") {enumerable = true;}
if (typeof writable === "undefined") {writable = true;}
if (typeof configurable === "undefined") {configurable = true;}
var wrapper = {};
for (var i in props) {
wrapper[i] = {
value: props[i],
enumerable: enumerable,
configurable: configurable,
writable: writable
};
}
return(Object.create(proto, wrapper));
}
Demo here: http://jsfiddle.net/jfriend00/vVjRA/
Related
I'm having this issue today:
I'd like to extend a JavaScript Object class with some useful functions, so I wrote something like this:
Object.prototype.fn = function() {
// Do some cool stuff...
};
Then I use another function to do a loop like this:
for( var i in obj ) {
this[i] = obj[i]; // 'this' --> is another Object
console.log( i, obj[i] );
}
So I moved stuff to another object from 'obj'. However, as you can see in logs, 'fn' function is now either in my extended object and in it's __proto__. All I want is to avoid this without using this in my 'for' loop:
if( typeof obj[i] !== 'function' ) // Then do it
So I was wondering how native functions (like toString() for example) are implemented to stay invisible outside the prototype. Any suggestions?
You can use a method called defineProperty to set your property and have it not be enumerable (or as you said, stay invisible):
var obj = {};
Object.defineProperty(obj, 'test', {
enumerable: false,
configurable: false,
writable: true,
value: 'hello'
});
your property obj.test exists (value is hello) but now wont be enumerable, so doing something like:
for(var i in obj) console.log(i);
// OR
Object.keys(obj);
will not print 'test', BUT you can still access it as a property (obj.test)
I have a DTO type that really is a map of key/value pairs. I would typically use an object literal for this for terseness, but this preculdes the resulting object having the [[prototype]].constructor set to a meaningful value.
e.g.
function MyDto() {
var o = {
myProperty: null
};
return o; //[[prototype]].constructor is meaningless
}
Is there a way to do something similar, but have the [[prototype]].constructor property set to the more meaningful MyDto (instead of Object)?
Not very much sure what you want to do. But this may help..
function MyDto() {
var o = {
myProperty: null
};
Object.setPrototypeOf(o,MyDto.prototype);
return o;
}
a = MyDto();
console.log(a);
To make obj instanceof Foo work, the prototype of obj has to point to the value of the prototype property of the function (Foo). A couple of ways have already been suggested, here is another one:
Call the function with new and return this (implicitly or explicitly). If you want to be able to call the function without new (not really clear from your question), check inside the function whether it was called with new or not:
function MyDto() {
if (!(this instanceof MyDto)) {
return new MyDto();
}
Object.assign(this, {myProperty: null});
}
Note: The constructor property has no meaning internally, only to the developers using your code.
I don't think I understand your question, but you might try this:
o.constructor = MyDto;
This will set o's constructor as MyDto, but will have no effect when doing o instanceof MyDto.
If this is what you want, my suggest is that you instantiate MyDto instead:
function MyDto() {
this.myProperty = null;
}
var o = new MyDto();
console.log(o instanceof MyDto); // true
console.log(o.constructor === MyDto); // true
EDIT: If you return within a function, then you will lost the reference to your new instance. In your case MyDto works as a factory to instances of Object that have an own property called myPropert.
EDIT 2: I still prefer the other way, but using Object.create also works:
function MyDto() {
return Object.create(MyDto.prototype, {
myProperty: {
writable: true,
configurable: true,
value: null
}
});
}
new MyDto() instanceof MyDto; // true
new MyDto().constructor === MyDto; // true
You're approaching this from the wrong direction. If you want the result of new Class() be a real instanceof Class, just extend the default instance object, instead of creating and returning a new one.
function Class () {
_.extend(this, {
property: 'value',
// ...
});
}
(The above code uses Lo-Dash function _.extend() to keep your code short & sweet. Similar implementations may be found in almost all utility libraries or bigger JavaScript frameworks).
I'm trying to define a non-enumerable toJSON function on a prototype object without much luck. I'm hoping for something similar to ECMAScript 5 toJSON:
Object.defineProperty(obj, prop, { enumerable: false });
However this defines it as a property which cannot be accessed as a method.
[EDIT: Nick is wrong; it can be accessed as a method. His mistake was in code that is not shown in this question - see his comments on answers below, for details.]
I was hoping to be able to define the function in a non-enumerable fashion, as I was planning to define in the prototypes of all primitive types (String, Number, Boolean, Array, and Object), so that I can recursively apply the function through complex objects.
The end goal here is to be able JSONify a Backbone model/collection with nested collections recursively.
I guess in total I have two main questions:
Is it possible to define a non-enumerable function on a prototype? If so how?
Is there a better way to JSONify nested Backbone models?
I don't get it, why can't you access it as a method?
var foo = {};
Object.defineProperty(foo, 'bar', {
enumerable: false,
value: function () {console.log('foo.bar\'d!');}
});
foo.bar(); // foo.bar'd!
If you wanted it on the prototype, it's as easy as
Object.defineProperty(foo.prototype, /* etc */);
or even directly in Object.create
foo.prototype = Object.create(null, {
'bar': {value: function () {/* ... */}}
});
However, unless you're creating instances of foo, it won't show up if you try to foo.bar, and only be visible as foo.prototype.bar.
If foo has it's own prototype (e.g. foo = Object.create({})), you can get it with Object.getPrototypeOf, add the property to that and then foo.bar would work even if it is not an instance.
var proto = Object.getPrototypeOf(foo); // get prototype
Object.defineProperty(proto, /* etc */);
You can see visibility of enumerable vs non-enumerable properties here.
Paul S. is right about needing to set the property definition's value instead of a get, but I wanted to add that you don't need to pass enumerable: false, because false is the default for that option in Object.defineProperty() The answer can be simplified to:
var foo = {};
Object.defineProperty(foo, 'bar', {
value: function(){ console.log('calling bar!'); }
});
foo.bar();
Always you can avoid enumerable functions properties in object when you looping through it. And instead of define property in each object and set enumerable to false , you can create function which will call to any object with the property you want and put a condition to not take the property in the looping list. here is the example :
const obj = {
name: "myName",
title: "developer"
}
function prop() {
this.loop = function(i) {
for (i in this) {
if (typeof(this[i]) == "function") {
continue;
} else {
console.log(this[i]);
}
}
}
}
prop.call(obj);
obj.loop();
output >> myName, developer
I came across this little snippet of code for property reflection in JavaScript:
function GetProperties(obj) {
var result = [];
for (var prop in obj) {
if (typeof obj[prop] !== "function") {
result.push(prop);
}
}
return result;
}
I've tested it using the following "CustomObject":
var CustomObject = (function () {
function CustomObject() {
this.message = "Hello World";
this.id = 1234;
}
Object.defineProperty(CustomObject.prototype, "Foo", {
get: function () {
return "foo";
},
enumerable: true,
configurable: true
});
Object.defineProperty(CustomObject.prototype, "Bar", {
get: function () {
return "bar";
},
enumerable: true,
configurable: true
});
return CustomObject;
})();
Here is a little test using jQuery:
$(document).ready(function () {
console.log(GetProperties(new CustomObject()));
});
Here are the results:
["message", "id", "Foo", "Bar"]
I understand that the GetProperties function just returns an array of anything in the input object that is not a function, but I want to filter the results to get only the "real" properties, so my output should be:
["Foo", "Bar"]
Is this possible?
Also, can I do the opposite and just return the fields?
There are two things you could do (and possibly more, it depends on your exact situation):
Name "private" properties differently, e.g. with a trailing underscore and check whether the property name ends with an underscore when you are iterating over the properties (and exclude them).
If by "real properties" you mean the properties defined on the prototype and you want to ignore all properties defined on the object itself, you can use .hasOwnPrototype to check where it is defined. Alternatively, you could use Object.getPrototypeOf and iterate over the properties of the prototype only.
Bad code. I'm leaving (with a comment) it 'cause the subsequent discussion
might help somebody else.
If you always use defineProperty() to get non enumerable properties, then this would work:
function GetProperties(obj) {
var result = [];
for (var prop in obj) {
// propertyIsEnumerable() returns false just because the properties
// are inherited thru the prototype chain. It was just a coincidence
// that it got the desired result. Don't do this.
if (typeof obj[prop] !== "function" && !obj.propertyIsEnumerable(prop)) {
result.push(prop);
}
}
return result;
}
Otherwise, I'd be curious to know a general solution to the problem.
EDIT: I see that the code has enumerable: true and still my code does exactly what was asked. Double you tee ef?
Does anyone have any example implementation of making individual object props readOnly/non-configurable? I mean primitive data types. Have tried using ES5 Object API, but hitting a brick wall.
I can't show code, because it's still at that "messy" phase, but basically I'm iterating through an outside object which, itself, holds numeruos objects. Those objects each hold various primitive data types. I have made the outer objects readOnly, non-config, etc, but can't figure out how to do likewise for individual props, the innermost props.
So, if outer.inner.prop === "Hello", I want to make that value readOnly.
Thanks!
UPDATE
I just figured this out, it was all in the for loop I was using to iterate over props. Now I've actually get data descriptors for the props, even the primitive ones. :) Thanks all!
You have to iterate through the inner object, since there is no way to deep-freeze an object using standard ES5 methods.
function deepFreeze(obj) {
Object.keys(obj).forEach(function (key) {
if (typeof obj[key] == 'object')
deepFreeze(obj[key]);
});
Object.freeze(obj);
}
Edit:
Also works for defineProperty if you don't want to freeze:
function deepWriteProtect(obj) {
Object.keys(obj).forEach(function (key) {
if (typeof obj[key] == 'object')
deepWriteProtect(obj[key]);
Object.defineProperty(obj, key, { writable: false });
});
}
I'm not 100% sure I understand your question correctly, but from what I gather you are asking for private variables. If so, that can be easily achieved using closures.
function myClass(){
var mySecretProperty = 10;
this.getMySecretProperty = function(){
return mySecretProperty;
}
this.changeMySecretProperty = function(s){
// whatever logic you need for a setter method
mySecretProperty = s;
}
}
var myObj = new MyClass();
myObj.changeMySecretProperty(120);
myObj.getMySecretProperty(); // will return 120
myObj.mySecretProperty // will return undefined
Would the following (ES5) example help? It creates an empty constructor, with a getter for property a (and no setter, so de facto a is read only):
var Obj = function(){};
Obj.prototype = {
get a() {return 5;}
}
var x = new Obj;
alert(x.a); //=> 5
x.a = 6; //=> TypeError: setting a property that has only a getter
Not using ES5 you can do
var Obj = function(){
var a = 5;
if (!Obj.prototype.getA) {
Obj.prototype.getA = {
toString: function() {
return a;
}
};
}
}
var y = new Obj;
alert(y.getA); //=> 5
But that is not 100% failsafe: Obj.prototype.getA can be overwritten.
Here is a jsfiddle showing how you can use ES5 getter/setter definitions to make a property of an object something that can only be fetched. The code looks like this:
var object = {
get x() {
return 17;
}, set x() {
alert("You cannot set x!");
}
};
Of course the getter could obtain the value of the property ("x") from anywhere, like a closure from a constructor or something. The point is that the setter simply does not change the value, so attempts to change it:
object.x = 100;
will not have any effect.