In the code below I would like to pass a reference to a function that resides on the parent scope to the nested scope of the function "nested", so I can call the function on the parent scope from the nested function. I tried passing it in as a parameter but it doesn't work. I'm just learning/messing around with nested closures and wondering if this could be done.
I would like to have the syntax for calling nested be: callme.nested()
var obj = function(val){
var access = val;
var apex = 0;
return {
callme : (function(siblyng){
var privatevar = 2;
return {
nested : function(){
privatevar++;
apex = privatevar;
return access + " " + privatevar + " " + siblyng("child");
}
}
})(this.sibling),
assess : function(){
return apex + " " + this.sibling("parent");
},
sibling : function(val){
return "returned from " + val + " scope";
}
}
}
var objref = obj(true);
console.log(objref.callme.nested());
console.log(objref.callme.nested());
console.log(objref.callme.nested());
console.log(objref.assess());
console.log(objref.sibling('global'));
If I understood you well, you can do it like so
var obj = function(val){
var access = val;
var apex = 0;
var ret;
return (ret = {
callme : function() {
var privatevar = 2;
return {
nested : function(){
privatevar++;
apex = privatevar;
return access + " " + privatevar + " " + ret.sibling("child");
}
};
}(),
assess : function(){
return apex + " " + this.sibling("parent");
},
sibling : function(val){
return "returned from " + val + " scope";
}
});
};
var objref = obj(true);
console.log(objref.callme.nested());
console.log(objref.callme.nested());
console.log(objref.callme.nested());
console.log(objref.assess());
console.log(objref.sibling('global'));
Your this in the following code was pointing to the global Window object and so it was not able to find the method. You could have directly called this.sibling in your nested method without the need of passing it.
callme : (function(siblyng){
var privatevar = 2;
return {
nested : function(){
privatevar++;
apex = privatevar;
return access + " " + privatevar + " " + siblyng("child");
}
}
})(this.sibling),
Related
I created this function to replace value in a string and call functions whose name is the value.
$(function() {
var str = "Homer drank {{countBeers}} beers";
function countBeers() {
console.log(this);
return 10 + 10;
}
function convert(str) {
console.log(this);
str = "'" + str.replace(/{{(.*?)}}/g, "' + $1.call(this) + '") + "'";
OR
str = "'" + str.replace(/{{(.*?)}}/g, "' + $1(this) + '") + "'";
return eval(str);
}
var output = convert.call(this, str);
$('body').append(output); //Homer drank 20 beers
});
This seems to work fine, but I would not use eval.
You can do it any other way?
Thank you
You could use an object with the function.
function convert(str) {
return str.replace(/{{(.*?)}}/g, function (_, f) {
return op[f] ? op[f]() : f;
});
}
var str = "Homer drank {{countBeers}} beers",
op = { countBeers: function () { return 10 + 10; } };
console.log(convert(str));
Why the undefined gets outputted instead of the object properties.
Created a function, defined setters for the parameters and function to output the string consisting of the parameters.
Below is the snippet for the app.js file.
// app.js
function Fruit(theColor, sweetness, theName, theOrigin) {
//properties
this.theColor = theColor;
this.sweetness = sweetness;
this.theName = theName;
this.theOrigin = theOrigin;
//functions
this.showName = function () {
console.log("This is a " + this.theName);
};
this.showLand = function () {
this.theOrigin.forEach(function (arg) {
console.log("Grown in: " + arg);
});
};
}
var mango = new Fruit("Yellow", 9, "Mango", ["India", "Central America"]);
console.log(mango.showName() + " " + mango.showLand());
This line:
console.log(mango.showName() + " " + mango.showLand());
calls those functions, then outputs their return values with a space between them. Neither showNames nor showLand returns anything, and so calling them gives you the result undefined.
If you just want to call those, just call them, without using console.log to output their result. E.g., replace:
console.log(mango.showName() + " " + mango.showLand());
with
mango.showName();
mango.showLand();
If you want them to return, rather than display, their result, edit them to do so. You'll have to decide how you want showLand to separate lines (e.g., with a \n, or by returning an array, etc.).
For instance, this showName will return a string, and showLand will return an array:
//functions
this.showName = function () {
return "This is a " + this.theName;
};
this.showLand = function () {
return this.theOrigin.map(function (arg) {
return "Grown in: " + arg;
});
};
which you could then call like this:
console.log(mango.showName() + ". " + mango.showLand().join(", "));
Live Example:
function Fruit(theColor, sweetness, theName, theOrigin) {
//properties
this.theColor = theColor;
this.sweetness = sweetness;
this.theName = theName;
this.theOrigin = theOrigin;
//functions
this.showName = function () {
return "This is a " + this.theName;
};
this.showLand = function () {
return this.theOrigin.map(function (arg) {
return "Grown in: " + arg;
});
};
}
var mango = new Fruit("Yellow", 9, "Mango", ["India", "Central America"]);
console.log(mango.showName() + ". " + mango.showLand().join(", "));
I have functions in JS that must be hard-coded for some reason. How do I make a function that writes this hard-coded function? Here's my example; assuming obj is a multi-array/JSON object:
function foo2(obj) {
var t = obj["key1"];
t = t["key2"];
return t;
}
function fooN(obj) {
var t = obj["key1"];
t = t["key2"];
...//more goes here
t = t["keyN"];
return t;
}
I know there're easier ways to access multi-array/object, but hard-coded functions like this is by far the fastest, since there is no variable substitution. Thank you.
I don't advocate it, but here's how you could do it:
function defineAccessor(propArray) {
var accessors = propArray.join('.');
return new Function('obj', 'return obj.' + accessors);
}
var x = { key1: { key2: 3 } };
var foo2 = defineAccessor(['key1', 'key2']);
alert(foo2(x)); // alerts 3
Seems like some strange requirements, but a possible solution is to add the functions to window using window['foo' + number].
The main tricky bit in this solution is the closure in the middle that ensures that the correct value of i is used. This is done by calling a function that takes i as an arg and returns a function.
var N = 5;
fooX = function(obj, x) {
var t = obj["key" + 1];
for (var i = 2; i <= x; i++) {
t = t["key" + i];
}
return t;
}
for (var i = 1; i <= N; i++) {
window['foo' + i] = (function(x) {
return function(obj) {
return fooX(obj, x);
}
})(i);
}
var obj = {
key1: {
key2: {
key3: {
key4: {
key5: 5
}
}
}
}
}
var message = "Results:<br>" +
"foo1(obj) = " + foo1(obj) + "<br>" +
"foo2(obj) = " + foo2(obj) + "<br>" +
"foo3(obj) = " + foo3(obj) + "<br>" +
"foo4(obj) = " + foo4(obj) + "<br>" +
"foo5(obj) = " + foo5(obj);
document.body.innerHTML = message;
Why am I getting values with data binding from my with {{myItem}} but in my controller when I try to assign a new variable I get or output to console I get undefined.
This is my controller:
var controllers = angular.module("myItem.controllers", []);
controllers.controller('myItemController', function ($scope, MyItemService) {
$scope.version_model = null;
$scope.itemGroup_model = null;
$scope.item_model = null;
$scope.myItem = null;
$scope.versions = null;
$scope.itemGroups = null;
$scope.hierarchies = null;
$scope.init = function () {
$scope.myItem = MyItemService.getMyItem();
console.log("myItem => id= " + $scope.myItem.versionID + " requestTypeID: " + $scope.myItem.requestTypeID);
$scope.versions = MyItemService.getVersions();
$scope.version_model = $scope.myItem.versionID;
console.log("version_model :" + $scope.version_model + "scope.myItem.versionID : " + $scope.myItem.versionID);
$scope.itemGroups = MyItemService.getItemGroups();
$scope.itemGroup_model = $scope.myItem.itemGroup;
console.log("itemGroup_model :" + $scope.itemGroup_model + " scope.myItem.itemGroup : " + $scope.myItem.itemGroup);
$scope.hierarchies = MyItemService.getItemList($scope.itemGroup_model);
$scope.item_model = $scope.myItem.itemName;
console.log("item_model :" + $scope.item_model + " scope.myItem.itemName : " + $scope.myItem.itemName);
}
$scope.getItems = function (group) {
$scope.hierarchies = MyItemService.getHierarchyList(group);
}
any time I try to assign anything from $scope.myitem I keep getting undefined. I have tried setting inside the init, out side the init., everything works on my page except for where the _model values are undefined, once I interact with someting and it gets updated all works fine, just not on initialization
Update
this is what my service looks like
angular.module('servicefactories', [])
.factory('MyItemService', function ($http, $q) {
obj.getMyItem= function () {
var mypromise = $q.defer();
$http.get('getforEdit')
.success(function (data, status, headers, config) {
mypromise.resolve(data);
});
return mypromise.promise;
}
return obj;
});
As I suspected this is issue with async call. You can use the then method on promise to access the data and assign once data comes from server. Something like
MyItemService.getMyItem().then(function(data) {
$scope.myItem=data;
console.log("myItem => id= " + $scope.myItem.versionID + " requestTypeID: " + $scope.myItem.requestTypeID);
$scope.versions = MyItemService.getVersions();
$scope.version_model = $scope.myItem.versionID;
console.log("version_model :" + $scope.version_model + "scope.myItem.versionID : " + $scope.myItem.versionID);
});
call your function:
$scope.init();
In your code init function do not run on initialize controller. You must call it manually.
My final task is to fully recover object previously saved using JSON. For now JSON only allows to recover data, but not behaviour. A possilbe solution is to create a new object (lets call it obj) and copy data from JSON-recovered-object to obj. But it doesn't look nice for me. What I am asking, is there a way to dynamically change object prototype in JavaScript?
It's how I solve problem at the moment (using self-made copy method):
(this code on JSFiddle)
function Obj() {
this.D = "D";
this.E = "E";
this.F = "F";
this.toString = function () {
return this.D + " * " + this.E + " * " + this.F;
};
this.copy = function (anotherObj) {
for (var property in anotherObj) {
if (isDef(anotherObj[property]) && isDef(this[property])) {
this[property] = anotherObj[property];
}
}
}
}
;
$(document).ready(function () {
var str = $.toJSON(new Obj());
$("#result").append("<p>JSON: " + str + "</p>");
var obj = new Obj();
obj.copy($.parseJSON(str));
$("#result").append("<p>Recovered obj: " + obj.toString() + "</p>");
});
function isDef(variable)
{
return typeof variable !== undefined;
}
There are easier methods provided by many popular JS libraries.
For example, if you are using jQuery, you might use the jQuery.extend() method instead of your copy function like so:
var obj = $.extend(new Obj(), $.parseJSON(str));
Forked jsFiddle here.
EDIT: Based on ideas from this question, I was able to get the restored object to have all the nested functionality as well, see the updated jsFiddle.
The core idea is to use prototypes instead of properties, and then make sure the object restored from JSON (which is just data) is the first parameter to $.extend():
function Obj2() {
this.A = "A";
}
Obj2.prototype.toString = function() {
return this.A;
};
function Obj() {
this.A = new Obj2();
this.D = "D";
this.E = "E";
this.F = "F";
}
Obj.prototype.toString = function() {
return this.A.toString() + " * " + this.D + " * " + this.E + " * " + this.F;
};
var str = $.toJSON(new Obj());
$("#result").append("<p>JSON: " + str + "</p>");
var obj = jQuery.extend($.parseJSON(str), new Obj());
$("#result").append("<p>Recovered obj: " + obj.toString() + "</p>");