Proper way to reference js property from another property - javascript

I am working on an angular project and I have a factory providing some global database methods. I tested this in a jsfiddle and it works, but I want to know if it is the right way to do it.
So here's the jsFiddle.
function DB () {
return {
newRecord: function () {
//create new record
var id = 3;
//this is the part I am wondering about
//is it ok to use this in this way??
this.setCurrentRecordId(id);
},
setCurrentRecordId: function (id) {
alert('setting id');
return localStorage.setItem('id', id);
},
getCurrentRecordId: function () {
return localStorage.getItem('id');
}
}
}
var dbStuff = new DB();
dbStuff.newRecord();
alert(dbStuff.getCurrentRecordId());
Like I said, it seems to be working; just want to know if there is a better way or not.
Thanks!

The "standard" way of using constructor functions in JavaScript would be the following:
function DB () {
this.newRecord = function () {
var id = 3;
// yes, since you invoked the DB constructor using
// using the new keyword, this will be pointing to
// the created instance
this.setCurrentRecordId(id);
};
this.setCurrentRecordId = function (id) {
alert('setting id');
return localStorage.setItem('id', id);
};
this.getCurrentRecordId = function () {
return localStorage.getItem('id');
};
}
var dbStuff = new DB();
dbStuff.newRecord();
alert(dbStuff.getCurrentRecordId());
In case you need to reference the instance in a callback or some other situation in which context is lost there are two common patterns to deal with this.
Either store a reference to this (considered "ugly" by some, very convenient though):
function Ctor(){
var self = this;
this.getSomething = function(id){
asyncThing(id).then(function(result){
// here, `this` will refer to the global object
self.doSomethingWith(result);
});
};
this.doSomethingWith = function(result){
// do something
};
}
Or use .bind() to create a new function with a predefined context:
function Ctor(){
this.getSomething = function(id){
var processResult = function(arg){
this.doSomethingWith(arg);
}.bind(this); // bind sets the function's context no matter where you'll use it
asyncThing(id).then(processResult);
};
this.doSomethingWith = function(result){
// do something
};
}

Since you are using localstorage, there isn't any problem.
function DB () {
return {
setCurrentRecordId: function (id) {
alert('setting id');
return localStorage.setItem('id', id);
},
getCurrentRecordId: function () {
return localStorage.getItem('id');
}
}
}
var dbstuff = new DB();
dbstuff.setCurrentRecordId(3);
dbstuff.getCurrentRecordId() // 3

Related

Javascript - dependency injection?

I'm looking to create a new object then call various methods against it. The idea is simply to group the code and make it tidier rather than have separate methods, all with the same required parameter.
In C# this would be easy - just a case of declaring a new instance of a class and passing the object into the constructor, but I'm unsure about how to go about this in javascript - if it's possible at all. Here's a visual (non working):
let wrapperMethods = function ($wrapper) {
let isOkay = function () {
return $wrapper.is("is something");
};
// more functions
};
// Initiate and call
let $wrapper2 = wrapperMethods($wrapper);
if ($wrapper2.isOkay()) {
alert("is okay");
}
As you can see, there's a bit of jquery ($wrapper) in there too.
Of course, this may not be the correct approach at all. Any advice would be appreciated.
The following code copies the input instance and inject the function with no modification in the original instance.
let wrapperMethods = function ($wrapper) {
let isOkay = function () {
return $wrapper.is("is something");
// note that you can use `this` like so: this.is("is something")
};
Object.assign(
Object.create(
Object.getPrototypeOf($wrapper).constructor
),
$wrapper,
{isOkay /*, other function names */}
)
// more functions
return
};
// Initiate and call
let $wrapper2 = wrapperMethods($wrapper);
if ($wrapper2.isOkay()) {
alert("is okay");
}
If you don't need to preserve the original instance you can inject the method in the class just like the following
let wrapperMethods = function ($wrapper) {
$wrapper.isOkay = function () {
return $wrapper.is("is something");
// same as above, you can use this.is("is something")
}
// more functions
return
};
If you need to inject properties or functions in the constructor of an object, then you can do the following
class MyReciver {
constructor(method){
this.method = method
}
}
$wrapper = new MyReciver(function() {alert('ok')})
If you don't need the this reference you can also use arrow functions
You're so close, just return the { isOkay } as an object.
let wrapperMethods = function ($wrapper) {
let isOkay = function () {
return $wrapper.is("is something");
};
let isNotOkay = function () {
return !$wrapper.is("is something");
};
return { isOkay, isNotOkay }
};
// Initiate and call
let $wrapper2 = wrapperMethods($wrapper);
if ($wrapper2.isOkay()) {
alert("is okay");
}

Is it more efficient to use a common empty function instead of creating a new one in each class instance?

Let's say I have a class that is designed to have some callbacks added to it later on.
function myclass() {
this.onSomething = function () {};
this.onOtherThing = function () {};
this.something = function () {
// stuff
this.onSomething();
};
this.otherThing = function () {
// other stuff
this.onOtherThing();
};
}
I can't have this.onSomething and this.onOtherThing being undefined or null because when they are called in something() and otherThing(), an error will be thrown, stating that their type is not a function.
Since those empty functions are needed, but they use memory, is the class going to be more memory efficient if I did this?
function myclass() {
this.onSomething = empty;
this.onOtherThing = empty;
...
}
function empty() {
}
This way each class instance's properties point to the same empty function, instead of creating new functions every time. I assume defining an empty method doesn't take a lot of memory, but still... is this technically better?
You are right about the fact that a new function is created for every instance of your class. In order to have this shared across all instances you can declare it on the prototype of the class:
var MyClass = function() {
this.something = function () {
// stuff
this.onSomething();
};
this.otherThing = function () {
// other stuff
this.onOtherThing();
};
}
MyClass.prototype.onSomething = function() {};
MyClass.prototype.onOtherThing = function() {};
This way, the methods will be shared by all instances.
why don't you try to return true or return false instead of returning empty functions.
or best you can use :
function myclass() {
this.onSomething = false;
this.onOtherThing = false;
...
}
as per your comment you can try :
function myclass() {
this.onSomething = empty();
this.onOtherThing = empty();
... }
function empty() {
//return something
return true;
}

javascript scope and global variables

I'm trying to avoid using global variable when using functions within objects.
I want to invoke a function inside other function and use a variable from the first function's scope.
For example:
var showForecast = {
'init': function () {
this.getData();
},
'buildView': function(){
var code = "Hey, you're from " + this.data.city;
$('body').append(code);
},
'getData': function () {
$.getJSON('http://ipinfo.io/', function (data) {
console.log(data);
showForecast.buildView();
})
}
}
Clearly it's not working. I want to use data inside buildView without making data a global variable.
I thought using this would be the right course of action because I'm calling buildView from a function where data is defined.
How can this be achieved? Thanks.
You can pass the information along:
var showForecast = {
'init': function () {
this.getData();
},
'buildView': function(data){
var code = 'Hey, you\'re from ' + data.city;
$('body').append(code);
},
'getData': function () {
$.getJSON('http://ipinfo.io/', function (data) {
console.log(data);
showForecast.buildView(data);
})
}
}
There is no way to access the data variable itself. That is locally scoped to the anonymous function you pass to getJSON (and getJSON passes it as an argument, which is beyond your control).
You have to copy the value somewhere.
In your particular example, there are no scopes shared between getData and buildView other than the global scope. So if you want to pass the value through scopes, then a global is your own (terrible) option.
You can simply pass it as an argument:
showForecast.buildView(data);
Or you can store it as a property:
showForecast.myData = data;
I like Vinny's answer.
One round-bout way is to make a module out of it:
var showForecast = function(){
var data;
var init = function () {
this.getData();
};
var buildView = function(){
var code = 'Hey, you\'re from ' + this.data.city;
$('body').append(code);
};
var getData = function () {
$.getJSON('http://ipinfo.io/', function (data) {
console.log(data);
this.data = data;
showForecast.buildView();
})
};
return {
'init': init,
'buildView': buildView,
'getData': getData
};
}();
This way the scope of var data is limited to the function. It's like a private variable.
As you are trying to avoid global, you should consider using namespaces. There is no such thing called namespace in Javascript. But you can define yourself using small utility method mentioned here.
http://www.zachleat.com/web/namespacing-outside-of-the-yahoo-namespace/
A utility method which helps creating custom namespaces.
jQuery.namespace = function() {
var a=arguments, o=null, i, j, d;
for (i=0; i<a.length; i=i+1) {
d=a[i].split(".");
o=window;
for (j=0; j<d.length; j=j+1) {
o[d[j]]=o[d[j]] || {};
o=o[d[j]];
}
}
return o;
};
Define name space
jQuery.namespace( 'jQuery.showForecast' );
Define methods using revealing module pattern
https://addyosmani.com/resources/essentialjsdesignpatterns/book/#revealingmodulepatternjavascript
jQuery.showForecast = (function() {
var data;
var init = function() {
getData();
}
var buildView = function() {
var code = "Hey, you're from " + data.city;
$('body').append(code);
}
var getData = function() {
$.getJSON('http://ipinfo.io/', function(_data) {
console.log(data);
data = _data;
buildView();
})
}
return {
init: init
};
})(); // Execute it immediately
Usage:
You can access only init method as it is exposed to outside.
jQuery.showForecast.init()
Define another namespace
jQuery.namespace( 'jQuery.showForecast.extended' );
jQuery.showForecast.extended = {
// Define some more
};

Javascript: Modify array directly only within its own function

I have a very simple function:
var errorsViewModel = function () {
var self = this;
var _errors = ko.observableArray([]);
self.get = function () {
return _errors;
};
self.insert = function ( error ) {
_errors.push(error);
};
}
What I want to acomplish is make _errors array modifiable directly only within its own function. That is users from outside can get the array for reading through the get method and insert itsert items only through the insert method.
But not to be able to do something like this:
var err = new errorsViewModel();
var array = err.get();
array.push('item');
Instead use the errorsViewModel interface :
err.insert('some error');
Is that possible?
Just copy the returned array:
self.get = function () {
return _errors.slice(0);
};
That way, when get is called, the caller can make changes to it if they want - but it won't modify the original.
To make sure that your array isn't accessible from outside your scope I would suggest that you expose the array via a ko.computed and then notify it's listeners on an insert.
var errorsViewModel = function () {
var self = this;
var _errors = [];
self.errors = ko.computed(function () {
return self.get();
});
self.get = function () {
return _errors.splice(0);
};
self.insert = function ( error ) {
_errors.push(error);
self.errors.valueHasMutated();
};
}

Is it possible to append functions to a JS class that have access to the class's private variables?

I have an existing class I need to convert so I can append functions like my_class.prototype.my_funcs.afucntion = function(){ alert(private_var);} after the main object definition. What's the best/easiest method for converting an existing class to use this method? Currently I have a JavaScript object constructed like this:
var my_class = function (){
var private_var = '';
var private_int = 0
var private_var2 = '';
[...]
var private_func1 = function(id) {
return document.getElementById(id);
};
var private_func2 = function(id) {
alert(id);
};
return{
public_func1: function(){
},
my_funcs: {
do_this: function{
},
do_that: function(){
}
}
}
}();
Unfortunately, currently, I need to dynamically add functions and methods to this object with PHP based on user selected settings, there could be no functions added or 50. This is making adding features very complicated because to add a my_class.my_funcs.afunction(); function, I have to add a PHP call inside the JS file so it can access the private variables, and it just makes everything so messy.
I want to be able to use the prototype method so I can clean out all of the PHP calls inside the main JS file.
Try declaring your "Class" like this:
var MyClass = function () {
// Private variables and functions
var privateVar = '',
privateNum = 0,
privateVar2 = '',
privateFn = function (arg) {
return arg + privateNum;
};
// Public variables and functions
this.publicVar = '';
this.publicNum = 0;
this.publicVar2 = '';
this.publicFn = function () {
return 'foo';
};
this.publicObject = {
'property': 'value',
'fn': function () {
return 'bar';
}
};
};
You can augment this object by adding properties to its prototype (but they won't be accessible unless you create an instance of this class)
MyClass.prototype.aFunction = function (arg1, arg2) {
return arg1 + arg2 + this.publicNum;
// Has access to public members of the current instance
};
Helpful?
Edit: Make sure you create an instance of MyClass or nothing will work properly.
// Correct
var instance = new MyClass();
instance.publicFn(); //-> 'foo'
// Incorrect
MyClass.publicFn(); //-> TypeError
Okay, so the way you're constructing a class is different than what I usually do, but I was able to get the below working:
var my_class = function() {
var fn = function() {
this.do_this = function() { alert("do this"); }
this.do_that = function() { alert("do that"); }
}
return {
public_func1: function() { alert("public func1"); },
fn: fn,
my_funcs: new fn()
}
}
var instance = new my_class();
instance.fn.prototype.do_something_else = function() {
alert("doing something else");
}
instance.my_funcs.do_something_else();
As to what's happening [Edited]:
I changed your my_funcs object to a private method 'fn'
I passed a reference to it to a similar name 'fn' in the return object instance so that you can prototype it.
I made my_funcs an instance of the private member fn so that it will be able to execute all of the fn methods
Hope it helps, - Kevin
Maybe I'm missing what it is you're trying to do, but can't you just assign the prototype to the instance once you create it? So, first create your prototype object:
proto = function(){
var proto_func = function() {
return 'new proto func';
};
return {proto_func: proto_func};
}();
Then use it:
instance = new my_class();
instance.prototype = proto;
alert(instance.prototype.proto_func());

Categories

Resources