RequireJS, Circular Dependencies and Exports "Magic" Method - javascript

I've been trying to get RequireJS set up to handle circular dependencies using the special 'exports' magic module as recommended by James Burke's answer to this question.
Following the example given by #jrburke in that question:
define("Employee", ["exports", "Company"], function(Company) {
function Employee(name) {
this.name = name;
this.company = new Company.Company(name + "'s own company");
};
exports.Employee = Employee;
});
define("Company", ["exports", "Employee"], function(Employee) {
function Company(name) {
this.name = name;
this.employees = [];
};
Company.prototype.addEmployee = function(name) {
var employee = new Employee.Employee(name);
this.employees.push(employee);
employee.company = this;
};
exports.Company = Company;
});
jsfiddle
The problem is that using his own example, the exports module is undefined, and therefore exports.Employee and exports.Company don't set. If I try to include exports as an argument of the define callback functions, it simply initializes in both cases as empty and does not carry the constructor functions it was assigned.
What am I doing wrong?
EDIT: Through trial and error, I got the above code working at: http://jsfiddle.net/jpk45vow/4/. Can anyone explain why it works, because it makes no sense to me.

Edit: I couldn't find more info about the magic exports method. I could, however, mimic its intended behavior with a dummy "Container" module. See it in this fiddle: http://jsfiddle.net/amenadiel/a7thxz98/
console.log("start");
define("Container",function() {
var Container={};
return Container;
});
define("Employee", ["Container"], function(Container) {
var Employee= function(name) {
this.name = name;
this.company = new Container.Company(name + "'s own company");
};
Container.Employee = Employee;
});
define("Company", ["Container"], function(Container) {
var Company=function(name) {
this.name = name;
this.employees = [];
};
Company.prototype.addEmployee = function(name) {
var employee = new Container.Employee(name);
this.employees.push(employee);
employee.company = this;
};
Container.Company = Company;
});
define("main", ["Container","Employee","Company" ], function ( Container) {
var john = new Container.Employee("John");
var bigCorp = new Container.Company("Big Corp");
bigCorp.addEmployee("Mary");
console.log(bigCorp);
});
require(["main"]);

Related

Inheritance in Javascript function error

I apply inheritance in JavaScript in following way:
var employee = function(name) {
this.name = name;
}
employee.prototype.getName = function() {
return this.name;
}
var pEmployee = function(salary) {
this.salary = salary;
}
pEmployee.prototype.getSalary = function() {
return this.salary;
}
var employee = new employee("mark");
pEmployee.prototype = employee;
var pe = new pEmployee(5000);
console.log(pe.getName());
console.log(pe.getSalary());
but it showing following error in console:
Uncaught TypeError: pe.getSalary is not a function
Could any one tell me what's the reason behind this error?
It's because you've added getSalary to the object pEmployee.prototype refers to, but then completely replaced pEmployee.prototype with a new object. So naturally the new object doesn't have getSalary.
What you've shown isn't the correct way to set up inheritance in ES5 and earlier. Instead, see inline comments:
var Employee = function(name) {
this.name = name;
};
Employee.prototype.getName = function() {
return this.name;
};
var PEmployee = function(name, salary) {
// Note call to superclass
Employee.call(this, name);
// Now this level's initialization
this.salary = salary;
};
// This sets up inheritance between PEmployee.prototype and
// Employee prototype (then fixes up the
// constructor property)
PEmployee.prototype = Object.create(Employee.prototype);
PEmployee.prototype.constructor = PEmployee;
// NOW you add the method
PEmployee.prototype.getSalary = function() {
return this.salary;
};
// Usage
var employee = new Employee();
var pe = new PEmployee("Mark", 5000);
console.log(pe.getName());
console.log(pe.getSalary());
See my answer here for a more thorough example, and also what it would look like if you used ES2015's class syntax instead.

Recursive prototype inheritance in nodejs / javascript

In my nodejs program, i have a module called person and it has a prototype object(about) which also has its child method (describe). I am trying to access objects properties but i am getting undefined.
Could someone correct my understanding? what i am doing wrong here?
index.js
var Person = require("./person");
var sam = new Person({name:"Sam",age:23})
sam.about.describe();
person.js
module.exports = (function() {
var person = function(options) {
if (options && options.name) this.name = options.name;
if (options && options.age) this.age = options.age;
}
person.prototype.about = {
describe : function(){
console.log("I am",this.name,"and",this.age,"years old");
}
}
return person;
})();
Expected output: "I am Sam and 23 years old"
Actual output: "I am undefined and undefined years old"
As others have said, this in your example refers to the about object, not the person instance itself.
One way to have the API you want, is to create that about namespace within the constructor, and use bind to set correct context to the describe handler.
module.exports = (function() {
var person = function(options) {
if (options && options.name) this.name = options.name;
if (options && options.age) this.age = options.age;
this.about = {
describe: function () {
console.log("I am",this.name,"and",this.age,"years old");
}.bind(this)
};
}
return person;
})();
This way you can simply call
var Person = require("./person");
var sam = new Person({name:"Sam",age:23})
sam.about.describe();
>>> I am Sam and 23 years old
It's because this refers to the direct parent of describe, which is about:
person.prototype.about = {
describe : function() {
console.log("I am",this.name,"and",this.age,"years old");
}
}
You'd need a weak reference to the main Person object passed to the about namespace for example, and use it in place of this. But I don't like it that way, it is circumvoluted, feels wrong and looks smelly.
Instead, let's just aknowledge the fact that it's a completely new functionality package added to the Person object and let's make a helper for it:
module.exports = (function() {
var PersonHelper = function(person) {
this.person = person;
describePerson: function() {
console.log("I am",this.person.name,"and",this.person.age,"years old");
}
/* more methods */
}
return PersonHelper;
})();
So you could then do:
module.exports = (function() {
var person = function(options) {
if (options && options.name) this.name = options.name;
if (options && options.age) this.age = options.age;
}
return person;
})();
var Person = require("./person");
var PersonHelper = require("./personHelper");
var sam = new Person({name:"Sam",age:23})
var helper = new PersonHelper(person);
helper.describePerson();
If you really want to be able to use sam.about.describe, with an about "namespace" containing methods, then one approach is
Object.defineProperty(person.prototype, 'about', {
get() {
return {
describe: () => console.log("I am", this.name, "and", this.age, "years old");
};
}
})
This works because the context within get is that of the Person instance, and the arrow function will thus refer to it properly.

Uncaught TypeError: Cannot set property 'getName' of undefined(anonymous function)

<script>
var Employee = new function(name)
{
this.name=name;
}
Employee.prototype.getName = function()
{
return this.name;
}
var PermanenetEmployee = new function(annualsalary)
{
this.annualsalary=annualsalary;
}
var employee = new Employee("rahul");
PermanenetEmployee.prototype = employee;
var pe = new PermanenetEmployee(5001);
document.write(pe.getName());
</script>
i am implementing inheritance in java script. From this code i want to print employee name like "rahul".But i am getting error in this like Uncaught TypeError: Cannot set property 'getName' of undefined(anonymous function).How to resolve this error?
Employee.prototype.getName = function()
{
return this.name;
}
This is the problem:
var Employee = new function(name)
// ------------^^^
{
this.name=name;
}
(And the same for PermanenetEmployee.)
You don't want new there. new calls the function. You want to do that later, as you have when assigning to employee.
Note that the way you're setting up inheritance between them is an anti-pattern. To make PermanenetEmployee correctly "subclass" Employee, do this:
PermanenetEmployee.prototype = Object.create(Employee.prototype);
PermanenetEmployee.prototype.constructor = PermanenetEmployee;
not
var employee = new Employee("rahul");
PermanenetEmployee.prototype = employee;
...and then have PermanenetEmployee accept name and pass it to Employee:
var PermanenetEmployee = function(name, annualsalary) {
Employee.all(this, name); // <====
// ...
};
...or better use, use ES2015 ("ES6") class (transpiling if you need to, for instance with Babel).
Here's a correct setup. I've also fixed the typo in PermanenetEmployee:
var Employee = function(name) {
this.name = name;
};
Employee.prototype.getName = function() {
return this.name;
};
var PermanentEmployee = function(name, annualSalary) {
Employee.call(this, name);
this.annualSalary = annualSalary;
};
// Set up subclass
PermanentEmployee.prototype = Object.create(Employee.prototype);
PermanentEmployee.prototype.constructor = PermanentEmployee.prototype;
PermanentEmployee.prototype.getAnnualSalary = function() {
return this.annualSalary;
};
// Using
var pe = new PermanentEmployee("Rahul", 5001);
console.log(pe.getName());
console.log(pe.getAnnualSalary());
And with ES2015:
class Employee {
constructor(name) {
this.name = name;
}
getName() {
return this.name;
}
}
class PermanentEmployee extends Employee {
constructor(name, annualSalary) {
super(name);
this.annualSalary = annualSalary;
}
getAnnualSalary() {
return this.annualSalary;
}
}
// Using
var pe = new PermanentEmployee("Rahul", 5001);
console.log(pe.getName());
console.log(pe.getAnnualSalary());
Again, note that you need to transpile if you want to use that syntax in the wild (for now).
There are a couple ways you can get inheritance to work in JS, I am using this pattern.
First declare the base prototype:
Employee = function () {
};
Employee.prototype = {
getName: function () {}
};
And then the prototype that inherits the base:
PermanentEmployee = function () {
Employee.call(this);
};
PermanentEmployee.prototype = Object.create(Employee.prototype);
PermanentEmployee.constructor = PermanentEmployee;
PermanentEmployee.prototype.foo = function() {}

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...

Patch 'this' variable with jQuery

How do you think, is it ok to rewrite this variable in constructor with patched one with jquery? Then you will have some helpful methods for handling events, etc. What advantages, disadvantages it has?
Some example:
var Transformer = function(name, rase) {
this = $(this); // patch `this` with jQuery
this.name = name;
this.rase = rase;
}
Transformer.prototype.transform = function() {
// now we can do that
this.trigger('transformstart');
}
Is this a bad practice?
UPD
It's just impossible, you'll have an error:
ReferenceError: "Invalid left-hand side in assignment"
I find helpful to have $this field.
var Transformer = function(name, rase) {
this.$this = $(this);
this.name = name;
this.rase = rase;
}
Transformer.prototype.transform = function() {
this.$this.trigger('transformstart');
}
If you only need a jQuery as a events helper, you can make an "events" property:
var Transformer = function(name, rase) {
this.events = $({});
// other code
}
Transformer.prototype.transform = function() {
this.events.trigger('transformstart');
}
// public usage
var optimus = new Transformer('Optimus', 'Autobots');
optimus.events.on('transformstart', function() {
console.log('say hello to my gun!');
});

Categories

Resources