I am a typical Java programmer with traditional OOPs knowledge. I am trying to learn JS now. I have learned that a function is JS is a first class object, like any other object it has properties and methods. based on this I have written following code:
function Note(name, text)
{
this.name = name;
this.text = text;
function displayNote()
{
alert(this.text);
}
}
I am now trying to access the displayNote function from the not object, with the follwing code:
var note = new Note("a", "c");
alert(note.displayNote);
But it alerts undefined. This may be due to the scope issue. So I tried with this:
function Note(name, text)
{
this.name = name;
this.text = text;
function displayNote()
{
var self = this;
alert(self.text);
}
}
var note = new Note("a", "c");
alert(note.displayNote);
Still the same result. Any explanations?
You need to do:
function Note(name, text)
{
this.name = name;
this.text = text;
this.displayNote = function()
{
alert(this.text);
}
}
It is showing undefined, because you haven't defined the displayNote property.
Also, to call a function you need to do:
var note = new Note("a", "c");
note.displayNote();// it will automatically alert. Otherwise you'll have two alerts. The second one being undefined.
Live Demo
Try this.
this.displayNote = function()
{
alert(this.text);
}
Now it's a property of the Note object. Plus I think you want to do this:
note.displayNote(); // Note the brackets to call.
In here with the code as
alert(note.displayNote);
You are calling the alert twice.
So please just call the function
note.displayNote();
you can use like below
<script language="javascript" type="text/javascript">
<!--
person = new Object()
person.name = "Tim Scarfe"
person.height = "6Ft"
person.run = function() {
this.state = "running"
this.speed = "4ms^-1"
}
//-->
</script>
Related
Firstly, sorry for my lack of terminology.
If I have a constructor
function myObject(name, value){
this.name = name;
this.value = value;
}
and I make a few objects from it
var One = new myObject("One", 1);
var Two = new myObject("Two", 2);
Can I loop through each new Object made from the myObject class, without putting each new Object into an Array?
would it be possible to add an Instantly Invoking Function to the constructor that adds the Object into an array?
e.g.
function myObject(name, value){
this.name = name;
this.value = value;
this.addToArray = function(){
theArray.push(this); // this is the IIFE
}();
}
that way any new objects created instantly run this function and get added to the array.
Is this possible? ( current syntax does not work, obviously )
EDIT Coming back to this one year later I can tell you that it IS possible. You just call the function inside the constructor like so:
function myObject(name, value){
this.name = name;
this.value = value;
this.addToArray = function(){
theArray.push(this);
};
this.addToArray();
}
Here is an example of this in JSFIDDLE, pushing each object into an array on instantiation and then calling each object's .speak() method directly from the array.
https://jsfiddle.net/Panomosh/8bpmrso1/
Without using an array, you can't, it is not the way it is meant to be used.
What you can do though, is watch over each instances created in a static member of myObject class
function myObject(name, value){
this.name = name;
this.value = value;
this.watch();
}
myObject.prototype.watch = function () {
if (myObject.instances.indexOf(this) === -1) {
myObject.instances.push(this);
}
};
myObject.prototype.unwatch = function () {
myObject.instances.splice(myObject.instances.indexOf(this), 1);
};
myObject.instances = [];
No, you cannot. You cannot do this with almost all programming languages.
You can, in the constructor, store a reference of every object you created into an array/map so that you can iterate over them any time. This, however, prevents all objects of this class from being garbage collected, so use it with care.
The WeakMap in JavaScript keeps only a week reference to the keys, but it, in turn, does not allow you to loop over all keys. So it is not an option either.
var MyClass = (function() {
var _instances = [];
function MyClass(name, value) {
_instances.push(this);
this.name = name;
this.value = value;
}
MyClass.each = function(cb) {
for (var i in _instances) {
if (_instances.hasOwnProperty(i)) {
cb(_instances[i]);
}
}
}
return MyClass;
})();
new MyClass('John', 10);
new MyClass('James', 20);
MyClass.each(function(item) {
console.log(item);
});
I am new to prototype, and I just made a simple example that won't work. From the code below its obvious what I want - a counter. But for some reason the output is always 5, I guess that the bind is making a deep copy of the this object, which is then bound to the function. There is an obvious solution where I make everything public, but I would like to know if there is a more elegant solution. (Or you may correct my and tell me that there is no deep copy, but some other bug). The documentation didn't help me.
The code:
<!DOCTYPE html>
<html>
<body>
<input type="button" value="click" onclick="javascript: doTest();" />
<div id="canvas"></div>
<script>
var ClassA = function (position, element) { // constructor
this.field1 = position;
this.target = element;
};
ClassA.prototype = function () {
// private functions
var _aPrivateMethod = function(){
console.log(this);
this.field1 += 1;
return this.field1;
};
var _aPublicMethod = function(){
this.target.innerHTML = _aPrivateMethod.bind(this)();
};
return { // interface
constructor: ClassA,
aPublicMethod : _aPublicMethod
};
}();
function doTest() {
var obj = new ClassA(4, document.getElementById('canvas'));
obj.aPublicMethod();
}
</script>
</body>
</html>
Here are a number of issues. First you call doTest each time. A new object is created on each click, so there's no way to increment field1.
As I understood you want field1 to be private. In the way you initialize it, it cant be private. You can do something like:
function Person() {
var counter = 0;
this.increment = function () {
return ++counter;
}
}
In that way you can't access the counter from outside the function scope.
Another issue is that you don't call the private method with the right context. You should use _aPrivateMethod.call(this) as pimvdb have mentioned.
Here is an example of:
function Person() {
var age = 0;
function incrementAge() {
return ++age;
}
function eatCake() {
console.log('Yey. I\'ll eat cake!');
}
this.birthday = function () {
eatCake();
return incrementAge();
}
}
var p = new Person();
p.birthday(); //'Yey. I'll eat cake!', returns 1
p.birthday(); //'Yey. I'll eat cake!', returns 2
In that way eatCake, incrementAge and age are all private because of the functional scope.
The bad thing in the example is the way I've declared the function birthday. For each instance of Person a new copy will be created.
The right way is:
Person.prototype.birthday = function () { ... }
My declaration is not like this one because I want to use a private data members. Actually not private just local for the function (age, eatCake, incrementAge).
There's no way to access them from a function which is declared in the prototype, so that's why for the example above I've choose that approach.
I am using easeljs for a HTML5 game.
I am calling onClick from within a method in a class, but the event object is overwriting my 'this' object so I can no longer access the other class methods or vars. For example I have something like (obviously this is not the actual code, just a quick illustration):
function Book(){
this.text = "this is the text";
this.makeText = function(){
//Define some shapes
var character = new Container();
character.addChild(some shapes);
character.onClick = this.detectClick;
}
this.detectClick = function(){
alert(this.text);
}
}
So, if I run this, I would get an undefined alert because in my detectClick method, this is now my event object.
So how do I call the original text from within this method?
Many Thanks
You need to us closure to pass the object reference
var self = this;
character.onClick = function(){ self.detectClick() };
Or use a simple proxy method.
function proxy(method, scope) {
return function() {
return method.apply(scope, params);
}
}
character.onclick = proxy(detectClick, this);
Ok that you really need to do is
function Book(){
this.text = "this is the text";
this.makeText = function(){
//Define some shapes
var character = new Container();
character.addChild(some shapes);
character.onClick = this.detectClick.bind(this);
}
this.detectClick = function(){
alert(this.text);
}
}
scope of 'this' is the problem in your code. Change your code like the below code
function Book(){
this.text = "this is the text";
var that=this;
this.makeText = function(){
//Define some shapes
var character = new Container();
character.addChild(some shapes);
character.onClick = that.detectClick;
}
this.detectClick = function(){
alert(this.text);
}
}
I am having some Problems with JavaScript. I have got the following code:
<html>
<head>
<title>Test</title>
<script type="text/javascript">
function Control(){
var name;
this.setName = function(newName){
name = newName;
};
this.getName = function(){
return name;
};
}
function SpecializedControl(){
}
SpecializedControl.prototype = new Control();
function Form(){
var formControls = [];
this.addControl = function(control){
formControls.push(control);
alert(formControls[0].getName());
};
}
var form = new Form();
var control1 = new SpecializedControl();
control1.setName("Control1");
form.addControl(control1);
var control2 = new SpecializedControl();
control2.setName("Control2");
form.addControl(control2);
</script>
</head>
<body>
</body>
</html>
The SpecializedControl inherits from the Control class.
The addControl function in the Form class just adds the control to an array.
The problem is that when I add more than one SpecializedControl, the values in the array are kind of overriden, that means that when I access the first item in the array, which should be "Control1" I get "Control2". Control1 is not in the array any longer.
When I use the same function with Control objects as parameters everything works as expected.
Does someone know why this happens and what can be done to correct this?
The values in the array aren't being overridden; the problem is both controls share the same name variable. Because the Control function only executes once, there is only one name variable ever declared.
You have two main options to fix this. (1) Make name an instance variable which is specific to each individual control (e.g. this._name). (2) Execute the Control function from inside the SpecializedControl constructor. (Actually, IMO, for a well-balanced and thorough inheritence model you should be doing a bit of both of these methods).
Here are three working solutions. The first two use options (1) and (2) respectively. The third combines both methods and is the way I would do it (but requires joi).
Option 1:
function Control(){
this.setName = function(newName){
this._name = newName;
};
this.getName = function(){
return this._name;
};
}
Option 2:
function SpecializedControl(){
Control.apply(this, arguments);
}
Option 3:
var Control = joi.Unit.sub(function() {
function constructor() {
this.base();
}
constructor.prototype = {
'#name': null,
setName: function(name) {
this['#name'] = name;
},
getName: function() {
return this['#name'];
}
};
return constructor;
}());
var SpecializedControl = Control.sub(function() {
function constructor() {
this.base();
}
return constructor;
}());
var Form = joi.Unit.sub(function() {
function constructor() {
this.base();
this['#formControls'] = [];
}
constructor.prototype = {
'#formControls': null,
addControl: function(control) {
this['#formControls'].push(control);
alert(this['#formControls'][0].getName());
}
};
return constructor;
}());
var form = new Form();
var control1 = new SpecializedControl();
control1.setName("Control1");
form.addControl(control1);
var control2 = new SpecializedControl();
control2.setName("Control2");
form.addControl(control2);
Your get/setName functions are getting/setting the value of the name variable that is local to the Control constructor function.
The only time you invoked that function, was to create an instance as the prototype object of SpecializedControl. So every time you invoked setName from a SpecializedControl instance, that single variable is being updated.
So since the get/setName methods that reference that variable are in the prototype chain of all SpecializedControl instances, they'll all be observing the same name.
In setName, you should do...
this.name = newName;
And in getName, you should do...
return this.name;
Lets say I have this class:
function classA(n){
this.name = n
}
classA.prototype.getName = function(){
return this.name
}
var x = new classA('john')
console.log(x.getName())
My question is: can I group multiple methods inside a namespace? So I would like to do that:
var x = new classA('john')
console.log(x.CONSTANT.getName())
So I would like to call some methods as x.someMethod() and others as x.CONSTANT.otherMethod()
PS: I'm looking for a cross-browser method. Bind is not working in Safari and IE9.
You can do it, for example, via bind. Google es5 shim for implementation of bind in browsers, which don't support it natively.
function MyClass(name) {
this.name = name;
this.CONSTANT.otherMethod = this.CONSTANT.otherMethod.bind(this);
}
MyClass.prototype.CONSTANT = {
otherMethod: function() {
alert(this.name);
}
};
As far as I know a constant is just a property and it can't contain methods, you need to separate your objects and use methods to have the same effect:
function A (id) {
this.id = id;
this.showId = function () { return this.id; }
};
function B (a) {
this.a = a;
this.getA = function () { return this.a; }
}
var a = new A(12);
var b = new B(a);
b.getA().showId();
edit:
You can use a literal object as follow
function B (id) {
this.id = id;
this.CONSTANT = { otherMethod: function () { alert("..."); } };
someMethod = function () { return this.id; }
}
but the literal CONSTANT object can't access B-object methods,
Consider the #kirilloid post to round this.
You can, but you have to be careful because it won't act like you think it will. The this for the method will be the namespace, not the root object.
For example, in x.CONSTANT.getName(), the this object will be x.CONSTANT, and not x.
Here's some sample code which kinda does what you ask (or in jsfiddle):
function MyClass() {}
MyClass.prototype.CONSTANT = {
getName: function() {
alert('Foo');
}
};
var c = new MyClass();
c.CONSTANT.getName();
To make sure the this is right, you need to do much more.
You can use getters/setters (read this article) to achieve this. For example you may define it like this:
classA.prototype.__defineGetter__('CONSTANT', function() {
var that = this;
return {
getName: function() {
return that.name;
}
};
});
Note that holding reference to the object. It will work now
x = new classA('test');
x.CONSTANT.getName();
// result - test