I have a function that looks like this:
var tempFun = function() {
return 'something';
}
tempFun.priority = 100;
Now I'm pushing it to an array and binding another object to it in the process like this:
var funArray = [];
var newObj = {};
funArray.push( tempFun.bind(newObj) );
and after this, I would like to acces the function's property like this:
funArray[0].priority
but it returns undefined. Is there some way to preserve the property on the function while binding a new object to it?
No, but you could write a function to do this yourself;
Function.prototype.bindAndCopy = function () {
var ret = this.bind.apply(this, arguments);
for (var x in this) {
if (this.hasOwnProperty(x)) {
ret[x] = this[x];
}
}
return ret;
};
... which you could then use via;
var funArray = [];
var newObj = {};
funArray.push( tempFun.bindAndCopy(newObj) );
No. Bind returns a new function, which "wraps" around the original one. All you can do is copy the properties on this new function:
var boundFun = tempFun.bind(newObj)
boundFun.priority = tempFun.priority;
funArray.push( boundFun );
If you want the properties to be in sync (changes in one visible on the other) you can do:
Object.defineProperty(boundFun, 'priority', {
get : function () { return tempFun.priority; },
set : function (val) { tempFun.priority = val; }
});
From MDN:
The bind() method creates a new function that, when called, has its
this keyword set to the provided value, with a given sequence of
arguments preceding any provided when the new function is called.
Hence, .bind() won't be useful for what you're trying to achieve. Besides using jQuery mappers or rewriting your code to use .prototype, a solution that I can think of is:
var obj = {};
for (var i in tempFun) {
if (tempFun.hasOwnProperty(i)) obj[i] = tempFun[i];
}
Related
I created a class in JavaScript as follows:
class TreeMatching
{
constructor()
{
this.thresholdPoints=0;
this.neighborWeight = 0.4;
this.totalFrequency = 0.0;
this.listSeq = [];
this.listFreq = [];
this.mapScore = new Object();
this.tree = new Trie();
}
createTree()
{
var list_Dictionary;
var loadWordList = $.get("../wordFrequencyTop5000.txt", function(data)
{
list_Dictionary = data.split("\n");
});
loadWordList.done(function()
{
for(var i=0;i<list_Dictionary.length;i++)
{
var string = list_Dictionary[i];
this.tree.insert(string); //<-- Cannot read property 'insert' of undefined
}
});
}
}
which is supposed to call the insert method in class Trie as follows:
class Trie
{
constructor()
{
this.count=1;
this.root = new TrieNode();
}
insert(word)
{
var children = new Object();
for(var i=0; i<word.length(); i++){
var c = word.charAt(i);
var t;
if(children[c]){
t = children[c];
}else{
t = new TrieNode(c);
children.put(c, t);
}
children = t.children;
//set leaf node
if(i==word.length()-1)
t.isLeaf = true;
}
}
}
However, the line of code where the error is marked, the outer function's this value, is not having properties tree, mapScore, etc.
Is there a way that I can access those values from the inner callback function?
Thanks
look at 'this' - you will have to define local variable to maintain reference to "this" inside the call, as described in the link.
createTree()
{
var self = this;
var list_Dictionary;
var loadWordList = $.get("../wordFrequencyTop5000.txt", function(data)
{
list_Dictionary = data.split("\n");
});
loadWordList.done(function()
{
for(var i=0;i<list_Dictionary.length;i++)
{
var string = list_Dictionary[i];
self.tree.insert(string); //<-- Now you should be able to do it
}
});
}
'this' in the inner anonymous has different scope. Try to use the advantage of closer in JS which will get access to the function caller scope.
var that = this;
loadWordList.done(function() {
for(var i=0;i<list_Dictionary.length;i++)
{
var string = list_Dictionary[i];
that.tree.insert(string); // 'that' will hold 'this' in the right scope
}
});
The anonymous function inside loadWordlist.done creates a new scope with an new context.
if you want to keep the old context you can use the ES2015 arrow function:
loadWordList.done(() => {
//code here
);
or make a var inside createTree() like this:
var that = this;
and then inside the loadWordList callback you can refer to the right context using:
that.tree.insert(string);
I personally prefer the arrow function because 'that' is a lousy choice for a var name. And since your using the ES2015 classes browser support must not be an issue.
What I would like is to use _h like $ jQuery. Every time I use _h i want to have new instance of Helper class, which would hold selected element, do stuff with them and so on.
The problem is that _h does not creates new instance of class, but rather use already created one.
I assumed if I use it like that:
var obj1 = _h('p');
var obj2 = _h('#testDiv');
I would have two different instances of class. However both instances store the same elements and seems to point to the same instance.
var _h = (function(){
var Helper = function(query){
if(window === this)
return new Helper(query);
this.Get.call(this, query);
this._allCurrentElements = [];
}
Helper.prototype.Get = function(query){
var els = document.querySelectorAll(query);
for (var i = 0; i < els.length; i++) {
_allCurrentElements.push(els[i])
};
return this;
}
Helper.prototype.AddClass = function(cl) {
// do stuff
return this;
}
Helper.prototype.RemoveClass = function(cl) {
// do stuff
return this;
}
// more methods
//return Helper;
//return Helper();
return new Helper();
})();
So if someone could point me that I am doing wrong, I would really appreciate that.
Edit: If I don't wrap it in IFFE, don't assign to _h var and call var t = Helper('p'), then it behaves as expected
(function(){})() is called an IIFE (Immediately Invoked Function Expression). Which means, you are declaring a anonymous function and invoking it in a single step. The return value of the IIFE is stored in _h.
When you return new Helper();, _h contains an instance of Helper.
When you return Helper, you are returning the constructor of the Helper class. You will have to use new _h() create new Helper object.
What you need is a wrapper function that will take the arguments, create a new Helper object and return it.
Try
var _h = (function(){
var Helper = function(query){
if(window === this)
return new Helper(query);
this.Get.call(this, query);
this._allCurrentElements = [];
}
Helper.prototype.Get = function(query){
var els = document.querySelectorAll(query);
for (var i = 0; i < els.length; i++) {
_allCurrentElements.push(els[i])
};
return this;
}
Helper.prototype.AddClass = function(cl) {
// do stuff
return this;
}
Helper.prototype.RemoveClass = function(cl) {
// do stuff
return this;
}
// more methods
return function (query) {
return new Helper(query);
};
})();
Update 1:
In Helper.prototype.Get, _allCurrentElements is a global variable. You need to use this._allCurrentElements.
Also, you need to initialize this._allCurrentElements = []; before calling this.Get
Working Example - https://jsfiddle.net/ucqgnbne/
Update 2
As #Bergi commented, returning Helper will work since you are using if(window === this) return new Helper(query);.
Can anyone advise me how to create a method call using a string and without using eval? Please note that methodCall cannot be hard-coded and has to be dynamic. methodCall will be created dynamically.
In the following example this refers to a Backbone view, e.g.
var amount = this.model.get('amount');
var $amount = this.$el.find('.amount');
var methodCall = 'this.parentController.parentController.addBinding';
//then need to call the method with args
methodCall('amount',$amount);
I first thought this would work:
this['controller']['parentController']['view']['addBinding'](amount, $amount);
However I came to realise that this would not be dynamic either. Does anyone have a solution?
As noted in this answer, Multiple level attribute retrieval using array notation from a JSON object, you can traverse the hierarchy of objects with something like this:
function findprop(obj, path) {
var args = path.split('.'), i, l;
for (i=0, l=args.length; i<l; i++) {
if (!obj.hasOwnProperty(args[i]))
return;
obj = obj[args[i]];
}
return obj;
}
You could then give your view/model/collection a method to apply an arbitrary path:
var V = Backbone.View.extend({
dyncall: function(path) {
var f = findprop(this, path);
if (_.isFunction(f))
return f.call(this, 'your args');
}
});
var v = new V();
v.dyncall('parentController.parentController.addBinding');
And a demo http://jsfiddle.net/nikoshr/RweEC/
A little more flexibility on passing the arguments :
var V = Backbone.View.extend({
dyncall: function() {
var f = findprop(this, arguments[0]);
if (_.isFunction(f))
f.apply(this, _.rest(arguments, 1));
}
});
var v = new V();
v.dyncall('parentController.parentController.addBinding', 'your arguments', 'another arg');
http://jsfiddle.net/nikoshr/RweEC/1/
In Google Apps JS. I would like to implement an array of objects, each with properties and methods. One of the properties needs to be an array of objects and I would like to be able to access this array by using methods in the parent array.
So far my best efforts is:
function myFunction () {
var teamNo = 3;
var allNames =["n1","n2","n3","n4"] ;
var createnames = function () {
var names = [];
for ( var j = 0; j <=3 ; j ++) {
(function (j) {
var localNames = ["local1-names"+j,"local2-names"+j];
names[j] = (function (player){
return {
namArr: localNames,
name: allNames[j],
addName: (function (player){
localNames.push(player);
}) (player),
team: teamNo
};
});
}) (j);
}
return names;
}
var myname = createnames();
var foo = myname[0]().namArr;
var foo1 = myname[1]().namArr;
myname[1]().addName("added");
var foo2 = myname[1]().namArr;
var foo3 = myname[2]().namArr;
var debug = true;
}
As soo as I add the code to implement the sub array I get a runtime error saying that addName does not exist.
You're invoking this immediately:
addName: (function (player) {
localNames.push(player);
})(player)
instead of assigning it:
addName: function (player) {
localNames.push(player);
}
Also, each names[] function takes a player, and so does the addPlayer() function, making the names[] parameter unreachable. If you're not going to pass anything to the names[] functions, then remove the parameter.
And I'd suggest using named functions instead of inlined IIFEs.
Do JavaScript objects/variables have some sort of unique identifier? Like Ruby has object_id. I don't mean the DOM id attribute, but rather some sort of memory address of some kind.
If you want to lookup/associate an object with a unique identifier without modifying the underlying object, you can use a WeakMap:
// Note that object must be an object or array,
// NOT a primitive value like string, number, etc.
var objIdMap=new WeakMap, objectCount = 0;
function objectId(object){
if (!objIdMap.has(object)) objIdMap.set(object,++objectCount);
return objIdMap.get(object);
}
var o1={}, o2={}, o3={a:1}, o4={a:1};
console.log( objectId(o1) ) // 1
console.log( objectId(o2) ) // 2
console.log( objectId(o1) ) // 1
console.log( objectId(o3) ) // 3
console.log( objectId(o4) ) // 4
console.log( objectId(o3) ) // 3
Using a WeakMap instead of Map ensures that the objects can still be garbage-collected.
No, objects don't have a built in identifier, though you can add one by modifying the object prototype. Here's an example of how you might do that:
(function() {
var id = 0;
function generateId() { return id++; };
Object.prototype.id = function() {
var newId = generateId();
this.id = function() { return newId; };
return newId;
};
})();
That said, in general modifying the object prototype is considered very bad practice. I would instead recommend that you manually assign an id to objects as needed or use a touch function as others have suggested.
Actually, you don't need to modify the object prototype. The following should work to 'obtain' unique ids for any object, efficiently enough.
var __next_objid=1;
function objectId(obj) {
if (obj==null) return null;
if (obj.__obj_id==null) obj.__obj_id=__next_objid++;
return obj.__obj_id;
}
I've just come across this, and thought I'd add my thoughts. As others have suggested, I'd recommend manually adding IDs, but if you really want something close to what you've described, you could use this:
var objectId = (function () {
var allObjects = [];
var f = function(obj) {
if (allObjects.indexOf(obj) === -1) {
allObjects.push(obj);
}
return allObjects.indexOf(obj);
}
f.clear = function() {
allObjects = [];
};
return f;
})();
You can get any object's ID by calling objectId(obj). Then if you want the id to be a property of the object, you can either extend the prototype:
Object.prototype.id = function () {
return objectId(this);
}
or you can manually add an ID to each object by adding a similar function as a method.
The major caveat is that this will prevent the garbage collector from destroying objects when they drop out of scope... they will never drop out of the scope of the allObjects array, so you might find memory leaks are an issue. If your set on using this method, you should do so for debugging purpose only. When needed, you can do objectId.clear() to clear the allObjects and let the GC do its job (but from that point the object ids will all be reset).
const log = console.log;
function* generateId() {
for(let i = 0; ; ++i) {
yield i;
}
}
const idGenerator = generateId();
const ObjectWithId = new Proxy(Object, {
construct(target, args) {
const instance = Reflect.construct(target, args);
instance['id'] = idGenerator.next().value;
return instance;
}
})
const myObject = new ObjectWithId({
name: '##NativeObject'
});
log(myObject.id);