I have heard about the prototype method to apply one function or variable to several objects. But it does not work for me somehow. I created many objects kind of like this:
var item = {
a: {
aa: "lalala",
ab: 1,
something: 3
},
b: {
ba: "jfjb",
bb: 2,
something: 4
}
}
But when I know use the prototype method
item.prototype.bob = 2;
it does not work and shows me the error
Cannot set property 'bob' of undefined"
Same for a method
item.prototype.bob = function() {
100 - this.something;
this.something++;
}
Do you know what I do wrong or is there a different method to apply the same variable or function to many objects?
You confuse classes with object instances. You just have an anonymous object. item is the instance, not the class.
In the snippet below, a class is declared (Item, with capital i), an instance is created (item), and the prototype of the class is modified. You will see then that you can set a property on the prototype and read it though the instance, if you like.
var Item = function() {
a = {
aa: "lalala",
ab: 1,
something: 3
};
b = {
ba: "jfjb",
bb: 2,
something: 4
};
}
var item = new Item();
Item.prototype.bob = 'x';
alert(item.bob);
The classic way is
function Person(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
Person.prototype.greet = function() {
console.log(this.firstName + " says hello");
}
var pete = new Person("Peter", "Pan");
pete.greet();
Note that the prototype attribute is on the constructor (the function object we invoke with new to construct the object), not the constructed object itself.
In ECMAScript 6, you'll be able to write this in a more compact way:
class Person {
constructor(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
greet() {
console.log(this.firstName + " says hello");
}
}
new Person("Peter", "Pan").greet();
The prototype property is called on the class name and not on the object name. So, if item is an object of class MyClass then you should write:
MyClass.prototype.bob = 2;
You can find more information on the links below:
http://www.w3schools.com/js/tryit.asp?filename=tryjs_object_prototype5
http://www.w3schools.com/js/js_object_prototypes.aspenter link description here
Here your item is an object by itself,it does not has any constructor, so you cannot define or set on prototype.Your code throws error
To fix this, it has to be something like the below
function item(a,b){
this.a=a;
this.b=b;
}
function a(aa,ab,something){
this.aa=aa;
this.ab=ab;
this.something=something
}
item.prototype.bob=2;
item.prototype.method1 = function() {
100 - this.something;
this.something++;
}
Hope this helps
Related
I understand the main principle of OOP and I somewhat know how to implement it into JS.
function Person(name) {
this.name = name;
this.speak = function(msg) {
console.log('Person says:' + msg);
}
}
var dad = new Person('David');
dad.speak('I am your dad!');
The script above does nothing more than printing out a message in the console. I don't understand how we approach the DOM with this technique. Maybe something like this?:
function Person(target, name) {
this.target = target;
this.name = name;
this.speak = function(msg) {
this.target.find('.speech-bubble').html(msg);
}
}
var dad = new Person($('#dad'), 'David');
dad.speak('I am your dad!');
Although this doesn't seem like a good approach.
How do we manipulate the DOM with objects, methods, constructors etc. through OO Javascript?
In relation to OO, if you are going to adopt for your DOM facing code, you are not too far of.
I'd say that a class should represent a component/element on the DOM. With it's methods being the state management part. But there is no correct answer here to be honest. This is but one way of designing OO with the DOM facing part.
Example:
const basicClassName = 'component';
const basicTemplate = '<h1>This is my basic component</h1>';
class MyComponent {
constructor(template = basicTemplate, className = basicClassName) {
this.template = template;
this.className = className;
this.element = document.createElement('div');
this.element.className = className;
this.element.innerHTML = template;
this.element.onclick = this.onClick.bind(this);
this.element.style.cursor = 'pointer';
}
onClick() {
this.element.classList.toggle('clicked');
}
}
const component = new MyComponent();
const container = document.querySelector('.container');
container.appendChild(component.element);
body {
font-size: 14px;
}
.component {
display: block;
padding: 1.3em;
box-shadow: 1px 1px 4px lightgray;
}
.clicked {
background-color: papayawhip;
}
<div class="container"></div>
What you need to understand is the concept of the Prototype.
When you create an instance using new, you are constructing an object based upon a prototype.
Consider the following:
function Person(name) {
this.name = name;
this.speak = function (msg) {
console.log('Person says:' + msg);
};
}
var dad = new Person('David');
dad.speak('I am your dad!');
console.log('Is dad.speak equal to dad.speak?', dad.speak === dad.speak);
var mom = new Person('David');
console.log('Is mom.speak equal to dad.speak?', mom.speak === dad.speak);
Each time you construct a new instance of Person, a new speak prototype now floats around somewhere in your logic. This is very inefficient.
To fix this, we need to modify the prototype of our function:
function Person(name) {
this.name = name;
}
Person.prototype.speak = function (msg) {
console.log('Person says:' + msg);
};
var dad = new Person('David');
dad.speak('I am your dad!');
console.log('Is dad.speak equal to dad.speak?', dad.speak === dad.speak);
var mom = new Person('David');
console.log('Is mom.speak equal to dad.speak?', dad.speak === dad.speak);
This way, we only have the function created once, on the prototype which is inherited to all instances. This is easier to maintain and a lot more efficient.
Now we can extend DOM object via their prototype, but it isn't recommended because you start to mess with the web standards, making troubleshooting much more difficult.
Array.prototype.isLengthGreaterThanFive = function(thisArg) {
return this.length > 5;
};
console.log([1, 2, 3, 4].isLengthGreaterThanFive(), [1, 2, 3, 4, 5, 6].isLengthGreaterThanFive());
A better way of handling this is to create extended objects or to simply use functions:
//Using functions
function isLengthGreaterThanFive(array) {
return array.length > 5;
}
console.log(isLengthGreaterThanFive([1, 2, 3, 4]), isLengthGreaterThanFive([1, 2, 3, 4, 5, 6]));
//Using a wrapper class
var MyArray = (function() {
function MyArray(array) {
if (array === void 0) {
array = [];
}
this.array = array;
}
MyArray.prototype.isLengthGreaterThanFive = function() {
return this.array.length > 5;
};
return MyArray;
}());
console.log(new MyArray([1, 2, 3, 4]).isLengthGreaterThanFive(), new MyArray([1, 2, 3, 4, 5, 6]).isLengthGreaterThanFive());
The benefits of using a class is that we can extend upon our idea of the object:
//Base class
function Person(firstname, lastname, says) {
if (firstname === void 0) {
firstname = "Leonado";
}
this.firstname = firstname;
if (lastname === void 0) {
lastname = "Da Vinci";
}
this.lastname = lastname;
if (says === void 0) {
says = "hello";
}
this.says = says;
}
//Base methods
Person.prototype.iAm = function () {
return this.firstname + " " + this.lastname;
};
Person.prototype.Speak = function () {
return this.says + " my name is " + this.iAm();
};
//Extended class
function Warrior(firstname, lastname, says) {
//Call in constructor
Person.call(this, firstname, lastname, says);
}
//Inheriting
Warrior.prototype = Object.create(Person.prototype);
Warrior.prototype.constructor = Warrior;
//Overruling "Speak"
Warrior.prototype.Speak = function () {
return "My name is " + this.iAm() + ", " + this.says;
};
console.log([new Warrior("Peter", "Allan", "Ahoyhoy").Speak(), new Person("Peter", "Allan", "Ahoyhoy").Speak()]);
In the example above we extend the prototype of Person for Warrior so that we retain the functionality of Person, and then simply modify what's different about a Warrior. This way we get to reuse the prototype method iAm, and we can focus on only changing what needs to change in the Speak method.
EDIT 1
I noticed too late that the question had changed a little.
You can treat DOM elements like any other class in JavaScript. The following setup has all Persons sharing a single DIV to speakUp:
var Person = (function () {
function Person(age, firstname, lastname) {
if (age === void 0) { age = 50; }
if (firstname === void 0) { firstname = "Peter"; }
if (lastname === void 0) { lastname = "Venkman"; }
this.age = age;
this.firstname = firstname;
this.lastname = lastname;
}
Person.prototype.speakUp = function () {
Person.bubble.innerHTML = this.firstname + " " + this.lastname + " is " + this.age + " years old";
};
return Person;
}());
Person.bubble = document.createElement("div");
document.body.appendChild(Person.bubble);
setInterval(function () {
var p = new Person(Math.floor(Math.random() * 100));
p.speakUp();
}, 3000);
This could easily become a DIV per Person, or a refereced DOM object (document.getElementById) shared among all Persons.
EDIT 2
In response to your comment:
In JavaScript everything is in essence and object. You create a function it registers an object with the functions name and returns and instance of that object. Everything like Arrays, Strings, DOM elements and custom functions has some master object hidden behind the scenes. Every time a new Array or DOM element or whatever is created, it has a reference to its master object (called the prototype). This is called the prototype chain.
If you look on my second example above, when dad.speak is called JavaScript first searches the instance for a speak property, but it won't find one because we haven't assigned it the way we did in example one were it was instance specific.
JavaScript will then try one level up the prototype chain and here it will find a matching property and use this instead. This way we can alter the default behavior of custom OR existing elements in JavaScript.
The idea being, that if you have some property that all instances of a prototype should have, then we simply modify the prototype once and they will all inherit this property.
Think of it this way. If you were to describe all living things on earth in JavaScript you would want some form of groupings. For instance the first level would be something like an Exists object that carries a property for a name and an id. From here you you could create Plant and Animal and have them both inherit the prototype of Exists. Now we could create a Flower class that inherits Plant and a Rose class that inherits Flower and so on.
The idea is to apply you properties in a way that makes sense to human beings via inheritance (an owl can fly because it is a bird / a shark can swim because it is a fish). Binding them at the level that makes sense, inheriting in a logical pattern and using your time efficiently.
If you are still confused, try looking up prototype tutorials.
Here is a good Youtube video to explain it:
https://www.youtube.com/watch?v=PMfcsYzj-9M
I need do add a method to a Javascript class using the new syntax. I tried this way:
class X{
constructor() {
this.a = 'b'
}
x(){
}
}
X.prototype.y = function (){
console.log('y')
}
var x = new X()
x.y()
console.log(X) // print the the class but not the new method.
It just prints:
class X{
constructor() {
this.a = 'b'
}
x(){}
}
But I expected
class X{
constructor() {
this.a = 'b'
}
x(){}
y(){
console.log('y');
}
}
How could I add a new method to a Javascript class?
this works fine, if you are checking this in google chrome console, then please check by expanding proto node.
or alternatively try checking
console.log(X.y)
or console.log(X.prototype.y)
or console.log(x.y)
this must print that function
There are two major ways to add methods to a class, this can be within the scope of the class when writing the code or using ".prototype" to attach a method to your class.
Below is an example of adding a method within the class scope:
class Person {
constructor(fName, lName) {
this.firstName = fName;
this.lastName = lName;
}
sayName = function() {
return `My Name is ${this.firstName} ${this.lastName}`
}
}
const firstPerson= new Person ("Thor", "Odinson")
console.log(firstPerson.sayName())
And below an example of a method creation from outside the scope of a class using a prototype:
class Person {
constructor(fName, lName) {
this.firstName = fName;
this.lastName = lName;
}
}
Person.prototype.sayName = function() {
return `My Name is ${this.firstName} ${this.lastName}`
}
const secondPerson= new Person ("Tony", "Stark")
console.log(secondPerson.sayName())
A very important thing to note is that using prototype to add a method to a class doesn't change the structure of the class. So logging the object won't render the method. But the method is available to all subclasses and instances of that class.
I know it's a bit late, but would this have solved your problem back then?
class XWithY extends X {
constructor() {
super()
}
y() {
console.log('y')
}
}
const xwy = new XWithY();
console.log(xwy instanceof X); // outputs true
You can use Object.setPrototypeOf.
Example:
// A class…
class Someone {
constructor(qui){
this.qui = qui
}
}
// A mixins with new methods
class MyMixins {
says(quoi){
console.log(this.qui + " says "+ quoi)
}
eats(quoi){
console.log(this.qui + " eats "+quoi)
}
}
// An instance…
const marion = new Someone("Marion")
// This fails…
//marion.says("hello!")
//marion.eats("potatoes")
//-----> Here's the trick <------
Object.setPrototypeOf(Someone.prototype, MyMixins.prototype);
// This pass…
marion.says("hello!") //=> "Marion says hello!"
marion.eats("potatoes") //=> "Marion eats potatoes"
I am going through few slides in developing a SPA using Durandal and other JavaScript library.
I have a few questions:
define('projects', [],
function() {
var Projects = function () {
this.myDataLocal = ko.observable();
this.myDataFromServices = null;
};
Projects.prototype.activate = function (activationData) {
this.myDataFromServices = activationData
this.myDataLocal(activationData());
};
Projects.prototype.detached = function () {
this.myDataFromServices(this.myDataLocal());
};
return Projects;
}
);
a. In the above code is
var Projects = function () {
};
a constructor?
b. When we are adding a function to prototype ,
Projects.prototype.activate = function (activationData) {
}
will this also be considered as a constructor and executed automatically on the function load?
c. To qualify a function for a constructor is it enough if we are defining the name of the just like this?
var ProjectsSample100 = function () {
}
A) Not exactly no. Although an object 'Projects' is created.
B) This is not a constructor. This assigns a function to a property on the prototype of the Projects object. In particular in Durandal, when a viewmodel is loaded, Durandal will check for the existance of an 'activate' function on the viewmodel. If none is present, it uses a default function, but in this case we have explicitly set the activation behaviour.
See more about what stages a viewmodel goes through in Durandal here:
http://durandaljs.com/documentation/Hooking-Lifecycle-Callbacks.html
C) No. That is just an object getting defined.
A typical constructor looks like this:
function person(first, last, age, eye) {
this.firstName = first;
this.lastName = last;
this.age = age;
this.eyeColor = eye;
}
var myFather = new person("John", "Doe", 50, "blue");
var myMother = new person("Sally", "Rally", 48, "green");
Notice the use of 'this' to assign to 'fields' on the object, and the use of 'new' to create new instances with this constructor.
I have stolen this example from here: http://www.w3schools.com/js/js_object_definition.asp
Note: This is not a question about classical and prototype inheritance. It's about what coding patterns to use to initialize objects. Class constructors create and initialize objects, whereas avoiding the new operator and going for Object.create() only creates the object and sets up the prototype chain. I have not yet found an online resource that explains best-practice coding patterns for doing creation and initialization when going for the Crockford Object.create() approach.
If I have a constructor (in my head this makes my class, although I know classes don't technically exist yet in mainstream JavaScript)
function Person(first, last) {
this.name = {
first: first,
last: last
};
}
Person.prototype.tellName = function() {
return this.name.first + ' ' + this.name.last;
}
I can then instantiate it like this
var p1 = new Person('John', 'Doe');
var p2 = new Person('Sven', 'Svensson');
And change the Person.name.first and Person.name.last separately
p1.tellName(); // Output: 'John Doe'
p2.tellName(); // Output: 'Sven Svensson'
p1.name.first = 'Peter';
p2.name.last = 'Celery';
And execute the object's function Person.tellName() with the following output
p1.tellName(); // Output: 'Peter Doe'
p2.tellName(); // Output: 'Sven Celery'
Which is very similar to how I'd approach building such a class in C++ or Java.
What patterns do I use to construct, or initiate an object where I can assign to a nested object when using the Crockford (Object.create()-ish) approach instead of new?
E.g.
...
// some code that does the same stuff as defining a class + constructor
...
var p1 = ???????
var p2 = ???????
// The following is the code and behavior I'm looking to get
p1.tellName(); // Output: 'John Doe'
p2.tellName(); // Output: 'Sven Svensson'
p1.name.first = 'Peter';
p2.name.last = 'Celery';
p1.tellName(); // Output: 'Peter Doe'
p2.tellName(); // Output: 'Sven Celery'
Instead of having:
function Person(first, last) {
this.name = {
first: first,
last: last
};
}
Person.prototype.tellName = function() {
return this.name.first + ' ' + this.name.last;
}
You'd just have:
function Person(first, last) {
return {
name: { first: first, last: last },
tellName: function() { return this.name.first + ' ' + this.name.last; }
};
};
Or, if you prefer how person.create() looks, then:
var person = {
create: function(first, last) {
return {
name: { first: first, last: last },
tellName: function() { return this.name.first + ' ' + this.name.last; }
};
}
};
But in the second case you'd have an unnecessary object (person) containing only one function (person.create()).
No need for Object.create nor new since those are for inheritance which you said you do not care about. This would let you do:
var p1 = Person('John', 'Doe');
var p2 = Person('Sven', 'Svensson');
A fun fact is that you can still use new in front of person.create this way if you want but it would offer no effect. If you have to use the existing function you can set the this context explicitly by using .call
// with your original `Person`
var p1 = Person.call({}, 'John', 'Doe');
var p2 = Person.call({}, 'Sven', 'Svensson');
This would not set the prototype either since the function is not called like a constructor. See this answer about what prototypical answer does and doesn't do - in a line it's about sharing functionality it's not about constructing properties of your objects.
Let's say I receive some JSON object from my server, e.g. some data for a Person object:
{firstName: "Bjarne", lastName: "Fisk"}
Now, I want some methods on top of those data, e.g. for calculating the fullName:
fullName: function() { return this.firstName + " " + this.lastName; }
So that I can
var personData = {firstName: "Bjarne", lastName: "Fisk"};
var person = PROFIT(personData);
person.fullName(); // => "Bjarne Fisk"
What I basically would want to do here, is to add a method to the object's prototype. The fullName() method is general, so should not be added to the data object itself. Like..:
personData.fullName = function() { return this.firstName + " " + this.lastName; }
... would cause a lot of redundancy; and arguably "pollute" the data object.
What is the current best-practice way of adding such methods to a simple data object?
EDIT:
Slightly off topic, but if the problem above can be solved, it would be possible to do some nice pseudo-pattern matching like this:
if ( p = Person(data) ) {
console.log(p.fullName());
} else if ( d = Dog(data) ) {
console.log("I'm a dog lol. Hear me bark: "+d.bark());
} else {
throw new Exception("Shitty object");
}
Person and Dog will add the methods if the data object has the right attributes. If not, return falsy (ie. data does not match/conform).
BONUS QUESTION: Does anyone know of a library that either uses or enables this (ie makes it easy)? Is it already a javascript pattern? If so, what is it called; and do you have a link that elaborates? Thanks :)
Assuming your Object comes from some JSON library that parses the server output to generate an Object, it will not in general have anything particular in its prototype ; and two objects generated for different server responses will not share a prototype chain (besides Object.prototype, of course ;) )
If you control all the places where a "Person" is created from JSON, you could do things the other way round : create an "empty" Person object (with a method like fullName in its prototype), and extend it with the object generated from the JSON (using $.extend, _.extend, or something similar).
var p = { first : "John", last : "Doe"};
function Person(data) {
_.extend(this, data);
}
Person.prototype.fullName = function() {
return this.first + " " + this.last;
}
console.debug(new Person(p).fullName());
There is another possibility here. JSON.parse accepts a second parameter, which is a function used to revive the objects encountered, from the leaf nodes out to the root node. So if you can recognize your types based on their intrinsic properties, you can construct them in a reviver function. Here's a very simple example of doing so:
var MultiReviver = function(types) {
// todo: error checking: types must be an array, and each element
// must have appropriate `test` and `deserialize` functions
return function(key, value) {
var type;
for (var i = 0; i < types.length; i++) {
type = types[i];
if (type.test(value)) {
return type.deserialize(value);
}
}
return value;
};
};
var Person = function(first, last) {
this.firstName = first;
this.lastName = last;
};
Person.prototype.fullName = function() {
return this.firstName + " " + this.lastName;
};
Person.prototype.toString = function() {return "Person: " + this.fullName();};
Person.test = function(value) {
return typeof value.firstName == "string" &&
typeof value.lastName == "string";
};
Person.deserialize = function(obj) {
return new Person(obj.firstName, obj.lastName);
};
var Dog = function(breed, name) {
this.breed = breed;
this.name = name;
}
Dog.prototype.species = "canine";
Dog.prototype.toString = function() {
return this.breed + " named " + this.name;
};
Dog.test = function(value) {return value.species === "canine";};
Dog.deserialize = function(obj) {return new Dog(obj.breed, obj.name);};
var reviver = new MultiReviver([Person, Dog]);
var text = '[{"firstName": "John", "lastName": "Doe"},' +
'{"firstName": "Jane", "lastName": "Doe"},' +
'{"firstName": "Junior", "lastName": "Doe"},' +
'{"species": "canine", "breed": "Poodle", "name": "Puzzle"},' +
'{"species": "canine", "breed": "Wolfhound", "name": "BJ"}]';
var family = JSON.parse(text, reviver)
family.join("\n");
// Person: John Doe
// Person: Jane Doe
// Person: Junior Doe
// Poodle named Puzzle
// Wolfhound named BJ
This depends on you being able to unambiguously recognizing your types. For instance, if there were some other type, even a subtype of Person, which also had firstName and lastName properties, this would not work. But it might cover some needs.
If you're dealing with plain JSON data then the prototype of each person object would simply be Object.prototype. In order to make it into an object with a prototype of Person.prototype you'd first of all need a Person constructor and prototype (assuming you're doing Javascript OOP in the traditional way):
function Person() {
this.firstName = null;
this.lastName = null;
}
Person.prototype.fullName = function() { return this.firstName + " " + this.lastName; }
Then you'd need a way to turn a plain object into a Person object, e.g. if you had a function called mixin which simply copied all properties from one object to another, you could do this:
//example JSON object
var jsonPerson = {firstName: "Bjarne", lastName: "Fisk"};
var person = new Person();
mixin(person, jsonPerson);
This is just one way of solving the problem but should hopefully give you some ideas.
Update: Now that Object.assign() is available in modern browsers, you could use that instead of writing your own mixin function. There's also a shim to make Object.assign() work on older browsers; see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign#Polyfill.
You should probably not do this.
JSON allows you to serialize a state, not a type. So in your use case, you should do something like this :
var Person = function ( data ) {
if ( data ) {
this.firstName = data.firstName;
this.lastName = data.lastName;
}
};
Person.prototype.fullName = function ( ) {
return this.firstName + ' ' + this.lastName;
};
//
var input = '{"firstName":"john", "lastName":"Doe"}';
var myData = JSON.parse( input );
var person = new Person( myData );
In other words you want to change prototype (a.k.a. class) of existing object.
Technically you can do it this way:
var Person = {
function fullName() { return this.firstName + " " + this.lastName; }
};
// that is your PROFIT function body:
personData.__proto__ = Person ;
After that if you will get true on personData instanceof Person
Use the new-ish Object.setPrototypeOf(). (It is supported by IE11 and all the other browsers now.)
You could create a class/prototype that included the methods you want, such as your fullName(), and then
Object.setPrototypeOf( personData, Person.prototype );
As the warning (on MDN page linked above) suggests, this function is not to be used lightly, but that makes sense when you are changing the prototype of an existing object, and that is what you seem to be after.
I don't think it is common to transport methods with data, but it seems like a great idea.
This project allows you to encode the functions along with your data, but it is not considered standard, and requires decoding with the same library of course.
https://github.com/josipk/json-plus
Anonymous objects don't have a prototype. Why not just have this:
function fullName(obj) {
return obj.firstName + ' ' + obj.lastName;
}
fullName(person);
If you absolutely must use a method call instead of a function call, you can always do something similar, but with an object.
var Person = function (person) { this.person = person; }
Person.prototype.fullName = function () {
return this.person.firstName + ' ' + this.person.lastName;
}
var person = new Person(personData);
person.fullName();
You don't need to use prototypes in order to bind a custom method in your barebone object.
Here you have an elegant example that don't pollute your code avoiding redundant code
var myobj = {
title: 'example',
assets:
{
resources: ['zero', 'one', 'two']
}
}
var myfunc = function(index)
{
console.log(this.resources[index]);
}
myobj.assets.giveme = myfunc
myobj.assets.giveme(1);
Example available in https://jsfiddle.net/bmde6L0r/