I have a javascript object with some functions inside, I wish I could call them in a loop, something like this:
funcs: {
func1: function() {
return true;
},
func2: function() {
return false;
}
}
for(func in funcs) {
console.log(funcs[func]());
console.log(funcs[func].call());
}
Both work. But the declaration of your object is not correct. It is var object = { /*something*/};
var funcs = {
func1: function() {
return true;
},
func2: function() {
return false;
}
};
for(func in funcs) {
console.log(funcs[func]());
console.log(funcs[func].call());
}
Output
true
true
false
false
Related
I recently learned about composing objects together using functions from reading this article. Following along, I end up with this code:
function withFlying(o) {
let _isFlying = false;
return {
...o,
fly () {
_isFlying = true;
},
land () {
_isFlying = false;
},
isFlying () {
return _isFlying
}
}
};
function withWalking(o) {
let isWalking = false;
return {
...o,
startWalking() {
isWalking = true;
return this
},
stopWalking() {
isWalking = false;
return this
},
isWalking: () => isWalking
}
}
const bird = withWalking(withFlying({}))
Everything here works. However, I would like to be able to call isFlying as a property instead of a function:
// current (working)
bird.isFlying() // return value of `_isFlying`
// desired
bird.isFlying // return value of `_isFlying`
I know that get and set are keywords that can be used in object literals, and so I tried this:
function withFlying(o) {
let _isFlying = false
return {
...
get isFlying () {
return _isFlying
}
}
}
But it doesn't show the correct value after updating using the other functions. I figured that with the get property being a function, closures would apply similar to the other functions. Am I wrong in this assumption? Is there underlying behavior with get that I'm not understanding, and is what I'm trying to achieve possible the way I'm doing it now?
Here's a snippet with the code I tried to use:
function withFlying(o) {
let _isFlying = false;
return {
...o,
fly () {
_isFlying = true;
},
land () {
_isFlying = false;
},
valueOf_isFlying() {
return _isFlying;
},
get isFlying () {
return _isFlying
}
}
};
function withWalking(o) {
let isWalking = false;
return {
...o,
startWalking() {
isWalking = true;
return this
},
stopWalking() {
isWalking = false;
return this
},
isWalking: () => isWalking
}
}
const bird = withWalking(withFlying({}))
// desired
console.log(bird.isFlying) // _isFlying starts false
bird.fly() // should set _isFlying to true
console.log(bird.isFlying) // still returns false
console.log(bird.valueOf_isFlying()) // shows _isFlying is true
The problem is that when you create your new object, you're using spread notation to copy the properties from the original object:
return {
...o,
// ...
};
The problem with that is it copies the then-current value of accessor properties, not the definition of the accessor property. You can see that here:
const obj1 = {
get example() {
return 42;
}
};
console.log("Notice that the property descriptor is for an accessor property:");
console.log(Object.getOwnPropertyDescriptor(obj1, "example"));
const obj2 = {...obj1};
console.log("Notice that the property descriptor is for a simple data property:");
console.log(Object.getOwnPropertyDescriptor(obj2, "example"));
.as-console-wrapper {
max-height: 100% !important;
}
It's very much as though you did:
for (const key of Object.keys(o) {
newObject[key] = e[key];
}
e[key] gets the then-current value of the property, not the definition of the property.
To fix it, use Object.getOwnPropertyDesciptors to get the descriptors of the properties, and use Object.defineProperties to define those same properties on the new object. Since you're doing that (and adding more properties) in at least two places, you probably want a utility function:
function assignPropertyDescriptors(target, obj, updates) {
// B
return Object.defineProperties(
// A
Object.defineProperties(
target,
Object.getOwnPropertyDescriptors(obj)
),
Object.getOwnPropertyDescriptors(updates)
);
}
The "A" Object.defineProperties call copies the original object's property descriptors and applies them to the new object. The "B" Object.defineProperties call applies the ones you're adding to that new object as well.
But let's generalize that into a loop, similar to Object.assign (hence the name assignPropertyDescriptors):
function assignPropertyDescriptors(target, ...updates) {
for (const update of updates) {
Object.defineProperties(
target,
Object.getOwnPropertyDescriptors(update)
);
}
return target;
}
withFlying and withWalking would then use that worker function, for instance:
function withFlying(o) {
let _isFlying = false;
return assignPropertyDescriptors({}, o, {
fly () {
_isFlying = true;
},
land () {
_isFlying = false;
},
get isFlying () {
return _isFlying
}
});
};
Here's a complete example:
function assignPropertyDescriptors(target, ...updates) {
for (const update of updates) {
Object.defineProperties(
target,
Object.getOwnPropertyDescriptors(update)
);
}
return target;
}
function withFlying(o) {
let _isFlying = false;
return assignPropertyDescriptors({}, o, {
fly () {
_isFlying = true;
},
land () {
_isFlying = false;
},
get isFlying () {
return _isFlying
}
});
};
function withWalking(o) {
let isWalking = false;
return assignPropertyDescriptors({}, o, {
startWalking() {
isWalking = true;
return this
},
stopWalking() {
isWalking = false;
return this
},
isWalking: () => isWalking
});
}
const bird = withWalking(withFlying({}))
console.log(bird.isFlying) // _isFlying starts false
bird.fly() // should set _isFlying to true
console.log(bird.isFlying) // _isFlying is true
Get Object function name from event list on IE works fine in Chrome btw
Example
var foo = {
fookeydown:function(e){
e.which;
... do something
}
}
$(document).on("keydown",foo.fookeydown)
$._data(document,"events").keydown[0].handler.name // return me fookeydown in Chrome
but ie is nut
You are trying to access a function's property function.name, which is not defined for IE. You could try the following implementation to define it (Notice the function name given to the function in foo):
if (!(function f() {}).name) {
Object.defineProperty(Function.prototype, 'name', {
get: function() {
var name = (this.toString().match(/^function\s*([^\s(]+)/) || [])[1];
Object.defineProperty(this, 'name', {
value: name
});
return name;
}
});
}
var foo = {
fookeydown: function fookeydown(e) {
console.log(e.which, 'keydown');
console.log($._data(document, "events").keydown[0].handler.name);
}
};
$(document).on("keydown", foo.fookeydown);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
Alternative, searching in foo.fooProp:
var foo = {
fooProp: {
foofookeydown: function(e) {
console.log(e.which, 'keydown');
console.log($._data(document, "events").keydown[0].handler.name);
},
init: function() {
$(document).on("keydown", this.foofookeydown);
},
},
init: function() {
this.fooProp.init()
}
};
if (!(function f() {}).name) {
Object.defineProperty(Function.prototype, 'name', {
get: function() {
var name = '';
var values = Object.keys(foo.fooProp).map(function(e) {
return foo.fooProp[e]
});
if (values.length > 0) {
if (values.indexOf(this) > -1)
name = Object.keys(foo.fooProp)[values.indexOf(this)];
}
Object.defineProperty(this, 'name', {
value: name
});
return name;
}
});
}
foo.init();
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
I have an object like this one:
var BrowserDetect = {
uniqueProps: [],
browserUID: '',
browserFonts: '',
isIPhonePad: function() {
return navigator.userAgent.match(/iPhone|iPod/i);
},
isDesktop: function() {
return !navigator.userAgent.match(/iPhone|iPad|android/i);
},
isAndroid: function() {
return navigator.userAgent.match(/android/i);
},
isFirefox: function() {
return navigator.userAgent.match(/firefox/i);
},
isIOS7: function() {
return navigator.userAgent.match(/.*CPU.*OS 7_\d/i);
},
isChromeCrios: function() {
return navigator.userAgent.match(/chrome|crios/i);
},
isIPad: function() {
return navigator.userAgent.match(/iPad/i);
}
}
(FYI: There are more functions inside the object)
So I want to go through "BrowserDetect" and check which of those functions inside it return "true" and get the function's name too.
What's the easy way to achieve that? I tried to use the jquery $.each, but without success.
Use Object.keys() with Array#filter to iterate the object and return all truthy function names:
Object.keys(BrowserDetect).filter(function(key) {
var f = BrowserDetect[key];
return typeof f === 'function' && f();
});
var BrowserDetect = {
uniqueProps: [],
browserUID: '',
browserFonts: '',
isIPhonePad: function() {
return navigator.userAgent.match(/iPhone|iPod/i);
},
isDesktop: function() {
return !navigator.userAgent.match(/iPhone|iPad|android/i);
},
isAndroid: function() {
return navigator.userAgent.match(/android/i);
},
isFirefox: function() {
return navigator.userAgent.match(/firefox/i);
},
isIOS7: function() {
return navigator.userAgent.match(/.*CPU.*OS 7_\d/i);
},
isChromeCrios: function() {
return navigator.userAgent.match(/chrome|crios/i);
},
isIPad: function() {
return navigator.userAgent.match(/iPad/i);
}
};
var result = Object.keys(BrowserDetect).filter(function(key) {
var f = BrowserDetect[key];
return typeof f === 'function' && f();
});
console.log(result);
I implemented a non-functional solution in pure js. It's fairly straightforward once you consider that a property can be a function as well. Once you use call() method on the property it will run the underlying function.
var BrowserDetect = {
uniqueProps: [],
browserUID: '',
browserFonts: '',
isIPhonePad: function() {
return navigator.userAgent.match(/iPhone|iPod/i);
},
isDesktop: function() {
return !navigator.userAgent.match(/iPhone|iPad|android/i);
},
isAndroid: function() {
return navigator.userAgent.match(/android/i);
},
isFirefox: function() {
return navigator.userAgent.match(/firefox/i);
},
isIOS7: function() {
return navigator.userAgent.match(/.*CPU.*OS 7_\d/i);
},
isChromeCrios: function() {
return navigator.userAgent.match(/chrome|crios/i);
},
isIPad: function() {
return navigator.userAgent.match(/iPad/i);
}
}
var functions = [];
for(var prop in BrowserDetect){
if(typeof(BrowserDetect[prop])=="function" && BrowserDetect[prop].call()){
functions.push(prop);
}
}
console.log(functions);
You can use common for ... in loop
for (var functionName in BrowserDetect) {
if (!BrowserDetect.hasOwnProperty(functionName)
|| typeof BrowserDetect[functionName] !== "function") continue
if (BrowserDetect[functionName]())
return functionName
}
or Object.keys
Object.keys(BrowserDetect).reduce(
(current, fnName) => typeof BrowserDetect[fnName] === "function" && BrowserDetect[fnName]() ? fnName : current )
I am new to this and can't figure this out. I have this simplified piece of code:
var StpTable = function () {
function setupPager() {
...
if(i<StpTable.a) {
...
}
...
};
return {
"a": 10,
"init": function() {
setupPager();
}
}
}();
How do I from with the setupPager() function reference the variable a without having to use the variable name StpTable. Tried with this.a but the scope is off.
Any suggestions?
Assign the object to a local variable before you return it and use that.
var StpTable = function () {
function setupPager() {
...
if(i<obj.a) {
...
}
...
};
var obj = {
"a": 10,
"init": function() {
setupPager();
}
};
return obj;
}();
Or simply assign the function as property of the object:
var StpTable = function () {
function setupPager() {
...
if(i<this.a) {
...
}
...
};
return {
"a": 10,
"init": setupPager,
};
}();
Then this.a will work, assuming the function is called with StpTable.init();.
Yes, a could be a local variable
var StpTable = function () {
var a = 10;
function setupPager() {
...
if(i<a) {
...
}
...
};
return {
"a": a,
"init": function() {
setupPager();
}
}
}();
I have this code...
var my = {
helpers: {
getName: function() {
return 'John Doe';
}
}
}
// in another file...
var my = {
helpers: {
getAge: function() {
return '40';
}
}
}
// Test...
$("#myDiv").html(my.helpers.getName + " " + my.helpers.getAge);
http://jsfiddle.net/MojoDK/8cmV7/
... but getName is undefined.
I was hoping javascript was smart enough to merge it into this...
var my = {
helpers: {
getName: function() {
return 'John Doe';
},
getAge: function() {
return '40';
}
}
}
How do I extend a method (or what it's called) like above? I have several "helper" files, that needs to "merge".
Redundancy is good for this:
my = window.my || {};
my.helpers = my.helpers || {};
my.helpers.getAge = function() {
return 40;
};
Demo of it in action
You can also use http://api.jquery.com/jquery.extend
as in:
var my = {
getName: function() {}
};
$.extend(my, {
getAge: function() {
}
});
Demo: http://jsfiddle.net/7KW3H/