Returning a private variable in JavaScript - javascript

I don't know why console.log(Set.current_index) shows 0 instead of 3.
var Set = (function() {
var set = [];
var index = 0;
function contains(set, e) {
for (var i = 0; i < set.length; i++) {
if (set[i] === e) {
return true;
}
}
return false;
}
var add = function(e) {
if (!contains(set, e)) {
set[index++] = e;
}
}
var show = function() {
for (var i = 0; i < set.length; i++) {
console.log(set[i]);
}
}
return {
add: add,
show: show,
current_index: index
};
})();​
Set.add(20);
Set.add(30);
Set.add(40);
Set.show();
console.log(Set.current_index);

As written current_index just gets the initial value of index - it doesn't mirror any changes to that value because that variable is of primitive type.
If you have a 'reference type' (i.e. an object or array) then changes to its contents become visible in any other variable that references the same object. That doesn't happen with primitive types, they're copied "by value" into the new variables, and changes to the original variable don't affect the copy.
You need to make current_index into a function that returns the current value of index, or write it as a getter which allows you to treat .index as a read-only property by invisibly calling a function to return the current value.
For an example of the latter method (which requires ES5, or shims to replicate the functionality) see http://jsfiddle.net/alnitak/WAwUg/, which replaces your current return block with this:
var interface = {
add: add,
show: show
};
Object.defineProperty(interface, 'index', {
get: function() {
return index;
},
enumerable: true
});
return interface;

Javascript always passes by value except when a variable refers to an object. So your initialization of current_index just gets the initial value of index rather than permanently pointing to the variable, so after that initialization, the two variables are on their separate ways therefore incrementing index doesn't increment current_index.

Related

How to pass an object's method as a parameter to another function in Javascript

First take a look at my simple codes below:
function mySecondFunction(objArray,setFunc)
{
for (let i = 0; i < objArray.length; i++)
{
objArray[i].info.setTop(72);
}
}
function myFunction()
{
let myObjArray = [];
for (let i = 0; i < 10; i++)
{
myObjArray.push({
info:{topVar:0,
bottomVar:0,
get top() {return this.topVar;},
get bottom() {return this.bottomVar;},
setTop: function(input) {this.topVar = input;},
setBottom: function(input) {this.bottomVar = input; }
}
});
}
mySecondFunction(myObjArray); // This works Fine
mySecondFunction(myObjArray,setTop); // I want something like this!!!
}
As you can see, I want to pass a method of an object to another function. I know a lot of possible solutions to avoid this, but I want to know whether it is possible or not.
Detach it and pass as an argument. Remember to use call to set the intended this value.
function mySecondFunction(objArray, setFunc)
{
for (let i = 0; i < objArray.length; i++)
{
setFunc.call(objArray[i].info, 72);
/* explicitly telling that:
please set 'this' value in this function to be 'objArray[i].info' when running,
allowing, e.g. `this.topVar` in
`setTop: function(input) {this.topVar = input;}`
to be operating on `objArray[i].info.topVar` */
}
}
function myFunction()
{
let myObjArray = [];
for (let i = 0; i < 10; i++)
{
myObjArray.push({
info:{topVar:0,
bottomVar:0,
get top() {return this.topVar;},
get bottom() {return this.bottomVar;},
setTop: function(input) {this.topVar = input;},
setBottom: function(input) {this.bottomVar = input; }
}
});
}
mySecondFunction(myObjArray, myObjArray[0].info.setTop);
/* once detaching the method from the object,
(if we are not using arrow functions),
we lose 'this' value, meaning we are losing
the target of object that we want to operate on */
console.log(myObjArray)
}
myFunction();
You can target item number in the array list. You can do statically (i.e. 1-???) or dynamically with an iteration and a variable. You can then the object property within that. For example:
myObjArray[0].info.setTop
That will target the 1st item in the array. Be sure to omit parentheses (()) when passing the method as you want to pass the function reference not the result

How to call a object method without object instance?

I have a method loadSet which creates elements with datas from the localstorage, and this should be run on page load i am calling it via
ReminderSet.prototype.loadSet(); // works fine
My question is, is there any other way to call a method that don't need a reference to an object instance? like person1.loadSet(); or should i abandon this and make it as a regular function?
ReminderSet.prototype.loadSet = function() {
var keys = Object.keys(localStorage),
i = 0,
key,
array;
for (; key = keys[i]; i++) {
const setId = localStorage.getItem(key);
array = JSON.parse(setId); //parse and store key values
let array_index = 0;
//Re-create the reminders and set their properties//
$reminderSection.append($('<div/>').addClass('set').attr('id', key) //Set the ID
.append($('<div/>').addClass('set-title').append($('<h1>').attr('contenteditable', 'true').text(array[array_index].set_title)), //Index is always at 0//
$('<div/>').addClass('create-reminder-control').append($('<button>').addClass('add-new-reminder').text("+ add new"), $('<input>').addClass('create-reminder-value').attr({ type: "text", placeholder: "get something done" })), $('<div/>').addClass('reminder-lists'), $('<div/>').addClass('save-control').append($('<button>').addClass('save-reminder-button').text('Save'))))
//Get our key values //
for (; array_index < array.length; array_index++) {
/*Select the element id */
$("#" + key).children('.reminder-lists').append($('<div/>').addClass('a-reminder').attr('contenteditable', 'true').text(array[array_index].description).append($('<div/>').addClass('delete-reminder').text('x'))) //Get the reminders
} //end
}
};
If loadSet doesn't need or use an instance, it doesn't make any sense for it to be on ReminderSet.prototype. Either make it a standalone function:
function loadSet() {
// ...
}
// Call it like so: loadSet();
...or a property on ReminderSet itself:
ReminderSet.loadSet = function() {
// ...
};
// Call it like so: ReminderSet.loadSet();
Only put functions on the object that a constructor's prototype property refers to if they need to use this (the instance).
You can set the function directly as a property of the other ReminderSet:
ReminderSet.loadSet = function() {//etc.}
Then you can simply call: ReminderSet.loadSet()

Pushing object into array erases instead of adding

I have a function like this :
$scope.saveSearch = function () {
var alreadyExist = false;
for (var i = 0; i < $scope.savedSearch.length; i++) {
if (JSON.stringify($scope.searched) === JSON.stringify($scope.savedSearch[i])) {
alreadyExist = true;
break;
}
}
if (!alreadyExist) {
$scope.savedSearch.push($scope.searched);
localStorage.setItem("savedSearch", JSON.stringify($scope.savedSearch));
}
};
Before that : $scope.savedSearch = [];
$scope.searched = {
IS: "",
area: "",
block: "",
type: "",
level: ""
};
The values in $scope.searched object are initialized and then modified by the user.
My problem is :
$scope.savedSearch always contains only the last pushed object. Instead of adding the object to the array, it just replaces the current object.
I don't understand why.
You'll want to change your push line to:
$scope.savedSearch.push(angular.copy($scope.searched));
I believe your problem is that objects are passed by reference. Since the object you have in the savedSearch is always pointing to the exact object you're searching, alreadyExist will always be true.
My guess is that the object reference is being stored in your array, not the actual object itself. Because of this, any subsequent calls to push the object to your array will not work because the object reference already exists in the array. It's merely updated.
Try this instead. Use angular.copy() to create a deep copy of the object and push the copy to your array. See if that works.
if (!alreadyExist) {
$scope.savedSearch.push(angular.copy($scope.searched));
localStorage.setItem("savedSearch", JSON.stringify($scope.savedSearch));
}
You are pushing the Object outside of the for so only 1 element get pushed in try move it inside the for and every object which doesnt already exist will be pushed in
$scope.saveSearch = function () {
var alreadyExist = false;
for (var i = 0; i < $scope.savedSearch.length; i++) {
if (JSON.stringify($scope.searched) === JSON.stringify($scope.savedSearch[i])) {
alreadyExist = true;
break;
}
if (!alreadyExist) {
$scope.savedSearch.push($scope.searched);
localStorage.setItem("savedSearch", JSON.stringify($scope.savedSearch));
}
}
};
easier way would be to just
$scope.saveSearch = function () {
var alreadyExist = false;
for (var i = 0; i < $scope.savedSearch.length; i++) {
if (JSON.stringify($scope.searched) != JSON.stringify($scope.savedSearch[i])) {
$scope.savedSearch.push($scope.searched);
localStorage.setItem("savedSearch", JSON.stringify($scope.savedSearch));
}else{
break
}
}
};

Accessing the value of an observable inside a plain JS array in Knockout JS?

Background
I have a plain JS array, initially empty. I later populate it with values. The values sent to it are numbers that are Knockout observables. Later, I want to compare those values to values in another, knockout observable array. My problem is that whenever I pass the index of the current item in my array loop, and pass that index value (a number!), the array returns a function. To get an idea, look at the JS that follows.
Note that my project and actual script is viewable on JSBin. Further, to view the problem in the console, you have to add assignments, then press 'sort'.
JSBin: http://jsbin.com/fehoq/177/edit]1
JS
//example script that follows actual script
var _this = this;
//initialize my array
this. lowest = [];
// I want to compare values in lowest to values in this array
this.scores = ko.observableArray();
// method that does comparison
this.myMethod = function(){
// initialize my helper, k
var k;
...
// loop through one array
ko.utils.arrayForEach(_this.scores(), function (score) {
// make sure my value is a number...
if (!isNaN(parseFloat(score()))) {
// this is important, I need to current index for comparison
k = _this.scores.indexOf(score);
console.log(k);
// this is where things break - it prints a function, not a value!
console.log(_this.lowest[k]);
// useless check, the value is a function, so they're always different
if (score()!=_this.lowest[k]){
// do stuff
}
}
}
}
Update
Putting the method I'm using, maybe someone will notice something I missed given that my syntax is correct(?).
this.mean = (function(scores,i) {
var m = 0;
var count = 0;
var k;
ko.utils.arrayForEach(_this.scores(), function(score) {
console.log([typeof score(), score()]);
if (!isNaN(parseFloat(score()))) {
console.log(i);
console.log(_this.lowest[i]);
if (score() != _this.lowest[i]) {
m += parseFloat(score());
count += 1;
}
}
});
if (count) {
m = m / count;
return m.toFixed(2);
} else {
return 'N/A';
}
});
}
Update 2
Just in case someone else wanders over here since my problem isn't solve still. The following code is how I set the value of lowest:
this.dropLowestScores = function() {
ko.utils.arrayForEach(_this.students(), function(student){
var comparator = function(a,b){
if(a()<b()){
return 1;
} else if(a() > b()){
return -1;
} else {
return 0;
}
};
var tmp = student.scores().slice(0);
tmp.sort(comparator);
student.lowest = ko.observableArray(tmp.splice((tmp.length-2),tmp.length-1));
});
};
Outstanding Questions, 5/9/2014
Jeremy's script runs but without the desired effects. For example, console.log(_this.lowest[k]) prints undefined, just as mine does. Further, the matched scores aren't skipped, which they should be.
Jeremy's script specifies lowest as a ko.observable. My script also now has lowest as a ko.observable, but why shouldn't a plain JS array work for this? I only need lowest to update when the button it's bound to is clicked, and those bindings are already taken care of.
That is how observables work in Knockout.
When you create one, you are creating a function.
var myObservable1 = ko.observable(); // Create it.
var myObservable2 = ko.observable("Hola!"); // Create it with a value.
console.log(typeof myObservable2); // It is indeed a function
console.log(typeof myObservable2()); // That returns a string
console.log(myObservable2()); // And get the value.
EDIT BASED ON QUESTION IN COMMENTS
var koTest = ko.observableArray();
koTest.push("Line0");
koTest.push("Line1");
koTest.push("Line2");
koTest.push("Line3");
koTest.push("Line4");
var jsTest = [];
jsTest.push("Line0");
jsTest.push("Line1");
jsTest.push("Line2");
jsTest.push("Line3");
jsTest.push("Line4");
alert(koTest()[2]);
alert(jsTest[2]);
alert(koTest()[2] === jsTest[2]);
Test Code
I went ahead and make a runnable test of your code and everything was working just fine for me. I had to make some assumptions about the contents of _this -- in particular the declaration of lowest, which I made an observableArray based on how you were accessing it.
Anyways, this code runs:
var _this = {
scores: ko.observableArray(),
lowest: ko.observableArray()
};
var mean = (function(scores) {
var m = 0;
var count = 0;
var k;
ko.utils.arrayForEach(_this.scores(), function(score) {
console.log([typeof score(), score()]);
if (!isNaN(parseFloat(score()))) {
k = _this.scores.indexOf(score);
console.log(k);
console.log(_this.lowest[k]);
if (score() != _this.lowest[k]) {
m += parseFloat(score());
count += 1;
}
}
});
if (count) {
m = m / count;
return m.toFixed(2);
} else {
return 'N/A';
}
});
for (var i = 0; i < 10; i++) {
_this.scores.push(ko.observable(i));
}
var m = mean();
alert(m);

variable closure using jQuery object notation

I have the following:
for (var i = 0; i <= 10; i += 1) {
var $page_button = $('<a>', {
html : i,
click : function () {
var index = i;
console.log(index);
return false;
}
});
$page_button.appendTo($wrapper);
}
I thought that var index would be defined separately for each iteration of the loop because it is enclosed within a function. In this case the value of index that is printed is always 10.
The link text is the correct value of i, because this is written to the DOM and is then immutable .
Why is this, and what should I change to fix my problem?
I know this is similar to lots of other questions but the behaviour of using this notation is causing a different result. I am using jQuery 1.7.2 (Can't use any newer unfortunately.)
You need to enclose that in a closure to solve the problem..
var $page_button = $('<a>', {
html : i,
click : (function (num) {
return function(){
var index = num;
console.log(index);
return false;
}
})(i)
});
A reference to i is closed up as part of the anonymous function. Note: not to its value, but a reference to i itself. When the function is run, the value is evaluated. Because the function runs after the loop has ended, the value will always be the last value of i. To pass just the value around, you do something like this:
click : (function (index) {
return function () {
console.log(index);
return false;
};
})(i)
You create an anonymous function which you execute immediately, which takes a value as argument and returns your actual function.
The variable index is defined separately for each execution of the function, but you copy the value from the variable i inside the function, so you will use the value of i as it is when the function runs, not when the function is created.
You need a function that is executed inside the loop to capture the value of the variable:
for (var i = 0; i <= 10; i += 1) {
(function(){
var index = i;
var $page_button = $('<a>', {
html : i,
click : function () {
console.log(index);
return false;
}
});
})();
$page_button.appendTo($wrapper);
}
Every handler is sharing the same i variable. Each one needs its own variable scope in order to reference a unique index.
for (var i = 0; i <= 10; i += 1) {
var $page_button = $('<a>', {
html : i,
click : makeHandler(i) // invoke makeHandler, which returns a function
});
$page_button.appendTo($wrapper);
}
function makeHandler(index) {
return function () {
console.log(index);
return false;
};
}
Here I made a makeHandler function that accepts the index argument, and returns a function that is used as the handler.
Because a function invocation sets up a new variable scope, and because a function is created and returned inside the makeHandler, each handler returned will reference its own scoped index number.

Categories

Resources