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;
Related
I am debugging. I want to print all the fields of a keydown event:
print(tag, stuff={}, newline=true){
this.key_transcript_plane.innerHTML += "(" + tag;
for (let [key, value] of Object.entries(stuff)) {
let key_string = key ? "<em>"+key.toString()+"</em>" : "<em>undefined</em>";
let value_string = value ? value.toString() : "<em>undefined</em>";
this.key_transcript_plane.innerHTML += "    <em>" + key_string + ":</em>" + value_string;
}
this.key_transcript_plane.innerHTML += ")";
if(newline) this.key_transcript_plane.innerHTML += "<br>";
}
and,
key_input_plane.addEventListener("keydown", (e) => {
this.print('keydown', e);
});
But this is all that it prints:
(keydown isTrusted:true)
However, if I set a breakpoint in the very same print function, and ask Chrome what the value of the 'stuff' object is, I get this:
> stuff
KeyboardEvent {isTrusted: true, key: "a", code: "KeyA", location: 0, ctrlKey: false, …}
altKey: false
bubbles: true
cancelBubble: false
cancelable: true
charCode: 0
code: "KeyA"
... and a hundred more things
Which you might agree is just a little bit different ..
The console shows 'isTrusted' as does my 'print()' function, but then goes on with 'key', 'code' etc. What does the console know that I don't? Or more to the point, how can I print all of the keys and values for this event 'e'?
Of course I want to know the 'key' and 'code' but also all the other stuff Chrome puts in there on the first level, even things I can't specifically ask for because I don't know what Chrome has done. (Being the whole point of the matter.)
Note, Currently I'm not intending to ask about recursive descent into the value here. I'm just looking to print the top level keys and their values.
The problem with what you are trying to do is that you want to iterate over properties that are not enumerable or they are not owned by the object.
"Enumerable properties are those properties whose internal enumerable flag is set to true, which is the default for properties created via simple assignment or via a property initializer"
"Ownership of properties is determined by whether the property belongs to the object directly and not to its prototype chain."
You can use something like that to get all the properties.
var typeA = new KeyboardEvent('keydown', {key:'a'});
var SimplePropertyRetriever = {
getOwnAndPrototypeEnumerablesAndNonenumerables: function(obj) {
return this._getPropertyNames(obj, true, true, this._enumerableAndNotEnumerable);
},
_enumerableAndNotEnumerable: function(obj, prop) {
return true;
},
// Inspired by http://stackoverflow.com/a/8024294/271577
_getPropertyNames: function getAllPropertyNames(obj, iterateSelfBool, iteratePrototypeBool, includePropCb) {
var props = [];
do {
if (iterateSelfBool) {
Object.getOwnPropertyNames(obj).forEach(function(prop) {
if (props.indexOf(prop) === -1 && includePropCb(obj, prop)) {
props.push(prop);
}
});
}
if (!iteratePrototypeBool) {
break;
}
iterateSelfBool = true;
} while (obj = Object.getPrototypeOf(obj));
return props;
}
};
SimplePropertyRetriever.getOwnAndPrototypeEnumerablesAndNonenumerables(typeA)
You must read this article for more details.
Enumerability and ownership of properties
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
When defining an array as a property of an ES5-style object, I want to make it so the property's value cannot be changed.
'use strict';
var global = Object.create(Object.prototype, {
names: {
value: ['Barney', 'Trogdor'],
writable: false
}
});
global.names.push('Jackson'); // I expected a read-only error here
console.log(global.names[2]); // >> Jackson
global.names = ['Ooga', 'Booga']; // >> TypeError: "names" is read-only
It seems that I have only protected against property assignment.
Is there any way to protect against stuff like Array.push() that modifies my "unwritable" array?
Object.seal() seems to work.
'use strict';
var global = Object.create(Object.prototype, {
names: { value: Object.seal(['Barney', 'Trogdor']) }
});
global.names.push('Jackson'); // >> TypeError: global.names.push(...) is not extensible
EDIT: Actually, nevermind.
In the previous example, were I to append the following line of code:
global.names[0] = 'Jackson'; // Still works
I believe Object.freeze() was what I actually wanted.
var global = Object.create(Object.prototype, {
names: { value: Object.freeze(['Barney', 'Trogdor']) }
});
global.names.push('Jackson'); // >> TypeError: global.names.push(...) is not extensible
global.names[0] = 'Jackson'; // >> TypeError: 0 is read-only
global.names = ['Jackson']; // >> TypeError: "names" is read-only
Once all the data is loaded, why not overwrite the push method on what you have:
global.names.push = function () {return false}
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).
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