I am currently working on creating a page that requires the Object.keys method from the Object class. The browser that this needs to run in however is the IGB of Eve online.
This browser does not support Object.keys and therefore
JavaScript error:
Uncaught TypeError: Object function Object() { [native code] } has no method 'keys'
My proposed solution of this was to find somewhere the defined Object class for javascript and reference it on my page beforehand like:
<script src="someURLtoJavascriptObjectClass"></script>
I have however not found it through my google searches.
I just realised this after having worked on the project for way too long, I only tried bits and pieces of it through the IGB but I forgot to test this method and it has become quite key to what I want to do. I would very much like this to resolve easily with a simple link. It can be solved with other methods but this would be by far the cleanest.
Can someone help me in the right direction?
You could try implementing the Polyfill in your code.
// From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys
if (!Object.keys) {
Object.keys = (function() {
'use strict';
var hasOwnProperty = Object.prototype.hasOwnProperty,
hasDontEnumBug = !({ toString: null }).propertyIsEnumerable('toString'),
dontEnums = [
'toString',
'toLocaleString',
'valueOf',
'hasOwnProperty',
'isPrototypeOf',
'propertyIsEnumerable',
'constructor'
],
dontEnumsLength = dontEnums.length;
return function(obj) {
if (typeof obj !== 'object' && (typeof obj !== 'function' || obj === null)) {
throw new TypeError('Object.keys called on non-object');
}
var result = [], prop, i;
for (prop in obj) {
if (hasOwnProperty.call(obj, prop)) {
result.push(prop);
}
}
if (hasDontEnumBug) {
for (i = 0; i < dontEnumsLength; i++) {
if (hasOwnProperty.call(obj, dontEnums[i])) {
result.push(dontEnums[i]);
}
}
}
return result;
};
}());
}
Related
I would like to create some custom JS syntax. Is there a way I can write a method so that the following would work:
var someDict = {"abc": 1, "efg": 2}
someDict.keys
someDict.values
instead of:
Object.keys(someDict)
Object.values(someDict)
In Swift this sort of thing can be done via extensions, I'm just wondering if there's a way in JS.
You can create an object with getters:
class ObjWithKeyValGetters {
constructor(obj) {
Object.assign(this, obj);
}
get keys() {
return Object.keys(this);
}
get values() {
return Object.values(this);
}
}
const myObj = new ObjWithKeyValGetters({"abc": 1, "efg": 2});
console.log(myObj.keys);
console.log(myObj.values);
Maybe you are looking for prototype . you can add new methods to Object with prototype like :
var current = Object.prototype.valueOf;
// Since my property "-prop-value" is cross-cutting and isn't always
// on the same prototype chain, I want to modify Object.prototype:
Object.prototype.valueOf = function() {
if (this.hasOwnProperty('-prop-value')) {
return this['-prop-value'];
} else {
// It doesn't look like one of my objects, so let's fall back on
// the default behavior by reproducing the current behavior as best we can.
// The apply behaves like "super" in some other languages.
// Even though valueOf() doesn't take arguments, some other hook may.
return current.apply(this, arguments);
}
}
as modified MDN pollyfill for Object.keys:
Object.prototype.pollufillKeys = (function() {
'use strict';
var hasOwnProperty = Object.prototype.hasOwnProperty,
hasDontEnumBug = !({
toString: null
}).propertyIsEnumerable('toString'),
dontEnums = [
'toString',
'toLocaleString',
'valueOf',
'hasOwnProperty',
'isPrototypeOf',
'propertyIsEnumerable',
'constructor'
],
dontEnumsLength = dontEnums.length;
return function(obj) {
if (typeof obj !== 'function' && (typeof obj !== 'object' || obj === null)) {
throw new TypeError('Object.keys called on non-object');
}
var result = [],
prop, i;
for (prop in obj) {
if (hasOwnProperty.call(obj, prop)) {
result.push(prop);
}
}
if (hasDontEnumBug) {
for (i = 0; i < dontEnumsLength; i++) {
if (hasOwnProperty.call(obj, dontEnums[i])) {
result.push(dontEnums[i]);
}
}
}
return result;
};
}());
The following code is supposed to extend foo with bar, the assignment was to find the "bug" in this snippet but for the life of my I can't seem to find the bug. Is there something I'm missing? Some major case where this code would break when extending objects with other objects?
var foo = {a:1,b:"2",c:[3]}, bar = {d:"3",e:4,f:5.0};
var extend = function (obj, extension) {
if (typeof obj === "object" && typeof extension === "object") {
for (var i in extension) {
if (extension.hasOwnProperty(i) && !obj.hasOwnProperty(i)) {
obj[i] = extension[i];
}
}
return obj;
}
}
var foo_bar = extend(foo,bar);
console.log(foo_bar); //this logs as expected
Maybe it's when you have repeated keys in both objects. Isn't the extending object able to override the extended object's properties?
var foo = {a:1,b:"2",c:[3]}, bar = {c:"3",d:4,e:5.0};
var foo_bar = extend(foo,bar); //will result in {a:1,b:"2",c:"3",d:4,e:5.0};
Is there any other way to look up for the prototype properties of an javascript object. Lets say I have like this.
function proton() {
this.property1 = undefined;
this.property2 = undefined;
};
proton.prototype = {
sample1 : function() {
return 'something';
},
sample2 : function() {
return 'something';
}
};
var my_object = new proton();
console.log(Object.keys(my_object));
returns ["property1", "property2"]
console.log(Object.getOwnPropertyNames(my_object));
returns ["property1", "property2"]
But what i want to print is the prototype properties of the object my_object.
['sample1', 'sample2']
In order for me to see the prototype properties of that object i need to console.log(object) and from developer tools i can look up for the properties of that object.
But since I am using third party libraries like phaser.js, react.js, create.js
so i don't know the list of the prototype properties of a created object from this libraries.
Is there any prototype function of Object to list down all the prototpye properties of a javascript object?
Not a prototype method, but you can use Object.getPrototypeOf to traverse the prototype chain and then get the own property names of each of those objects.
function logAllProperties(obj) {
if (obj == null) return; // recursive approach
console.log(Object.getOwnPropertyNames(obj));
logAllProperties(Object.getPrototypeOf(obj));
}
logAllProperties(my_object);
Using this, you can also write a function that returns you an array of all the property names:
function props(obj) {
var p = [];
for (; obj != null; obj = Object.getPrototypeOf(obj)) {
var op = Object.getOwnPropertyNames(obj);
for (var i=0; i<op.length; i++)
if (p.indexOf(op[i]) == -1)
p.push(op[i]);
}
return p;
}
console.log(props(my_object)); // ["property1", "property2", "sample1", "sample2", "constructor", "toString", "toLocaleString", "valueOf", "hasOwnProperty", "isPrototypeOf", "propertyIsEnumerable"
function prototypeProperties(obj) {
var result = [];
for (var prop in obj) {
if (!obj.hasOwnProperty(prop)) {
result.push(prop);
}
}
return result;
}
EDIT: This will grab all the properties that were defined on any ancestor. If you want a more granular control of what is defined where, Bergi's suggestion is good.
The quick and dirty one-liner solution would be:
console.log(Object.getOwnPropertyNames(Object.getPrototypeOf({ prop1: 'val1' })))
If you want something more precise, go with the accepted answer!
I have some test class
TestKlass = (function() {
function TestKlass(value) {
this.value = value != null ? value : 'hello world';
console.log(this.value);
}
return TestKlass;
})();
x = new TestKlass;
x instanceof TestKlass; (gives true)
I have new empty object
y = {}
y instanceof Object
Can I find any ways to set any properties for y, something like this
y.__proto__ = x.__proto__
y.constructor.prototype = x.constructor.prototype
for to have this result
y instanceof TestKlass => true
====================================================
UPD:
So. My main aim - it's to create CLONE function. Now my solution works for me. Please look at this code:
JavaScript JS object clone
Object._clone = function(obj) {
var clone, property, value;
if (!obj || typeof obj !== 'object') {
return obj;
}
clone = typeof obj.pop === 'function' ? [] : {};
clone.__proto__ = obj.__proto__;
for (property in obj) {
if (obj.hasOwnProperty(property)) {
value = obj.property;
if (value && typeof value === 'object') {
clone[property] = Object._clone(value);
} else {
clone[property] = obj[property];
}
}
}
return clone;
};
CoffeeScript JS object clone
# Object clone
Object._clone = (obj) ->
return obj if not obj or typeof(obj) isnt 'object'
clone = if typeof(obj.pop) is 'function' then [] else {}
# deprecated, but need for instanceof method
clone.__proto__ = obj.__proto__
for property of obj
if obj.hasOwnProperty property
# clone properties
value = obj.property
if value and typeof(value) is 'object'
clone[property] = Object._clone(value)
else
clone[property] = obj[property]
clone
Now you can try to do that
A = new TestKlass
B = Object._clone(A)
B instanceof TestKlass => true
It's work fine with Moz FF 13. But I think it's not cross-browser. and proto is deprecated.
I think there is no universal solution. But maybe It's will be helpful for somebody.
Perhaps you should read the following answer first. What you are trying to achieve is really simple. However before I explain let's look at your solutions:
Solution 1
y.__proto__ = x.__proto__;
I wouldn't recommend doing this as the __proto__ property is now deprecated. The above code won't work in Rhino.
Solution 2
y.constructor.prototype = x.constructor.prototype;
This is a very bad solution. The reason is that y.constructor is actually y.__proto__.constructor and since y is an object y.__proto__ is the same as Object.prototype. This means that y.constructor is the same as Object and you're essentially changing the prototype property of Object. This could potentially break other code on your page. I strongly discourage this practice.
Solution 3 (my solution)
What you want to do is simply change the internal [[proto]] property of an object. Since there's no cross platform way to do so we need to employ a hack. A simple solution would be to create a new object with the desired __proto__ property and set getters and setters on it to change the original object. It would be as follows:
function setPrototypeOf(object, prototype) {
var constructor = function () {
for (var key in object) {
if (object.hasOwnProperty(key)) {
(function (key) {
Object.defineProperty(this, key, {
get: function () {
return object[key];
},
set: function (value) {
object[key] = value;
},
enumerable: true
});
}).call(this, key);
}
}
};
constructor.prototype = prototype;
return new constructor;
}
After this all you need to do is:
y = setPrototypeOf(y, TestKlass.prototype);
Here is a working fiddle.
Edit:
The problem with your clone function is that it only clones objects and arrays. You forgot to account for functions which are also passed by reference. Thus when you clone an object which has methods which close over the internal state of the object, the object and its clone will share the same internal state. See the following fiddle:
function Value(value) {
this.get = function () {
alert(value);
};
this.set = function (newValue) {
value = newValue;
};
}
var a = new Value(5);
var b = Object._clone(a);
b.set(10); // setting b to 10 also sets a to 10
a.get(); // alerts 10, should alert 5
There's absolutely no way in JavaScript to clone the internal state of an object so cloning objects and arrays in JavaScript will only work for objects which do not expose closures as methods.
To know more about the problems with cloning objects in JavaScript read this answer. Click on the following link to read John Resig's answer.
obj instanceof SomeConstructor checks whether SomeConstructor is found anywhere in the prototype chain of obj.
If you want inheritance, you need to set the .prototype of FirstConstructor to a newly created SomeConstructor object.
SomeConstructor = function() {}
FirstConstructor = function() {}
FirstConstroctor.prototype = new SomeConstructor();
var y = new FirstConstroctor();
y instanceof FirstConstructor; // true
y instanceof SomeConstructor ; // true, it bubbles from FirstConstructor.__proto__ to SomeConstructor
json2.js seems to ignore members of the parent object when using JSON.stringify(). Example:
require('./json2.js');
function WorldObject(type) {
this.position = 4;
}
function Actor(val) {
this.someVal = 50;
}
Actor.prototype = new WorldObject();
var a = new Actor(2);
console.log(a.position);
console.log(JSON.stringify(a));
The output is:
4
{"someVal":50}
I would expect this output:
4
{"position":0, "someVal":50}
Well that's just the way it is, JSON.stringify does not preserve any of the not-owned properties of the object. You can have a look at an interesting discussion about other drawbacks and possible workarounds here.
Also note that the author has not only documented the problems, but also written a library called HydrateJS that might help you.
The problem is a little bit deeper than it seems at the first sight. Even if a would really stringify to {"position":0, "someVal":50}, then parsing it later would create an object that has the desired properties, but is neither an instance of Actor, nor has it a prototype link to the WorldObject (after all, the parse method doesn't have this info, so it can't possibly restore it that way).
To preserve the prototype chain, clever tricks are necessary (like those used in HydrateJS). If this is not what you are aiming for, maybe you just need to "flatten" the object before stringifying it. To do that, you could e.g. iterate all the properties of the object, regardless of whether they are own or not and re-assign them (this will ensure they get defined on the object itself instead of just inherited from the prototype).
function flatten(obj) {
var result = Object.create(obj);
for(var key in result) {
result[key] = result[key];
}
return result;
}
The way the function is written it doesn't mutate the original object. So using
console.log(JSON.stringify(flatten(a)));
you'll get the output you want and a will stay the same.
Another option would be to define a toJSON method in the object prototype you want to serialize:
function Test(){}
Test.prototype = {
someProperty: "some value",
toJSON: function() {
var tmp = {};
for(var key in this) {
if(typeof this[key] !== 'function')
tmp[key] = this[key];
}
return tmp;
}
};
var t = new Test;
JSON.stringify(t); // returns "{"someProperty" : "some value"}"
This works since JSON.stringify searches for a toJSON method in the object it receives, before trying the native serialization.
Check this fiddle: http://jsfiddle.net/AEGYG/
You can flat-stringify the object using this function:
function flatStringify(x) {
for(var i in x) {
if(!x.hasOwnProperty(i)) {
// weird as it might seem, this actually does the trick! - adds parent property to self
x[i] = x[i];
}
}
return JSON.stringify(x);
}
Here is a recursive version of the snippet #TomasVana included in his answer, in case there is inheritance in multiple levels of your object tree:
var flatten = function(obj) {
if (obj === null) {
return null;
}
if (Array.isArray(obj)) {
var newObj = [];
for (var i = 0; i < obj.length; i++) {
if (typeof obj[i] === 'object') {
newObj.push(flatten(obj[i]));
}
else {
newObj.push(obj[i]);
}
}
return newObj;
}
var result = Object.create(obj);
for(var key in result) {
if (typeof result[key] === 'object') {
result[key] = flatten(result[key]);
}
else {
result[key] = result[key];
}
}
return result;
}
And it keeps arrays as arrays. Call it the same way:
console.log(JSON.stringify(flatten(visualDataViews)));
While the flatten approach in general works, the snippets in other answers posted so far don't work for properties that are not modifiable, for example if the prototype has been frozen. To handle this case, you would need to create a new object and assign the properties to this new object. Since you're just stringifying the resulting object, object identity and other JavaScript internals probably don't matter, so it's perfectly fine to return a new object. This approach is also arguably more readable than reassigning an object's properties to itself, since it doesn't look like a no-op:
function flatten(obj) {
var ret = {};
for (var i in obj) {
ret[i] = obj[i];
}
return ret;
}
JSON.stringify takes three options
JSON.stringify(value[, replacer[, space]])
So, make use of the replacer, which is a function, that is called recursively for every key-value-pair.
Next Problem, to get really everything, you need to follow the prototpes and you must use getOwnPropertyNames to get all property names (more than you can catch with keysor for…in):
var getAllPropertyNames = () => {
const seen = new WeakSet();
return (obj) => {
let props = [];
do {
if (seen.has(obj)) return [];
seen.add(obj);
Object.getOwnPropertyNames(obj).forEach((prop) => {
if (props.indexOf(prop) === -1) props.push(prop);
});
} while ((obj = Object.getPrototypeOf(obj)));
return props;
};
};
var flatten = () => {
const seen = new WeakSet();
const getPropertyNames = getAllPropertyNames();
return (key, value) => {
if (value !== null && typeof value === "object") {
if (seen.has(value)) return;
seen.add(value);
let result = {};
getPropertyNames(value).forEach((k) => (result[k] = value[k]));
return result;
}
return value;
};
};
Then flatten the object to JSON:
JSON.stringify(myValue, flatten());
Notes:
I had a case where value was null, but typeof value was "object"
Circular references must bee detected, so it needs seen