How to get property call a function with javascript? - javascript

I need to know who call a function, for example i have code like this :
var observe = function(newvalue, callback) {
console.log('who call me?');
callback('new value is ' + newvalue);
}
var ViewModel = function() {
var self = this;
self.Id = '1';
self.Name = observe;
self.NickName = observe;
self.someFunction = function() {
return 1 + 2;
}
}
var vm = new ViewModel();
vm.NickName('test', function(resp) {
console.log(resp);
})
For this example, in observe i need the code know who call it is vm.NickName or NickName.
How to trick this problem with pure javascript?

From within a function, you cannot determine what reference was used to call it. If you want to do that, you need to create separate functions (which can then call the central one), or pass it an argument that tells it how it's being called, etc.
For instance, this example passes it an argument:
var observe = function(who, newvalue, callback) {
console.log('Called by: ' + who);
callback('new value is ' + newvalue);
};
// ...
self.Name = observe.bind(self, 'Name');
self.NickName = observe.bind(self, 'NickName');

Related

How to insert new function without adding inside the self object in JS

I have the following JS code, how could I insert new function without adding inside the self object?
Function select(selector){
Var self = {
Print: ()=>{},
Delete: ()=>{}
};
Return self;
}
//I want to add new function here so I can call it by using select("something").newFunc()
Is there any ways to do that? I tried something as below, but I'm wondering if there's another way to do it.
After several hours of testing, I tried to assign all the methods inside a variable prototype's method (A), and then set A's prototype back to the variable's prototype. Then I can call it using the variable.
var myScript = function(selector){
return new myScript.prototype.init(selector);
}
var init = myScript.prototype.init = function(selector){
var self = {
//codes
};
return self;
}
init.prototype = myScript.prototype;
(function(mS){
mS.prototype.newFunc = function(){
return "Coding is Fun";
}
})(myScript);
After that, I can use myScript("something").newFunc()
That's what I've tried, please share if you know another way, thanks.
Constructor functions do not require the 'self' object you designed. In your case
You return the self object from the constructor
You use arrow functions that do not change the value of this. Arrow functions are cool, but they are not interchangeable with regular functions (at least not in every case - and inside a constructor function is such a case)
Look at the two constructor functions below (Select1 is based on your code, Select2 is based on a "how to do it"):
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/new
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/prototype
// adding a self object - arrow functions
function Select1(selector) {
this.selector = selector
var self = {
print: () => {
return 'Select1 print: ' + this.selector
},
delete: () => {
return 'Select1 delete: ' + this.selector
}
}
return self
}
// no self object - regular functions
function Select2(selector) {
this.selector = selector
this.print = function() {
return 'Select2 print: ' + this.selector
}
this.delete = function() {
return 'Select2 delete: ' + this.selector
}
return this
}
// instantiating the two different Objects
let select1 = new Select1('selector1')
let select2 = new Select2('selector2')
// calling the print method on both Objects
console.log(select1.print())
console.log(select2.print())
// logging both objects
console.log(select1)
console.log(select2)
// modifying the prototype of the constructor functions
// modifying Select1.self's prototype would throw an error
/*Select1.prototype.self.newFunc = () => {
return 'Select1 newFunc: ' + this.selector
}*/
Select1.prototype.newFunc = function() {
return 'Select1 newFunc: ' + this.selector
}
Select2.prototype.newFunc = function() {
return 'Select2 newFunc: ' + this.selector
}
// logging the new function
// logging select1.newFunc() would throw an error, as you return 'self', that's not modified
// console.log(select1.newFunc())
console.log(select2.newFunc())
// logging the modified object
console.log(select1)
console.log(select2)
BUT
You should use correct indentation - code is much more readable that way
You should follow naming and casing conventions, so others can quickly see what is what in your code (constructor functions begin with a capital letter, but other reserved words do not (so casing is important too)
USAGE
And if you want to use it like myScript('selector').myFunc(), then you need to wrap it in another function (like you did in your second code sample):
function Select2(selector) {
this.selector = selector
this.print = function() {
return 'Select2 print: ' + this.selector
}
this.delete = function() {
return 'Select2 delete: ' + this.selector
}
return this
}
function myFunc(selector) {
return new Select2(selector)
}
console.log(myFunc('s1').print())
console.log(myFunc('s2').print())
If you want to be able to set the selector, then you need a set() function:
function Select2(selector) {
this.selector = selector
this.print = function() {
return 'Select2 print: ' + this.selector
}
this.delete = function() {
return 'Select2 delete: ' + this.selector
}
// this.setSelector is the set() function
this.setSelector = function(s) {
this.selector = s
}
return this
}
function myFunc(selector) {
return new Select2(selector)
}
// instantiating Select2
const selector2 = myFunc('s1')
console.log(selector2.print()) // expected: s1
// setting the 'selector' of the instantiated
// Select2 Object to a new value
selector2.setSelector('s2')
console.log(selector2.print()) // expected: s2

Access Computed Value in subscribable function

When using my view model:
function SummaryViewModel (arrayString) {
//------- Attributes -------
var self = this;
self.Claims = ko.observableArray(namesapce.Helpers.subnamespace.ToCollection(arrayString));
self.ShowTable = ko.computed(function() {
var collection = ko.unwrap(self.Claims());
return collection.length > 0;
}, this);
self.showWarningPanel = self.ShowTable.Not();
}
I am trying to invert the computed value. So either the table will be displayed or a warning message.
I have created the following subscribable function:
ko.subscribable.fn.Not = function () {
return ko.pureComputed(function() {
var bool = this();
return !(ko.unwrap(bool));
});
};
However, the value for this() does not provide the value of the computed attribute. It instead returns all the objects in the current scope.
Originally I marked the method as ko.computed.fn and this also did not return the computed value.
I have been referring to the documentation on the KO website to help build the function.
http://knockoutjs.com/documentation/fn.html
You'll have to pass this to the pureComputed you're creating inside:
ko.subscribable.fn.Not = function () {
return ko.pureComputed(function() {
return !this();
}, this);
// ^^^^ Here, you tell knockout to execute the function with `this`
// context. Alternatively, you could use the var `self = this`
// pattern.
};
var myObs = ko.observable(true);
var invertedObs = myObs.Not();
myObs(false);
console.log("observable: " + myObs());
console.log("inverted: " + invertedObs());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>

private object not setting data

Hi I'm trying to implement a LinkedList in Javascript. When i assign a value to my node it doesn't seem to store it when I use my getter. For example:
var Node =function() {
var _data;
var _next ={};
var that = this;
that.getData = function() {
return _data;
};
that.setData = function(data) {
that._data = data;
};
that.getNext = function() {
return _next;
};
that.setNext = function(next) {
that._next = next;
};
return that;
};
Will not work with:
var nodeObj = new Node();
nodeObj.setData("hello");
console.log(nodeObj.getData());
_data is not the same as that._data, you must do this:
that.getData = function() {
return that._data;
};
OR you could do this instead:
that.setData = function(data) {
_data = data;
};
the benefit of the second approach being that you're simulating a private variable (because you cannot do nodeObj._data in the second case but you can in the first)
also var that = this; is unnecessary, you can simply do this._data in this case.
For your case here, you can assume that if you're calling a function like yourObject.someFunction(), then within someFunction the value of this equals yourObject. (And this isn't always true in javascript but since you're starting off you should think about it this way for now. If you pass a function to another function as a variable and then call it then this wouldn't be the case).

Javascript closure and "this"

My question is... in CallMeLaterTestObj function in the TestObj the "this" is the window object and not TestObj. How can I restructure this so that within the CallMeLater function I don't have to wrap the call function() { v.CallMeLaterTestObj(); } in a closure or using the bind function since it has limited support to newer browsers. Two objectives:
Keeping "this" in function calls within the object
Maintaining a separate value for "value" for each separate object so they don't share the same value.
// Emulating public api, private methods, private variables, public fields.
// New portion of question
Re-written to include binding function and prototypical notation. How do you move the Binding function into a base object that all new objects would get?
This is as close as I can come to getting this to use the best of both worlds. I have no idea what the pitfalls of this approach are though
var BaseObject = function ()
{
_getBinding = function (method)
{
var _self = this;
return function ()
{
_self[method].apply(_self, arguments);
};
};
return {
CallInline: _getBinding
}
}();
var TestObj = function (value)
{
$.extend(this, BaseObject);
// public var
this._value = value;
};
TestObj.prototype = function()
{
var privateVar = false;
// these are private
_giveMe = function () {
return this._value;
},
_callMeLaterTestObj = function () {
console.log('I am ' + this.constructor.name + ' my value is ' + this._value);
};
// public API
return {
GiveMe : _giveMe,
CallMeLaterTestObj : _callMeLaterTestObj
}
}();
function CallMeLater(v, i)
{
setTimeout(v.CallInline('CallMeLaterTestObj'), 10);
}
var V1 = new TestObj(1);
var V2 = new TestObj(2);
var V3 = new TestObj(3);
console.log('V1= ' + V1.GiveMe());
console.log('V2= ' + V2.GiveMe());
console.log('V3= ' + V3.GiveMe());
console.log('---');
V1.CallMeLaterTestObj();
console.log('---');
I think what you're looking for is this:
function TestObj(value) {
var _value = value;
this.giveMe = function() {
return _value;
};
this.callMeLaterTestObj = function() {
console.log('I am ' + this.constructor.name + ' my value is ' + _value);
};
return this;
};
function callMeLater(v, i) {
setTimeout(function() {
v.callMeLaterTestObj();
}, 10);
}
var v1 = new TestObj(1);
var v2 = new TestObj(2);
var v3 = new TestObj(3);
console.log('V1= ' + v1.giveMe());
console.log('V2= ' + v2.giveMe());
console.log('V3= ' + v3.giveMe());
console.log('---');
callMeLater(v1, 1);
callMeLater(v2, 2);
callMeLater(v3, 3);​
To access constructor.name, you need to declare the function with function name() syntax, rather than var name = function() syntax.
To keep private variables and expose a public api, expose the public variables as properties of this in the function.
Be sure to return this from the constructor function to make it work.
It's also good practice to follow the naming convention of CamelCase for class names (of which TestObj is one) and lowerCamelCase for variables / methods / objects / etc. Helps keep things clear as to which variables are instances, and which are Classes.
Test and see the console output expected here.
note
Regarding wrapping v.callMeLaterTestObj() in a closure for the setTimeout, this technique is completely cross-browser compatible. You won't have any issues.
The bind method is newer, although there are many libraries that will shim that for you in older browsers. My personal favourite is underscore.
note 2
You can't call a method on an object in setTimeout without wrapping it in a closure somewhere, however if you want to you can abstract the closure in the Class without using a generic bind function (as provided by Underscore or jQuery and others) you can 'roll your own' in the Class like this:
function TestObj(value) {
var _value = value;
var _self = this;
this.giveMe = function() {
return _value;
};
this.callMeLaterTestObj = function() {
console.log('I am ' + this.constructor.name + ' my value is ' + _value);
};
this.getBinding = function(method) {
var _self = this;
return function() {
_self[method].apply(_self, arguments);
};
};
return this;
};
function callMeLater(v, i) {
setTimeout(v.getBinding('callMeLaterTestObj'), 10);
}
var v1 = new TestObj(1);
var v2 = new TestObj(2);
var v3 = new TestObj(3);
console.log('V1= ' + v1.giveMe());
console.log('V2= ' + v2.giveMe());
console.log('V3= ' + v3.giveMe());
console.log('---');
callMeLater(v1, 1);
callMeLater(v2, 2);
callMeLater(v3, 3);​
explanation:
You need to use some sort of binding because, when you pass the method to setTimeout, you pass it by reference. So all setTimeout sees is a function - not the object it was on, which is why you lose the context of this.
Since setTimeout will therefore execute the function in the default scope - i.e. the browser window - you need a way to get this back, by reference, either through an inline anonymous function, or by returning a closure that uses the apply method to 'reset' this.
note 3
If you wanted to have your own bind method, and not include a library that provides it for you or include it in every class then you can use this one from Underscore, which defers to the native method in newer browsers:
function bind(func, context) {
var bound, args;
if (func.bind === nativeBind && nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));
if (!_.isFunction(func)) throw new TypeError;
args = slice.call(arguments, 2);
return bound = function() {
if (!(this instanceof bound)) return func.apply(context, args.concat(slice.call(arguments)));
ctor.prototype = func.prototype;
var self = new ctor;
var result = func.apply(self, args.concat(slice.call(arguments)));
if (Object(result) === result) return result;
return self;
};
};
Then use it like this:
function callMeLater(v, i) {
setTimeout(bind(v.callMeLaterTestObj, v), 10);
}
This will work well in all browsers.
No, you can't. That's just the way to do it. Btw, you can easily shim the bind method so that it is available in older browsers, too.
An alternative would be to move the closure into the prototype method, if you know that you always will need to bind the actual function:
TestObj.prototype.getCallMeLaterTestObj = function () {
var that = this;
return function() {
console.log('I am ' + that.constructor.name + ' my value is ' + that._value);
};
};
setTimeout(v.getCallMeLaterTestObj(), 10);
Btw, your prototype has no constructor property so the log will not work as expected.
Your only chance is to avoid the this keyword entirely:
TestObj = function() {
var privateVar = false; // these are private static
function TestObj(value) {
function giveMe() {
return value;
}
function callMeLaterTestObj() {
console.log('I am TestObj my value is ' + giveMe());
}
this._value = value;
this.giveMe = giveMe;
this.callMeLaterTestObj = callMeLaterTestObj;
/* you could do this as well:
return {
_value: value,
giveMe: giveMe,
callMeLaterTestObj: callMeLaterTestObj
}; */
}
return TestObj;
})();
var v = new TestObj;
setTimeout(v.callMeLater, 10);
But this is not very memory-efficient, as it does not use prototypical inheritance at all.

how to set a passed function in javascript

I am creating a new object variable and passing an object as an argument:
var obj = new test({
first : $('#fname').val(),
last : $('#lname').val(),
fn : function() {
alert(this.first + " " + this.last);
}
});
This is the called function when creating the above variable:
var test = function(obj) {
this.fn = function() {
alert("No custom function was made.");
}
this.first = obj.first;
this.last = obj.last;
if(obj.fn)
this.fn = obj.fn(); //I also tried it without the '()' after 'obj.fn'
};
The first and last variables are fine, but I cannot figure out how to get the custom function that is passed to be set.
You mentioned that you tried removing the '()' after 'obj.fn'... seems to work when I do that. Here's the link to verify: http://jsfiddle.net/TD93x/3/
it worked for me
var test = function(obj) {
this.fn = function() {
alert("No custom function was made.");
}
this.first = obj.first;
this.last = obj.last;
if(obj.fn)
this.fn = obj.fn;
};

Categories

Resources