Hello I've used this patter to get a static variable
var uniqueID = (function() {
var id = 0; // This is the private persistent value
// The outer function returns a nested function that has access
// to the persistent value. It is this nested function we're storing
// in the variable uniqueID above.
return function() { return id++; }; // Return and increment
})(); // Invoke the outer function after defining it.
Now I'm trying to clone this function, but backup and the original still return sequential values. How can i "freeze" the status of the function when copy it?
Thanks
OK, something like this extremely convoluted contraption should work (fiddle, http://jsfiddle.net/dPLj6/):
var uniqueIdFunction = function(initialValue) {
var id = initialValue || 0;
var result = function() { return id++; };
result.clone = function(){ return uniqueIdFunction(id); }
return result;
};
var uniqueId1 = uniqueIdFunction();
Use the clone method to get a clone. The original will keep it's own internal id value. The clone will take its initial internal id from the clone source.
Here is a function that generates unique id generators:
var createGenerator = function(id) {
var id = id || 0;
return function() { return id++; };
}
var g1 = createGenerator();
var g2 = createGenerator();
console.log(g1(), g1(), g1());
console.log(g2(), g2());
console.log(g1());
console.log(g2());
// OP's cloning scenario
var freezeId = g1();
var clone = createGeenrator(freezeId);
console.log(g1(),g1());
console.log(clone());
#pax162's answer is more in line with what the OP wants to do. I just decided to post the more normal way of doing it.
Related
I have some code like this
$scope.grabItems = function(data) {
data.model = ['test'];
console.log($scope.ui.projects);
}
$scope.ui.projects = [];
$scope.grabProjects = function() {
$scope.grabItems({model: $scope.ui.projects});
}
I'm trying to change the $scope.ui.projects variable using the parameter of another function (this is so that I can write an abstract grabItems function using any variable).
The problem is it looks like the data.model = ['test'] isn't changing the $scope.ui.projects variable at all but is creating a brand new variable.
How would I modify the outer variable in a reusable way like this?
Note that $scope.ui.projects could potentially be any variable.
You are not actually not changing $scope.ui.projects, Try like this
$scope.grabItems = function(data) {
data.model = ['test'];
console.log($scope.ui.projects);
}
$scope.ui.projects = [];
$scope.grabProjects = function() {
$scope.grabItems($scope.ui.projects);
}
If it's not clear, share what is your expected object.
You could achieve it by pushing items into the array:
$scope.grabItems = function(data) {
if(!angular.isArray(data.model))
throw new Error('Array expected');
data.model.length = 0; // empty array
data.model.push('test'); // push items
console.log($scope.ui.projects);
}
$scope.ui.projects = [];
$scope.grabProjects = function() {
$scope.grabItems({model: $scope.ui.projects});
}
On load of the document, I am initiating a function calling itself. And later from the returned function, i am trying to get the output. but i am not getting. the way what i do this wrong here.
any one correct me and teach the correct way to use the self initiated functions?
here is my try :
var BankAccount = (function () {
function BankAccount() {
this.balance = 0;
}
BankAccount.prototype.deposit = function(credit) {
this.balance += credit; return this.balance;
};
return BankAccount;
})();
var myDeposit = BankAccount.deposit(50); //throws error as ankAccount.deposit is not a function
Live
You need to return instance of BankAccount:
return new BankAccount();
You need to invoke your constructor before you can call .deposit
var account = new BankAccount();
var balance = account.deposit(50);
console.log(balance); // 50
This would allow you to manage multiple accounts where each account has its own balance.
var a = new BankAccount();
a.deposit(50); // 50
var b = new BankAccount();
b.deposit(20); // 20
console.log(a.balance); // 50
console.log(b.balance); // 20
You've written a constructor function, but you haven't called it as one.
var myBankAccount = new BankAccount();
var myDeposit = myBankAccount.deposit(50);
I'm trying to reinvent the wheel, sort of.. Just messing around trying to remake some jquery functions.. I've come this far
var ye = function (ele) {
if (ele[0] == "#")
{
return document.getElementById(ele.slice(1));
}
else if (ele[0] == ".")
{
// returns an array, use index
return document.getElementsByClassName(ele.slice(1));
}
else
{
// also returns an array
return document.getElementsByTagName(ele);
}
}
but how can I use this element as a parameter in a function in the 'ye' prototype. For example, if I wanted to make fontsize how could I get the dom element like here:
ye.prototype.fontSize = function (ele)
{
ele.style.fontSize = "30px";
}
Just to add a bit to make the title relevant.. forEach inserts three arguments into the callback function, just like I want ye to insert ele into the fontSize function.
Just messing around trying to remake some jquery functions...
...but how can I use this element as a parameter in a function in the 'ye' prototype..
Here is a very crude and simple way to start...
Create a function with a property called elems which is an array and will store the selected DOM elements.
Like this:
var oye = function() { this.elems = []; };
On its prototype, you can create your custom functions which you want to expose. e.g. the function fontSize (as in your question), iterate over the elems array property that we created earlier changing the font size of each DOM element stored in. this points to the instance which is calling this function which we will ensure to be of type oye later on. To enable chaining, we simply return itself via this.
Like this:
oye.prototype.fontSize = function(size) {
this.elems.forEach(function(elem) {
elem.style.fontSize = size;
});
return this;
};
Now create the selector function called ye. This serves the purpose of selecting the DOM elements, storing them in the elems array property of a new instance of oye class, and return the instance. We call the slice of the array prototype to convert the nodeList to an array.
Like this:
var ye = function(elem) {
var newOye = new oye;
newOye.elems = [].slice.call(document.querySelectorAll(elem));
return newOye;
};
Now start using it in your code. Just like jQuery, you can use ye to select and then call your custom functions.
Like this:
ye("#elem1").fontSize('30px');
Just like jQuery, you can also chain multiple custom functions as shown in the complete working example below:
ye("P").fontSize('24px').dim(0.4);
Next step: Remember this is just a very crude example. You can now proceed to club the step 1 and 2 into a single call using the init pattern returning the new object from the selector function itseld. Learn more about Javascript and best practices.
Here is a sample working demo:
var oye = function() { this.elems = []; };
oye.prototype.fontSize = function(size) {
this.elems.forEach(function(elem) {
elem.style.fontSize = size;
});
return this;
};
oye.prototype.dim = function(value) {
return this.elems.forEach(function(elem) {
elem.style.opacity = value;
});
return this;
};
var ye = function(elem) {
var newOye = new oye;
newOye.elems = [].slice.call(document.querySelectorAll(elem));
return newOye;
};
ye("#elem1").fontSize('30px');
ye(".elem2").fontSize('20px');
ye("P").fontSize('24px').dim(0.4);
<div>This is normal text.</div>
<div id="elem1">size changed via id.</div>
<div class="elem2">size changed via class.</div>
<div class="elem2">size changed via class.</div>
<p>size changed and dimmed via tag name</p>
<p>size changed and dimmed via tag name</p>
Regarding your question, I may think you're new to JavaScript, or not familiar with its basic concepts. I'm not sure reinventing the wheel is a good thing in such conditions.
Since you've cited jQuery, you can have a look at its source code to understand how it works under the hood:
https://github.com/jquery/jquery/blob/99e8ff1baa7ae341e94bb89c3e84570c7c3ad9ea/src/core.js#L17-L23
https://github.com/jquery/jquery/blob/99e8ff1baa7ae341e94bb89c3e84570c7c3ad9ea/src/core.js#L38-L81
https://github.com/jquery/jquery/blob/99e8ff1baa7ae341e94bb89c3e84570c7c3ad9ea/src/core/init.js#L19-L114
Having that said, I would have done something like this:
var ye = function ( ele ) {
return new ye.prototype.init(ele);
};
ye.prototype.init = function( ele ) {
this._elements = [].slice.call(document.querySelectorAll(ele));
return this;
};
ye.prototype.forEach = function( fn ) {
this._elements.forEach(fn);
return this;
};
ye.prototype.fontSize = function( fontSizeValue ) {
this.forEach(function (ele) {
ele.style.fontSize = fontSizeValue;
});
return this;
};
The associated usage is as follow:
var myCollection = ye('.someClassName');
myCollection.forEach(function ( item, index ) {
console.log(item.style.fontSize);
});
myCollection.fontSize('45px');
myCollection.forEach(function ( item, index ) {
console.log(item.style.fontSize);
});
Use ye function calling before setting style, something like:
ye.prototype.fontSize = function(ele) {
ye(ele).style.fontSize = '30px';
}
returned object should be richer, like that:
var baseObject = {
// Will be used for the element:
element: null,
width: function(){ return this.element.getwidth(); /* or anything similar*/ }
// ... Further methods
}
and then in your ye function:
var ye = function (ele) {
var yeElem = clone(baseObject); // See comment below!!
if (ele[0] == "#") { yeElem.element = document.getElementById(ele.slice(1)); }
else if (ele[0] == "."){ /*...*/ }
else { /*...*/ }
return yeElem;
}
This way the new element has built in methods.
As for the clone() method used, it doesn't exist but you have to use some clone method.
I recommend Loadsh's _.cloneDeep() (here).
I'm developing a simple plugin with jQuery. As I see it I need to do it like this:
(function($) {
var somePrivateFn = function() {
alert(this.x);
}
$.fn.myPlugin = function() {
if(typeof arguments[0] === "string") {
var fnargs = Array.prototype.slice(arguments, 1);
// pluginData will be undefined
var pluginData = this.pluginData;
somePrivateFn.apply(pluginData, fnargs);
}
return this.each(function() {
var data = this.pluginData = {};
data.x = 1;
};
}
})(jQuery);
Of course my plugin needs to store some variables for the jquery object it's working on. To do this I just place an additional variable "pluginData" at the jQuery object itself. The problem is it is not accessible later. How to deal with it? What's the best approach to do this?
Pass the object when you run the plugin. $('div').myPlugin(a_data_object) It will persist to within the this.each loop.
This var options=$.extend({}, pluginData); will keep the data in one iteration from passing through to the next.
If you want to manipulate then store that data on the element you would use $.data().
(function($) {
console.clear();
var somePrivateFn = function(data) {
console.log(data.x);
}
$.fn.myPlugin = function(pluginData) {
// send to another func
somePrivateFn(pluginData);
// use on each div
return this.each(function(i) {
var options=$.extend({}, pluginData);
// add some example data
options.y = "i am y";
options.iteration=i;
options.text=$(this).text();
// store it on the element
$(this).data('pluginData', options);
});
}
// run the plugin on div elements
$('div').myPlugin({x:"i am x"});
// check out the data later
$('div').each(function(){
console.log($(this).data('pluginData'));
});
})(jQuery);
Example: http://jsfiddle.net/xXt5W/1/
$.data() docs: https://api.jquery.com/jQuery.data/
I've been thinking about how to display the newest array (because the array list would update from time to time.). I think there's a default function to it but I can't find it. Anyone knows it?
var bgImg = new Array();
bgImg[0] = "background.jpg";
bgImg[1] = "background2.jpg";
bgImg[2] = "background3.jpg";
bgImg[3] = "background4.jpg";
If you want the last element of the array,
>>> a = ['background1','background2'];
["background1", "background2"]
>>> b = a[a.length-1]
"background2"
You shouldn't need to manually assign indexes. Just do bgImg.push('background2.jpg'), and it will mutate the array for you. In your case the syntax would be...
var last = bgImg[bgImg.length-1]
If you want the newest then you need to make a variable and set it with whatever you update when you push the newest one, if it doesn't become the last one.
You could create a little object to handle this for you.
function creatImageState() {
var images = [];
return {
get latest() {
return images[images.length - 1];
},
set latest (value) {
images.push(value);
}
};
}
var s = creatImageState();
s.latest = "a.png";
s.latest = "b.png";
alert(s.latest);
IE doesn't support getters and setters so you probably need to use this.
function creatImageState() {
var images = [];
return {
getLatest : function() {
return images[images.length - 1]
},
setLatest : function(value) {
images.push(value);
}
};
}
var s = creatImageState();
s.setLatest("a.png");
s.setLatest("b.png");
alert(s.getLatest());