I am creating an attribute in a javascript object by replacing some strings in an existing object, as a side effect I want to make some additional changes to a third property, which I try to access with this.property however in the replace function this is referring to the window instead of my 'master' object. How can I pass in the encapsulating object, so that I can use this to access third property.
b = {
a: 'onetwothree',
count: 0,
rp: function () {
this.c = this.a.replace(/e/g, function (str, evalstr) {
this.count++; // <-- this is refering to window.
return 'e' + this.count
})
}
};
b.rp();
b.c = 'oneNaNtwothreNaNeNaN whereas I want it to be one1twothre2e3
rp: function () {
this.c = this.a.replace(/e/g, function (str, evalstr) {
this.count++;
return 'e' + this.count
}.bind( this )) // <-- bind the function
}
Cache the this context in another variable.
rp: function () {
var self = this; // Cache here
this.c = this.a.replace(/e/g, function(str, evalstr) {
return 'e' + (++self.count); // Use here
});
}
Protip: ++self.count gives the new value after incrementing.
You can usually solve this by making use of the closure you're creating, like this:
b = {
a: 'onetwothree',
count: 0,
rp: function () {
var self = this; // <-- Create a variable to point to this
this.c = this.a.replace(/e/g, function (str, evalstr) {
self.count++; // <-- And use it here
return 'e' + self.count; // <-- And here (also added the ;)
})
}
};
b.rp();
More to explore (disclosure: both are posts on my blog):
You must remember this
Closures are not complicated
Related
var Obj = {
func1 : function() {
// some code
if (this._hasChainedFunc()) {
// block should be CALLED
}
return this;
},
func2 : function() {
// some code
if (this._hasChainedFunc()) {
// block should be NOT called
}
return this;
},
_hasChainedFunc : function() {
// code which detects if there is a chained function???
}
}
Obj.func1().func2();
Is there a possible implementation for function _hasChainedFunc()? This function should return true on the first call (because func2() is called afterwards), false on the second call.
In a more advanced version, _hasChainedFunc() may also returned the function which is actually called afterwards.
Technically you can never know in advance whether there's another call chained after the current call -- this plainly doesn't make sense because it implies you're aware of some code that's gonna be called before it's called. You can't do this without a pre-compiler, which I guess is not what you're after.
Conversely, it is possible to check whether there's been a previous call chained before the current call. This just requires you to keep some state in the object regarding the previous calls, and update it whenever you call a new function on it. If you only use one chain of calls, you can do this by making func1 and func2 change some state on the this object before returning it.
If you want to call multiple chains on the same object, you face the problem of how to detect the end of a chain. For this you will need to make each chained function return a wrapper around the original this, which would store the state about the previous calls.
If you use the wrapper approach, obj.func1().func2() calls func1 on obj, but func2 is called on a wrapper returned from func1 and this wrapper could be aware of the previous func1 call. If you later call obj.func2().func1() then func2 is now called on obj whereas func1 is called on the wrapper which is aware of the previous func2 call, etc.
(NOTE: This answer was originally posted by Scholle as part of the question. I extracted it from the question into an actual answer, as it should have been in the first place. It is not my solution, so I have marked it as Community Wiki.)
Scholle ultimately created a library that does what he wanted.
It's available on GitHub, and some documentation is here.
In short: Take an arbitrary JavaScript function and "chainify" it:
var Model = function() {};
Model.prototype.func1 = function() {
console.log('func1 has ' + this.c_getPredecessors().length + ' preceding functions');
return this.c_delay().c_chain(function() {
console.log('func1 has ' + this.c_getSuccessors().length + ' succeeding functions');
console.log('func1 processing...');
this.c_next();
});
};
Model.prototype.func2 = function() {
console.log('func2 has ' + this.c_getPredecessors().length + ' preceding functions');
return this.c_delay().c_chain(function() {
console.log('func2 has ' + this.c_getSuccessors().length + ' succeeding functions');
console.log('func2 processing...');
this.c_next();
});
};
Chainify and instantiate it,
and call some functions:
chainify(Model);
var Obj = new Model();
Obj.func1().func2();
Console output:
func1 has 0 preceding functions
func2 has 1 preceding functions
func1 has 1 succeeding functions
func1 processing...
func2 has 0 succeeding functions
func2 processing...
Of course, this is a simple example. It just demonstrates that every
functions is now capable to access information about what happens
before and after the current function call.
No, it's not possible.
It's semantically identically to:
var tmp = Obj.func1();
tmp.func2();
When Obj.func1() is called, there's no way for it to know whether the subsequent result will be used to call func2.
The best you could achieve is for func2 to detect whether func1 was previously called, but for it to work the way you've described would require func1 to be capable of predicting the future.
What you can do is add a member property indicating if it's the first call made on the object or not:
var Obj = {
_first : true,
func1 : function() {
// some code
if (this._hasChainedFunc()) {
// block should be CALLED
}
return this;
},
func2 : function() {
// some code
if (this._hasChainedFunc()) {
// block should be NOT called
}
return this;
},
_hasChainedFunc : function() {
var isFirst = this._first;
this._first = false;
return isFirst;
}
}
Obj.func1().func2();
However, this means you have to reset the state of the object before each call (by setting this._first back to true). You may want to rethink how you're going about this.
here's how i would do this:
var Obj = {
first:0, //<--- will store whether it's the first call
func1 : function() {
// some code
if (this._hasChainedFunc()) {
console.log("called1");
}
return this;
},
func2 : function() {
// some code
if (this._hasChainedFunc()) {
console.log("called2");
}
return this;
},
_hasChainedFunc : function() {
return (this.first++ > 0);
}
}
Obj.func1().func2();
and this seems to work:
called2
http://jsfiddle.net/2VThj/1/
Why would you want to do this?
That question aside, you could, rather than returning the actual object, make a clone of it, and add an attribute to tell you it is a returned version of the object. That is the only way I can think of. Sounds complex though, depending on how complex this object is.
Something like:
func1 : function() {
// some code
if (this._hasChainedFunc()) {
// block should be CALLED
}
return deepCloneWithFlag(this);
},
_hasChainedFunc : function() {
return this.flag;
}
Nope. this won't work. you could possibly tell that func1() had at some point been called on this object, but you cannot tell WHEN it was called, i.e. right before func2
for example this:
obj.func1();
obj.func2();
is equivalent to your example call. And there is no way func1 could know that func2 will be called in the future.
I solved a problem similar to this with chain functions (docs) This allows true function chaining with the ability to "look-ahead" to see what's coming in the chain.
What you could do is have two separate classes, one for the first element in the chain and one for the remaining elements. Then all you would have to do is change the first class to return an equivalent object from the second class instead of the current object.
var Class1 = function(state){
return {
func1 : function() {
// some code
// block should be CALLED
return Class2(state)
},
func2 : function() {
// some code
// block should be NOT called
return Class2(state)
}
};
}
var Class2 = function(state){
return {
func1 : function() {
// some code
return this;
},
func2 : function() {
// some code
return this;
}
};
}
Class1(initial_state).func1().func2();
Althought knowing that a function will be called after another function is impossible in Javascript, here is a solution to chainify an object :
(function(window, undefined)
{
var chainify = function(prop)
{
return new chainify.init(prop);
};
/**
*
* #param prop :
* Properties to apply to the object
* #returns {chainify.init}
*/
chainify.init = function(prop)
{
for ( var key in prop)
this[key] = prop[key];
};
chainify.init.prototype = {
_attributes : {},
_chain_in_progress : false,
_chain_level : 1,
_chain_function : '',
/**
* Returns the chained object
*
* #param name -
* name of the previous function
* #this chainify.init
* #returns {chainify.init}
*/
_chain : function(name)
{
var tmp = chainify(this);
tmp._chain_in_progress = true;
tmp._chain_function = name || '';
_chain_level++;
return tmp;
},
get : function(key)
{
return this._attributes[key];
},
set : function(key, value)
{
this._attributes[key] = value;
return this;
},
attr : function(prop)
{
for ( var key in prop)
this._attributes[key] = prop[key];
return this;
},
};
// Make global
window.chainify = chainify;
})(window);
var myObject = window.chainify({
// f1() function is using _chain()
f1 : function(s)
{
// Do something
this.set('s1', s);
if (this._chain_in_progress) alert('f1 after ' + this._chain_function);
// return the chain by calling this._chain()
return this._chain('f1');
},
// f2() function is using _chain()
f2 : function(s)
{
this.set('s2', s);
if (this._chain_in_progress) alert('f2 after ' + this._chain_function);
return this._chain('f1');
},
// that() function is not using _chain(), but we return this so the chaining
// is not broken
that : function(s)
{
// Do something
return this;
}
});
// Check if the f1 function is working
myObject.f1('a'); // Set s1 to "a"
alert(myObject.get('s1')); // should be "a"
// check if the f2 chaining is working
myObject.f1('b').f1('c'); // f1 after f1
alert(myObject.get('s1')); // should be "c" -> changed on last f1 function
// Check if the f2 function is working
myObject.f2('a');
alert(myObject.get('s2')); // should be "a"
// check if the f2 and f1 chaining is working
myObject.f2('b').f1('c').f1('d').f2('e'); // f1 after f2, f1 after f1 ...
alert(myObject.get('s1')); // should be "d" -> changed on last f1 function
alert(myObject.get('s2')); // should be "e" -> changed last f2 function
// check the chain with that() -
myObject.that('b').f1('a').f1('z'); // f1 chained after f1
alert(myObject.get('s1')); // should be "z" -> changed on last f1 function
I want a main object M containing a sub-object S which has some method E which has a private variable P. I also want the method E to have access to M via another variable V. For the private variables I'm doing this:
M.S = function () {
var P,
V; // how to set V to M?
return {
E: function () {
// stuff goes here
}
}
}();
One solution I came up with was to remove the () at the last line, and then calling the anonymous S-creating function as a method of M. this solves the problem, but I'm thinking there might be a more elegant way to go about it.
M.S = function () {
var P,
V = this;
return {
E: function () {
// stuff goes here
}
}
};
M.S = M.S()
Mostly I need to know what is good practice for this, since I'm new to private variables in Javascript.
A pretty straightforward method to do this is:
M.S = function (V) { // <-- V is declared locally
var P;
return {
E: function () {
// stuff goes here
}
};
}(M);
V is locally declared through the formal parameter. M's reference is assigned to V, through function(V){...}(M);.
Even when M is redeclared at a later point, V will still point to the right object.
What about this? You invoke S in context of M:
M.S = function () {
var P,
V = this; // how to set V to M?
return {
E: function () {
// stuff goes here
// you can refer M via V reference
}
}
}.call(M);
I'm fairly certain this isn't possible, but wanted to see if anyone had some ingenious ideas as to how to make it possible.
I want the following code to work:
var x = new foo();
x.a.getThis() === x; // true
In other words, I want x.a.getThis to have a reference to this being x in this case. Make sense?
In order to get this to work one level deep is simple:
function foo(){}
foo.prototype.getThis = function(){ return this; }
var x = new foo();
x.getThis() === x; // true
One thing, I want this to work as a prototype, no "cheating" by manually binding to this:
function foo(){
this.a = {
getThis : (function(){ return this; }).bind(this)
};
}
Although the above is a perfect functional example of what I'm trying to achieve, I just don't want all the extra functions for each instance :)
FYI, the actual use case here is that I'm creating classes to represent Cassandra objects in node and I want to be able to reference a super-column --> column-family --> column via foo.a.b and keep a reference to foo in the deep function.
You can't do this without a forced bind of some kind. You say you don't want to "cheat" but this breaks the standard rules about what this is, so you have to cheat. But JS lets you cheat, so it's all good.
BTW, for what it's worth coffee script makes this so trivial.
foo = ->
#a = getThis: => this
The fat arrow => preserves the context of this for from the scope it was called in. This allows you to easily forward the context to another level.
That code gets compiled to this JS:
var foo;
var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
foo = function() {
return this.a = {
getThis: __bind(function() {
return this;
}, this)
};
};
Which basically just does what you say you do not want to do.
Or if the value doesn't have to this specifically, you can set the "owner" in the child object.
var A = function(owner) {
this.owner = owner;
};
A.prototype.getThis = function() {
return this.owner;
};
var Foo = function() {
this.a = new A(this);
};
var foo = new Foo();
if (foo.a.getThis() === foo) {
alert('Happy dance');
} else {
window.location = 'https://commons.lbl.gov/download/attachments/73468687/sadpanda.png';
}
http://jsfiddle.net/4GQPa/
And the coffee script version of that because I am a passionate and unreasonable zealot for it:
class A
constructor: (#owner) ->
getThis: -> #owner
class Foo
constructor: -> #a = new A(this)
foo = new Foo()
if foo.a.getThis() is foo
alert 'Happy Dance'
else
window.location = 'https://commons.lbl.gov/download/attachments/73468687/sadpanda.png'
Impossible to do reliably without binding the value at the start since the value of a function's this is set by the call. You can't know beforehand how it will be called, or which functions need a special or restricted call to "preserve" the this -> this relationship.
The function or caller's this may be any object, there may not be a this -> this at all. Consider:
var x = {
a : {
b: function() {return this;}
}
}
When you call x.a.b(), then b's this is a. But if you do:
var c = x.a.b;
c(); // *this* is the global object
or
x.a.b.call(someOtherObject);
What is the value of this -> this in these cases?
Answering my own question because someone else may find it useful. Not sure if I'll end up going with this or Squeegy's solution. The functions are only ever defined once and then the containing object is cloned and has parent = this injected into it:
function foo(){
var self = this, nest = this.__nestedObjects__ || [];
nest.forEach(function(prop){
self[prop] = extend({ parent : self }, self[prop]);
});
}
// bound like this so that they're immutable
Object.defineProperties(foo.prototype, {
bar : {
enumerable : true,
value : {
foobar : function(){
return this.parent;
},
foo : function(){},
bar : function(){}
}
},
__nestedObjects__ : { value : ['bar'] }
});
var fooInst = new foo();
console.log(fooInst.bar.foobar() == fooInst);
or based on Squeegy's solution:
function foo(){
for(var cls in this.__inherit__){
if(!this.__inherit__.hasOwnProperty(cls)){ continue; }
this[cls] = new (this.__inherit__[cls])(this);
}
}
var clsA;
// bound like this so that they're immutable
Object.defineProperties(foo.prototype, {
__inherit__ : { value : {
bar : clsA = function(parent){
Object.defineProperty(this, '__parent__', { value : parent });
}
}
}
});
clsA.prototype = {
foobar : function(){
return this.__parent__;
}
};
var fooInst = new foo();
console.log(fooInst.bar.foobar() == fooInst);
I am having a JavaScript namespace say
A={
CA: function() {
this.B();
},
B: function() {
var test='test';
var result='t1';
C: function() {
this.test='test1';
.....
.....
return 'test1';
}
result=this.C();
return result;
}
}
Now when I am executing such code it is giving that TypeError: this.C is not a function. Can somebody tell me why it is so. I know it is something related with lexical scoping but am unable to understand this.
You have to be careful when you use this to identify anything in Javascript because each time you change scope "this" changes.
Assigning the 'this' reference to it's own variable helps get around this.
var a = new function() {
var self = this;
self.method = function() { alert('hiya'); };
var b = function() {
this.method(); // this isn't 'a' anymore?
self.method(); // but 'self' is still referring to 'a'
};
};
I think the problem is that when this.C() is executed inside the function referred to by B, this refers to the object that contains B, that is, object A. (This assumes B() is called within the context of A)
The problem is, C does not exist on the object A, since it's defined within B. If you want to call a local function C() within B, just use C().
EDIT:
Also, I'm not sure what you've posted is valid JavaScript. Specifically, B should be defined this way, since you can't use the object:property syntax within a function.
B: function()
{
var test='test';
var result='t1';
var C = function()
{
this.test='test1';
return 'test1';
}
result=C();
return result;
}
I am actually surprised that your code doesn't give you error on the 'C:' line.
Anyway, your syntax to define a function is not correct. Define it using the var keyword. Also, notice that I created the 'closure' so that the function C can access 'this'. See the code below:
A={
CA: function()
{
this.B();
},
B: function()
{
var test='test';
var result='t1';
var self = this;
var C = function()
{
self.test='test1';
.....
.....
return 'test1';
}
result=C();
return result;
}
}
If you want to assign C to 'this' object, you can also do:
A={
CA: function()
{
this.B();
},
B: function()
{
var test='test';
var result='t1';
var self = this;
this.C = function()
{
self.test='test1';
.....
.....
return 'test1';
};
result= this.C();
return result;
}
}
Solution for calling methods from another method. (Essentially the pointer "this" must be assigned to a variable and the new variable used in place of this.)
function myfunction(){
var me = this;
me.method1 = function(msg){ alert(msg); }
me.method2 = function(){
me.method1("method1 called from method2");
}
}
var f as new myfunction();
f.method2();
This example shows how one can call a method from within another method or from outside using an instance of the function.
If you want to use global functions and variable dynamically you can use:
window[functionName](window[varName]);
Is it possible to do the same thing for variables in the local scope?
This code works correctly but currently uses eval and I'm trying to think of how else to do it.
var test = function(){
//this = window
var a, b, c; //private variables
var prop = function(name, def){
//this = window
eval(name+ ' = ' + (def.toSource() || undefined) + ';');
return function(value){
//this = test object
if ( !value) {
return eval('(' + name + ')');
}
eval(name + ' = value;')
return this;
};
};
return {
a:prop('a', 1),
b:prop('b', 2),
c:prop('c', 3),
d:function(){
//to show that they are accessible via to methods
return [a,b,c];
}
};
}();
>>>test
Object
>>>test.prop
undefined
>>>test.a
function()
>>>test.a()
1 //returns the default
>>>test.a(123)
Object //returns the object
>>>test.a()
123 //returns the changed private variable
>>>test.d()
[123,2,3]
To answer your question, no, there is no way to do dynamic variable lookup in a local scope without using eval().
The best alternative is to make your "scope" just a regular object [literal] (ie, "{}"), and stick your data in there.
No, like crescentfresh said. Below you find an example of how to implement without eval, but with an internal private object.
var test = function () {
var prv={ };
function prop(name, def) {
prv[name] = def;
return function(value) {
// if (!value) is true for 'undefined', 'null', '0', NaN, '' (empty string) and false.
// I assume you wanted undefined. If you also want null add: || value===null
// Another way is to check arguments.length to get how many parameters was
// given to this function when it was called.
if (typeof value === "undefined"){
//check if hasOwnProperty so you don't unexpected results from
//the objects prototype.
return Object.prototype.hasOwnProperty.call(prv,name) ? prv[name] : undefined;
}
prv[name]=value;
return this;
}
};
return pub = {
a:prop('a', 1),
b:prop('b', 2),
c:prop('c', 3),
d:function(){
//to show that they are accessible via two methods
//This is a case where 'with' could be used since it only reads from the object.
return [prv.a,prv.b,prv.c];
}
};
}();
I think you actually sort of can, even without using eval!
I might be wrong so please correct me if I am, but I found that if the private variables are declared inside the local scope as arguments, instead of using var, i.e:
function (a, b, c) { ...
instead of
function () { var a, b, c; ...
it means that those variables/arguments, will be bound together with the function's arguments object if any values are given to them in the function's invocation, i.e:
function foo (bar) {
arguments[0] = 'changed...';
console.log(bar); // prints 'changed...'
bar = '...yet again!';
console.log(arguments[0]); // prints '..yet again!'
}
foo('unchanged'); // it works (the bound is created)
// logs 'changed...'
// logs '...yet again!'
foo(undefined); // it works (the bound is created)
// logs 'changed...'
// logs '...yet again!'
foo(); // it doesn't work if you invoke the function without the 'bar' argument
// logs undefined
// logs 'changed...'
In those situations (where it works), if you somehow store/save the invoked function's arguments object, you can then change any argument related slot from arguments object and the changes will automatically be reflected in the variables themselves, i.e:
// using your code as an example, but changing it so it applies this principle
var test = function (a, b, c) {
//this = window
var args = arguments, // preserving arguments as args, so we can access it inside prop
prop = function (i, def) {
//this = window
// I've removed .toSource because I couldn't apply it on my tests
//eval(name+ ' = ' + (def.toSource() || undefined) + ';');
args[i] = def || undefined;
return function (value) {
//this = test object
if (!value) {
//return eval('(' + name + ')');
return args[i];
}
//eval(name + ' = value;');
args[i] = value;
return this;
};
};
return {
a: prop(0, 1),
b: prop(1, 2),
c: prop(2, 3),
d: function () {
// to show that they are accessible via to methods
return [a, b, c];
}
};
}(0, 0, 0);
If the fact that you can pass the values as arguments into the function annoys you, you can always wrap it with another anonymous function, that way you really don't have any access to the first defined values passed as arguments, i.e:
var test = (function () {
// wrapping the function with another anomymous one
return (function (a, b, c) {
var args = arguments,
prop = function (i, def) {
args[i] = def || undefined;
return function (value) {
if (!value) {
return args[i];
}
args[i] = value;
return this;
};
};
return {
a: prop(0, 1),
b: prop(1, 2),
c: prop(2, 3),
d: function () {
return [a, b, c];
}
};
})(0, 0, 0);
})();
Full Dynamic Access Example
We can map all argument variable names into an array by getting the function itself (arguments.callee) as a string, and filtering its parameters using a regex:
var argsIdx = (arguments.callee + '').replace(/function(\s|\t)*?\((.*?)\)(.|\n)*/, '$2').replace(/(\s|\t)+/g, '').split(',')
Now with all the variables in an array, we can now know the corresponding variable name for each function's arguments slot index, and with that, declare a function (in our case it's prop) to read/write into the variable:
function prop (name, value) {
var i = argsIdx.indexOf(name);
if (i === -1) throw name + ' is not a local.';
if (arguments.hasOwnProperty(1)) args[i] = value;
return args[i];
}
We can also dynamically add each variable as a property, like in the question's example:
argsIdx.forEach(function (name, i) {
result[name] = prop.bind(null, name);
});
Finally we can add a method to retrieve variables by name (all by default), and if true is passed as the first argument, it returns the hard-coded array with all the variables by their identifiers, to prove that they are being changed:
function props (flgIdent) {
var names = [].slice.call(arguments.length > 0 ? arguments : argsIdx);
return flgIdent === true ? [a, b, c, d, e, f] : names.map(function (name) {
return args[argsIdx.indexOf(name)];
});
}
The prop and props functions can be made available as methods inside the returned object, in the end it could look something like this:
var test = (function () {
return (function (a, b, c, d, e, f) {
var argsIdx = (arguments.callee + '').replace(/function(\s|\t)*?\((.*?)\)(.|\n)*/, '$2').replace(/(\s|\t)+/g, '').split(','),
args = arguments,
result = {
prop: function (name, value) {
var i = argsIdx.indexOf(name);
if (i === -1) throw name + ' is not a local.';
if (arguments.hasOwnProperty(1)) args[i] = value;
return args[i];
},
props: function (flgIdent) {
var names = [].slice.call(arguments.length > 0 ? arguments : argsIdx);
return flgIdent === true ? [a, b, c, d, e, f] : names.map(function (name) {
return args[argsIdx.indexOf(name)];
});
}
};
args.length = argsIdx.length;
argsIdx.forEach(function (name, i) {
result[name] = result.prop.bind(null, name);
});
return result;
})(0, 0, 0, 0, 0, 0);
})();
Conclusions
It's impossible to read/write a function's local scope variable without eval, but if those variables are function's arguments and if they're given values, you can bound those variable identifiers to the function's arguments object and indirectly read/write into them from the arguments object itself.
Hopefully I'm not over-simplifying, but what about something as simple as using an object?
var test = {
getValue : function(localName){
return this[localName];
},
setValue : function(localName, value){
return this[localName] = value;
}
};
>>> test.a = 123
>>> test.getValue('a')
123
>>> test.a
123
>>> test.setValue('b', 999)
999
>>> test.b
999