I am reading up a little more on ECMAScript 5 (in the browser, using the ES5-shim which I know doesn't support everything). And just to clear up any confusion, considering I have this object (stolen from this post):
var Person = {
firstName: null, // the person’s first name
lastName: null // the person’s last name
};
Is there any difference between this:
var Employee = Object.create(Person, {
id: {
value: null,
enumerable: true,
configurable: true,
writable: true
}
});
And this:
var Employee = Object.create(Person);
Employee.id = null;
// Or using jQuery.extend
$.extend(Employee, {
id : null
});
As far as I understood enumerable, configurable and writable are set to true when defining a property this way (which would also be backwards compatible to legacy JavaScript engines). Am I missing something or can I just omit the verbose property descriptors whenever I want this to be the desired behaviour?
They're the same.
When creating new properties by assignment
obj.prop = val;
all three Boolean attributes of the newly created property are set to true.
Also, notice that when adding properties via Object.defineProperty
Object.defineProperty( obj, 'prop', {
value: val
});
the Boolean attributes are set to false (by default).
Related
I created a object using Object.create in the following way.
var myObject = {
price: 20.99,
get_price: function() {
return this.price;
}
};
var customObject = Object.create(myObject, {
price: {
value: 100
}
}
);
console.log(delete customObject.price);
I tried to delete customObject price using
delete customObject.price returns false
The second parameter to Object.create() is interpreted exactly the same way as the second parameter of Object.defineProperties(). That false is being returned from the delete expression because the property you're deleting is an own non-configurable property and you're not in "strict" mode. In "strict" mode you'd get an exception.
If you created the property with the "configurable" flag set to true, you would get true from the delete:
var customObject = Object.create(myObject, {
price: {
value: 100,
configurable: true
}
}
);
Or you can create the object and just set the property with a simple assignment:
var customObject = Object.create(myObject);
customObject.price = 100;
Such properties are always "born" as configurable.
You can use Object.getOwnPropertyDescriptor(customObject, "price") to check whether the property you're deleting is configurable:
if (Object.getOwnPropertyDescriptor(customObject, "price").configurable)
delete customObject.price;
I am extending the array object with a new method called custommethod as follows and looping through the values inside an array. However, the looping through the indices also prints the name of the property which is extended via Array.prototype.<method>.
Array.prototype.custommethod = function() {
console.log("Hello from Custom Method");
}
Object.defineProperty(Array.prototype, "anothercustommethod", {
value: function() {
console.log("Hello from Another Custom Method");
}
});
Loop through the array
> x = [1,2]
[ 1, 2 ]
> for (i in x) { console.log(i); }
0
1
custommethod
Why doesn't the anothercustommethod get printed in this loop?
Is using the Object.defineProperty() a safer way to create Array.prototype?
I am curious as to how the for loop in javascript actually works. Does it use Object.keys() internally? If so how does it print custommethod which is inside __proto__ property but not anothercustommethod which is also inside __proto__ property?
Why doesn't the anothercustommethod get printed in this loop?
for in iterates over those properties, which data descriptor enumerable is set to true.
In the Documentation
enumerable
true if and only if this property shows up during
enumeration of the properties on the corresponding object. Defaults to
false.
When using defineProperty, you can also pass an extra property - enumerable.
By default it is set to false.
Array.prototype.custommethod = function() {
console.log("Hello from Custom Method");
}
Object.defineProperty(Array.prototype, "anothercustommethod", {
value: function() {
console.log("Hello from Another Custom Method");
},
enumerable: true
});
const x = [1,2]
for (i in x) { console.log(i); }
Is using the Object.defineProperty() a safer way to create Array.prototype?
There is nothing about safe. Try to change the build in prototypes rarely
you can check the property by this API:
Object.getOwnPropertyDescriptor(Array.prototype,'custommethod');//{value: ƒ, writable: true, enumerable: true, configurable: true}
Object.getOwnPropertyDescriptor(Array.prototype,'anothercustommethod');//{value: ƒ, writable: false, enumerable: false, configurable: false}
see? the first one got:enumerable: true,the second one got:enumerable: false,
also other values is different too that is because the default settings is different when set by different API
acutally ,want to be more safe,you can use this API:
Object.getOwnPropertyNames
Object.getOwnPropertyNames(Array.prototype).filter(v=>v.endsWith('custommethod'));//["custommethod", "anothercustommethod"]
if there is symbol,you still need to
Object.getOwnPropertySymbols(Array.prototype);//[Symbol(Symbol.iterator), Symbol(Symbol.unscopables)]
Object.keys works like for in,they won't traverse property whose enumerable is false;
when you try to traverse property with other API,should think about these things
Let's take the following code:
var obj = {};
var x = Symbol();
Object.defineProperties(obj, {
[x]: {
value: true,
writable: true
},
"property2": {
value: "Hello",
writable: false
}
// etc. etc.
});
console.log(obj[x])
Is this valid?
With the native Object.defineproperties code we get in the console.log true.
With the polyfill of zone.js
which is of the form of :
Object.defineProperties = function (obj, props) {
Object.keys(props).forEach(function (prop) {
Object.defineProperty(obj, prop, props[prop]);
});
return obj;
};
we get for the same code of console.log undefined.
This is because of the Object.keys function. I googled around and did not find in any place if this should be allowed or not.
I googled around and did not find in any place if this should be allowed or not.
You can always check the spec, in this case § 19.1.2.3 Object.defineProperties(O, Properties).
It uses the OwnPropertyKeys internal method, which does indeed list all string and symbol keys of an object.
This is because of the Object.keys function
Correct. It should be Object.getOwnPropertyNames(props).concat(Object.getOwnPropertySymbols(props)) instead. You may want to file a bug with zone.js. I do however wonder why you'd need a polyfill for the ES5 Object.defineProperties function when you're working with ES6 symbols?
I want a property that would always be undefined:
var foo = {bar:undefined}
If someone later try to change property bar then also it should results in undefined.
foo.bar = 'someValue'//
foo.bar//this time this should result in undefined.
I mean how to set a property that is unchangeable.
OK, based on the hint from #somethinghere (who is breaking his/her own answer again…) here is a working example, that sets up an object member, which defaults to undefined and is not writeable:
var foo = {}
Object.defineProperty(foo, 'bar', {
enumerable: false,
configurable: false,
writable: false
});
foo.bar = 'abcdefg';
console.log( foo.bar ); // returns undefined
https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty
Be aware that you may still remove and re-set this property. (While you can't simply overwrite it.)
You can currently do this:
var foo = {}
Object.defineProperty(foo, 'bar', {
enumerable: false,
configurable: false,
writable: false,
value: undefined
});
As some have pointed out, undefined would not equal "undefined", one obviously being a string. However, I have added it for the sake of clarity as omitting the value property really loses the point of being able to use defineProperty to make them non-configurable. Once configured, its over, so its useful to have the value. Omit value if you want it to return undefined.
https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty
Is it possible to make a javascript object property read-only? I want to set a property that cannot be modified...
It's possible, but expensive. You can do it by having a truly private member variable and then providing an accessor function:
var NiftyThing = function() {
var trulyPrivateVariable;
trulyPrivateVariable = 5; // For instance
this.accessorFunction = function() {
return trulyPrivateVariable;
}
};
That works because the accessor function is a closure over the var. The cost is that each instance has its own copy of the accessor function.
EDIT: Usage:
var n = new NiftyThing();
alert(n.trulyPrivateVariable);
// Alerts "undefined"
alert(n.accessorFunction());
// Alerts "5"
See Private Member Variables in JavaScript for more.
You can implement something like this, making use of Object.defineProperty():
function blockProperties(object, properties) {
"use strict";
// If not properties passed, then use the already defined ones:
if (typeof properties === "undefined") {
properties = object;
}
// Loop trough the properties
for (var property in properties) {
if (properties.hasOwnProperty(property)) {
// Make property read-only
Object.defineProperty(object, property, {
value: properties[property],
writable: false,
configurable: false,
enumerable: false
});
}
}
return object;
}
var someObject = {};
blockProperties(someObject, {
propertie1: "someValue",
propertie2: "someOtherValue"
});
someObject.propertie1 = "this doesn't change anything";
console.log(someObject.propertie1); // Output: "someValue"
// Because "window" is also an object, you can set an only-read global var:
blockProperties(window, {
onlyReadVariable: "onlyReadValue"
});
I agree with the answer, and would like to note that some JavaScript frameworks like bob.js support such built-in mechanisms:
var obj = { };
//declare read-only property.
bob.prop.namedProp(obj, 'name', 'Bob', true);
//declare read-write property.
bob.prop.namedProp(obj, 'age', 1);
//get values of properties.
console.log(bob.string.formatString('{0} is {1} years old.', obj.get_name(), obj.get_age()));
//set value of read-write property.
obj.set_age(2);
console.log(bob.string.formatString('Now {0} is {1} years old.', obj.get_name(), obj.get_age()));
//cannot set read-only property of obj. Next line would throw an error.
// obj.set_name('Rob');
//Output:
//========
// Bob is 1 years old.
// Now Bob is 2 years old.
However, if you have special needs regarding property, such as specific get accessor implementation needs, then better define a function which gets the value as you need it.
-
Tengiz