Javascript subclass code explanation - javascript

I have this piece of code (from "Secrets of the Javascript Ninja"):
(function() {
var initializing = false,
superPattern = /xyz/.test(function() { xyz; }) ? /\b_super\b/ : /.*/;
Object.subClass = function(properties) {
var _super = this.prototype;
initializing = true;
var proto = new this();
initializing = false;
for (var name in properties) {
proto[name] = typeof properties[name] == "function" &&
typeof _super[name] == "function" &&
superPattern.test(properties[name]) ?
(function(name, fn) {
return function() {
var tmp = this._super;
this._super = _super[name];
var ret = fn.apply(this, arguments);
this._super = tmp;
return ret;
};
})(name, properties[name])
:
properties[name];
}
function Class() {
if (!initializing && this.init) {
this.init.apply(this, arguments);
}
}
Class.prototype = proto;
Class.constructor = Class; // Why do we need this?
Class.subClass = arguments.callee; // Why is this not Object.subClass?
return Class;
};
})();
var Person = Object.subClass({
init: function(isDancing) {
this.dancing = isDancing;
return true;
},
dance: function() {
return this.dancing;
}
});
var person = new Person(true);
alert (person.dance());
I am having a hard time understanding 2 things:
Why is Class.constructor = Class?
Why do we need to override it at all? I tried commenting it out and it worked perfectly fine.
Why do we have Class.subClass = arguments.callee?
I tried using Class.subClass = Object.subClass (which makes more sense?) and it seems to work fine.

Why is Class.constructor = Class?
I don't know, it does not make any sense. It probably was supposed to be proto.constructor = Class;.
Why do we have Class.subClass = arguments.callee? I tried using Class.subClass = Object.subClass (which makes more sense?) and it seems to work fine.
Yes, that's what he meant. arguments.callee is deprecated, but had the same effect. Your version is better.
You might also have a look at Is John Resig's Javascript inheritance snippet deprecated?.

Related

Best design pattern for pipable wrapper when this value matters

So... I've been coding up a wrapper (I'm calling it Medium) that seems to be working just fine. Problem is it's pretty ugly:
var Medium = function(fn, args){
var __fn = cloneFunction(fn);
var __length = fn.length;
var __args = args.length >= fn.length ? args : fill(fn.length, args);
var __self = this;
this.getFn = function(){ return cloneFunction(__fn) }
this.getLength = function(){ return __length }
this.getArgs = function(){ return clone(__args) }
this.pass = function(sprdArgs){ ... }
this.force = function(sprdArgs){ ... }
this.call = function(sprdArgs){ ... }
this.apply = function(thisArg, sprdArgs){ ... }
this.bind = function(thisArg, sprdArgs){ ... }
this.rewrite = function(fn){ ... }
this.recharge = function(sprdArgs){ ... }
this.clear = function(){ ... }
this.reload = function(args){ ... }
this.use = function(funcName, sprdArgs){ ... }
this.method = function(thisArg, funcName, args){ ... }
}
At first I was making it as a simple constructor with some private attributes and getters and the prototype:
var Medium = function(fn, args){
var __fn = cloneFunction(fn);
var __length = fn.length;
var __args = args.length >= fn.length ? args : fill(fn.length, args);
var __self = this;
this.getFn = function(){ return cloneFunction(__fn) };
...
}
Medium.prototype.pass = function(sprdArgs){
var fnArgs = this.getArgs();
return arguments.length > 0
? this.use("load", arguments)
: this.use("update", fnArgs);
}
...
Then I thought it would be cool to add some methods visible to the Medium itself but not to the user. I haven't found a good solution, so I just moved this methods to a getter that returns the called function only if the this values matches:
var Medium = function(fn, args){
...
this.method = function(thisArg, funcName, args){
var funcs = {
load: function(args){ ... },
update: function(params){ ... },
execute: function(){ ... }
};
return thisArg === __self
? funcs[funcName].apply(thisArg, args)
: null;
}
}
Medium.prototype.use = function(funcName, sprdArgs){
var args = clone(arguments);
var sprdArgs = filter(args, function(elem, i){
return i !== 0;
});
return this.method(this, funcName, sprdArgs);
}
Thing is, I also have a pipe function and it uses apply. Result: I pass the functions to my pipe, and the this value goes null. Nothing big 99% of the time, but with Medium it simply breaks all the getter's stuff:
function pipe(fns){
var fns = clone(arguments);
return function(vals){
var vals = clone(arguments)
return reduce(fns, function(args, fn, i){
var params = i === 0 ? args : [args];
return fn.apply(null, params);
}, vals);
}
}
In order to repare it, Medium uses prototype no more. All the functions are declared inside the constructor. Unfortunally, this makes it hard to read - and I think it may cause other problems since in the research I have done about wrappers people usually recomended to implement behaviour on the prototype.
Just in case, I have made a JSFiddle of the whole thing. You can see it here: https://jsfiddle.net/hguerra/0q1urc9o/15/
OBS: You can see some strange functions like filter, reduce and stuff. Yeah, they are the Array's ones. The build of JS I'm using fails to have any array-related functions so I have made my better to pollyfeel everyone of them. For what I can say, they all work as you should expect for expection of push and unshift; these two returns the new array instead of the new length. Anyway, these functions are also on fiddle so you can check there if you have any doubts.

extending objects in Javascript

I'm kinda lost in getting the object extending to work. I have read dozens of sites related to this topic, but I'm still no wiser. It seems that everyone uses it's own approach to make this work, and so do I , I'm trying to find the best approach for extending/inheriting objects.
I am also aware that there are tons of frameworks/plugins out there to cover this functionality, but i'd just like to understand how it works in general. Not mentioning that most of these frameworks include lots of other stuff I may never use, hence I'm trying to make my own.
I was able to extend an object , everything seemed to be ok until I started adding methods to the target object. To understand the issue, please see the below example...
or just try this JSFiddle
The thing is, that after initializing the new instance of Rabbit object, I wasn't able to access Rabbit's method changeName.
And I don't understand why it's happening, i.e why it doesn't recognize the method.
[*] Please see my updated code below (also the JFiddle), everything now seems to be working ok.
Can anoyne please advise, if this is a good approach or what am I missing?
var Class = (function(NewClass){
if(NewClass.length != 0){
var extend = function(target, source, args) {
Object.getOwnPropertyNames(source).forEach(function(propName) {
if(propName !== "Extend")
{
Object.defineProperty(
target, propName,
Object.getOwnPropertyDescriptor(source, propName)
);
}
if (typeof source[propName] !== 'undefined'){
delete source[propName];
}
});
return target;
};
var inherit = function(source, args){
var baseClass = Object.getPrototypeOf(this);
baseClass.prototype = extend.call(this, baseClass, source, args);
};
if(NewClass.Extend){
var Class = function(){ //// New Class Constructor ////
if(typeof NewClass.Extend === 'function'){
NewClass.Extend.apply(this, arguments);
inherit.call(this, NewClass.Extend);
console.log(NewClass)
inherit.call(this, NewClass, arguments);
if(NewClass.Initialize){
NewClass.Initialize.call(this, arguments);
}
}
};
Class.prototype.constructor = Class;
return Class;
}
}
});
var Animal =(function(args){//// constructor ////
var self = this;
self.name = typeof args !== 'undefined' ? args.name : null;
self.bags = 0;
});
var Rabbit = new Class({
Extend: Animal ,
Initialize: function(){
console.log(this.name)
},
changeName: function(a){
console.log(this.name)
}
});
var LittleRabbit = new Rabbit({name: "LittleRabbit", type: "None"});
console.log(LittleRabbit instanceof Rabbit)
console.log(LittleRabbit)
LittleRabbit.changeName("alex");
your extend function work wrong, because Object.getPrototypeOf return prototype, so in more cases it object
var extend = function(source, args){
var baseClass = Object.getPrototypeOf(this);
source.apply(this, args);
//so here you just add property prototype to object, and this not same as set prototype to function.
baseClass.prototype = Object.create(source.prototype);
};
So you can fix this like in snippet below:
function Class(args) {
if (arguments.length != 0) {
var C = function() {
if (typeof args.Extend == 'function') {
args.Extend.apply(this, arguments)
}
if (args.Initialize) {
args.Initialize.call(this);
}
};
if (typeof args.Extend == 'function') {
C.prototype = Object.create(args.Extend.prototype);
}
Object.keys(args).filter(function(el) {
return ['Extend', 'Initialize'].indexOf(el) == -1
}).forEach(function(el) {
C.prototype[el] = args[el];
});
return C;
}
};
var Animal = (function(args) { //// constructor ////
var self = this;
self.name = typeof args !== 'undefined' ? args.name : null;
self.bags = 0;
});
var Rabbit = Class({
Extend: Animal,
Initialize: function() {
console.log(this.name);
},
changeName: function(a) {
this.name = a;
}
});
var LittleRabbit = new Rabbit({
name: "LittleRabbit",
type: "None"
});
console.log(LittleRabbit instanceof Rabbit);
console.log(LittleRabbit instanceof Animal);
console.log(LittleRabbit.name);
LittleRabbit.changeName('new little rabbit');
console.log(LittleRabbit.name);
I would suggest reading the MDN article detailing the JavaScript object model. It contains examples of "manually" subclassing:
function Employee() {
this.name = "";
this.dept = "general";
}
function Manager() {
Employee.call(this);
this.reports = [];
}
Manager.prototype = Object.create(Employee.prototype);
function WorkerBee() {
Employee.call(this);
this.projects = [];
}
WorkerBee.prototype = Object.create(Employee.prototype)
Translating your example to this style is simple:
function Animal(name) {
this.name = name;
this.bags = 0;
}
function Rabbit(name) {
Animal.call(this, name);
console.log(this.name);
}
Rabbit.prototype = Object.create(Animal.prototype);
Rabbit.prototype.changeName = function(name) {
this.name = name;
};
Then you can easily run your example, modified a bit:
var LittleRabbit = new Rabbit("LittleRabbit");
console.log(LittleRabbit instanceof Rabbit)
console.log(LittleRabbit)
LittleRabbit.changeName("new name");
Once you understand this, I'd recommend not building your own class creation mechanism and just use ES6 classes:
class Animal {
constructor(name) {
this.name = name;
this.bags = 0;
}
}
class Rabbit extends Animal {
constructor(name) {
super(name);
console.log(this.name);
}
changeName(name) {
this.name = name;
}
}
You can see this example in the Babel REPL. Some browsers/js runtimes natively support ES6 classes already, but you can use Babel to translate your code to ES5 for environments that don't yet.
As an aside, there is actually more that needs to be done to subclass completely correctly. A more complete example (that may not work in all environments) is this:
function Animal() {}
function Rabbit() {
Animal.call(this);
}
Rabbit.prototype = Object.create(Animal.prototype);
Rabbit.prototype.constructor = Rabbit;
Rabbit.__proto__ = Animal;
May ES6 class inheritance an option for you:
'use strict';
class Animal {
constructor( name ) {
this.name = name;
}
changeName( name ) {
this.name = name;
}
}
class Rabbit extends Animal {
constructor() {
super( 'rabbit' );
}
}
let littleRabbit = new Rabbit();
console.log( littleRabbit.name ); //log default name
littleRabbit.changeName( 'littleRabbit' ); //executing an method of animal class
console.log( littleRabbit.name ); //log changed name
You don't need the "overhead" for making OOP inheritance for old good javascript because there are "translators" out there which translate your es6 code to es5 code. For Example babel: https://babeljs.io/
I think it is worth to give it a try...

Javascript class properties behave as shared variable

I have the following declaration to easily create classes in Javascript. The Class is from this article: http://ejohn.org/blog/simple-javascript-inheritance/
JSfiddle: http://jsfiddle.net/xUCQp/
The problem is that it seems like that options of the object seems shared through the objects, but I need them to be instance variables. What is the problem with the code?
Code:
/* Simple JavaScript Inheritance
* By John Resig http://ejohn.org/
* MIT Licensed.
*/
// Inspired by base2 and Prototype
(function() {
var initializing = false, fnTest = /xyz/.test(function() {
xyz;
}) ? /\b_super\b/ : /.*/;
// The base Class implementation (does nothing)
this.NClass = function() {
};
// Create a new Class that inherits from this class
NClass.extend = function(prop) {
var _super = this.prototype;
// Instantiate a base class (but only create the instance,
// don't run the init constructor)
initializing = true;
var prototype = new this();
initializing = false;
// Copy the properties over onto the new prototype
for (var name in prop) {
// Check if we're overwriting an existing function
prototype[name] = typeof prop[name] == "function" &&
typeof _super[name] == "function" && fnTest.test(prop[name]) ?
(function(name, fn) {
return function() {
var tmp = this._super;
// Add a new ._super() method that is the same method
// but on the super-class
this._super = _super[name];
// The method only need to be bound temporarily, so we
// remove it when we're done executing
var ret = fn.apply(this, arguments);
this._super = tmp;
return ret;
};
})(name, prop[name]) :
prop[name];
}
// The dummy class constructor
function NClass() {
var $this = this;
// All construction is actually done in the init method
if (!initializing && this.init)
this.init.apply(this, arguments);
}
// Populate our constructed prototype object
NClass.prototype = prototype;
// Enforce the constructor to be what we expect
NClass.prototype.constructor = NClass;
// And make this class extendable
NClass.extend = arguments.callee;
return NClass;
};
})();
(function (scope, undefined) {
scope.ssTypeBase = NClass.extend({
options: {
test: 0
},
init: function(test){
this.options.test = test;
}
});
var a = new scope.ssTypeBase(1);
var b = new scope.ssTypeBase(2);
console.log(a.options.test,b.options.test);
})(window);
Prototype properties are always shared between the objects. So in this case if you want options object to be instance variable than set options object inside constructor like this
(function (scope, undefined) {
scope.ssTypeBase = NClass.extend({
sharedOptionsObject: {
test: 0
},
init: function(test){
// create options object here, it will create seperate options object for each instance
this.options = {
test: 0
}
this.options.test = test;
}
});
});

function inside and outside subclass definition

Below are some code from cocos2dx-js binding official javascript sample
cc.Class = function(){};
cc.Class.extend = function (prop) { //John Resig's extend
var _super = this.prototype;
// Instantiate a base class (but only create the instance,
// don't run the init constructor)
initializing = true;
var prototype = new this();
initializing = false;
fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;
// Copy the properties over onto the new prototype
for (var name in prop) {
// Check if we're overwriting an existing function
prototype[name] = typeof prop[name] == "function" &&
typeof _super[name] == "function" && fnTest.test(prop[name]) ?
(function (name, fn) {
return function () {
var tmp = this._super;
// Add a new ._super() method that is the same method
// but on the super-class
this._super = _super[name];
// The method only need to be bound temporarily, so we
// remove it when we're done executing
var ret = fn.apply(this, arguments);
this._super = tmp;
return ret;
};
})(name, prop[name]) :
prop[name];
}
// The dummy class constructor
function Class() {
// All construction is actually done in the init method
if (!initializing && this.ctor)
this.ctor.apply(this, arguments);
}
// Populate our constructed prototype object
Class.prototype = prototype;
// Enforce the constructor to be what we expect
Class.prototype.constructor = Class;
// And make this class extendable
Class.extend = arguments.callee;
return Class;
};
var Explosion = cc.Sprite.extend({
tmpWidth:0,
tmpHeight:0,
active:true,
ctor:function () {
this._super();
//blahblah
},
destroy:function () {
//blahblah
}
});
Explosion.sharedExplosion = function () {
//blahblah
};
Just wonder why sharedExplosion is putted outside the definition of var Explosion
It's a simple extension of Explosion outside its declaration. It is just an implication, I don't think there has to be particular reason for doing it.

Javascript inherance and use of super: is this possible?

if (typeof Object.create !== 'function') {
Object.create = function (o) {
function F() {
}
F.prototype = o;
var f = new F();
if(f.init){
f.init();
};
return f;
};
}
var inherit = function(P, C) {
var i;
for(i in P) {
// if is the current parent
if(P.hasOwnProperty(i) === false) {
continue;
};
// define the uper property
C.uper = {};
// methods
if(typeof P[i] === 'function') {
// set as super
C.uper[i] = P[i];
// if child already defined, skip
if(typeof C[i] === 'function') {
continue;
};
C[i] = P[i];
}
// properties
else {
// if child already defined a property, skip
if(!(typeof C[i] === 'undefined')) {
continue;
};
C[i] = P[i];
}
}
return C;
}
var Parent1 = (function(){
var that = {};
// private var
var _name = 'Parent1';
// public var
that.lastName = 'LastName';
// public method
that.getName = function(){
// if this.uper.getName.call(this)
return _name + this.lastName;
// else
// return _name + that.lastName;
}
// return the literal object
return that;
}());
var Parent2 = {
// fake private var
_name: 'Parent2',
// public method
getName: function(){
// as we call this method with the call method
// we can use this
return this._name;
}
}
var Child1 = inherit(Parent1, (function(){
var that = {};
// overriden public method
that.getName = function(){
// how to call the this.uper.getName() like this?
return 'Child 1\'s name: ' + this.uper.getName.call(this);
}
that.init = function(){
console.log('init');
}
// return the literal object
return that;
}()));
var Child2 = inherit(Parent2, {
getName: function(){
// how to call the this.uper.getName() like this?
return 'Child 2\'s name: ' + this.uper.getName.call(this);
}
});
var child1 = Object.create(Child1);
// output: Child 1's name: Parent1LastName
console.log(child1.getName());
var child2 = Object.create(Child2);
// output: Child 2's name: Parent2
console.log(child2.getName());
// how to call the this.uper.getName() like this?
how to call the this.uper.getName() like this?
Yes
Javascript uses Prototypal inheritance. So essentially objects inherit Objects (and everything is an Object)
Here are a couple links that should help get the point across.
Javascript Module Pattern
Module Pattern In-depth
Here's the basic module pattern:
var MODULE = (function (my) {
my.anotherMethod = function () {
// added method...
};
return my;
}(MODULE));
Then you can do something like this to mimic inheritance:
var MODULE_TWO = (function (old) {
var my = {},
key;
for (key in old) {
if (old.hasOwnProperty(key)) {
my[key] = old[key];
}
}
var super_moduleMethod = old.moduleMethod;
my.moduleMethod = function () {
// override method on the clone, access to super through super_moduleMethod
};
return my;
}(MODULE));
This style of coding takes a bit of getting used to, but I definitely prefer it to classical inheritance at this point. If this code isn't making sense, check out the Douglas Crockford lectures and it should clarify most of it.
addressing the edit:
You can create different instantiations of these objects by using the new operator.
OR
I'd recommend using this little method which extends the Object Prototype (again if this doesn't make sense see the Douglas Crockford video). I forget the exact reasons why this is so heavily recommended by him, but at the very least it eliminates some confusion in that the new operator is a bit different than in classical languages. Needless to say using only using the new operator is insufficient.
What this function does is extends the Object prototype with a method create. It then...
Defines function F in a contained namespace.
Assigns the function F's prototype to the object that is passed
returns the newly constructed Object.
(outlined better by douglas crockford himself in the prototypal inheritance link)
if (typeof Object.create !== 'function') {
Object.create = function (o) {
function F() {}
F.prototype = o;
return new F();
};
}
newObject = Object.create(oldObject);
So using your code...
var a = Object.create(MODULE_TWO),
var b = Object.create(MODULE_TWO);
Answering based on your last edit, you could use something like this:
function Class(ctor, parent) {
var c = Function.prototype.call;
function clas() {
// expose the parent as super
this.super = parent;
ctor.apply(this, arguments);
}
// save the constructor
clas.constructor = ctor;
// provide a static constructor
clas.init = function() {
c.apply(parent.constructor, arguments);
};
// Setup the prototype
clas.prototype = parent ? parent.prototype : {};
// provide an extend method
clas.extend = function(methods) {
for(var i in methods) {
if (methods.hasOwnProperty(i)) {
clas.prototype[i] = methods[i];
}
}
return clas;
};
return clas;
}
Examples:
var Animal = Class(function(name) {
this.name = name;
});
var Cat = Class(function(name) {
this.super(name);
}, Animal).extend({
meow: function() {
console.log('Meow! My name is ' + this.name + '.');
}
});
new Cat('Neko').meow();
There are at least a trillion different ways to implement "Classes" in JavaScript, the more you want to hide the internals the more "magical" the code becomes, the above is very simple though.
You can (and probably need) customize this to fit your needs. But always keep in mind that there might be situations where a full blown Class emulation approach might not be the best one.
I already posted it as a comment, but in case you want to have everything hidden away for you, I've written a pretty feature rich, but still fast, Class library my own:
https://github.com/BonsaiDen/neko.js

Categories

Resources