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'
};
*/
Related
I have a reference to an object, which has a getter and setter for a certain property somewhere in its prototype chain. I want to get a reference to the getter and setter methods, and the object they're on. I know I can do it by manually iterating over every prototype object and checking hasOwnProperty, as in the following snippet:
const obj2 = (() => {
const baseProto = {
get prop() {
return 'propval';
},
set prop(newVal) {
console.log('setting...');
}
};
const obj1 = Object.create(baseProto);
return Object.create(obj1);
})();
// From having a reference to obj2, want to get the getter or setter methods,
// and want to get the object they're on, without invoking them:
let currentObj = obj2;
const propToFind = 'prop';
let get, set, foundProto;
while (currentObj) {
if (currentObj.hasOwnProperty(propToFind)) {
foundProto = currentObj;
({ get, set } = Object.getOwnPropertyDescriptor(currentObj, propToFind));
break;
}
currentObj = Object.getPrototypeOf(currentObj);
}
if (foundProto) {
console.log('Found:', get, set, foundProto);
}
This seems rather cumbersome, and while loops are ugly. Of course, the getter and setter can be invoked, with the calling context of the current object, with very simple code like
obj2.prop = 'newVal'; // invoke setter
const val = obj2.prop; // invoke getter
But that invokes the functions without being able to interact with them (or the prototype object they're on).
Is there any clearer, shorter way of achieving what I'm doing in the snippet above?
This seems rather cumbersome, and while loops are ugly
I don't think this is cumbersome, it's just what you have to do when trying to find a property anywhere on the prototype chain.
You don't have to write a while loop of course, the iteration can be easily expressed as a for loop:
let get, set, foundProto;
for (let currentObj = obj2; currentObj; currentObj = Object.getPrototypeOf(currentObj)) {
if (currentObj.hasOwnProperty('prop')) {
foundProto = currentObj;
({ get, set } = Object.getOwnPropertyDescriptor(currentObj, 'prop'));
break;
}
}
if (foundProto) {
console.log('Found:', get, set, foundProto);
}
You can of course also write a helper function to do this, like
function getInheritedPropertyDescriptor(obj, prop) {
for (; obj != null; obj = Object.getPrototypeOf(obj)) {
if (Object.prototype.hasOwnProperty.call(obj, prop)) {
return { foundProto: obj, ...Object.getOwnPropertyDescriptor(obj, prop) };
}
}
return null;
}
var result = getInheritedPropertyDescriptor(obj2, 'prop');
if (result) {
console.log('Found:', result.get, result.set, result.foundProto);
}
I'm creating a GAS Spreadsheets Service based app that reads/writes & updates a row of data. I have a key-value object that represents a row of data, like the example data provided in snippet.
Use case:
var exampleData = [{weekendVolume=5186270,midweekVolume=16405609}];
// tuple length 2 of two known values
function _DataRecordObject( exampleData ) {
this._endOfWeek = new Date().endOfWeek();// Date.prototype method
}
var _DataRecordMethods = {
weekEnding: function() {
return this._endOfWeek.formatDateString()
},
weekMonth: function() {
return this._endOfWeek.getMonthLabelShort()
},
/* Processed volume */
weekendVolume: function() {
return 'weekendVolume'
},
midweekVolume: function() {
return 'midweekVolume'
},
totalVolumeProcessed: function() {
return _SumTotal(
this.weekendVolume(),
this.midweekVolume()
)
}
}
_DataRecordObject.prototype = _DataRecordMethods;
The new DataRecordObject is prototype of a Sheet object that provides other helpful properties. _SumTotal is a helper function.
My question:
When I call a new DataRecordObject with sheet range as argument, how do I update the exampleData object with the new properties such as totalVolumeProcessed?
For example:
var foo = new _DataRecordObject( exampleData );
Console.log( foo );
//[{weekEnding='Aug-17',weekMonth=4,weekendVolume=5186270,midweekVolume=16405609,totalVolumeProcessed=21591879}]
I'd like the flexibility of using constructor-prototype inheritence, but using a boilerplate style template like Object-literals. My intuition suggests that I need to pass the data object keys when constructing a new dataRecordObject.
I'm a newcomer to JavaScript and have not yet gotten my head around inheritance, prototypes, and respective design-patterns. Factories and Modules, or perhaps Observers seem like appropriate patterns but my limited experience with JS is a limiting factor to solving my problem.
This might work for you.
1) Define the prototype as an object literal:
var methods = {
sayName: function() {
return "My name is " + this.name;
},
sayAge: function() {
return "I am " + this.age + " years old";
}
};
2) You can either make the 'methods' variable global or define it inside the following function. The function creates a new object using 'methods' variable as a prototype and populates it with values from the 'data' argument.
function createNewObj (data) {
var data = data || null;
var result = Object.create(methods);
for (var key in data) {
if (data.hasOwnProperty(key)) {
result[key] = data[key];
}
}
return result;
}
3) Bringing things together
function test() {
var data = {name: "John", age: "32"};
var row = createNewObj(data);
Logger.log(row.name); //logs 'John'
Logger.log(row.age); //logs '32'
Logger.log(row.sayName()); //logs 'My name is John'
Logger.log(row.sayAge()); //logs 'I am 32 years old'
Logger.log(Object.getPrototypeOf(row)); // returns contents of the 'methods' object literal
Logger.log(row.hasOwnProperty("sayName")); //logs 'false' because 'hasOwnProperty' doesn't go up the prototype chain
Logger.log("sayName" in row); //logs 'true' because 'in' goes up the chain
}
I suggest you take a look at this blog post by Yehuda Katz that dives deeper into prototypes http://yehudakatz.com/2011/08/12/understanding-prototypes-in-javascript/ It has examples of much cleaner code that might be helpful.
I've found a solution, which expands on #Anton-Dementiev 's response. His suggestion to read the Yehudi Katz was also most helpful.
The create new object function, _DataRecordObject is where the solution lies..
function _DataRecordObject( RowDataObject ) {
this._endOfWeek = new Date().endOfWeek();// Date.prototype method
var data = RowDataObject || null;
var result = Object.create( _DataRecordMethods );
for (var key in data) {
if ( data.hasOwnProperty( key ) ) {
// if value is present in the RowDataObject,
// then assign its value to the result
result[key] = data[key];
} else {
// if not, the value is a method function,
// which should be evaluated in that context,
// and then return the method value as result
var foo = Object.getPrototypeOf( result )[ key ];
result[key] = foo.call( data );
}
}
return result;
}
//simples
Since the methods are passed as property functions, they need to be called as functions in that context.
I have the following javascript code:
function testClass() {
this.SaveValue = function (value) {
var isInstance = value instanceof TestEnum;
if (!isInstance) {
return;
}
}
}
TestEnum = {
VALUE_0: 0,
VALUE_1: 1,
VALUE_2: 2
}
I create an instance of this object in the following way:
$(function () {
var a = new testClass();
a.SaveValue(TestEnum.VALUE_1);
});
All I'd like to do is test that the value passed to the SaveValue function is actually the type of TestEnum. However, when I run this code I get the following error: Uncaught TypeError: Expecting a function in instanceof check, but got 1
Am I going about this the right way? I tried typeof but it only returns number which is not particularly useful to me.
You could create the values as instances of the "class":
function TestEnum(value) {
this._value = value;
}
TestEnum.prototype.valueOf = function() {
return this._value;
}
TestEnum.prototype.toString = function() {
return 'TestEnum_' + this._value;
}
TestEnum.VALUE_0 = new TestEnum(0);
TestEnum.VALUE_1 = new TestEnum(1);
The following would work then:
TestEnum.VALUE_0 instanceof TestEnum
But it also means you'd have to explicitly access the numerical value of one value with .valueOf. In some cases JS will do this automatically for you (like in 5 + TestEnum.VALUE_1). Overriding toString so that you can use a value as property might also be necessary.
It really depends on your use case whether this is a viable solution.
Alternatively, if just want to test whether a value is part of the enum, you can have an additional property which holds all possible values:
TestEnum.values = {0: true, 1: true, ...};
And then test it with
value in TestEnum.values
// or more reliable (fails for inherited `Object` properties)
TestEnum.values.hasOwnProperty(value);
You could even automate this:
function collectValues(obj) {
var values = {}; // or Object.create(null) if available
for (var prop in obj) {
if (obj.hasOwnProperty(prop)) {
values[obj[prop]] = true;
}
}
return values;
}
TestEnum.values = collectValues(TestEnum);
This will only reliably work for primitive values though and won't distinguish between the string "1" and the number 1.
You are passing a number to the function in
a.SaveValue(TestEnum.VALUE_1);
Since TestEnum is simply an Object, and you are referencing a number property on that object, you're calling your function with a number. You should instead create a TestEnumValue object and use that for your Object's properties:
JSFiddle link for below
function testClass() {
this.SaveValue = function (value) {
var isInstance = value instanceof TestEnumValue;
if (!isInstance) {
return;
}
}
}
TestEnumValue = function(arg) {
arg = arg ? arg : 0; // sensible default
this.key = 'VALUE_' + arg;
this.val = arg;
}
Level = {
NumSpiders : new TestEnumValue(0),
NumCreepers: new TestEnumValue(1),
NumZombies : new TestEnumValue(2),
NumChickens: new TestEnumValue // uses default enum value
};
$(function() {
var a = new testClass();
a.SaveValue(Level.NumSpiders);
$('#hi').text(Level.NumSpiders.key);
});
Playing around with this, I noticed that you can leverage the fact that an enum compiles into an object that binds the values both ways combined with a hasOwnProperty check.
export enum TEST_ENUM{
ZERO, // 0
ONE, // 1
TWO, // 2
}
let a = 1;
let b = TEST_ENUM.TWO // 2
let c = 5 // incorrect value
TEST_ENUM.hasOwnProperty(a); // TRUE
TEST_ENUM.hasOwnProperty(b); // TRUE
TEST_ENUM.hasOwnProperty(c); // FALSE
This comes with a few caveats though;
// An object's keys are always strings...
// Although this shouldn't not matter usually (e.g. parsed user input)
TEST_ENUM.hasOwnProperty("2"); // TRUE
// And the enum is bound two-way so:
let input = "TWO";
if (TEST_ENUM.hasOwnProperty(input) { // TRUE
let result = input // "TWO"
// result is now the enum's value, instead of the key.
result = TEST_ENUM[input]; // this would be the correct assignment
};
Of course you can fix both of these with a typeof check, in case of a string assign it TEST_ENUM[mystring].
Note that my intellisense didn't autocomplete the hasOwnProperty function on an enum, but it doesn't complain about it either, and it's available on all browsers.
Edit
Here's an example of how you could do it.
function TestEnum(val) {
this.vals = this.vals || [];
if (this.vals.indexOf(val) == -1) console.log('nope: ' + val);
else console.log('ok: ' + val);
}
(function() {
var vals = {
VALUE_0: 0,
VALUE_1: 1,
VALUE_2: 2
};
TestEnum.prototype.vals = [];
for (var key in vals) {
TestEnum[key] = vals[key];
TestEnum.prototype.vals.push(vals[key]);
}
})();
Now new TestEnum(TestEnum.VALUE_0); is OK, but if you try, say, new TestEnum(3), then it throws an exception.
This is a bit backwards -- x instanceof y means that x has been created as x = new y(). Since TestEnum isn't even a function, you can't create an instance of it, so this isn't going to work.
What you could do is maybe something like this:
function MyEnum(enumVal) { this.val = enumVal; }
a.SaveValue( new MyEnum(TestEnum.VALUE_1) );
Then check using isInstance = value instanceof MyEnum.
I'm trying to make a "copy" function and add it to the object's prototype. I planned to recursively typecheck and assign properties to a new object and then return the object... But, there seems to be a problem, see this snippet of code:
Object.prototype.copy = function()
{
for (prop in this)
{
console.log(prop); //Logs copy (the function)!!!
}
}
x = {"a": 1};
y = x.copy();
As I've pointed out in the comment, I found this very weird behavior, but why is this happening? The copy function should be in Object.prototype, not in the instanced object itself! How do I fix it? Can I just set this.copy = undefined, and still rely on Object.prototype.copy?
This is the full code sample, as requested:
Object.prototype.copy = function()
{
var object = this; //The object we are copying.
var newObject = {}; //The object we will return.
//Cycle through the properties of the object we are creating, copy them recursively.
for (prop in object)
{
if (!Object.prototype.hasOwnProperty.call(this, prop) || object[prop] == null)
{
continue;
}
if (prop == "copy")
{
console.log("Well, blah."); //This never prints!
}
if (typeof(object[prop]) == "object" && !(object[prop] instanceof Array)) //If the object's property is another object...
{
newObject[prop] = object[prop].copy(); //Set the copy of it to the new object as well.
console.log("1 --- " + prop); //This prints copy - two times! That defies logic!
}
else if (typeof(object[prop]) == "object") //If it's an array...
{
newObject[prop] = object[prop].slice(); //Do it in a nicer fashion.
console.log("2 --- " + prop);
}
else //You're safe to copy it.
{
newObject[prop] = object[prop];
console.log("3 --- " + prop + " --- " + object[prop]);
}
}
return newObject;
}
There's a method called "hasOwnProperty" that you can use:
if (this.hasOwnProperty(prop)) { ... }
If the function returns true the it's a "direct" property on the object.
If you fear that the "hasOwnProperty" method may be borked, you can do this:
if (Object.prototype.hasOwnProperty.call(this, prop)) { ... }
instead.
Newer versions of JavaScript have fancier ways of examining and controlling objects.
edit — also your updated code involves a problem that'll bite due to the nested calls to "copy": you didn't declare "prop" with var, so after the call to copy an object, the value of "prop" will have changed! (Every call to "copy" shares the same variable, in other words.)
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/