I want to change default window.location setter and getter functions.
The following code are successfully works. But This codes are only work limited area.
var _window = window;
(function () {
window = {};
var window = {};
Object.defineProperty(window, 'location', {
get: function () { alert('called getter '); return _window.location; },
set: function () { alert('not in my house'); }
});
window.__proto__ = _window;
alert('window.location : '+ window.location);
}());
When I remove the block,
var _window = window;
window = {};
var window = {};
Object.defineProperty(window, 'location', {
get: function () { alert('called getter '); return _window.location; },
set: function () { alert('not in my house'); }
});
window.__proto__ = _window;
alert('window.location : '+ window.location);
I met the following error.
ERROR Error: cyclic __proto__ value
I know that can't redefine non-configurable property "location" in normal environment. I have changed binding IDL properties to test these code in webkit library. How can I solve this problem?
I have another question. When I run the following codes, I met the same error.(jsc - Javascript Core, Spidermonkey, nodejs)
var o1 = { p1: 1 };
var o2 = { p2: 2 };
o2.__proto__ = o1;
var o3 = { p3: 3 };
o3.__proto__ = o2;
o1.__proto__ = o3;
JavascriptCore shell(jsc)
Exception: Error: cyclic __proto__ value
node.js (v0.10.25)
Error: Cyclic __proto__ value
Spidermonkey JavaScript-C24.2.0
1.js:7:0 TypeError: cyclic __proto__ value
Is it related? Thank you ^^
You really create a prototype chain loop in both cases.
In the closure the window is the local variable (because of hoisting), therefore you do not create a prototype loop on global window object.
var o1 = { p1: 1 };
var o2 = { p2: 2 };
var o3 = { p3: 3 };
o2.__proto__ = o1;
o1.__proto__ = o3;
o3.__proto__ = o2;
VM432:7 Uncaught TypeError: Cyclic __proto__ value
at Object.set __proto__ [as __proto__] (<anonymous>)
at <anonymous>:7:14
Reason: JavaScript will throw an error if we try to assign __proto__ in a circle.The references can’t go in circles.
Related
var vehicle = function(){
var type;
var tyre;
this.tellTyres = function(){
console.log(type + " has " + tyre + " tyres");
};
this.__defineGetter__("type", function(){
return type;
});
this.__defineSetter__("type", function(val){
type = val;
});
this.__defineGetter__("tyre", function(){
return tyre;
});
// this.__defineSetter__("tyre", function(val){
// tyre = val;
// });
};
var car = new vehicle();
car.type = "Car";
car.tyre = 4;
console.log(car.tyre);
car.tellTyres();
I was learning about the getter and setter. Then I realized Javascript is not throwing any error while setting the value of car.tyre without having its setter method.
What happens to the car.tyre property outside the constructor. Where does the value 4 store? Does it override?
JavaScript objects are more like dictionaries than like Java objects. This means that you can set and get an object's properties just by using the property accessor operators . and []:
var obj = { foo: 'bar' };
obj.baz = 17;
console.log(obj.foo, obj.baz); // logs '"bar" 17'
And that is absolutely fine.
But sometimes, you want to do something whenever someone modifies a property of your object. In these cases, you define a getter or setter function for that property (use Object.defineProperty instead of defineGetter and defineSetter):
var obj = { foo: 'bar' };
Object.defineProperty(obj, 'baz', {
get: function () {
console.log('Someone wants to read the property "baz"!');
return 34;
},
set: function (value) {
console.log('You are not allowed to modify the property "baz"!');
}
});
obj.baz = 17; // doesn't work
console.log(obj.foo, obj.baz); // logs '"bar" 34'
When you create a new vehicle(), you create a new object, on which you can set or read properties. You don't need the getters and setters.
I want to convert a data property to accessor property using Object.defineProperty() . Consider the code for this which leads to Uncaught RangeError: Maximum call stack size exceeded error
var c = { name: 'abcde'};
Object.defineProperty(c, 'name', {
get: function() {
return this.name; //causes stack overflow
},
set: function(x) {
this.name = x; //causes stack overflow
}
});
c.name="xyz";
console.log(c.name);
I understood why the error crops in.
One of the proposed solution is to remove 'this' from getter and setter and it seems to work.
var c = { name: 'abcde'};
Object.defineProperty(c, 'name', {
get: function() {
return name; //removed this
},
set: function(x) {
name = x; //removed this
}
});
c.name="xyz";
console.log(c.name);
What is happening ? In general , I want to ask how to convert a data property to accessor property using Object.defineProperty() ?
The second code doesn't actually work because it uses the global variable called name to store the value, instead of storing it in the object c.
It would be rejected by ES5 "strict mode", if it weren't for the fact that window.name is a default property of the global object in browsers.
A more appropriate fix would be to store the value in a lexically scoped private variable:
var c = (function() {
var obj = {};
var name = "abcde";
Object.defineProperty(obj, "name", {
get: function() {
return name;
},
set: function(x) {
name = x;
}
});
return obj;
})();
I am new to JS, I write below code, but I got the error "Prototype is not defined".
var proto = {
describe: function () {
return 'name: ' + this.name;
}
};
var obj = {
[[Prototype]]: proto, //error in this line
name:'obj'
};
console.log(proto.describe());
console.log(obj.describe());
[[Prototype]] is only specification-speech for an internal property (the link in the prototype chain). To link obj to proto through the prototype chain, you can use Object.create:
var obj = Object.create(proto);
obj.name = 'obj';
Or Object.setPrototypeOf in ES6/ES2015:
var obj = {
name:'obj'
};
Object.setPrototypeOf(obj, proto);
Alternatively, there is the legacy property __proto__, which is not necessarily recommended though:
var obj = {
__proto__: proto,
name:'obj'
};
Answers (please read them below, their respective authors provided valuable insights):
"writable: false" prevents assigning a new value, but
Object.defineProperty is not an assignement operation and therefore
ignores the value of "writable"
property attributes
are inherited, therefore a property will remain non writable on every
subclasses/instances until one subclass (or instance of subclass) changes the value of "writable" back to true for itself
Question:
MDN documentation concerning the property "writable" descriptor states:
writable
true if and only if the value associated with the property may be changed with an assignment operator.
Defaults to false.
The official ECMA-262 6th edition more or less states the same.
The meaning is clear but, to my understanding, it was limited to the original property (i.e. the property on that specific object)
However, please consider the following example (JSFiddle):
//works as expected, overloading complete
var Parent = function() {};
Object.defineProperty(Parent.prototype, "answer", {
value: function() { return 42; }
});
var Child = function() {};
Child.prototype = Object.create(Parent.prototype, {
answer: {
value: function() { return 0; }
}
});
var test1 = new Parent();
console.log(test1.answer()); //42
var test2 = new Child();
console.log(test2.answer()); //0
//does not work as expected
var Parent2 = function() {};
Object.defineProperty(Parent2.prototype, "answer", {
value: function() { return 42; }
});
var Child2 = function() {};
Child2.prototype = Object.create(Parent2.prototype);
test3 = new Parent2();
console.log(test3.answer()); //42
test4 = new Child2();
test4.answer = function() { return 0; };
console.log(test4.answer()); //42
Following this example, we see that, although the property is not writable, it can be overloaded on the prototype of a subclass (test2), as I would expect.
However, when trying to overload the method on an instance of a subclass (test4), it fails silently. I would have expected it to work just as test2. The same happens when trying to overload the property on an instance of Parent.
The same thing occurs in both NodeJS and JSFiddle and, under some conditions, overloading on the instance throws a TypeError concerning the readonly nature of the property.
Could you please confirm to me that this is the expected behaviour ? If so, what is the explanation ?
Yes, this is expected behaviour.
it fails silently.
Not exactly. Or: Only in sloppy mode. If you "use strict" mode, you'll get an
Error { message: "Invalid assignment in strict mode", … }
on the line test4.answer = function() { return 0; };
it can be overloaded on the prototype of a subclass (test2), but not an instance of a subclass (test4)
This has nothing to do with instances vs. prototypes. What you didn't notice is that you're using different ways to create the overloading property:
an assignment to a property that is inherited and non-writable fails
an Object.defineProperty call just creates a new property, unless the object is non-extensible
You can do the same for your instance:
Object.defineProperty(test4, "answer", {
value: function() { return 42; }
});
You cannot write to an instance property if its prototype defines that property as unwritable (and the object instance doesn't have a descriptor) because the set operation goes up to the parent (prototype) to check if it can write, even though it would write to the child (instance). See EcmaScript-262 Section 9.1.9 1.c.i
4. If ownDesc is undefined, then
a. Let parent be O.[[GetPrototypeOf]]().
b. ReturnIfAbrupt(parent).
c. If parent is not null, then
i. Return parent.[[Set]](P, V, Receiver).
However, if you are trying to get around that, you can set the descriptor of the instance itself.
var proto = Object.defineProperties({}, {
foo: {
value: "a",
writable: false, // read-only
configurable: true // explained later
}
});
var instance = Object.create(proto);
Object.defineProperty(instance, "foo", {writable: true});
instance.foo // "b"
I've taken your example code and structured all the possible ways to change the possible outcome: https://jsfiddle.net/s7wdmqdv/1/
var Parent = function() {};
Object.defineProperty(Parent.prototype,"type", {
value: function() { return 'Parent'; }
});
var oParent = new Parent();
console.log('parent', oParent.type()); // Parent
var Child1 = function() {};
Child1.prototype = Object.create(Parent.prototype, {
type: {
value: function() { return 'Child1'; }
}
});
var oChild1 = new Child1();
console.log('child1', oChild1.type()); // Child1
var Child2 = function() {};
Child2.prototype = Object.create(Parent.prototype);
Object.defineProperty(Child2.prototype, 'type', {
value: function() { return 'Child2'; }
});
var oChild2 = new Child2();
console.log('child2', oChild2.type()); // Child2
var Child3 = function() {};
Child3.prototype = Object.create(Parent.prototype);
var oChild3 = new Child3();
oChild3.type = function() { return 'Child3'; };
console.log('child3', oChild3.type()); // Parent
var Child4 = function() {};
Child4.prototype = Object.create(Parent.prototype);
Child4.prototype.type = function() { return 'Child4'; };
var oChild4 = new Child4();
console.log('child4', oChild4.type()); // Parent
Object.defineProperty(Parent.prototype,"type", {
value: function() { return 'Parent2'; }
});
var oParent2 = new Parent();
console.log('parent2',oParent2.type());
When you use Object.create(...) to clone the prototype, the original descriptors are still attached higher up the prototype chain.
When assigning something to child.answer = 10 it will use Child.prototype.answer.writable. If that doesn't exist it will try Child.prototype.constructor.prototype.answer.writable if it does.
However, if you try Object.defineProperty(Child.prototype, ...) it won't check the prototype chain. It will test if it is defined on Child.prototype. if it's not defined, it will define it then.
I want to create a settings object on the prototype chain which acts as a lookup for my application. I've tried this:
http://jsfiddle.net/7kwXd/3/
var d9l = {};
d9l.aMethod = function() {
//fails here with Cannot read property 'dimension1' of undefined
alert(this.anObject.dimension1);
};
d9l.aMethod.prototype.anObject = {
dimension1 : "x1",
dimension2 : "y1"
};
var testing = d9l.aMethod();
But I just get Cannot read property 'dimension1' of undefined in the constructor. Is it not possible to define a prototype as an object?
Because d9l is not a contructed object, its methods don't refer to this as you might expect. To verify, try alert(this) and see what you get.
To fix, do this:
function d9l() {}
d9l.prototype.aMethod = function() {
alert(this.anObject.dimension1);
};
d9l.prototype.anObject = {
dimension1: "x1",
dimension2: "y1"
};
var testing = (new d9l()).aMethod();
The prototype property only works on constructor functions (fiddle: http://jsfiddle.net/7kwXd/2/) :
var Ctor = function(){
}
Ctor.prototype = {
aMethod:function(){
alert(this.anObject.dimension1);
},
anObject:{
dimension1 : "x1",
dimension2 : "y1"
}
}
var d9l = new Ctor();
var testing = d9l.aMethod();
This is a very good article about how protos work: http://msdn.microsoft.com/en-us/magazine/ff852808.aspx