I use this setup to add properties to an object:
var obj = {
extend: function(key, obj){
this[key] = obj;
}
}
This works well. However, in some cases i want to add sub-objects as properties:
obj.extend('new', {
test: function(){
console.log('test called');
}
});
I would like to automatically wrap all functions occurring in the object passed (as second parameter) to obj.extend() in a function which in itself wraps the actual call of the function in $(document).ready(function(){})
My approach was this:
extend: function(key, obj){
for(var prop in obj){
if(typeof prop == 'function') {
console.log(prop + ' is a function'
} else
console.log(prop + ' is NOT a function');
}
}
this[key] = obj;
}
But typeof prop seems to return string and not 'function'. I have checked a variety of articles and posts but the approach:
typeof propertyOfObject == 'function'
seems totally valid.
What am i doing wrong here? Why does typeof propertyOfObject give me 'string'? How can i check if prop is a function?
Because you're just iterating over the Object keys, not its properties.
You need to fully qualify the property to access it:
if( typeof obj[ prop ] === 'function' ) { }
Related
Hoping to understand more about how more complex directives work, I was looking at the source of ngRepeat. On line 205, a hashFnLocalsvariable is defined to be an object with an $id property. But this property is assigned a value of hashKey, which I don't see defined anywhere. Is it defined in another script, which it gets access to when they're concatenated during the build process?
It is a function declared in angular.js:
function hashKey(obj) {
var objType = typeof obj,
key;
if (objType == 'object' && obj !== null) {
if (typeof (key = obj.$$hashKey) == 'function') {
// must invoke on object to keep the right this
key = obj.$$hashKey();
} else if (key === undefined) {
key = obj.$$hashKey = nextUid();
}
} else {
key = obj;
}
return objType + ':' + key;
}
It's defined in apis.js, starts on line 16.
Consider the following example.
var obj = function(){};
function apply(target, obj) {
if (target && obj && typeof obj == "object") {
for (var prop in obj) {
target[prop] = obj[prop];
}
}
return target;
}
apply(obj.prototype, {
firstFunction: function (){
this.secondFunction();
},
secondFunction: function (){
// how do I know what function called me here?
console.log("Callee Name: '" + arguments.callee.name + "'");
console.log("Caller Name: '" + arguments.callee.caller.name + "'");
}
});
var instance = new obj();
instance.firstFunction();
UPDATE
Both answers are really awesome. Thank you. I then looked into the problem of calling a recursive, or parent function within an object and found a solution here. This would allow me to retrieve the function name without using the arguments.callee/caller properties.
https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Operators/function
A function's name is an immutable property of that function, set in the initial function expression.
var notTheName = function thisIsTheName() { ... }
someObj.stillNotTheName = function stillTheName() { ... }
If your function expression does not have a name, there is (unsurprisingly) no way to identify it by name. Assigning a function to a variable does not give it a name; if that were the case, you could not determine the name of an expression assigned to multiple variables.
You should set firstFunction's name property by expressing it as
firstFunction: function firstFunction(){
this.secondFunction();
}
Also, arguments.callee is deprecated. See Why was the arguments.callee.caller property deprecated in JavaScript? for a very good explanation of the history of arguments.callee.
Give name to the functions
like:
var obj = function(){};
function apply(target, obj) {
if (target && obj && typeof obj == "object") {
for (var prop in obj) {
target[prop] = obj[prop];
}
}
return target;
}
apply(obj.prototype, {
firstFunction: function firstFunction(){
this.secondFunction();
},
secondFunction: function secondFunction(){
// how do I know what function called me here?
console.log("Callee Name: '" + arguments.callee.name + "'");
console.log("Caller Name: '" + arguments.callee.caller.name + "'");
}
});
var instance = new obj();
instance.firstFunction();
take a look on this question
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 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'
};
*/
How can I call each function in this object?
var obj = {
hey1: function() {
alert('hey');
},
hey2: function() {
alert('hey2');
},
hey3: function() {
alert('hey3');
}
}
I'd like each function to run one after the other. I'm looking for something like:
for (var hey in obj) {
hey();
}
But obviously that doesn't run (otherwise I wouldn't be asking this question).
Thanks guys!!!
for (var hey in obj) {
obj[hey]();
}
In a situation where it is not guaranteed that each property will be a function, you can weed out other properties:
for (var hey in obj) {
if (typeof obj[hey] == "function") {
obj[hey]();
}
}
To further restrict it to only immediate properties of the object (and not the ones inherited from its prototype):
for (var hey in obj) {
if (typeof obj[hey] == "function" && obj.hasOwnProperty(hey)) {
obj[hey]();
}
}
Lopping will give you they keys, not the values. Use the key to get the value:
for (var hey in obj) {
obj[hey]();
}
jsfiddle.net/s8tbr/
Note: Depending on from where you get the object, you might want to check that the properties are members of the object itself, not inherited from a prototype:
for (var hey in obj) {
if (obj.hasOwnProperty(hey)) {
obj[hey]();
}
}