Call javascript dynamic method in object - javascript

If I create a Class with two method like following:
var Car = function() {}
Car.prototype = {
run: function() {
alert('run');
},
stop: function() {
alert('stop');
}
}
And when the DOM is ready will call test() method,
and this test() method will create a instance of Car and call the method of Car
$(document).ready(function() {
test("run");
});
function test(method) {
var instance = new Car();
// call instance.run()
}
I know I have to use apply() method,
but I have tried lots of times and failed
So, how can I use apply() method in Object?

An object in javascript is an associative array, so you can use the [] operator to address by string any member of the object, functions as well.
function test(method) {
var instance = new Car();
instance[method]();
}
this will allow to call a function by name over the object Car.
See it working on JsFiddle.
The context inside the called method will properly point to the newly created Car instance without any additional effort, that I guess is what you want. Using apply or call is generally used when you need to change the default 'this' value to something else.

I know I have to use apply() method,
You don't. Uncomment the instance.run() code, call the test() method and it works.
demo: http://jsfiddle.net/JkAnb/
how can I use apply() method in Object
It depends on what you want to achieve. There is documentation at MDN.

perhaps you want to call
instance.method()
call dynamically i.e. based on value passed run/stop. You can try this -
instance[method].call(instance)
or
instance[method].apply(instance)
where instance is passed as scope param which is scope of the function to be called (value of "this" inside function).

Why do You need classes in an (object based) functional language?
Your intent is bit unclear. What is the implied type of the argument 'method' in Your test function?
I assume it is a method name. In any case code bellow might help.
/* prefer this class pattern */
var Car = (function () {
"use strict";
Car.prototype = {
name : undefined ,
run: function () {
return name_ + ":: run";
},
stop: function () {
return name_ + ":: stop";
}
};
/* constructor */
function Car(name) {
name_ = name;
}
return Car;
})();
function printer(msg_) {
document.body.innerHTML += msg_ + "<br/>";
}
function test(car_, method_name_1, method_name_2) {
printer(car_[method_name_1]());
printer(car_[method_name_2]());
}
test( new Car("BMW"), "run", "stop");
test( new Car("Lada"), "run", "stop")

in addition, you could also use the eval function:
var instance = new Car();
eval('instance.'+method+'();');
though Felice Pollano's way is far neater imo.
http://jsfiddle.net/VZdXb/

Related

Am I passing an object to object.defineProperties?

I am learning javascript and would love help understanding the snippet of code.
From Object.DefineProperties definition, the first parameter is an object. Is MyObjectConstructor a declaration or an object. With a constructor function I would expect to call new to make it an object.
This is what is confusing me. Or as I read in Javascript functions are objects so do I treat it as an object and the this property is where all staticProps and instanceProps are added to?
var _prototypeProperties = function (child, staticProps, instanceProps) {
if (staticProps){
Object.defineProperties(child, staticProps)
};
if (instanceProps) {
Object.defineProperties(child.prototype, instanceProps);
}
};
function myFunction() {
function MyObjectConstructor(element) {
this.element = element;
this.initialized = false;
}
_prototypeProperties(MyObjectConstructor, {...}, {...});
}
Yes, (constructor) functions are objects as well in javascript, and you can add properties directly to them.
The _prototypeProperties function in your example snippet does put the staticProperties on the constructor, so that they could be accessed as MyObjectConstructor.myStaticProperty. It also does put instanceProps (better: "class properties", "prototype properties") on the MyObjectConstructor.prototype object, from where they are inherited by instances: (new MyObjectConstructor).myPrototypeProperty. Lastly, your MyObjectConstructor does put "real" (own) properties on the instances, specifically (new MyObjectConstructor).element and .initialised.
In JavaScript, once defined, the resulting functions act identically:
function Hat() { }
var Hat = function() { }
Conventionally the first is used to create objects (usually with new) and the second is used as a regular method.
The new operator, when preceding a function gets kinda weird. Using the new operator will:
create a new Object "it"
set the function being called as "it"s prototype
binds "it" to this within the function
overrides the return value of the function with "it". This overrides both explicit returns and implicit returns of undefined.
For example:
// first define Hat function
function Hat() { this.color = 'red' }
// calling with `new` implicitly returns a new object bound to the function
new Hat()
// > Hat { color: "red" }
// invoking without `new` implicitly returns `unefined`,
// but `this` points to Hat's parent object.
// If running in the browser, Hat's parent object would be `window`
// so now Window.color is set to "red"
Hat()
//> undefined
Window.color
//> "red"
Be careful with new, because the new object will be returned instead of any explicit returns.
var color = function() { return "blue" }
color()
//> "blue"
new color()
//> color {}
JavaScript is prototypal by nature. The new operator reflects neither prototypical nor classical inheritance. I avoid it when possible, although many popular libraries use it.
I recommend reading through Crockford's explanation of JavaScript's prototypal inheritance: http://javascript.crockford.com/prototypal.html
Its terse, but if you understand his 10 lines of demo code you'll be good.
Play with bind, call and apply, and different scoping contexts too. After understanding scoping and the prototypal nature, the rest is just syntax.
first : the function is the first type object in the javascript . it means you can deliver the function as value . for example :
function test(){
return function(){
console.log('function');
}
}
test()();
you return the function as return an object , function can be assigned and the function another kind of value !
var test = function(i) {
// body...
return i;
}
test('123');
a character string 'test' refer to a Anonymous function , you can understand that you transmit a function to a character string .
second : if you use new to create a instance via function , this function will be called construction function , normally it used to init the params , and the instance will take the construction function own property or method and prototype's property or method from the construction function .
third : the instance's property of "__proto__" is refer to the construction function's prototype object . this is why the instance can use the prototype's property or method from the construction function .
forth : if you create the instance by new , the this will refer to the instance object ! so that instance can use the property and method .
from your code :
var _prototypeProperties = function (child, staticProps, instanceProps) {
if (staticProps){
Object.defineProperties(child, staticProps)
};
// the properties from the own construction function , like this :
// this.element = element;
// this.initialized = false;
if (instanceProps) {
Object.defineProperties(child.prototype, instanceProps);
}
};
// the properties from the construction function's prototype object . like this :
// MyObjectConstructor.prototype.name = 'somebody';
// MyObjectConstructor.prototype.getName = function(){
// return this.name;
// }

How can I make an object callable as an instance and a function

I'm working on building a collection of prototype helper methods inside a wrapper. However for ease of use, I'd like to be able to call the object as both a new instance and single global instance under the same call.
For example, with jQuery, you can call both "$" and "$()" which can be used differently http://learn.jquery.com/using-jquery-core/dollar-object-vs-function/:
So given the bellow as simple example, how could I do something similar?
(function () {
var myWrapper = function (foo) {
return new helper(foo);
};
var helper = function (foo) {
this[0] = foo;
return this;
}
helper.prototype = {
putVar: function(foo) {
this[0] = foo;
}
}
if(!window.$) {
window.$ = myWrapper;
}
})();
// create an new instace;
var instance = $("bar");
console.log(instance);
// call a prototype method
instance.putVar("foo");
console.log(instance);
// call a prototype method using the same call without new instance
// this doesnt work :(
$.putVar("foo");
// however this will work
window.myLib = $("foo");
myLib.putVar("bar");
http://jsfiddle.net/2ywsunb4/
If you want to call $.putVar, you should define putVar like this:
myWrapper.putVar = function (foo) {
console.log('Work');
}
In your code, the instance and myLib are the same thing, they are both instances created by you.
If you want to call both $.putVar and $(...).putVar, you should add the code I show you above. That means you have to define two putVar functions, one is used like a 'instance' method, while the other one is used like a 'static' method.
If you search through jQuery source code, you will see two each functions defined. That's why you can all both $.each(...) and $(...).each for different usages.

Calling a function being "hidden" in another function

There is such a function from a third-party library:
(function($) {
window.NestedFormEvents = function() {
this.addFields = $.proxy(this.addFields, this);
this.removeFields = $.proxy(this.removeFields, this);
};
//......
NestedFormEvents.prototype = {
addFields: function(e) { .....
});
Is there any way to call addFields() from a client? typeof window.NestedFormEvents returns "function", but window.NestedFormEvents.addFields returns undefined as well as window.NestedFormEvents.addFields().
The source code is here: https://github.com/ryanb/nested_form/blob/master/vendor/assets/javascripts/jquery_nested_form.js
This is constructor function. When called with new keyword it returns new object which has methods defined in constructor's prototype:
var nestedFormEvent = new NestedFormEvents();
nestedFormEvent.addFields();
Now if you check console.log(nestedFormEvent) object you will see all necessary methods in it.
You're calling addFields as though it were a static method of NestedFormEvents.
Rather, it is a method of its prototype. So:
var instance = new NestedFormEvents;
alert(NestedFormEvents.addFields); //undefined
alert(instance.addFields); //finds function
In short, any time you're trying to use methods declared on a function's prototype, you need to instantiate it, with new.

What is a best practice for ensuring "this" context in Javascript?

Here's a sample of a simple Javascript class with a public and private method (fiddle: http://jsfiddle.net/gY4mh/).
function Example() {
function privateFunction() {
// "this" is window when called.
console.log(this);
}
this.publicFunction = function() {
privateFunction();
}
}
ex = new Example;
ex.publicFunction();
Calling the private function from the public one results in "this" being the window object. How should I ensure my private methods are called with the class context and not window? Would this be undesirable?
Using closure. Basically any variable declared in function, remains available to functions inside that function :
var Example = (function() {
function Example() {
var self = this; // variable in function Example
function privateFunction() {
// The variable self is available to this function even after Example returns.
console.log(self);
}
self.publicFunction = function() {
privateFunction();
}
}
return Example;
})();
ex = new Example;
ex.publicFunction();
Another approach is to use "apply" to explicitly set what the methods "this" should be bound to.
function Test() {
this.name = 'test';
this.logName = function() {
console.log(this.name);
}
}
var foo = {name: 'foo'};
var test = new Test();
test.logName()
// => test
test.logName.apply(foo, null);
// => foo
Yet another approach is to use "call":
function Test() {
this.name = 'test';
this.logName = function() {
console.log(this.name);
}
}
var foo = {name: 'foo'};
var test = new Test();
test.logName()
// => test
test.logName.call(foo, null);
// => foo
both "apply" and "call" take the object that you want to bind "this" to as the first argument and an array of arguments to pass in to the method you are calling as the second arg.
It is worth understanding how the value of this in javascript is determined in addition to just having someone tell you a code fix. In javascript, this is determined the following ways:
If you call a function via an object property as in object.method(), then this will be set to the object inside the method.
If you call a function directly without any object reference such as function(), then this will be set to either the global object (window in a browser) or in strict mode, it will be set to undefined.
If you create a new object with the new operator, then the constructor function for that object will be called with the value of this set to the newly created object instance. You can think of this as the same as item 1 above, the object is created and then the constructor method on it is called.
If you call a function with .call() or .apply() as in function.call(xxx), then you can determine exactly what this is set to by what argument you pass to .call() or .apply(). You can read more about .call() here and .apply() here on MDN.
If you use function.bind(xxx) this creates a small stub function that makes sure your function is called with the desired value of this. Internally, this likely just uses .apply(), but it's a shortcut for when you want a single callback function that will have the right value of this when it's called (when you aren't the direct caller of the function).
In a callback function, the caller of the callback function is responsible for determining the desired value of this. For example, in an event handler callback function, the browser generally sets this to be the DOM object that is handling the event.
There's a nice summary of these various methods here on MDN.
So, in your case, you are making a normal function call when you call privateFunction(). So, as expected the value of this is set as in option 2 above.
If you want to explictly set it to the current value of this in your method, then you can do so like this:
var Example = (function() {
function Example() {
function privateFunction() {
// "this" is window when called.
console.log(this);
}
this.publicFunction = function() {
privateFunction.call(this);
}
}
return Example;
})();
ex = new Example;
ex.publicFunction();
Other methods such as using a closure and defined var that = this are best used for the case of callback functions when you are not the caller of the function and thus can't use 1-4. There is no reason to do it that way in your particular case. I would say that using .call() is a better practice. Then, your function can actually use this and can behave like a private method which appears to be the behavior you seek.
I guess most used way to get this done is by simply caching (storing) the value of this in a local context variable
function Example() {
var that = this;
// ...
function privateFunction() {
console.log(that);
}
this.publicFunction = function() {
privateFunction();
}
}
a more convenient way is to invoke Function.prototype.bind to bind a context to a function (forever). However, the only restriction here is that this requires a ES5-ready browser and bound functions are slightly slower.
var privateFunction = function() {
console.log(this);
}.bind(this);
I would say the proper way is to use prototyping since it was after all how Javascript was designed. So:
var Example = function(){
this.prop = 'whatever';
}
Example.prototype.fn_1 = function(){
console.log(this.prop);
return this
}
Example.prototype.fn_2 = function(){
this.prop = 'not whatever';
return this
}
var e = new Example();
e.fn_1() //whatever
e.fn_2().fn_1() //not whatever
Here's a fiddle http://jsfiddle.net/BFm2V/
If you're not using EcmaScript5, I'd recommend using Underscore's (or LoDash's) bind function.
In addition to the other answers given here, if you don't have an ES5-ready browser, you can create your own "permanently-bound function" quite simply with code like so:
function boundFn(thisobj, fn) {
return function() {
fn.apply(thisobj, arguments);
};
}
Then use it like this:
var Example = (function() {
function Example() {
var privateFunction = boundFn(this, function() {
// "this" inside here is the same "this" that was passed to boundFn.
console.log(this);
});
this.publicFunction = function() {
privateFunction();
}
}
return Example;
}()); // I prefer this order of parentheses
Voilà -- this is magically the outer context's this instead of the inner one!
You can even get ES5-like functionality if it's missing in your browser like so (this does nothing if you already have it):
if (!Function.prototype.bind) {
Function.prototype.bind = function (thisobj) {
var that = this;
return function() {
that.apply(thisobj, arguments);
};
}:
}
Then use var yourFunction = function() {}.bind(thisobj); exactly the same way.
ES5-like code that is fully compliant (as possible), checking parameter types and so on, can be found at mozilla Function.prototype.bind. There are some differences that could trip you up if you're doing a few different advanced things with functions, so read up on it at the link if you want to go that route.
I would say assigning self to this is a common technique:
function Example() {
var self = this;
function privateFunction() {
console.log(self);
}
self.publicFunction = function() {
privateFunction();
};
}
Using apply (as others have suggested) also works, though it's a bit more complex in my opinion.
It might be beyond the scope of this question, but I would also recommend considering a different approach to JavaScript where you actually don't use the this keyword at all. A former colleague of mine at ThoughtWorks, Pete Hodgson, wrote a really helpful article, Class-less JavaScript, explaining one way to do this.

prototype odd result when changing way to define

When trying to test prototype functionality, I got this odd result:
Here is my first test:
<script>
function Hello() {
}
var a = new Hello();
Hello.prototype.name = "Fred";
alert(a.name);
</script>
And, here's the second one:
<script>
function Hello() {
}
var a = new Hello();
Hello.prototype = {
name : "Fred",
}
alert(a.name);
</script>
I can't understand why the first will return a alert with "Fred" and the second is "undefined" though these mean the same thing?
Could you help me with it?
Thank you.
When you define a function in JavaScript, the interpreter makes a special prototype property available on the function, which points to an object, in case you use that function as a constructor. The [[Prototype]] internal property points to this object when you create a new object using the constructor.
When you replace the prototype property with a new one, you are replacing that reference, and if you do it after you instantiate an object, you will find the prototype object appears to be stale (that object's [[Prototype]] is pointing to the original object that prototype pointed to).
Solutions
Only assign new properties directly on the prototype property.
var constructor = function() { };
constructor.prototype.someMethod = function() { };
Use an extend type function to extend the existing prototype property with your new object (in this example, I used Underscore's extend() function).
var constructor = function() { };
_.extend(constructor.prototype, { someMethod: function() { } });
Make sure after the constructor, assigning the prototype property is the very next step in your program (generally not recommended).
var constructor = function() { };
constructor.prototype = { someMethod: function() { } };
Your ordering is messed up. You need assign the object to the prototype before using the new operator:
function Hello() {
}
Hello.prototype = {
name : "Fred",
}
var a = new Hello();
alert(a.name);​
Demo.
The two code snippets are not actually equal.
In the first script you only override Hello.prototype.name, while in the second script you override the whole content of Hello.prototype.

Categories

Resources