Javascript class with array can't get length - javascript

I have a custom javascript class that contains an array, when I add a method to return the length, it is always 0. I'm sure its something I'm missing.. any ideas?
http://jsfiddle.net/ypWsZ/1/
function MyObject(){
this.name='';
}
var selected = (function(){
var arr = new Array();
return {
add : function(s){
arr.push(s);
},
length: function(){
return arr.length;
}()
};
})();
$(document).ready(function(){
var obj = new MyObject();
obj.name = 'test';
selected.add(obj);
selected.add(obj);
alert(selected.length);
});

length: function(){
return arr.length;
}()
Typo: length is the result (0) of invoking an anonymous function. Remove the () at the end and use:
alert(selected.length());

Related

Creation of array and append value inside closure in javascript

I am using this code -
var gArray = (function (value) {
var array = [];
return function () {
array.push(value);
return array;
}
}());
gArray(1);
gArray(2);
gArray(3);
I am expecting to this code snippet [1, 2, 3]
but i am getting [undefined, undefined, undefined]
The gArray function doesn't have an argument, the immediately invoked function does, but you pass nothing when you call it:
var gArray = (function (value) { //<- Argument of IIFE
var array = [];
return function () { //<- No arguments for gArray
array.push(value);
return array;
}
}()); //<- No arguments passed to IIFE
What you need is to define an argument for the returned function, which is gArray:
var gArray = (function () {
var array = [];
return function (value) {
array.push(value);
return array;
}
}());
Your outer function is a self-invoked function. That means that it will be executed as soon as () is reached. In this particular case, it's returning:
function () {
array.push(value);
return array;
}
which is taking value as undefined. To solve this issue, you can rewrite your code as follows:
var gArray = (function () {
var array = [];
return function (value) {
array.push(value);
return array;
}
}());
Change var array = []; to this.array = [];.
It needs to have the right scope.
var myArray = [];
function addToArray(value){
myArray.push(value);
console.log(value + " was added to " + myArray);
}
addToArray(1);
addToArray(2);
addToArray(3);
If you give back myArray console will say [1, 2, 3]
This is the best way to do it:
const array = [];
const pushArray = function(value, array) {
array.push(value);
return array;
};
pushArray(1, array);
pushArray(2, array);
pushArray(3, array);

clear and undo clear the array

If I do have the following code then empty the arry:
var a1 = [1,2,3];
a1 = [];
//returns []
But I'm trying to make a function to clear and undo clear the array, it's not working as expected:
var foo = ['f','o','o'];
var storeArray;
function clearArray(a){
storeArray = a;
a = [];
}
function undoClearArray(a){
a = storeArray;
}
clearArray(foo);
foo; //still returns ['f','o','o']
//but expected result is: []
Here's the problem:
You assign an array to a variable foo.
Then you pass this object to your function which stores it in another variable a. Now you have one object that two variable are pointing at. In the function you then reassign a to a different object an empty array []. Now a points at the empty object and foo still points at the original object. You didn't change foo by reassigning a.
Here's a concise way to store you're array:
var storeArray = [];
function clearArray(a){
while (a.length>0){
storeArray.push(a.shift()) //now a is empty and storeArray has a copy
}
}
I tried something different. Maybe it's dirty, but the storage itself is on the object.
the fiddle
//define the object to hold the old data
Object.defineProperty(Array.prototype, "storage", {
enumerable: false,
configureable: true,
get: function () {
return bValue;
},
set: function (newValue) {
bValue = newValue;
}
});
//define the prototype function clear to clear the data
Object.defineProperty(Array.prototype, "clear", {
enumerable: false,
writable: false,
value: function () {
this.storage = this.slice(0); //copy the data to the storage
for (var p in this) {
if (this.hasOwnProperty(p)) {
delete this[p]; //delete the data
}
}
return this; //return the object if you want assign the return value
}
});
//define the prototype function restore to reload the data
Object.defineProperty(Array.prototype, "restore", {
enumerable: false,
writable: false,
value: function () {
var a = this.storage.slice(0); //copy the storage to a local var
for (var p in this.storage) {
if (this.storage.hasOwnProperty(p)) {
this[p] = a[p]; //assign the pointer to the new variable
delete this.storage[p]; //delete the storage
}
}
return this;
}
});
var a = ['f','o','o'];
console.log(a); //--> displays ['f','o','o']
a.clear();
console.log(a); //--> displays []
a.restore();
console.log(a); //--> displays ['f','o','o']
You can use splice() method to delete all elements of an array likes below
function clearArray(a){
storeArray = a;
a.splice(0,a.length);
}
var a = [1,2,3,4];
var tempArr ;
clearArray = function() {
tempArr = a.slice(0);
a.length = 0;
}
undoArray = function() {
a = tempArr.slice(0);
}
Here is a small jsfiddle: http://jsfiddle.net/66s2N/
Here's working way of what you want to achieve:
var foo = ['f','o','o'];
var storeArray;
function clearArray(a){
storeArray = a.slice(0);
for (var i=0; i<a.length; i++)
delete a[i];
a.length = 0;
}
function undoClearArray(a){
for (var i=0; i<storeArray.length; i++)
a.push(storeArray[i]);
}
console.log(foo);
clearArray(foo);
console.log(foo); //now foo is []
undoClearArray(foo);
console.log(foo); // now foo is ['f','o','o']
http://jsfiddle.net/44EF5/1/
When you do:
var a1 = [1,2,3];
a1 = [];
it's as if you've written:
var a1 = [1,2,3];
var a1 = [];
You're overwriting variables.
Now, why your approach doesn't work - in JS there's no passing by reference. MarkM response explains what's happening within the function.
Now, why does the above work - while you've got two variables pointing towards the same array, nothing prevents you from modifying that array. As such storeArray = a.slice(0) will create a copy of the array. Then by using delete we're removing all values of the array, and then as length isn't enumerable (so using for (var i in a) wouldn't help) we reassign the length of the array. This has removed the values of original array, while creating a new array assigned to storeArray.
function clearArray(a){
storeArray = a.slice(0);
return a.length = 0;
}
or set foo.length = 0;
Just update your both function with below ones
function clearArray(a){
storeArray = a.slice(0);
a.length = 0;
}
function undoClearArray(a){
a = storeArray;
return a;
}
in undoClearAray() we are returning the variable which have new reference(in your clearArray(), Both the original and new array refer to the same object. If a referenced object changes, the changes are visible to both the new and original arrays). so use it as foo=undoClearArray(foo); for old values.
try
var foo = ['f','o','o'];
var storeArray;
function clearArray(a){
storeArray = a;
a = [];
return a;
}
function undoClearArray(a){
a = storeArray;
}
foo = clearArray(foo);
foo; //returns []
You can use wrappers to do this quite nicely. First create a wrapper function with the additional methods defined, then create your array using that function instead of []. Here is an example (see JSFiddle):
var extendedArray = function() {
var arr = [];
arr.push.apply(arr, arguments);
arr.clearArray = function() {
this.oldValue = this.slice(0);
this.length = 0;
}
arr.undoArray = function() {
this.length = 0;
for (var i = 0; i < this.oldValue.length; i++) {
this.push(this.oldValue[i]);
}
}
return arr;
};
var a = extendedArray('f', 'o', 'o');
alert(a);
a.clearArray();
alert(a);
a.undoArray();
alert(a);

How to print array data in alert

saveData: function()
{
var element = $('input');
for(var i=0;i<element.length;i++)
{
//alert($(element[i]).val());
var p=new Array($(element[i]).val());
}
alert(p);
},
How to print array data in alert.
You need to create an array and then push all the values to it, instead you are resetting it in the loop
var element = $('input');
var p = element.map(function () {
return this.value
}).get();
alert(JSON.stringify(p));//or alert(p);
changing your code will be
var element = $('input');
var p = [];
for (var i = 0; i < element.length; i++) {
p.push($(element).eq(i).val());
}
alert(JSON.stringify(p));//or alert(p)
Use javascript toString() function to get result
var array = ["a","b","c"];
solution:
alert(array.toString());
In JavaScript you could just
for(i=0; i<p.length; ++i){
alert(p[i]);
}
so if p is an array containing ["one","two","three"] your browser will alert "one", "two" and then "three" in the loop.
To display array values properly try this :
saveData: function()
{
var p=new Array();
var element = $('input');
for(var i=0;i<element.length;i++)
{
//alert($(element[i]).val());
p[0] = $(element[i]).val();
}
alert(p.join("\n"));
},
Use javascript forEach() function to get result
var array = ["a","b","c"];
solution:
array.forEach(function(data){alert(data)});
You can use join().
It converts each array element into a string.
It acts much like toString(), moreover you can determine the separator.
var arr = ["HTML","CSS","JS"];
...
alert(arr.join(' - '));

Set length property of JavaScript object

Let's say I have a JavaScript object:
function a(){
var A = [];
this.length = function(){
return A.length;
};
this.add = function(x){
A.push(x);
};
this.remove = function(){
return A.pop();
};
};
I can use it like so:
var x = new a();
x.add(3);
x.add(4);
alert(x.length()); // 2
alert(x.remove()); // 4
alert(x.length()); // 1
I was trying to make .length not a function, so I could access it like this: x.length, but I've had no luck in getting this to work.
I tried this, but it outputs 0, because that's the length of A at the time:
function a(){
var A = [];
this.length = A.length;
//rest of the function...
};
I also tried this, and it also outputs 0:
function a(){
var A = [];
this.length = function(){
return A.length;
}();
//rest of the function...
};
How do I get x.length to output the correct length of the array inside in the object?
You could use the valueOf hack:
this.length = {
'valueOf': function (){
return A.length;
},
'toString': function (){
return A.length;
}
};
Now you can access the length as x.length. (Although, maybe it's just me, but to me, something about this method feels very roundabout, and it's easy enough to go with a sturdier solution and, for example, update the length property after every modification.)
If you want A to stay 'private', you need to update the public length property on every operation which modifies A's length so that you don't need a method which checks when asked. I would do so via 'private' method.
Code:
var a = function(){
var instance, A, updateLength;
instance = this;
A = [];
this.length = 0;
updateLength = function()
{
instance.length = A.length;
}
this.add = function(x){
A.push(x);
updateLength();
};
this.remove = function(){
var popped = A.pop();
updateLength();
return popped;
};
};
Demo:
http://jsfiddle.net/JAAulde/VT4bb/
Because when you call a.length, you're returning a function. In order to return the output you have to actually invoke the function, i.e.: a.length().
As an aside, if you don't want to have the length property be a function but the actual value, you will need to modify your object to return the property.
function a() {
var A = [];
this.length = 0;
this.add = function(x) {
A.push(x);
this.length = A.length;
};
this.remove = function() {
var removed = A.pop();
this.length = A.length;
return removed;
};
};
While what everyone has said is true about ES3, that length must be a function (otherwise it's value will remain static, unless you hack it to be otherwise), you can have what you want in ES5 (try this in chrome for example):
function a(){
var A = [],
newA = {
get length(){ return A.length;}
};
newA.add = function(x){
A.push(x);
};
newA.remove = function(){
return A.pop();
};
return newA;
}
var x = a();
x.add(3);
x.add(4);
alert(x.length); // 2
alert(x.remove()); // 4
alert(x.length); // 1
You should probably use Object.create instead of the function a, although I've left it as a function to look like your original.
I don't think you can access it as a variable as a variable to my knoledge cannot return the value of a method, unless you will hijack the array object and start hacking in an update of your variable when the push/pop methods are called (ugly!). In order to make your method version work I think you should do the following:
function a(){
this.A = [];
this.length = function(){
return this.A.length;
};
this.add = function(x){
this.A.push(x);
};
this.remove = function(){
return this.A.pop();
};
};
These days you can use defineProperty:
let x = {}
Object.defineProperty(x, 'length', {
get() {
return Object.keys(this).length
},
})
x.length // 0
x.foo = 'bar'
x.length // 1
Or in your specific case:
Object.defineProperty(x, 'length', {
get() {
return A.length
}
})
function a(){
this.A = [];
this.length = function(){
return this.A.length;
};
this.add = function(x){
this.A.push(x);
};
this.remove = function(){
return this.A.pop();
};
};

new Function() with variable parameters

I need to create a function with variable number of parameters using new Function() constructor. Something like this:
args = ['a', 'b'];
body = 'return(a + b);';
myFunc = new Function(args, body);
Is it possible to do it without eval()?
Thank you very much, guys! Actually, a+b was not my primary concern. I'm working on a code which would process and expand templates and I needed to pass unknown (and variable) number of arguments into the function so that they would be introduced as local variables.
For example, if a template contains:
<span> =a </span>
I need to output the value of parameter a. That is, if user declared expanding function as
var expand = tplCompile('template', a, b, c)
and then calls
expand(4, 2, 1)
I need to substitute =a with 4. And yes, I'm well aware than Function is similar to eval() and runs very slow but I don't have any other choice.
You can do this using apply():
args = ['a', 'b', 'return(a + b);'];
myFunc = Function.apply(null, args);
Without the new operator, Function gives exactly the same result. You can use array functions like push(), unshift() or splice() to modify the array before passing it to apply.
You can also just pass a comma-separated string of arguments to Function:
args = 'a, b';
body = 'return(a + b);';
myFunc = new Function(args, body);
On a side note, are you aware of the arguments object? It allows you to get all the arguments passed into a function using array-style bracket notation:
myFunc = function () {
var total = 0;
for (var i=0; i < arguments.length; i++)
total += arguments[i];
return total;
}
myFunc(a, b);
This would be more efficient than using the Function constructor, and is probably a much more appropriate method of achieving what you need.
#AndyE's answer is correct if the constructor doesn't care whether you use the new keyword or not. Some functions are not as forgiving.
If you find yourself in a scenario where you need to use the new keyword and you need to send a variable number of arguments to the function, you can use this
function Foo() {
this.numbers = [].slice.apply(arguments);
};
var args = [1,2,3,4,5]; // however many you want
var f = Object.create(Foo.prototype);
Foo.apply(f, args);
f.numbers; // [1,2,3,4,5]
f instanceof Foo; // true
f.constructor.name; // "Foo"
ES6 and beyond!
// yup, that easy
function Foo (...numbers) {
this.numbers = numbers
}
// use Reflect.construct to call Foo constructor
const f =
Reflect.construct (Foo, [1, 2, 3, 4, 5])
// everything else works
console.log (f.numbers) // [1,2,3,4,5]
console.log (f instanceof Foo) // true
console.log (f.constructor.name) // "Foo"
You can do this:
let args = '...args'
let body = 'let [a, b] = args;return a + b'
myFunc = new Function(args, body);
console.log(myFunc(1, 2)) //3
If you're just wanting a sum(...) function:
function sum(list) {
var total = 0, nums;
if (arguments.length === 1 && list instanceof Array) {
nums = list;
} else {
nums = arguments;
}
for (var i=0; i < nums.length; i++) {
total += nums[i];
}
return total;
}
Then,
sum() === 0;
sum(1) === 1;
sum([1, 2]) === 3;
sum(1, 2, 3) === 6;
sum([-17, 93, 2, -841]) === -763;
If you want more, could you please provide more detail? It's rather difficult to say how you can do something if you don't know what you're trying to do.
A new feature introduced in ES5 is the reduce method of arrays. You can use it to sum numbers, and it is possible to use the feature in older browsers with some compatibility code.
There's a few different ways you could write that.
// assign normally
var ab = ['a','b'].join('');
alert(ab);
// assign with anonymous self-evaluating function
var cd = (function(c) {return c.join("");})(['c','d']);
alert(cd);
// assign with function declaration
function efFunc(c){return c.join("");}
var efArray = ['e','f'];
var ef = efFunc(efArray);
alert(ef);
// assign with function by name
var doFunc = function(a,b) {return window[b](a);}
var ghArray = ['g','h'];
var ghFunc = function(c){return c.join("");}
var gh = doFunc(ghArray,'ghFunc');
alert(gh);
// assign with Class and lookup table
var Function_ = function(a,b) {
this.val = '';
this.body = b.substr(0,b.indexOf('('));
this.args = b.substr(b.indexOf('(')+1,b.lastIndexOf(')')-b.indexOf('(')-1);
switch (this.body) {
case "return":
switch (this.args) {
case "a + b": this.val = a.join(''); break;
}
break;
}
}
var args = ['i', 'j'];
var body = 'return(a + b);';
var ij = new Function_(args, body);
alert(ij.val);
Maybe you want an annoymous function to call an arbitary function.
// user string function
var userFunction = 'function x(...args) { return args.length}';
Wrap it
var annoyFn = Function('return function x(...args) { return args.length}')()
// now call it
annoyFn(args)
new Function(...)
Declaring function in this way causes
the function not to be compiled, and
is potentially slower than the other
ways of declaring functions.
Let is examine it with JSLitmus and run a small test script:
<script src="JSLitmus.js"></script>
<script>
JSLitmus.test("new Function ... ", function() {
return new Function("for(var i=0; i<100; i++) {}");
});
JSLitmus.test("function() ...", function() {
return (function() { for(var i=0; i<100; i++) {} });
});
</script>
What I did above is create a function expression and function constructor performing same operation. The result is as follows:
FireFox Performance Result
IE Performance Result
Based on facts I recommend to use function expression instead of function constructor
var a = function() {
var result = 0;
for(var index=0; index < arguments.length; index++) {
result += arguments[index];
}
return result;
}
alert(a(1,3));
function construct(){
this.subFunction=function(a,b){
...
}
}
var globalVar=new construct();
vs.
var globalVar=new function (){
this.subFunction=function(a,b){
...
}
}
I prefer the second version if there are sub functions.
the b.apply(null, arguments) does not work properly when b inherits a prototype, because 'new' being omitted, the base constructor is not invoked.
In this sample i used lodash:
function _evalExp(exp, scope) {
const k = [null].concat(_.keys(scope));
k.push('return '+exp);
const args = _.map(_.keys(scope), function(a) {return scope[a];});
const func = new (Function.prototype.bind.apply(Function, k));
return func.apply(func, args);
}
_evalExp('a+b+c', {a:10, b:20, c:30});

Categories

Resources