JavaScript - error with inheritance - javascript

I'm trying to use inheritance, but I'm stuck. I'm getting an error and don't know what I'm doing wrong. All examples I see online don't pass objects into their constructors, so I'm not sure what I should be doing. Here is a dumbed down example -
function Automobile(obj){
this.name = obj.name;
this.model = obj.model;
}
Automobile.prototype = {
getName: function(){
console.log(this.name);
},
getModel: function(){
console.log(this.model);
}
}
var bmw = new Automobile({
name: 'BMW',
model: 'm5'
})
bmw.getName();
bmw.getModel();
function Truck(obj){
this.cabSize = obj.cabSize
}
Truck.prototype = new Automobile();
Truck.prototype = {
getCabSize: function(){
console.log(this.cabSize);
}
}
var fordF150 = new Truck({
name: 'Ford',
model: 'F150'
})
//Uncaught TypeError: Cannot read property 'name' of undefined test.js:2
//Automobile test.js:2
//(anonymous function)

The error is occurring at Truck.prototype = new Automobile();. You are not passing obj, which Automobile expects.
To avoid this try Truck.prototype = Object.create(Automobile.prototype)
After that use
Truck.prototype.getCabSite = function(){
}
so that you don't overwrite the inherited properties from Automobile.

Instead of this:
Truck.prototype = {
getCabSize: function(){
console.log(this.cabSize);
}
}
try this:
Truck.prototype.getCabSite = function(){
}
Truck.prototype.constructor = Truck; //this will insure the appropriate constructor is called
that way you don't delete get rid of the automobile protoype

You changed "prototype" of Truck -> you can't inherit.
Truck.prototype = new Automobile();
Truck.prototype = {
getCabSize: function(){
console.log(this.cabSize);
}
}; // Change prototype of Truck
You can repair
Truck.prototype.getCabSize = function(){};
1 Example about the change of prototype, i write some line of code use your Automobile function
function Automobile(obj){
this.name = obj.name;
this.model = obj.model;
}
Automobile.prototype = {
getName: function(){
console.log(this.name);
},
getModel: function(){
console.log(this.model);
}
}
var bmw = new Automobile({
name: 'BMW',
model: 'm5'
})
Automobile.prototype = {
sayHello:function(){
console.log("Hello, I'm new");
}
}
// Make new Automobile
var honda = new Automobile({
name: 'BMW',
model: 'm5'
});
// Try to call getName, getModel
bmw.getName(); // BMW
bmw.getModel(); // m5
honda.getName(); // TypeError : undefined is not function
honda.getModel(); // TypeError : undefined is not function
// Try to call sayHello
bmw.sayHello(); // TypeError : undefined is not function
honda.sayHello(); // Hello, I'm new
Why bmw can't call sayHello and honda can't call getName and getModel ?
Because i changed the prototype reference of Automobile after create bmw object.
bmw hold the reference to "old" prototype that contain getName, getModel but not sayHello . And honda hold the reference to "new" prototype that contain sayHello but not getName and getModel.
Hope it'll help you.

Related

Assigning functions to Objects

I want to add functions to JSON Objects, but I can't find function for assigning to all objects, only to one.
This code works with Arrays:
Object.defineProperty(Array.prototype, 'random', {
value: () => {
return this[Math.floor(Math.random() * this.length)];
},
});
I've also found this code:
const obj = {name: 'Bob'};
obj.fullName = function() { return this.name }
But that one only works for specific object, not all of them.
Is it event possible to write global functions for all JSON Objects, and if is, then how to do it?
You could add the function to Object.prototype. Note that this is not considered a very good practice because it could impact the rest of the code (like shown in the comments):
Object.prototype.fullName = function() { return this.name; };
const obj = { name: 'Bob' };
console.log(obj.fullName());
You should consider doing this instead:
const baseObject = { fullName: function() { return this.name; } };
const obj = Object.create(baseObject, { name: { value: 'Bob', writable: true } });
console.log(obj.fullName());
And if your target runtime (browser?) supports ECMAScript 6, you could also create a dedicated class for this:
class MyClass {
constructor(name) {
this.name = name;
}
fullName() { return this.name; }
}
const bob = new MyClass('Bob');
console.log(bob.fullName());
Finally, the class syntax for ECMAScript 5:
function MyClass(name) {
this.name = name;
}
MyClass.prototype.fullName = function() { return this.name; }
const bob = new MyClass('Bob');
console.log(bob.fullName());

Simple JS example of Singleton Pattern not working

I made a simple example of Singleton Pattern but it is giving blank objects in both cases. Please someone help?
var a = ( function(){
var instance = {};
function init() {
return {
name: "rosy",
age: 29,
printName: function(){
console.log(this.name)
}
}
}
return {
getInstance: function(){
if(!instance) {instance = init();}
return instance;
}
}
})();
console.log(a.getInstance());
console.log(a.getInstance());
As I said in a comment, the problem is that instance is initialized to {}, which is truthy, so !instance will never be true.
There's only one instance variable, created for the one call to your anonymous function. That's the whole point of having it, it tracks the singleton. So initialize it with a falsy value (or leave it with the default undefined, which is falsy):
var a = ( function(){
var instance = null;
function init() {
return {
name: "rosy",
age: 29,
printName: function(){
console.log(this.name)
}
}
}
return {
getInstance: function(){
if(!instance) {instance = init();}
return instance;
}
}
})();
const first = a.getInstance();
console.log(first);
const second = a.getInstance();
console.log(second);
console.log(first === second); // true
.as-console-wrapper {
max-height: 100% !important;
}
That said, you only need all of this complexity if you want late initialization. Otherwise, just create the object:
var a = {
instance = {
name: "rosy",
age: 29,
printName: function(){
console.log(this.name)
}
}
};
Then a.instance is the singleton. If you want it to be non-writable:
var a = Object.defineProperty({}, "instance", {
value: {
name: "rosy",
age: 29,
printName: function(){
console.log(this.name)
}
}
});
(That will also be non-enumerable. If you want it to be enumerable, add enumerable: true.)
Or just:
var instance: {
name: "rosy",
age: 29,
printName: function(){
console.log(this.name)
}
};
If you want that to be read-only, use const in any modern environment:
const instance: {
name: "rosy",
age: 29,
printName: function(){
console.log(this.name)
}
};
In all of the above, remember that although you may have a singleton, and although the instance property/constant may be read-only, nothing about the object itself is immutable. Properties can be added, removed, changed, etc. To prevent that, use Object.freeze.
If in case this can be a variety to T.J. Crowder answer, You may create an instance once and save it to a variable, then every time the method is called, you may return that variable. I think closure plays a role here.
var a = ( function(){
var instance = {
name: "rosy",
age: 29,
printName: function(){
console.log(this.name)
}
};
return {
getInstance: function(){
return instance;
}
}
})();
let b = a.getInstance();
let c = a.getInstance();
console.log(b);
console.log(c);
console.log( b === c )

How to achieve Observable behaviour in vanila js

I am trying to achieve knockout observe computed behavior in vanila js
In react, you will have both these variables on state and compute the fullname
How can we achieve the same in vanila js
function person(firstname,lastname) {
this.firstname = firstname;
this.lastname = lastname;
this.fullName = `${this.firstname}${this.lastname}`
}
var person1 =new person("abc","k");
Object.defineProperty(person,'firstname',{
get: () => {
return person1['firstname'];
},
set: (name) => {
person1['firstname'] = name;
}
});
Object.defineProperty(person,'lastname',{
get: () => {
return person1['lastname'];
},
set: (name) => {
person1['lastname'] = name;
}
});
Object.defineProperty(person,'fullName',{
get: () => {
return `${person1['firstname']}-${person1['lastname']}`;
}
});
console.log(person1.firstname, "firstnmae");
person1.firstname ="sah";
console.log(person1.lastname, "lastname");
console.log(person1.fullName, "fullname");
Look at this code:
var person = {
firstname: 'abc',
lastname: 'k'
};
Object.defineProperty(person,'fullName',{
get: function() {
return `${this['firstname']}-${this['lastname']}`;
}
});
var person1 = Object.create( person );
console.log(person1.firstname, "firstnmae");
person1.firstname ="sah";
console.log(person1.lastname, "lastname");
console.log(person1.fullName, "fullname");
there's no need defining firstname and lastname by .defineProperty because those setter and getter will work by default as you wrote them.
you should use this syntax, as it's easier to read/maintain afterward, otherwise, if you would like to stick to that constructor syntax, here you go:
var person = function() {};
Object.defineProperty(person,'firstname',{
value: 'abc',
writable: true
});
Object.defineProperty(person,'lastname',{
value: 'k',
writable: true
});
Object.defineProperty(person,'fullName',{
get: function() {
return `${this['firstname']}-${this['lastname']}`;
}
});
var person1 = Object.create( person );
console.log(person1.firstname, "firstnmae");
person1.firstname ="sah";
console.log(person1.lastname, "lastname");
console.log(person1.fullName, "fullname");
I think you need more investigation about Object.create() and MDN is the best place.
arrow-functions have lexical this, be careful!
this syntax maybe clarifies some points about Object.create() for you:
var person = function() {};
person.prototype.firstname = 'abc';
person.prototype.lastname = 'k';
Object.defineProperty(person.prototype,'fullName',{
get: function() {
return `${this['firstname']}-${this['lastname']}`;
}
});
var person1 = Object.create( person.prototype );
console.log(person1.firstname, "firstnmae");
person1.firstname ="sah";
console.log(person1.lastname, "lastname");
console.log(person1.fullName, "fullname");

Acess to this from subobject in JavaScript

How do I get access to the properties or method of the main object, from sub-obiect level two (sub3). If possible I would like to avoid solutions chaining return this.
Obj = function () {};
Obj.prototype = {
name: 'name',
main: function(){
console.log(this.name);
},
subobject: {
sub2: function () {
console.log(this);
},
sub3: function () {
console.log(this.name); // How access to Obj.name ??
}
}
}
o = new Obj();
o.main(); // return name
o.subobject.sub2(); // return subobject
o.subobject.sub3(); // return undefined
With your current syntax, you can't. Because for sub2 and sub3, the this variable is Obj.prototype.subobject.
You have multiple choice:
The obvious one: don't use a suboject.
Create subobject, sub2 and sub3 in the constructor
Obj = function() {
var self = this;
this.subobject = {
sub1: function() { console.log(self); }
}
}
Use bind at each call:
o.subobject.sub2.bind(o)();

Unit Testing Factories Jasmine-Sinon

I have a factory with a getter and setter
.factory('myService', function() {
var car = null;
return {
car: car,
get: function get() {
return car;
},
set: function set(newCar) {
car = newCar;
}
};
});
I am writing test for it but I cannot call the set method and have it actually set car to newCar
myService.set = sinon.spy();
myService.get = sinon.spy()
it('should set car to new car', function () {
var newCar = ['a','b','c'];
expect(myService.car).toEqual(null); //pass
myService.set(newCar);
dump(myService.car); //null
expect(myService.set).toHaveBeenCalledWith(newCar);//pass
expect(myService.get).toHaveReturned(newCar);//fail
});
Any advice on what I am doing wrong here?
There are more problems here.
One is that the .car property will always be null.
var car = null;
return {
car: car,
get: function get() {
return car;
},
set: function set(newCar) {
car = newCar;
}
};
Here you initialize it with car which is null. There will be no reference between them. This will always be null since you never change that property on the object:
dump(myService.car); //null
You might do something like:
return {
car: null,
get: function get() {
return this.car;
},
set: function set(newCar) {
this.car = newCar;
}
};
But with this you might run into some this context issues later. Why are you trying to expose car if you have a getter for it?
The other thing is that you replace the entire get and set functions with this:
myService.set = sinon.spy();
myService.get = sinon.spy();
Sinon knows nothing about your original get and set.
You should do it like this:
sinon.spy(myService, 'set');
So sinon can wrap your function with a spy while preserving it's original behavior. Check Sinon documentation

Categories