I'm digging deeper into JavaScript. I know it is possible to modify/enhance the behavior of JavaScript objects my modifying the prototypes.
For a special purpose I'd like to take Array and add all sorts of behavior to it. I TOTALLY get it is BAD to change all Arrays for my case. But it regular OO language, I could just extend it, add my functions, leave the original alone. Is there anything comparable in JS? Like, can I copy the Array prototype & put it onto another class & fiddle with that without affecting other arrays? Or any notions?
Not critical, but it would be helpful and certainly help me get a deeper understanding of JS which I am really getting into after years of Java, C++, etc.
You can extend the Array class and implement your extra functionality as methods on that class.
Instead of using let x = []; you now use let y = new myArray();.
class myArray extends Array {
constructor() {
super();
console.log(typeof this.push);
console.log(typeof this.shift);
}
logAndAdd(variable) {
this.push(variable);
console.log(variable);
}
}
let y = new myArray();
y.logAndAdd('hi');
I think you can simply use ES6 extends like you would in any other OO language.
Here is an example of a class that extends the native array and adds a log method. Only issue with this is that you will have to use the new keyword to create a new array.
class CustomArray extends Array {
log() {
console.log(this);
}
}
var customArrayInstance = new CustomArray();
customArrayInstance.push(1,2,3,4,5);
customArrayInstance.log();
// Creating an array using the array constructor
//
new CustomArray(200).fill(null).log()
What you could also do is use Symbols to extend the behavior of the native array in a non OOP kind of way. You will be basically extending the prototype of the native array in a non browser breaking way.
I think taking a look at Javascript Iterators and their possibilities is one option.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols
I have used this approach in Typescript/Angular2 to achieve array-like collections, where I can define custom push/pop/pull operations in an OO like way.
Yes, you can do this, and in JavaScript we call this interface-ish thing a prototype.
Function.prototype.method = function(name, func) {
this.prototype[name] = func;
return this;
};
Although personally, I would generally prefer to make "method" an independent function that takes and returns an array.
Related
Let's say I want to create an array of Person using a random data library.
I could do something like
import {generateRandom} from 'someLib'
let people = []
function getPerson() ({
name: generateRandom.string()
age: generateRandom.number()
})
for (let i = 0; i < 10; i++) {
people.push(getPerson())
}
But I could also do something like
import {generateRandom} from 'someLib'
class Person {
constructor() {
this.name = generateRandom.string(),
this.age = generateRandom.number()
}
}
let people = []
for (let i = 0; i < 10; i++) {
people.push(new Person())
}
On a memory level, is there any difference in the outcome?
(This is just a theoretical question, I am not trying to solve any problem in particular)
I have found this question that is related to this Difference between creating a class in javascript to create an object and creating an class and object in Java
Which states that there are no classes in JS.
Is this just syntactic sugar? 2 ways of doing exactly the same thing?
If you want to examine the memory yourself, you can put this code in the console:
class Test{};
const test = new Test();
class Class {};
test.Class = Class;
test.classObj = new Class();
function func() {return {};};
test.func = func;
test.funcObj = func();
Take a heap snapshot with google chrome dev tools, sort by constructor and find the Test class.
You can then examine the memory of these functions and objects. Here's a screenshot of what I got:
You can see that the object instantiated by the class constructor is slightly larger than the one instantiated by the function. Expanding the prototype, you can see they both use the Object constructor, but the classObj has the additional Class constructor in its prototype chain.
You can also see that the class constructor appears to retain more memory than regular functions, retained size meaning memory that will be cleaned up by garbage collection if the function is no longer in use.
Seems to be an extra 172 bytes for the class constructor, and 24 bytes per object for an empty object.
Following VLAZ's comment, here's the results with 10 methods added, and 10 instances.
class Test{};
const test = new Test();
class Class {
method0(){};
method1(){};
method2(){};
method3(){};
method4(){};
method5(){};
method6(){};
method7(){};
method8(){};
method9(){};
};
test.Class = Class;
for (let i=0; i < 10; i++){
test["classObj" + i] = new Class();
}
function func0(){};
function func1(){};
function func2(){};
function func3(){};
function func4(){};
function func5(){};
function func6(){};
function func7(){};
function func8(){};
function func9(){};
function constructorFunc() {
return {
method0: func0,
method1: func1,
method2: func2,
method3: func3,
method4: func4,
method5: func5,
method6: func6,
method7: func7,
method8: func8,
method9: func9,
};
};
test.constructorFunc = constructorFunc;
for (let i=0; i < 10; i++){
test["funcObj" + i] = constructorFunc();
}
The shallow size of the class objects are now much smaller. This seems to be due to the fact that they can just store a reference to the class prototype rather than reference all of their methods directly.
At first glance, the retained size of Class seems to be smaller than constructorFunc, but expanding Class you can see a property named prototype which is an object retaining an extra 1.38 KB. Adding that to the 520 B of the class itself pushes it above the retained memory of constructorFunc. But the memory saved by creating instances of the class instead of an object will outweigh that pretty quick.
So seems like classes are the way to go.
Your first getPerson() is invalid syntax and the function does not even attempt to return anything (if it was valid syntax). So, people.push(getPerson()) would generate an array of undefined(if the syntaxError was fixed) which will be entirely different than your second code block which generates an array of objects.
If, what you meant to ask about was something like this:
let people = []
function getPerson() {
return {
name: generateRandom.string()
age: generateRandom.number()
}
}
for (let i = 0; i < 10; i++) {
people.push(getPerson())
}
Then, this and your class will each create an array of objects and those objects will each have your two properties on them. The Class will contain some additional setup on the prototype that allows for subclassing, but if you're just asking about using the object with two properties, then these two would fundamentally do the same thing.
On a memory level, is there any difference in the outcome?
Without methods, your two properties will occupy the same amount of memory. The two properties are assigned directly to the object so those two properties use the same memory.
The class will actually set up a few other things on the object like a .constructor property and the object will get its own separate prototype. The prototype is shared among all instances. So, there could be slightly more memory usage for the class instance, but it's unlikely this makes a material difference.
If you defined methods directly on the object in the first implementation and methods in the class in the second implementation, then the class would definitely be more efficient because there would be one copy of the methods on the prototype vs. many separate copies of the methods on each instance of the object. But, you didn't show that in your question.
Which states that there are no classes in JS. Is this just syntactic sugar? 2 ways of doing exactly the same thing?
Classes are syntactic sugar, but they make it easy to define things in an efficient way without having to do a bunch of manual things. And, because the language sets things up for you, then everyone using a class definition creates code that works the same way.
You can manually build the same object that instantiating an instance of a class does, but it's a lot more code to do write all the bookkeeping that a class does for you. Your simple object with just two instance properties doesn't show any of that or need any of that, but when you start having methods and sub-classing things and overriding base methods and then calling base methods in your derived implementation, the class definition takes care of a some bookkeeping for you and simply lets you write, good extensible code faster and with everyone doing it the same way. Again, your simple object with just two properties does not show or need what a class does, but other uses of objects do.
For example, the class definition and new Person() creates an object that automatically sets yourObject.constructor to be the constructor that created the object. This allows other code to know what type of object it is or allows other code to abstractly create new instances of the same object. Methods are put on the prototype of the object and made non-enumerable. Constructors can call super(...) to execute the base class constructor (whatever it happens to be). Without the class definition, you have to do that very manually by calling the exact right function in the base class.
I am writing an object pool class that holds onto old unused objects, and when a new one is needed can use one of its reserves (Without the expensive creation of objects). However, I want to be able to save any data to this pool (not limited to something such as extends Poolable). The problem is that most object pools require something like this:
class MyData {
constructor(str) {
this.reset(str)
}
reset(str) {
this.str = str;
}
}
So that when a new instance is needed, the pool can call oldInstance.reset(prams). As stated before, I do not want this (Working with lots of third party tools that I am not feeling like writing wrappers for), so my data looks like this:
class MyData {
constructor(str) {
this.str = str;
}
}
When the pool needs to reset an instance, I need to be able to call the constructor as a function and set the this value to the instance that is being wiped.
This is super easy with old classes that used function/prototype syntax when they were made:
const MyClass = function(str) {
this.str = str;
}
// Reset an instance
const instance = new MyClass("foo");
MyClass.apply(instance, ["bar"])
// done
However when I do that for classes, it complains that you can not use a class without the new keyword. How do I go about doing this?
Fiddle: https://jsfiddle.net/wuqek405/
Edit:
As I said, most object pools need a reset function. I am trying to use the constructor as this function, because it is supposed to “set up” the instance. Another solution would be to generate this reset function based on the class. However, I want it to be as fast as possible, so terribly hacky solutions such as stringifying the class and evaling the constructor wouldn’t be optimal.
This is not possible. A class is not callable1, only constructable - which means creating a new object. This was new in ES6, which overhauled the inheritance model of classes and introduced super. Also, ES2022 will introduce class fields, which also get created during construction without being mentioned in the constructor code.
Your only option is to use only ES5 function-based classes, a transpiler, or writing explicit reset methods.
1: technically, it is callable (typeof C == 'function'), but [[Call]] will always throw an exception
I am learning about compositions in Javascript. So I want to ask if this is correct way of doing things.
I made some exercises that look like this:
class Animal {
// constructor() {
// }
eat = () => {
console.log("this creature is eating");
}
}
const AnimalsWithWings = superclass => class extends superclass {
constructor(Feathers, ...args) {
super(...args);
Object.assign(this, { Feathers });
}
}
const CanDive = superclass => class extends superclass {
// constructor( ...args) {
// super(...args);
// }
dive = () => {
console.log("Can Dive");
}
}
class Duck extends AnimalsWithWings(CanDive(Animal)) {
constructor(eats, ...args) {
super(...args);
Object.assign(this, { eats });
}
}
const duffy = new Duck("watermelon", true);
console.log(duffy);
duffy.dive();
duffy.eat()
I am still in learning process so I just need some pointers.
Did it do more or less what you expected? Then, sure, it's a correct way to do it, whatever "correct" means here.
It looks to me like it does what it was meant to do when I pop it into the console. I can't really say much more on your code specifically because I'm not sure what concrete domain it's trying to model, aside from maybe breaking down Ducks into atomic pieces.
If you're going to do it this way, though, I'd personally prefer to use a params object instead of just changing the constructor signature like that with AnimalsWithWings. That way, the order of extra parametrizations doesn't depend on the order in which the mixins were applied, which I would consider a Surprise. Surprises are bad.
const AnimalsWithWings = superclass => class extends superclass {
// Everyone receives the same `params` object.
// They only take what they know about, and ignore the rest.
constructor(params) {
super(params);
Object.assign(this, { Feathers: params.Feathers });
}
}
Even more personal opiniony, I'd name them WithDiving and WithWings instead, just to keep a somewhat consistent naming scheme, and to better imply that these are modifiers, not "real" base classes.
Your code does saddle every Duck with a prototype chain 4 prototypes long, but eh, whatever. If it somehow becomes a performance problem then you can create a utility function to optimize the mixin process or something. Flatten the prototypes, maybe.
Your code does also let you call super.method() in methods, though it's debatable whether you should ever use that in a mixin at all. I'd say you shouldn't, unless you want your mixins to implicitly depend on each other, which is a Surprise.
There are plenty of other ways of doing mixins, too.
You could create a utility function to flatten all the prototypes into a single new one and return a base class from that which you extend. (Just be sure to iterate property descriptors rather than just using Object.assign() when doing that flattening, if you want to properly handle things like get/set accessors, etc.)
You could eschew Classes and just directly create prototype objects and use Object.create() to create instances. (same thing about iterating property descriptors.)
You could create a Duck prototype using a bunch of iterative calls to Object.create() instead of iteratively extending base classes.
You could control the additional behaviors with helper Controller Classes instead of composing behavior directly into the base.
You could deal just in plain objects with data, and pass the objects to functions that expect the object to have certain properties on it in order to do things. (Amusingly, called "duck typing") I'll grant that's not really mixins, just calling functions, but if it accomplishes the same thing in effect...
Probably a bunch others I can't really think about at the moment. It's all sticking sets of behaviors onto some base thing.
I am receiving an ajax feed of documents that looks something like this (much simplified):
aDocs = [{title:'new doc', ext:'pdf'}, {title:'another', ext:'xlsx'}];
I am going to iterate through the aDocs array and display information about each doc, while adding some methods to each doc that will allow for modifying the HTML for display and making API calls to update the database.
I read here that in order to add methods to existing objects, you can use the __proto__ attribute. Something along the lines of:
function Doc(){}
Doc.prototype.getExt = function(){return this.ext}
Doc.prototype.getTitle = function(){return this.title}
for (var i=0; i<aDocs.length; i++){
aDocs[i].__proto__ = Doc.prototype
}
According to that article above,this isn't official javascript, isn't supported by IE (never will be), and will likely be deprecated in webkit browsers.
Here's an alternative stab at it:
function getExt(){ return this.ext }
function getTitle(){return this.title}
for (var i=0; i<aDocs.length; i++){
aDocs[i].getExt = getExt;
aDocs[i].getTitle = getTitle;
}
Is this second alternative viable and efficient? Or am I re-creating those functions and thereby creating redundant overhead?
Again the above examples are simplified (I know aDocs[i].ext will solve the problem above, but my methods for display and API calls are more complicated).
Is this second alternative viable and efficient?
Yes.
Or am I re-creating those functions and thereby creating redundant overhead?
No, the functions are reused, not re-created. All of the objects will share the single copy of the getExt and getTitle functions. During the call to the functions from (say) aDocs[1], within the call, this will refer to the object the function is attached to. (This only applies if you call it as part of an expression retrieving it from the object, e.g., var title = aDocs[1].getTitle();)
Alternately, if you liked, you could create new objects which shared a prototype, and copy the properties from the aDocs objects to the new objects, but you've asked about assigning new functions to existing objects, so...
Augmenting (adding methods to) the prototype is often the best way to go, but since you're dealing with object literals (or JSON.parse results), you'd have to either augment the Object.prototype which is not done, or create a wrapper constructor, with the methods you need attached to its prototype. The problem will be: getting to grips with this in that case... I'd leave things as they are: use the second approach: a simple loop will do just fine. Besides: prototype methods are (marginally) slower anyway...
The function objects themselves are being created ASAP (if they are defined in the global namespace, they're created as soon as the script is parsed). By simply looping through those objects, and assigning a reference to any function to each object, you're not creating additional functions at all.
Just try this:
var someObj = {name:'someObj'},
anotherObj = {name: 'anotherObj'},
someFunction = function()
{
console.log(this);
};
someObj.func = someFunction;
anotherObj.func = someFunction;
//or, shorter
someObj.func = anotherObj.func = someFunction;
//therefore:
console.log(someObj.func === anotherObj.func);//logs true! there is only 1 function object
someObj.func();//logs {name: 'someObj'}
anotherObj.func();//logs: {name: 'anotherObj'}
There have been posted many questions (and answers) that deal with this matter more in-depth, so if you're interested:
Objects and functions in javascript
Print subclass name instead of 'Class' when using John Resig's JavaScript Class inheritance implementation
What makes my.class.js so fast?
What are the differences between these three patterns of "class" definitions in JavaScript?
Are all more or less related to your question
In this case, I would just pass the object to the constructor of Doc;
function Doc(obj){
this.obj = obj;
}
Doc.prototype.getExt = function(){
return this.obj.ext;
}
Doc.prototype.getTitle = function(){
return this.obj.title;
}
var docs = [];
for (var i=0; i<aDocs.length; i++){
docs.push(new Doc(aDocs[i]));
}
There are two problems with your approach:
You have to copy each method individually for every instance.
Your "class" is not documented anywhere, making it a class makes it clearer that your object has those methods.
I just need to know what is the rudimentary difference between creating an object using these two methods, and the effect of using the objects.
//////////////////////////////////////////
myTestObject.prototype.var1 = null;
function myTestObject()
{
this.var1 = "test me";
}
myTestObject.prototype.action = function()
{
alert("alertme");
};
// to use
var a = new myTestObject();
a.action();
//////////////////////////////////////////
//////////////////////////////////////////
(function()
{
$.myTestObject3 = {
var1 : "test me",
action : _actionMethod
};
function _actionMethod()
{
alert("alertme3");
}
})();
$.myTestObject3.action();
//////////////////////////////////////////
A few updates... the answers to this question lead me to google for different keys words which lead to two interesting articles which really went into a lot of detail about Constructor vs Literal objects in javascript
http://net.tutsplus.com/tutorials/javascript-ajax/the-basics-of-object-oriented-javascript/
Then as a segue into why to use prototype when using function constructors paying particular attention to the section on Why is Using Prototype Better
http://net.tutsplus.com/tutorials/javascript-ajax/prototypes-in-javascript-what-you-need-to-know/?search_index=1
So as a follow up, you can use a combination of the two like in the guitar example below as a good practice. If only one copy of an object is needed and a change to it, knowing it affects the whole script is ok, then an object literal is fine. However if many objects need be created using the prototype approach is best so all created objects use a reference to the same function rather than having a copy of the function.
Another good article to make better use of namespacing and combining all these approaches
http://javascriptweblog.wordpress.com/2010/12/07/namespacing-in-javascript/
LAST FOLLOW-UP
I hope others find this useful as these topics threw me for a loop for a while.
Knowing what to look for now, I found two really good urls that describe many design patterns and how to intermix them in javascript using several examples.
This one describes how to use the module pattern in a way that makes use of the prototype notation. This will allow you to compartmentalize code while still making sure that instances are referencing the same objects.
http://briancray.com/posts/javascript-module-pattern/
This goes through several design patterns and list advatanges and disadvantages
http://addyosmani.com/resources/essentialjsdesignpatterns/book/
Take a look at Why is JavaScript prototyping? , some extract from there:
If you add all of your properties and methods to the object function
constructor, then create 100 instances of that object, you get 100
copies of all of the properties and methods. Instead, if you add all
of your properties and methods to the prototype of the object function
constructor, then create 100 instances of that object, you get 100
references to the single (1) copy of the object's properties and
methods. This is obviously faster and more efficient and is why
prototype is used (aside from altering things like String and Image,
as mentioned below)
Another reason to use prototypes is to emulate classical inheritance:
var Instrument = {
play: function (chord) {
alert('Playing chord: ' + chord);
}
};
var Guitar = (function() {
var constructor = function(color, strings) {
this.color = color;
this.strings = strings;
};
constructor.prototype = Instrument;
return constructor;
}());
var myGuitar = new Guitar('Black', ['D', 'A', 'D', 'F', 'A', 'E']);
myGuitar.play('D5');
Objects using $ won't be able to use myTestObject3 because it has not been extended on the jQuert prototype. myTestObject3 is an object literal and not a constructor function.
$('#test').myTestObj3.action(); // wrong
If you wanted it to be on the prototype of jQuery, extend the $.fn object.
You can have an arbitrary amount of "children" deriving from myTestObject (the constructor) using prototypal inheritance.