This question already has answers here:
Why wouldn't I use Child.prototype = Parent.Prototype rather than Child.prototype = new Parent(); for Javascript inheritance?
(3 answers)
Closed 7 years ago.
Hi I'm new in Javascript OO and want to know more about about inheritance. Hope you can provide some advice!
I see this great post:
How to "properly" create a custom object in JavaScript?
which talks about how a class is inherited as I see in other websites, ex.:
function man(x) {
this.x = x;
this.y = 2;
}
man.prototype.name = "man";
man.prototype.two = function() {
this.y = "two";
}
function shawn() {
man.apply(this, arguments);
};
shawn.prototype = new man;
The above post claims that in order not to call "man"'s constructor while inheriting, one can use a helper like this instead:
function subclassOf(base) {
_subclassOf.prototype= base.prototype;
return new _subclassOf();
}
function _subclassOf() {};
shawn.prototype = subclassOf(man);
While I understand its intention, I don't see why we can't call
shawn.prototype = man.prototype;
I see it works exactly the same. Or is there something I'm missing? Thanks in advance!
Well, examples are better than words in my humble opinion. All below examples are using your code, with some additions.
The first example will prove that using shawn.prototype = new man; you're calling the constructor twice
function man(h, w) {
SendMessage("man is created with height " + h + " and weight " + w);
this.height = h;
this.weight = w;
}
man.prototype.name = "man";
man.prototype.double = function() {
this.height *= 2;
this.weigth *= 2;
}
function shawn() {
man.apply(this, arguments);
};
function SendMessage(msg) {
document.getElementById("Console").innerHTML += msg + "<br />";
}
window.onload = function() {
shawn.prototype = new man;
var p = new shawn(180, 90);
SendMessage("Shawn height: " + p.height);
}
<div id="Console"></div>
As you see, the constructor is called twice - once with no arguments then with the actual arguments you give it.
The second example just proves that using the subclassOf solve that "double calling" issue.
function man(h, w) {
SendMessage("man is created with height " + h + " and weight " + w);
this.height = h;
this.weight = w;
}
man.prototype.name = "man";
man.prototype.double = function() {
this.height *= 2;
this.weigth *= 2;
}
function shawn() {
man.apply(this, arguments);
};
function subclassOf(base) {
_subclassOf.prototype= base.prototype;
return new _subclassOf();
}
function _subclassOf() {};
function SendMessage(msg) {
document.getElementById("Console").innerHTML += msg + "<br />";
}
window.onload = function() {
shawn.prototype = subclassOf(man);
var p = new shawn(180, 90);
SendMessage("Shawn height: " + p.height);
}
<div id="Console"></div>
The third example shows what's wrong with your idea of shawn.prototype = man.prototype and I'll explain. shawn inherits from man so I've added new method that should affect only shawn, called marriage (that of course cause him to gain some weight ;)) - that method should not affect the base class man as it's not inheriting from shawn, inheritance is one way only. But.... as you see in the example, ordinary man can also get married - big problem.
function man(h, w) {
SendMessage("man is created with height " + h + " and weight " + w);
this.height = h;
this.weight = w;
}
man.prototype.name = "man";
man.prototype.double = function() {
this.height *= 2;
this.weight *= 2;
}
function shawn() {
man.apply(this, arguments);
};
function SendMessage(msg) {
document.getElementById("Console").innerHTML += msg + "<br />";
}
window.onload = function() {
shawn.prototype = man.prototype;
var p = new shawn(180, 90);
SendMessage("Shawn height: " + p.height);
p.double();
SendMessage("Shawn height: " + p.height);
shawn.prototype.marriage = function() {
SendMessage("Shawn is getting married, current weight: " + this.weight);
this.weight += 20;
};
p.marriage();
SendMessage("Shawn weight: " + p.weight);
var q = new man(170, 60);
q.marriage();
SendMessage("q weight: " + q.weight);
}
<div id="Console"></div>
Finally, the fourth example shows that using the subclassOf everything work fine, as shawn inherits man properly, and marriage is not passed to the base class.
function man(h, w) {
SendMessage("man is created with height " + h + " and weight " + w);
this.height = h;
this.weight = w;
}
man.prototype.name = "man";
man.prototype.double = function() {
this.height *= 2;
this.weight *= 2;
}
function shawn() {
man.apply(this, arguments);
};
function subclassOf(base) {
_subclassOf.prototype= base.prototype;
return new _subclassOf();
}
function _subclassOf() {};
function SendMessage(msg) {
document.getElementById("Console").innerHTML += msg + "<br />";
}
window.onload = function() {
shawn.prototype = subclassOf(man);
var p = new shawn(180, 90);
SendMessage("Shawn height: " + p.height);
p.double();
SendMessage("Shawn height: " + p.height);
shawn.prototype.marriage = function() {
SendMessage("Shawn is getting married, current weight: " + this.weight);
this.weight += 20;
};
p.marriage();
SendMessage("Shawn weight: " + p.weight);
var q = new man(170, 60);
if (q.marriage)
q.marriage();
else
SendMessage("marriage is undefined for man");
SendMessage("q weight: " + q.weight);
}
<div id="Console"></div>
Hope this makes some sense! :)
shawn.prototype = man.prototype;
will share the prototypes, ie modifying one will modify the other.
shawn.prototype = new man;
will set shawn.prototype to a newly created object which inherits from man.prototype and thus changes to it won't propagate to man instances.
However, using new means that the constructor man() will be executed, which can have undesired side-effects.
It's better to manually clone the prototype via
shawn.prototype = Object.create(man.prototype);
if available or a custom clone function (which works the same way as your subclassOf)
shawn.prototype = clone(man.prototype);
otherwise.
In addition to #Shadow's excellent answer, you can think of shawn.prototype = man.prototype as meaning "shawn is the same as man", rather than, "shawn is a man"
Related
This question already has answers here:
Why wouldn't I use Child.prototype = Parent.Prototype rather than Child.prototype = new Parent(); for Javascript inheritance?
(3 answers)
Closed 7 years ago.
Hi I'm new in Javascript OO and want to know more about about inheritance. Hope you can provide some advice!
I see this great post:
How to "properly" create a custom object in JavaScript?
which talks about how a class is inherited as I see in other websites, ex.:
function man(x) {
this.x = x;
this.y = 2;
}
man.prototype.name = "man";
man.prototype.two = function() {
this.y = "two";
}
function shawn() {
man.apply(this, arguments);
};
shawn.prototype = new man;
The above post claims that in order not to call "man"'s constructor while inheriting, one can use a helper like this instead:
function subclassOf(base) {
_subclassOf.prototype= base.prototype;
return new _subclassOf();
}
function _subclassOf() {};
shawn.prototype = subclassOf(man);
While I understand its intention, I don't see why we can't call
shawn.prototype = man.prototype;
I see it works exactly the same. Or is there something I'm missing? Thanks in advance!
Well, examples are better than words in my humble opinion. All below examples are using your code, with some additions.
The first example will prove that using shawn.prototype = new man; you're calling the constructor twice
function man(h, w) {
SendMessage("man is created with height " + h + " and weight " + w);
this.height = h;
this.weight = w;
}
man.prototype.name = "man";
man.prototype.double = function() {
this.height *= 2;
this.weigth *= 2;
}
function shawn() {
man.apply(this, arguments);
};
function SendMessage(msg) {
document.getElementById("Console").innerHTML += msg + "<br />";
}
window.onload = function() {
shawn.prototype = new man;
var p = new shawn(180, 90);
SendMessage("Shawn height: " + p.height);
}
<div id="Console"></div>
As you see, the constructor is called twice - once with no arguments then with the actual arguments you give it.
The second example just proves that using the subclassOf solve that "double calling" issue.
function man(h, w) {
SendMessage("man is created with height " + h + " and weight " + w);
this.height = h;
this.weight = w;
}
man.prototype.name = "man";
man.prototype.double = function() {
this.height *= 2;
this.weigth *= 2;
}
function shawn() {
man.apply(this, arguments);
};
function subclassOf(base) {
_subclassOf.prototype= base.prototype;
return new _subclassOf();
}
function _subclassOf() {};
function SendMessage(msg) {
document.getElementById("Console").innerHTML += msg + "<br />";
}
window.onload = function() {
shawn.prototype = subclassOf(man);
var p = new shawn(180, 90);
SendMessage("Shawn height: " + p.height);
}
<div id="Console"></div>
The third example shows what's wrong with your idea of shawn.prototype = man.prototype and I'll explain. shawn inherits from man so I've added new method that should affect only shawn, called marriage (that of course cause him to gain some weight ;)) - that method should not affect the base class man as it's not inheriting from shawn, inheritance is one way only. But.... as you see in the example, ordinary man can also get married - big problem.
function man(h, w) {
SendMessage("man is created with height " + h + " and weight " + w);
this.height = h;
this.weight = w;
}
man.prototype.name = "man";
man.prototype.double = function() {
this.height *= 2;
this.weight *= 2;
}
function shawn() {
man.apply(this, arguments);
};
function SendMessage(msg) {
document.getElementById("Console").innerHTML += msg + "<br />";
}
window.onload = function() {
shawn.prototype = man.prototype;
var p = new shawn(180, 90);
SendMessage("Shawn height: " + p.height);
p.double();
SendMessage("Shawn height: " + p.height);
shawn.prototype.marriage = function() {
SendMessage("Shawn is getting married, current weight: " + this.weight);
this.weight += 20;
};
p.marriage();
SendMessage("Shawn weight: " + p.weight);
var q = new man(170, 60);
q.marriage();
SendMessage("q weight: " + q.weight);
}
<div id="Console"></div>
Finally, the fourth example shows that using the subclassOf everything work fine, as shawn inherits man properly, and marriage is not passed to the base class.
function man(h, w) {
SendMessage("man is created with height " + h + " and weight " + w);
this.height = h;
this.weight = w;
}
man.prototype.name = "man";
man.prototype.double = function() {
this.height *= 2;
this.weight *= 2;
}
function shawn() {
man.apply(this, arguments);
};
function subclassOf(base) {
_subclassOf.prototype= base.prototype;
return new _subclassOf();
}
function _subclassOf() {};
function SendMessage(msg) {
document.getElementById("Console").innerHTML += msg + "<br />";
}
window.onload = function() {
shawn.prototype = subclassOf(man);
var p = new shawn(180, 90);
SendMessage("Shawn height: " + p.height);
p.double();
SendMessage("Shawn height: " + p.height);
shawn.prototype.marriage = function() {
SendMessage("Shawn is getting married, current weight: " + this.weight);
this.weight += 20;
};
p.marriage();
SendMessage("Shawn weight: " + p.weight);
var q = new man(170, 60);
if (q.marriage)
q.marriage();
else
SendMessage("marriage is undefined for man");
SendMessage("q weight: " + q.weight);
}
<div id="Console"></div>
Hope this makes some sense! :)
shawn.prototype = man.prototype;
will share the prototypes, ie modifying one will modify the other.
shawn.prototype = new man;
will set shawn.prototype to a newly created object which inherits from man.prototype and thus changes to it won't propagate to man instances.
However, using new means that the constructor man() will be executed, which can have undesired side-effects.
It's better to manually clone the prototype via
shawn.prototype = Object.create(man.prototype);
if available or a custom clone function (which works the same way as your subclassOf)
shawn.prototype = clone(man.prototype);
otherwise.
In addition to #Shadow's excellent answer, you can think of shawn.prototype = man.prototype as meaning "shawn is the same as man", rather than, "shawn is a man"
Am using raphael.js to render shapes to a canvas, but the problem is that there is a memory leak and the page crashes after some time. Can some one help me how it can be handled ? I am using underscore.js to handle the loop while removing still no luck. I tried changing the library to svg.js but the problem was worse. Thanks in advance, the code is as follows:
var paper = new Raphael(document.getElementById('visualizerContainer'), 545, 545);
paper.canvas.style.backgroundColor = '#000';
Raphael.fn.line = function(startX, startY, endX, endY){
return this.path('M' + startX + ' ' + startY + ' L' + endX + ' ' + endY);
};
var flag=0;
window.particleObject={
"id":"",
"obj":[]
}
function addParticles(){
if(flag) removeParticles(particleObject);
for(var i=0;i<5000;i++){
var x=Math.floor((Math.random() * 628) + 1);
var y=Math.floor((Math.random() * 571) + 1);
var circleName = "var circle"+i;
circleName = paper.circle(x, y, 1);
//var fillColor='#'+ ('000000' + (Math.random()*0xFFFFFF<<0).toString(16)).slice(-6);
circleName.attr("fill", "#0F0");
circleName.attr("stroke", "#ff");
particleObject.id=i;
particleObject.obj.push(circleName);
}
flag=1;
}
function removeParticles(particleObject){
_.map(particleObject.obj, function(o) {o.remove(); });
}
$.getJSON('assets/data/jp_Wall.json', function(data) {
$.each(data.features, function(id, obj) {
var wall = paper.line(obj.x1, obj.y1, obj.x2, obj.y2).attr({stroke:'red',fill:'red',"stroke-width": 3});
});
});
$.getJSON('assets/data/jp_ImpossibleSpace.json', function(data) {
$.each(data.features, function(id, obj) {
var width=(obj.x2-obj.x1);
var height=(obj.y2-obj.y1);
var top_left_x=obj.x1;
var top_left_y=obj.y1;
var rectangle = paper.rect(top_left_x, top_left_y, width, height).attr({stroke:'blue',"stroke-width": 3, "stroke-dasharray": "."});
});
});
addParticles();
setInterval(addParticles, 500);
Wall.json-->https://jsonblob.com/54f47ff7e4b0ae1ed0b1fcf2
jp_ImpossibleSpace.json-->https://jsonblob.com/54f48114e4b0ae1ed0b1fd04
It was my problem only. I forgot to clear my array of objects in window variable
I've been trying to make a julia set viewer over at my site http://thejamespaterson.com/scripts/julia/, but I'm currently having trouble getting the program to display the correct julia set. For example, when testing with C value 0+0i, I get the following image:
The result is supposed to be a circle. I'm not sure why this is happening. I wrote my own complex numbers library and plotting functions, and they are posted below. Any help would be appreciated;
function complexNum(real, imaginary) {
this.real = real;
this.imaginary = imaginary;
return this;
}
function addComplex(c1, c2) {
this.real = c1.real + c2.real;
this.imaginary = c1.imaginary + c2.imaginary;
return this;
}
function multComplex(c1, c2) {
this.real = (c1.real * c2.real) - (c1.imaginary * c2.imaginary);
this.imaginary = (c1.real * c2.imaginary) + (c2.real * c1.imaginary);
return this;
}
function dispComplex(c) {
var sign = '';
if (c.imaginary >= 0) {
sign = '+';
}
return c.real + sign + c.imaginary + "i";
}
function getComplexModulus(c) {
return Math.sqrt((c.real * c.real) + (c.imaginary * c.imaginary));
}
//globals
var MAXITERATION = 100;
var BOUNDARY = 4;
var CANVASID = "juliaDraw";
var CONTEXT = document.getElementById("juliaDraw").getContext('2d');
var HEIGHT = 750;
var WIDTH = 750;
var juliaImageData = CONTEXT.createImageData(WIDTH, HEIGHT);
function readInput(inputID) {
return document.getElementById(inputID).value;
}
function drawPointOnCanvas(x, y, color) {
//console.log('drawing pixel at '+x+','+y);
CONTEXT.fillStyle = color;
CONTEXT.fillRect(x, y, 1, 1);
}
function createArray(length) {
var arr = new Array(length || 0),
i = length;
if (arguments.length > 1) {
var args = Array.prototype.slice.call(arguments, 1);
while (i--) arr[length - 1 - i] = createArray.apply(this, args);
}
return arr;
}
function doesPointEscape(c, complexNum) {
var iterations = 0;
var escaped = false;
while ((!escaped) && (iterations < MAXITERATION)) {
if (getComplexModulus(complexNum) > BOUNDARY) {
escaped = true;
}
complexNum = addComplex(multComplex(complexNum, complexNum), c);
iterations++;
}
if (escaped) {
return true;
} else {
return false;
}
}
function plotJuliaSet(canvasID, width, height, c, start, stepsize) {
var complexNumberArray = createArray(width + 1, height + 1);
var doesPointEscapeArray = createArray(width + 1, height + 1);
var real = start.real;
var imaginary = start.imaginary;
console.log('====Drawing Set====');
console.log('c = ' + dispComplex(c));
for (var x = 0; x <= width; x++) {
imaginary = start.imaginary;
for (var y = 0; y <= height; y++) {
complexNumberArray[x][y] = new complexNum(real, imaginary);
doesPointEscapeArray[x][y] = doesPointEscape(c, complexNumberArray[x][y]);
if (doesPointEscapeArray[x][y]) {
//drawPointOnCanvas(x, y,'blue');
} else {
drawPointOnCanvas(x, y, 'black');
//console.log('point '+dispComplex(complexNumberArray[x][y])+' does not escape');
}
imaginary = imaginary - stepsize;
}
real = real + stepsize;
}
//CONTEXT.putImageData(juliaImageData, 0, 0);
console.log('done');
}
function defaultDraw() {
CONTEXT.clearRect(0, 0, WIDTH, HEIGHT);
var start = new complexNum(-2, 2);
var c = new complexNum(0, 0);
plotJuliaSet(CANVASID, WIDTH, HEIGHT, c, start, 2 / 350);
}
function drawJulia() {
CONTEXT.clearRect(0, 0, WIDTH, HEIGHT);
var start = new complexNum(-2, 2);
var c = new complexNum(readInput('realValue') * 1, readInput('imagValue') * 1);
plotJuliaSet(CANVASID, WIDTH, HEIGHT, c, start, 2 / 350);
}
<!doctype html>
<html>
<head>
<title>Julia Set Viewer</title>
<style>
.desc {
float: right;
width: 300px;
}
#juliaDraw {
border: 1px dotted;
float: left;
}
</style>
</head>
<body>
<div class="desc">
<h1>Julia Set Viewer</h1>
<p>You can view Julia sets with this simple online tool. Don't know what a Julia set is? Learn about it here.
This script uses a complex number library that I built to handle the arithmetic required to process these images. The source code is hosted on my github.
</p>
</div>
<canvas id="juliaDraw" width=750 height=750 onClick="defaultDraw()"></canvas>
<div class="controls">
<form>
<label>Real:
<input type="text" id="realValue" value="0">
</label>
<label>Imag:
<input type="text" id="imagValue" value="0">
</label>
<input type="button" onClick="drawJulia()">
</form>
</div>
<script src="complex.js"></script>
<script src="juliaset.js"></script>
</body>
</html>
The problem stems from confusion over the use of the this pointer in Javascript.
Change your Julia calculation in doesPointEscape() to
complexNum = new addComplex(new multComplex(complexNum, complexNum), c);
and it works.
This will return a new complex number from multComplex, then add it to c and return a new complex number from addComplex that is assigned to complexNum.
Your multComplex and addComplex functions use the this pointer, but for the this pointer to be referring to one of your complex numbers, you would have to be calling the function on an existing one, or calling new to create a new one.
Alternatively, you could rewrite your multComplex() and addComplex() functions as
function multComplex(c1, c2) {
var real = (c1.real * c2.real) - (c1.imaginary * c2.imaginary);
var imaginary = (c1.real * c2.imaginary) + (c2.real * c1.imaginary);
return new ComplexNum(real, imaginary);
}
function addComplex(c1, c2) {
var real = c1.real + c2.real;
var imaginary = c1.imaginary + c2.imaginary;
return new ComplexNum(real, imaginary);
}
then your doesPointEscape() function should work as-is.
this code works, but my question is I dont understand the purpose of var that = this. Why do I need to reference it like that to pass it to setInterval. I read about 'this' in http://www.sitepoint.com/what-is-this-in-javascript/, but it doesn't really answer my question
my JavaScript code
function spinClass(imageSource, height, width, forward, el){
this.src = imageSource;
this.spinFoward = forward;
this.element = document.getElementById(el);
this.height = height;
this.width = width;
this.d = 0;
var img = document.createElement("img");
img.setAttribute('src', this.src);
img.setAttribute('height', this.height);
img.setAttribute('width', this.width);
this.element.appendChild(img);
this.letSpin = function letSpin(){
//alert(this.d);
var that = this;
img.style.transform = "rotate(" + this.d + "deg)";
img.style.WebkitTransform= "rotate(" + this.d + "deg)";
img.style.MozTransform= "rotate(" + this.d + "deg)";
img.style.msTransform= "rotate(" + this.d + "deg)";
img.style.OTransform= "rotate(" + this.d + "deg)";
//alert(this.spinFoward);
if (this.spinFoward == true){
this.d++;
}else{
this.d--;
}
setInterval(function(){that.letSpin();}, 20);
};
}
The value of the this keyword is tied to the function it's used within and to how that function was called.
That includes both letSpin() and the short, anonymous function being passed to setTimeout(). And, the anonymous function won't automatically inherit or share the this value from letSpin() just by its placement.
So, you have to either capture the value in a variable with another name.
var that = this;
Or, bind the function so it will use a particular value when it's called.
setTimeout(function(){
this.letSpin();
}.bind(this), 20);
And, with bind, you can also pass the method without the anonymous function.
setTimeout(this.letSpin.bind(this), 20);
Instantiate object with this function:
function newClass(klass) {
var obj = new klass;
$.map(obj, function(value, key) {
if (typeof value == "function") {
obj[key] = value.bind(obj);
}
});
return obj;
}
This will do automatic binding of all function, so you will get object in habitual OOP style,
when methods inside objects has context of its object.
So you instantiate you objects not through the:
var obj = new spinClass();
But:
var obj = newClass(spinClass);
I used the following code to change the stroke-width when mouseover the path, but it doesn't work... I have checked many solutions on this matter, they seem to use the same solution as mine. My canvas is Raphael("svgContainer", 100, 100);
function drawPath(i,floorlevel,pointsNum){
var x1 = floorlevel[i].x;
var y1 = floorlevel[i].y;
var x2 = floorlevel[i+1].x;
var y2 = floorlevel[i+1].y;
var p = canvas.path("M"+x1 +" "+ y1);
p.attr("stroke", get_random_color());
p.attr("stroke-width",4);
p.attr("id",floorlevel[i].node+floorlevel[i+1].node);
p.animate({path:"M"+x1 +" "+ y1+ " L" + x2 +" "+ y2}, 1000);
var set = canvas.set();
var hoverIn = function() {
this.attr({"stroke-width": 10});
};
var hoverOut = function() {
this.attr({"stroke-width": 10});
}
p.hover(hoverIn, hoverOut, p, p);
set.push(p);
}
It seems to work fine when I sub in dummy values for the arguments you pass to the function:
http://jsfiddle.net/hKCDg/
I noticed you have the same stroke-width for hoverIn and hoverOut, which defeats the purpose.
var hoverIn = function() {
this.attr({"stroke-width": 10});
};
var hoverOut = function() {
this.attr({"stroke-width": 10});
};
I changed the latter to 5 in the demo here for visual effect.
Perhaps there's an error in the values you pass to the function?