I'm trying to extend Object functionality this way:
Object.prototype.get_type = function() {
if(this.constructor) {
var r = /\W*function\s+([\w\$]+)\(/;
var match = r.exec(this.constructor.toString());
return match ? match[1].toLowerCase() : undefined;
}
else {
return typeof this;
}
}
It's great, but there is a problem:
var foo = { 'bar' : 'eggs' };
for(var key in foo) {
alert(key);
}
There'll be 3 passages of cycle.
Is there any way to avoid this?
I, for one, am not completely against extending native types and ECMA-262 5th ed. solves the problems mentioned in other answers and linked articles for us in a nice manner. See these slides for a good overview.
You can extend any object and define property descriptors that control the behavior of those properties. The property can be made non enumerable meaning when you access the objects properties in a for..in loop, that property will not be included.
Here's how you can define a getType method on Object.prototype itself, and make it non enumerable:
Object.defineProperty(Object.prototype, "getType", {
enumerable: false,
writable: false,
configurable: false,
value: function() {
return typeof this;
}
});
// only logs "foo"
for(var name in { "foo": "bar" }) {
console.log(name);
}
The getType function above is mostly useless as it simply returns the typeof object which in most cases will simply be object, but it's only there for demonstration.
[].getType();
{}.getType();
(6).getType();
true.getType();
You shouldn't extend the object prototype, for that exact reason:
http://erik.eae.net/archives/2005/06/06/22.13.54/
Use a static method instead.
If you have no choice, you can use the "hasOwnProperty" method:
Object.prototype.foo = function(){ alert('x'); }
var x = { y: 'bar' };
for(var prop in x){
if(x.hasOwnProperty(prop)){
console.log(prop);
}
}
You can use the hasOwnProperty() method to check if the property belongs to the foo object:
var foo = { 'bar' : 'eggs' };
for (var key in foo) {
if (foo.hasOwnProperty(key)) {
alert(key);
}
}
Is there any way to avoid this?
Yes, don't extend native types.
Use a wrapper instead:
var wrapper = (function(){
var wrapper = function(obj) {
return new Wrapper(obj);
};
function Wrapper(o) {
this.obj = obj;
}
Wrapper.prototype = wrapper.prototype;
return wrapper;
}());
// Define your get_type method:
wrapper.prototype.get_type = function(){
if(this.obj.constructor) {
var r = /\W*function\s+([\w\$]+)\(/;
var match = r.exec(this.obj.constructor.toString());
return match ? match[1].toLowerCase() : undefined;
}
else {
return typeof this.obj;
}
};
Usage:
var obj = { 'bar' : 'eggs' };
alert(wrapper(obj).get_type());
for(var i in obj) { ... works properly }
When you loop over enumerable properties of an object, you can can determin if the current property was "inherited" or not with Object.hasOwnProperty()
for ( var key in foo )
{
if ( foo.hasOwnProperty( key ) )
{
alert(key);
}
}
But let the dangers of monkey patching be known to ye, especially on Object, as others have posted about
Create your own object instead of extending the default Object.
Also see:
http://erik.eae.net/archives/2005/06/06/22.13.54/
http://dean.edwards.name/weblog/2006/07/erlaubt/
Related
I want to create function that will return all properties of an object:
This is my initial code:
function dir(obj) {
if (obj === null || obj === Object.prototype) {
return [];
}
var names = Object.getOwnPropertyNames(obj);
return names.concat(dir(Object.getPrototypeOf(obj)));
}
and I have ES6 class where I want to hide some properties:
class Lexer {
constructor(input, { whitespace = false } = {}) {
this.__input__ = input.replace(/\r/g, '');
var internals = {};
[
'_i', '_whitespace', '_col', '_newline', '_line',
'_state', '_next', '_token', '_prev_char'
].forEach(name => {
Object.defineProperty(this, name, {
configurable: false,
enumerable: false,
get() {
return internals[name];
},
set(value) {
internals[name] = value;
}
});
});
this._whitespace = whitespace;
this._i = this._line = this._col = this._newline = 0;
this._state = this._next = this._token = null;
this._prev_char = '';
}
token() {
}
...
}
The problem I have is that Object.getOwnPropertyNames return enumerable properties and Object.key don't return ES6 class methods. So I if Use the first function I get hidden props and if I use second I only get __input__.
Is there a way to detect if something is hidden property and not ES6 class method? I don't want to detect if something is a function because I want something more generic there can be not enumerable properties that are functions.
I've also found that for ES5 constructor function there is constructor property that is also not enumerable and behave like ES6 method (in case of my Lexer class it work the same token method).
NOTE: this is not Y/X problem I want to create dir function for my Scheme interpreter that will work like similar function in Python, it should work with ES5 function constructor objects and ES6 class objects.
I've tried this:
function dir(obj, proto) {
if (obj === null || obj === Object.prototype) {
return [];
}
var names = Object.getOwnPropertyNames(obj);
if (!proto) {
names = names.filter(name => {
var d = Object.getOwnPropertyDescriptor(obj, name);
return d.configurable && d.enumerable;
});
}
return names.concat(dir(Object.getPrototypeOf(obj), true));
}
but I don't know if this is the best way, since the user may want to create property descriptor of prototype (i'm not sure if this is something that may happen). I want 100% working solution and not work around. Do you know any method to make it work?
EDIT:
Even that I have working solution for my case, I'm wondering if ES6 properties in fact are indistinguishable from not enunumerable properties.
Especially if you can detect if foo is different then bar:
class Test {
constructor() {
Object.defineProperty(this, 'foo', {
enumerable: false,
configurable: false,
value: function() { alert('foo') }
});
}
bar() {
alert('bar');
}
}
Is the only difference is that bar is on prototype object and there are no other way to detect the difference?
Is there a way to detect if something is hidden property and not ES6 class method? I don't want to detect if something is a function
There's no distinction. A class method is just a non-enumerable function-valued property of the prototype object.
If you absolutely had to distinguish them, you could .toString() the function and check whether it has method syntax, but that still won't catch methods that are copied around and miss ES5 methods. (See also How do you check the difference between an ECMAScript 6 class and function? and How can I differentiate between an arrow function, class and a normal function? for related topics)
I have all non - enumerable properties in object, I want to clone that object.
My problem non-enumerable properties are not getting cloned.
Take below example
Object.defineProperty(this, 'prop', {
get: function () {
return prop;
},
set: function (value) {
prop= value;
}
});
Object.defineProperty(this, 'newprop', {
get: function () {
return newprop;
},
set: function (value) {
newprop= value;
}
});
For example I have above two properties in my object doing clone using following methods, my properties are not getting cloned I believe it is because they are non - enumerable.
var newObject = $.extend({},oldObject);
var newObject= Object.assign({},oldobject);
How do I copy non-enumerable properties in javascript.
If one or more properties aren't enumerable, how do you want to auto-magically enumerate them?
Since you know their names, you should do something like this:
var sourceObj = this;
var targetObj = {};
["prop", "otherProperty"].forEach(function(property) {
targetObj[property] = sourceObj[property];
});
Or you can build the whole property name array whenever you define a non-enumerable property:
var propertyNames = [];
Object.defineProperty(this, 'newprop', {
get: function () {
return newprop;
},
set: function (value) {
newprop= value;
}
});
propertyNames.push("newprop"); // <---
Object.defineProperty(this, 'newprop2', {
get: function () {
return newprop;
},
set: function (value) {
newprop= value;
}
});
propertyNames.push("newprop2"); // <---
propertyNames.forEach(function(property) {
targetObj[property] = sourceObj[property];
});
Alternate approach: Object.getOwnPropertyNames
The Object.getOwnPropertyNames() method returns an array of all
properties (enumerable or not) found directly upon a given object.
Maybe this is the best approach. Object.getOwnPropertyNames gets the name of own object's properties either if they're enumerable or not. That is, you can avoid building the whole propertyNames array, and this approach should be fine with you because you said that all properties aren't enumerable:
var sourceObj = this;
var targetObj = {};
Object.getOwnPropertyNames(sourceObj).forEach(function(property) {
targetObj[property] = sourceObj[property];
});
You can use Object.defineProperties and Object.getOwnPropertyDescriptors() function
The Object.defineProperties() method defines new or modifies existing properties directly on an object, returning the object.
The Object.getOwnPropertyDescriptors() method returns all own property descriptors of a given object.
So, you can add the properties of the original object into an empty object, as follow
var source_obj = this
var cloned_obj = Object.defineProperties({}, Object.getOwnPropertyDescriptors(source_obj));
I have a similar question in How to implement Ruby's extend module in JavaScript.
And because I don't wanna use iterate attach each method to new object, so the final best way I can do is like that, welcome give me any idea and suggestion, thanks.
class Test {
constructor(name) {
this.name = name;
}
test1(){
return this.name + "test1"
}
}
const Other = {
test2: function(){
return this.name + "test2"
},
test3: function(){
return this.name + "test3"
},
test4: function(){
return this.name + "test4"
}
}
var person = new Test("HKE")
Object.assign(person,Other) //this like object.extend(Module) in ruby
console.log(person.test1()) // HKEtest1
console.log(person.test2()) // HKEtest2
console.log(person.test3()) // HKEtest3
console.log(person.test4()) // HKEtest4
var house = new Test("Tsao")
console.log(house.test1())
console.log(house.test2()) // TypeError: house.test2 is not a function
console.log(house.test3()) // TypeError: house.test3 is not a function
console.log(house.test4()) // TypeError: house.test4 is not a function
var Editor = {};
Editor.Basic = function(obj) {
this.config ={
value: obj
}
};
Editor.Basic.prototype = {
getValue: function() {
return this.config.value;
}
};
Editor.Advanced = function(obj) {
Editor.Basic.call(this, obj);
};
Editor.Advanced.prototype = {
config: {
notValue: !this.config.value
}
};
var extendByPrototype = function(obj1, obj2) {
for (var key in obj2.prototype) {
if (obj2.prototype.hasOwnProperty(key) && obj1.prototype[key] === undefined)
obj1.prototype[key] = obj2.prototype[key];
}
};
extendByPrototype(Editor.Advanced, Editor.Basic);
Is there anyway to get the Editor.Advanced.prototype to extend existing objects (recursively of course) instead of overriding them? (As seen in extendByPrototype)
I know I would check obj1.prototype[key] !== undefined, but I am unsure as what I need to do to extend existing keys in a generic way, without moving the config from Editor.Advanced.prototype to the constructor and using the push function.
The proper way of extending an Object in JavaScript is using Object.create(prototype). Objects created this way will have proper inheritance setup. To get the prototype of any object, you'll use Object.getPrototypeOf(object).
Object.create(prototype) is new in JavaScript 1.8.5. If you're looking for backward compatible, you'll have to use the non-standard way
function extend(child, supertype) {
child.prototype.__proto__ = supertype.prototype;
}
extend(Animal, Lifeform);
extend(Plant, Lifeform);
var anOnion = new Plant();
After that, you can get the prototype object by...
object.contructor.__proto__
More details on __proto__ here: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/proto
I'm in the process of writing a simple prototype in Node.js with some helper methods that I'll probably need in objects using that prototype. One method that I'd like to have is an implementation of jQuery's .each(). I've looked at jQuery's implementation in their development release, and tried to emulate it in my simplified version here.
// Loop through the object using a callback
BaseProto.prototype.each = function (cb, args) {
var obj = this.get(), // Get our object as a plain object
prop;
/** Code to make sure the the args passed are actually an array **/
if (typeof cb === "function") {
// For each property in our object
for (prop in obj) {
// Apply the callback to the object's property and pass
// the index, the property, and whatever else as arguments
if (cb.apply(obj[prop], [ prop, obj[prop] ].concat(args)) === false) {
// Get out if the function returns false
break;
}
}
}
// Reset our object with the new one
return this.reset(obj);
};
The problem is that while the callback is definitely being fired, it doesn't have any effect on the object's property. No matter what I do inside the callback, the changes stay inside the callback's scope.
Here is an example of a simple callback that I've been testing with.
var BaseProtoTestObj = new BaseProto();
/** Set some properties to BaseProtoTestObj **/
function cb1 ( key, val ) {
var prop;
key = key.toString() + " Callbacked";
val = val.toString() + " Callbacked";
for (prop in this) {
this[prop] = this[prop].toString() + " Callbacked";
}
}
// Doesn't have any effect on BaseProtoTestObj
BaseProtoTestObj.each(cb1);
I can see that there is a lot more going on in jQuery's .each(), but from what I can gather it's for optimization and the ability to iterate over arrays as well as objects.
In the end, my question is simple. What is it that jQuery is doing to affect the properties, that I'm not in my .each()?
Edit
I suppose another question to ask would be if my logic is fundamentally wrong, and you can't modify an object's properties this way.
You don't need a custom method:
for(var prop in object) {
var value = object[prop];
// do something with value and/or prop
}
Although if you really needed .each(), you could do something like this:
Object.prototype.each = function(cb) {
for(var propName in this) {
cb(propName, this[propName]);
}
}
var foo = { prop: 'value', prop2: 'value2' };
foo.each(function(key,value) {
// do something here
});
Since you need to modify the actual values of the properties, try this:
Object.prototype.mutate = function(cb) {
for(var propName in this) {
this[propName] = cb(propName, this[propName]);
}
}
var obj = {
a: 'foo',
b: 'bar',
c: 'baz'
};
obj.mutate(function(propName, propValue) {
return propName + '-' + propValue;
});
/*
obj will now be:
var obj = {
a: 'a-foo',
b: 'b-bar',
c: 'c-baz'
};
*/
In Javascript, if an object has lots of properties that are functions:
var obj = { foo: function() { ... },
bar: function() { ... },
...
}
then how can you get an array of names of those functions? That is, an array
["foo", "bar", ... ]
thanks.
var names = [];
for( var k in obj ) {
if(obj.hasOwnProperty(k) && typeof obj[k] == 'function') {
names.push(k);
}
}
var functions = [];
for (var prop in obj) {
if ((typeof obj[prop]) == 'function') {
// it's a function
functions.push(prop);
}
}
Edit: I've slightly misread the question, you want to extract the names of only the properties that are function objects:
function methods(obj) {
var result = [];
for (var prop in obj) {
if (obj.hasOwnProperty(prop) && typeof obj[prop] == 'function') {
result.push(prop);
}
}
return result;
}
var obj = {
foo: function() { },
bar: function() { },
};
methods(obj); // ["foo", "bar"]
I'm using the hasOwnProperty method, to ensure that the enumerated properties in fact exist physically in the object.
Note that this approach and all other answers have a small problem IE.
The JScript's DontEnum Bug, custom properties that shadow non-enumerable properties (DontEnum) higher in the prototype chain, are not enumerated using the for-in statement, for example :
var foo = {
constructor : function() { return 0; },
toString : function() { return "1"; },
valueOf : function() { return 2; }
toLocaleString : function() { return "3"; }
};
for (var propName in foo ) { alert(propName); }
The object foo clearly has defined four own properties, but those properties exist in Object.prototype marked as DontEnum, if you try to enumerate the properties of that object with the for-in statement in IE, it won't find any.
This bug is present on all IE versions, and has been recently fixed in IE9 Platform Preview.
To complete other answers: you can also use instanceof:
var obj = { foo: function() { ... },
bar: function() { ... },
...
},
fnArr = [];
for (var label in obj){
if (obj[label] instanceof Function){
fnArr.push(label)
}
}
With ES5:
var obj = {
foo: function() {},
bar: function() {},
baz: true
};
function getMethods(object) {
return Object.keys(object).filter(function(key) {
return typeof object[key] == 'function';
});
}
getMethods(obj); // [foo, bar]
Object.keys(<object>) returns the names of all enumerable properties of an object as an array, of which the non-functions are filtered out.
Example - works on Chrome release and nightly builds of Webkit and Tracemonkey (Firefox).