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>
Related
From the link qr-code.js I have the code below.
Then I don't understand, on the highlighted line (60), what means the suffix: "Changed"?
attributeChangedCallback: {
value: function (attrName, oldVal, newVal) {
var fn = this[attrName+'Changed'];
if (fn && typeof fn === 'function') {
fn.call(this, oldVal, newVal);
}
this.generate();
}
Also I don't understand the usage of:
this[attrName+'Changed']
Could you explain me this?, I don't find any clear explanation about this on Google. Thanks.
Below is the full code:
'use strict';
(function(definition) {
if (typeof define === 'function' && define.amd) {
define(['QRCode'], definition);
} else if (typeof module === 'object' && module.exports) {
var QRCode = require('qrjs');
module.exports = definition(QRCode);
} else {
definition(window.QRCode);
}
})(function(QRCode) {
//
// Prototype
//
var proto = Object.create(HTMLElement.prototype, {
//
// Attributes
//
attrs: {
value: {
data: null,
format: 'png',
modulesize: 5,
margin: 4
}
},
defineAttributes: {
value: function () {
var attrs = Object.keys(this.attrs),
attr;
for (var i=0; i<attrs.length; i++) {
attr = attrs[i];
(function (attr) {
Object.defineProperty(this, attr, {
get: function () {
var value = this.getAttribute(attr);
return value === null ? this.attrs[attr] : value;
},
set: function (value) {
this.setAttribute(attr, value);
}
});
}.bind(this))(attr);
}
}
},
//
// LifeCycle Callbacks
//
createdCallback: {
value: function () {
this.createShadowRoot();
this.defineAttributes();
this.generate();
}
},
attributeChangedCallback: {
value: function (attrName, oldVal, newVal) {
var fn = this[attrName+'Changed'];
if (fn && typeof fn === 'function') {
fn.call(this, oldVal, newVal);
}
this.generate();
}
},
//
// Methods
//
getOptions: {
value: function () {
var modulesize = this.modulesize,
margin = this.margin;
return {
modulesize: modulesize !== null ? parseInt(modulesize) : modulesize,
margin: margin !== null ? parseInt(margin) : margin
};
}
},
generate: {
value: function () {
if (this.data !== null) {
if (this.format === 'png') {
this.generatePNG();
}
else if (this.format === 'html') {
this.generateHTML();
}
else if (this.format === 'svg') {
this.generateSVG();
}
else {
this.shadowRoot.innerHTML = '<div>qr-code: '+ this.format +' not supported!</div>'
}
}
else {
this.shadowRoot.innerHTML = '<div>qr-code: no data!</div>'
}
}
},
generatePNG: {
value: function () {
try {
var img = document.createElement('img');
img.src = QRCode.generatePNG(this.data, this.getOptions());
this.clear();
this.shadowRoot.appendChild(img);
}
catch (e) {
this.shadowRoot.innerHTML = '<div>qr-code: no canvas support!</div>'
}
}
},
generateHTML: {
value: function () {
var div = QRCode.generateHTML(this.data, this.getOptions());
this.clear();
this.shadowRoot.appendChild(div);
}
},
generateSVG: {
value: function () {
var div = QRCode.generateSVG(this.data, this.getOptions());
this.clear();
this.shadowRoot.appendChild(div);
}
},
clear: {
value: function () {
while (this.shadowRoot.lastChild) {
this.shadowRoot.removeChild(this.shadowRoot.lastChild);
}
}
}
});
//
// Register
//
document.registerElement('qr-code', {
prototype: proto
});
});
As #Jhecht suggested, it's a combination of the name of a attribute and the suffix "Changed" in order to create generic method names.
For example if the <qr-code> element has an attribute "foo" that is added, updated or removed, then the callback will define the fn variable to this["fooChanged"], which is equivalent to this.fooChanged.
If this method exists, it will be invoked by fn.call().
However I see nowhere in the code you posted such method signature attached to the custom element prototype, so it's useless code until further notice.
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'm playing around with a javascript object that defines some getters and setters using the Object.defineProperty method.
function User() {
var _username;
var _id;
Object.defineProperty(User, 'id', {
get: function() {
return _username;
}
});
Object.defineProperty(User, 'username', {
get: function() {
return _username;
},
set: function(username) {
this._username = username;
}
});
}
For one of the properties (id), I only want a getter. Originally I had a typo and it was returning the value of _username, but I quickly realized that the above did not work. Just for curiosity sake though, I'm trying to understand why it didn't work as expected. If I did the following:
var u = new User();
u.username = 'bob';
alert(u.username);
alert(u.id);
the last statement would alert undefined instead of bob. Why is that? And is there a way to get it to return another property?
You must define the properties on this instead of the constructor function
function User(params) {
var _username;
Object.defineProperty(this, 'id', {
get: function() {
return _username;
}
});
Object.defineProperty(this, 'username', {
get: function() {
return _username;
},
set: function(username) {
_username = username;
}
});
if (params && params.username) {
this.username = params.username;
}
}
User.prototype.stringify = function () {
return JSON.stringify({ username: this.username});
}
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
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/