Inject variable into function's scope - javascript

So, I want to do something like this:
var a = 'a';
var dummy = function() {
// Print out var 'a', from the scope above
console.log('Dummy a: ' + a);
// Print out 'b', from the 'compelled' scope
console.log('Dummy b: ' + b);
}
(function() {
var b = 'otherscope';
// I know apply won't work, I also don't want to merge scopes
dummy.apply(this);
// I want something like this:
dummy.compel(this, [], {b: 'injected!'});
})();
But that won't work.
I don't actually want a function to be able to reach 2 scopes, I DO want to be able to set the 'b' variable used inside the dummy function from the outside.

You can make b a parameter for the function, or a global variable.
var a = 'a';
var dummy = function(b) {
...
}
or
var a = 'a';
var b;
var dummy = function() {
...
}
The first allows you to choose when the dummy function has access to the variable, the second allows it to be accessed everywhere.

So, I found a little faster way to do such a thing:
var C = function(ctx, funcBody){
var newBody = [];
for(var k in ctx){
var i = "var "+k + " = ctx['"+k+"'];";
newBody.push(i);
}
var res = "return function(t){ " +funcBody+ " }";
newBody.push(res);
var F = new Function("ctx", newBody.join('\n'));
return F(ctx);
}
var newFunction = C({"foo":10, "bar":100}, "return foo+bar*t")
newFunction(50);

Use this:
Function.prototype.applyVars = function(scope, params, scope_variables) {
if (scope_variables) {
var variable, defVars = [];
for (variable in scope_variables) {
if (scope_variables.hasOwnProperty(variable)) {
defVars.push(variable + '=scope_variables["' + variable + '"]');
}
}
eval('var ' + defVars.join(',') + ';');
return eval('(' + this + ').apply(scope, params);');
}
return this.apply(scope, params);
}
// Example
function foo(p1) {
document.write('Variable [p1]: ', p1);
document.write('<br />');
document.write('Variable [x]: ', x);
document.write('<br />');
document.write('Variable [y]: ', y);
}
foo.applyVars(this, ['param X'], { x: "1'2\"3", y: false });
Or this:
function callWithVars(fn, scope, params, scope_variables) {
if (scope_variables) {
var variable, defVars = [];
for (variable in scope_variables) {
if (scope_variables.hasOwnProperty(variable)) {
defVars.push(variable + '=scope_variables["' + variable + '"]');
}
}
eval('var ' + defVars.join(',') + ';');
return eval('(' + fn + ').apply(scope, params);');
}
return fn.apply(scope, params);
}
// Example
function foo(p1) {
document.write('Variable [p1]: ', p1);
document.write('<br />');
document.write('Variable [x]: ', x);
document.write('<br />');
document.write('Variable [y]: ', y);
}
callWithVars(foo, this, ['param X'], { x: "1'2\"3", y: false });

the #vasiliy's solution extended to inject any function
function injectScope(ctx, func) {
var newBody = [];
for (var k in ctx) {
var i = " var " + k + " = ctx['" + k + "'];";
newBody.push(i);
}
let res = (func + '').replace(/^(.*?)\)\s*{(.*)$/gms, `return(\n$1){\n${newBody.join('\n')}\n$2)`);
// console.log(res)
let F = new Function("ctx", res);
return F(ctx);
}
function sum(t) {
return foo + bar * t
}
var injectedSum = injectScope({ "foo": 10, "bar": 100 }, sum)
// console.log(injectedSum+'')
console.log(injectedSum(50));

Related

How can I create a function which treats a passed parameter as a literal?

I am sure that this has been asked and answered before, but I can't seem to find the right terminology to find an answer. I need to dynamically create a series of functions for later use which use certain values defined by parameters upon creation. For example:
var i = "bar";
var addBar = function(x) {
// needs to always return x + " " + "bar"
return x + " " + i;
}
i = "baz";
var addBaz = function(x) {
// needs to always return x + " " + "baz"
return x + " " + i;
}
alert(addBar("foo")); // returns "foo baz" because i = "baz"
Is there a way I can pass i to these functions so that the original value is used, and not the reference to the variable? Thank you!
You would have to do something that stores the variable. Making a function that returns a function is one way to do it.
var i = "bar";
var addBar = (function (i) {
return function(x) {
return x + " " + i;
}
}(i));
i = "baz";
var addBaz = (function (i) {
return function(x) {
return x + " " + i;
}
}(i));
console.log(addBar("foo"));
console.log(addBaz("foo"));

Cannot read property of undefined of function inside loop

I have the "error: Cannot read property 'shorten' of undefined" error when running my test. I want my loop to run the shorten function to check if the string is longer then 20 characters, and if so limit it to that.
function ListView(list) {
this.list = list;
this.converted;
}
ListView.prototype.convert = function() {
var output = [];
this.list.notelist.forEach(function(element) {
this.shorten(element);
output += "<li><div>" + element.text + "</div></li>";
});
this.converted = "<ul>" + output + "</ul>";
};
ListView.prototype.shorten = function(string) {
if (string.length > 20) {
return string.substring(0, 20);
}
return string;
};
list is from another constructor but i mocked it with;
var mockList = { notelist: [{ text: "hello" }, { text: "goodbye" }] };
There are serveral problems with your code:
You encountered the what is this problem which is a very common problem with beginners, have a look at this link. In the anonymous function body of function (element) { .. it doesn't obtain the context of your custom type, thus this is a reference to your browser window.
Your shorten method is called with a different usage in its semantics. You did not take what it returns, but element is not modified at all with the method.
So, let's try to correct the code for what you attempt to do:
<script>
function ListView(list) {
this.list = list;
this.converted;
}
ListView.prototype.convert = function () {
var output = [];
var that = this;
this.list.notelist.forEach(function (element) {
that.shorten(element);
output += "<li><div>" + element.text + "</div></li>";
});
this.converted = "<ul>" + output + "</ul>";
};
ListView.prototype.shorten = function (el) {
var string = el.text;
if (string.length > 20) {
el.text = string.substring(0, 20);
}
};
var mockList = { notelist: [{ text: "hello" }, { text: "goodbye0123456789012345" }] };
var listview1 = new ListView(mockList);
listview1.convert();
alert(listview1.converted);
</script>
goodbye0123456789012345 is modified intentionally for the test, in the result it will be shorten as goodbye0123456789012.
You lost the this binding in the forEach
Try:
ListView.prototype.convert = function() {
var output = [];
this.list.notelist.forEach(function(element) {
this.shorten(element);
output += "<li><div>" + element.text + "</div></li>";
}.bind(this));
this.converted = "<ul>" + output + "</ul>";
};
or
this.list.notelist.forEach((element) => {
this.shorten(element);
output += "<li><div>" + element.text + "</div></li>";
});
Similar to: Passing scope to forEach
Try by binding this
this.list.notelist.forEach(function(element) {
this.shorten(element);
output += "<li><div>" + element.text + "</div></li>";
}.bind(this));
You can use an ES6 arrow function in this case to ensure that this is maintained within your function.
this.list.notelist.forEach((element) => {
this.shorten(element);
output += "<li><div>" + element.text + "</div></li>";
});
Remember to check the availability of arrow functions on browsers. If you want a more widely supported solution use the bind function like others have mentioned.
forEach accepts a second argument which is the thisArg, by default it is undefined.
Try this:
this.list.notelist.forEach(function(element) {
this.shorten(element);
output += "<li><div>" + element.text + "</div></li>";
}, this); // <-- note the `this` argument passed here

javascript print nested object using for loop?

how do i print out all the person in the person object?
For Example i want my output to be like this.
John Doe 25
Paul Vosper 23
var txt = "";
var person = {
p1: {fname:"John", lname:"Doe", age:25},
p2: {fname:"Paul", lname:"Vosper", age:23}
};
var x;
for (x in person)
{
txt += person[x] + " ";
}
document.getElementById("demo").innerHTML = txt;
You can do a map/join:
var txt = Object.keys(person).map(function(k) {
var p = person[k];
return [p.fname, p.lname, p.age].join(' ');
}).join(' ');
Output in the console:
If you want a line break element (<br>) between them, just join on a <br>:
document.getElementById("demo").innerHTML = Object.keys(person)
.map(combineAllProperties)
.join('<br>');
function combineAllProperties(k) {
var p = person[k];
return [p.fname, p.lname, p.age].join(' ');
}
You can use Array.prototype.reduce in conjunction with Object.keys:
var person = {
p1: {fname:"John", lname:"Doe", age:25},
p2: {fname:"Paul", lname:"Vosper", age:23}
};
document.write(Object.keys(person).reduce(function(s, p, i) {
var o = person[p];
return s + (i>0?'<br>':'') + o.fname + ' ' + o.lname + ' ' + o.age
}, '')
);

Javascript OOP - inheritance, prototyping, callback function

I'm trying to use OOP in Javascript with inheritance, prototyping and callback functions. Would you please have a look at my JSfiddel http://jsfiddle.net/Charissima/5g6GV/.
The first problem is solved already in Javascript OOP - inheritance and prototyping, but unfortunately the callback functions don't work any more.
function Car () {
this.totalDistance = 0;
};
Car.prototype.putTotalDistance = function(distance) {
this.totalDistance = distance;
};
Car.prototype.getTotalDistance = function() {
return this.totalDistance;
};
Car.prototype.drive = function(distance) {
this.totalDistance += distance;
return this.totalDistance;
};
function RaceCar () {};
RaceCar.prototype = new Car();
RaceCar.prototype.parent = Car.prototype;
RaceCar.prototype.drive = function(distance) {
return this.parent.drive.call(this, (distance * 2));
};
var myText;
car = new Car;
raceCar = new RaceCar;
car.putTotalDistance(200);
myText = 'car totalDistance = ' + car.drive(10) + ' - ok<br>';
raceCar.putTotalDistance(200);
myText += 'raceCar totalDistance before drive = ' + raceCar.getTotalDistance() + ' - ok<br>';
myText += 'raceCar totalDistance after drive = ' + raceCar.drive(10) + ' - ok<br><br>';
car.putTotalDistance(0);
raceCar.putTotalDistance(100);
var drivingFunctions = [car.drive, raceCar.drive];
myText += drivingFunctions[0](10) + '<br>';
try {
myText += drivingFunctions[1](100) + '<br>';
}
catch(err) {
myText += err + + '<br>'
}
document.body.innerHTML = myText;
You've put the two functions in an array, so when called, this get changed.
You could use function bind :
var drivingFunctions = [car.drive.bind(car), raceCar.drive.bind(raceCar)];
Here is an example to help you understand:
function Man(name){
this.name = name;
this.getName = function(){
return this.name;
};
}
var man = new Man('toto');
var a = [man.getName];
console.log(a[0]());//undefined
a.name = 'titi';
console.log(a[0]());//titi, because this refers to the array.

How to Use Inheritance in JavaScript with Constructor Methods Returning Object Literals with Private Properties?

var Animal = function(config) {
config = config || {};
var name = config.name,
numLegs = config.numLegs,
weight = config.weight,
speed = config.speed,
sound = config.sound
return {
getName: function () {
return name;
},
getNumLegs: function () {
return numLegs;
},
getWeight: function () {
return weight;
},
getSpeed: function () {
return speed;
},
getSound: function () {
return sound;
},
run: function(distance, unit) {
unit = unit || 'miles';
return 'The ' + name + ' ran ' + distance + ' ' + unit;
},
speak: function() {
return 'The ' + name + ' says "' + sound + '"';
}
}
};
function DragonFly(config) {
var me = {},
numWings = config.numWings;
me.prototype = new Animal(config);
me.getNumWings = function() {
return numWings;
};
me.fly = function(distance, unit) {
unit = unit || 'miles';
return 'The ' + me.name + ' flew ' + distance + ' ' + unit;
}
return me;
}
var dragonFly = new DragonFly({
numWings: 2,
name: 'DragonFly',
numLegs: 6
});
Okay, coming from a PHP background, I don't understand inheritance in JavaScript one bit and I'd like some help.
Basically, here's what I'd like to be able to do with an instance of the dragonFly object:
dragonFly.getName(); // 'DragonFly'
dragonFly.fly(1, 'mile'); // 'The dragonfly flew 1 mile';
dragonFly.run(1, 'yard'); // 'The dragonfly ran 1 yard';
I'd also like to know how to override methods and call the parent of those overridden methods. What is wrong with my approach? All the examples above return undefined or throw an error. The main reason I went with the object-literal style is so I could make properties private.
the "fastest" way :
var Animal = function(config) {
config = config || {};
var name = config.name,
numLegs = config.numLegs,
weight = config.weight,
speed = config.speed,
sound = config.sound
return {
getName: function () {
return name;
},
getNumLegs: function () {
return numLegs;
},
getWeight: function () {
return weight;
},
getSpeed: function () {
return speed;
},
getSound: function () {
return sound;
},
run: function(distance, unit) {
unit = unit || 'miles';
return 'The ' + name + ' ran ' + distance + ' ' + unit;
},
speak: function() {
return 'The ' + name + ' says "' + sound + '"';
}
}
};
function DragonFly(config) {
var me = new Animal(config);
var numWings = config.numWings;
me.getNumWings = function() {
return numWings;
};
me.fly = function(distance, unit) {
unit = unit || 'miles';
return 'The ' + me.name + ' flew ' + distance + ' ' + unit;
}
return me;
}
var dragonFly = new DragonFly({
numWings: 2,
name: 'DragonFly',
numLegs: 6
});
You are mixing 2 kind of "inheritance" in your script , the "classical" inheritance and the prototypal inheritance , you cant do that unless you want to be in serious trouble. both work , both have their pros and cons. Stick to the "classical" inheritance , or object augmentation since you began with it.
An object literal doesnt have a prototype , functions have prototypes. That's why in my opinion js isnt "really" object oriented , but it can mimic object oriented langages
A good exercice now would be to try using functions and prototypes , though i'm not sure you could create private fields with that.
Edit : the me.name should be me.getName() since name is "private". i think.

Categories