Currently I'm trying to use John Resigs Simple Javascript Inheritence library, this works great but when I try to modify default member variables, it seems to influence the prototype rather than only the instance:
http://jsfiddle.net/u2MWL/1/
As you can see, the events is of different length each time you create a new instance. Am I using the library in the wrong way or is this a known flaw? What are the alternatives?
The problem here is that you are extending the class with events: []
Array (and {} object) variables are references. So by extending the class with an array, every instance of the class is going to have an events equal to the same referenced array.
To get around this you could do
var Test = Class.extend({
events: null,
init: function() {
this.events = [];
this.events.push(1);
alert(this.events.length);
},
say: function(words) {
}
});
This way, each time a class is initialized a new array (thus reference) is appointed to events. Not elegant, I know, but it should work.
Related
In my journey to become stronger in JavaScript, I have taken up a challenge to add a method, myMethod(), to the JavaScript Object class that can be called on a given Object, myObject, in order to manipulate its contents.
I have gotten as far as adding myMethod() to the Object prototype and traversing through myObject via myObject.myMethod(); however, I'm running into a curious problem: at the end of the given object, myMethod() is printed as an value of myObject even though, to my knowledge, it should not be.
Object.prototype.myMethod = function()
{
for (var key in this)
{
console.log(this[key]);
}
}
function processData(input)
{
//Enter your code here
var myObject = JSON.parse("{ \"myParam\": \"myValue\", \"anotherParam\": 123 }");
myObject.myMethod();
}
The following is the output of this code:
myValue
123
[Function]
Changing myObject[key] to myObject[key].toString() has the following output:
myValue
123
function ()
{
for (var key in this)
{
console.log(this[key].toString());
}
}
I am executing this script in WebStorm using a Node.js runtime.
The requirements of this challenge only calls for the contents of myObject, and I can't figure out how to stop myMethod() from showing up here. Is there something I'm missing?
In my journey to become stronger in JavaScript, I have taken up a challenge to add a method, myMethod(), to the JavaScript Object class that can be called on a given Object, myObject, in order to manipulate its contents.
Beware that this practice is seriously frowned-upon (here in 2017, possibly more than it strictly necessary*). Extending any built-in prototype is a bit fraught, but extending Object.prototype is particularly problematic since, of course, almost all objects inherit from it.
But if you're going to extend a prototype, it's important to add a non-enumerable property via Object.defineProperty or similar:
Object.defineProperty(Object.prototype, "myMethod", {
value: function() { /* ... */ }
});
The default for enumerable is false, so it won't show up in for-in loops, Object.keys arrays, etc.
* The "don't extend built-in prototypes" mantra started before ES5's introduction of a way of defining non-enumerable properties (e.g., defineProperty). Now that we can define non-enumerable properties, the chief concern is inter-script conflict, each trying to use the same name for something, or conflict with new features as they're added to JavaScript.
Use Object.defineProperty().
The Object class is used in a lot of libraries and frameworks and therefore is best left alone, but can be useful for things such as poly-fills for browser compatability.
if (!Object.prototype.myMethod) {
Object.defineProperty(Object.prototype, 'myMethod', {
value: function() { /* customer code here */ }
})
}
I want to turn a plain JavaScript object into one with a prototype etc, without cloning the object. Is this possible?
Essentially, instead of this:
var MyClass = function(fromObj) {
this.propA = fromObj.propA;
this.propB = fromObj.propB;
...
}
inherit (SuperClass, MyClass);
I would like to:
var MyClass = function(fromObj) {
this = fromObj;
}
inherit (SuperClass, MyClass);
To be extra clear: I would like the object returned from the constructor to be the same object that was passed in (not a duplicate, not a clone).
Is there a way to achieve this? Or a better way to describe what I'm asking for? :)
I'd like this for two reasons:
We're loading a lot of objects, and speed and memory may start to matter.
There are lots of places where the "plain old javascript objects" are used, and it may be hard to make sure they all point to the new instantiated object.
I guess you want to do this so that the objects created by JSON.parse are instances of something other than Object.
One option is to assign a new [[Prototype]] that is from the specified constructor using Object.setPrototypeOf, however there are performance warnings for this on MDN (I have no idea on the validity of the claims).
A more efficient solution might be to create "wrapper" objects and attach those created by JSON as a property. This approach is used by a number of DOM libraries and avoids the possibility of property names from the JSON data clashing with method names of the object (e.g. the DOM libraries can use methods with the same name and characteristics as DOM methods, but don't overwrite or clash with them).
Data could be held in a closure so that only getters and setters defined by the constructor can access it.
I've been given a class -
Zoo.Controller = (function() {
function Controller() {}
Controller.prototype.params = {};
Controller.prototype.set_params = function(params) {
this.params = params;
return this;
};
return Controller;
})();
and I want to inherit from that class using _.extend
Zoo.Controllers.WhaleController = _.extend({
new: function () {
// do something
}
}, Zoo.Controller);
When I try to instantiate that class like so...
this.whale_controller = new Zoo.Controllers.WhaleController();
I get -
Uncaught TypeError: object is not a function
Is it possible to do what I'm trying? I've read multiple articles on inheritance in JS, but had assumed that the Underscore library had it solved for me.
As Bergi pointed out; it isn't hard to inherit in JavaScript. You should know what a constructor function does and what prototype is used for. This answer may help with that, I tried to demonstrate prototype through simple and hopefully easy to understand examples. You can copy and paste the code in your browsers JS commandline (in the console) and change it to see if you understand how prototype behaves in JavaScript.
To inherit from ZooController you can:
Zoo.Controllers.WhaleController = function(args){
Zoo.Controller.apply(this,arguments);//re use Zoo.Controller constructor
//and initialize instance variables
//instance specific members of Whale using an args object
this.weitht=args.weight||4;
this.wu=args.weightUnit||wu.metricTon;
//Zoo.Controller.call(this,arg1,arg2); can be used too but I usually use
// an args object so every function can pick out and mutate whatever they want
// for example: var w = new WhaleController({weight:3,weightUnit:wu.metricTon});
// now it looks more like pythons optional arguments: fn(spacing=15, object=o)
};
//set Zoo.controller.prototype to a shallow copy of WhaleController.prototype
//may have to polyfill the Object.create method if you want to support older browsers
Zoo.Controllers.WhaleController.prototype=Object.create(Zoo.Controller.prototype);
//repair constructor
Zoo.Controllers.WhaleController.prototype.constructor=Zoo.Controllers.WhaleController;
//extend Zoo.controller.prototype.set_params
Zoo.Controllers.WhaleController.prototype.set_params=function(){
//re use parent set_params
Zoo.Controller.prototype.set_params.apply(this,arguments);
//and do something extra
console.log("extra in set_params from WhaleController");
};
//WhaleController own function
Zoo.Controllers.WhaleController.prototype.whaleSpecific=function(){
//funciton specific to WhaleController
};
Polyfill for Object.create here.
I was wondering this myself, and this is what I came up with.
Define the parent
var parentObj = function(parentMemo) {
console.log(parentMemo);
};
parentObj.prototype = {
parentCall : function() {
console.log('parentCall');
}
}
Define the child
var childObj = function(childMemo, parentMemo) {
parentObj.call(this, parentMemo);
console.log(childMemo);
};
_.extend(childObj.prototype, parentObj.prototype, {
childCall : function() {
console.log('childCall');
}
});
Construct a new a child to see the results
var newChild = new childObj("Constructing Child", "Constructing Parent");
newChild.childCall();
newChild.parentCall();
Console Results:
Constructing Child
Constructing Parent
childCall
parentCall
I've read multiple articles on inheritance in JS, but had assumed that the Underscore library had it solved for me.
No, Underscore does have no helper functions for prototypical inheritance. Read the docs on what extend does:
_.extend(destination, *sources): Copy all of the properties in the source objects over to the destination object, and return the destination object. It's in-order, so the last source will override properties of the same name in previous arguments.
Most interestingly, it does not return a function, but its first argument (which is a plain object).
So get back to the articles you've read, and choose a framework that does actually have an inherit function or implement the inheritance yourself - it's not hard.
John Resig has a good blog post on implementing Javascript inheritance that may be useful to you as it contains a solution for prototype inheritance, whereas Underscore extend is designed to extend simple Javascript objects.
As others have explained, underscore's extend method creates (very) shallow copies of object instances and doesn't preserve prototype chains. However I recently came up against a small library — Compose.js — whose wider remit is to provide a more flexible API for JS's OO properties.
The second example in the readme seems to deal with your use case almost exactly — I believe in your situation it would be invoked as follows:
Zoo.Controllers.WhaleController = Compose( Zoo.Controller, { new: function () {
// do something
} );
You could reuse the extend() function used by backboneJs. Its a quite simple function that uses _.extend() too
http://backbonejs.org/docs/backbone.html#section-208
Then attach it to your Controller prototype so you could do something like:
var MyController = Controller.extend({ /* your attributes, methods */ });
hope this helps
I'm not a guru at JS. Today I was told during a code review that for the following code below, Reference type values need to be initialized in the constructor which means change this somehow referring to the constraints: {}
define(["dojo/_base/declare",
"dijit/form/CurrencyTextBox",
"xxxx/util/currencyUtil",
"./_InputWidgetMixin",
"../secure/_SecureWidgetMixin"
],
function (declare, xxxTextBox, xxxUtil, _InputWidgetMixin, _SecureWidgetMixin) {
return declare("xxx.widget.input.xxxTextBox", [xxxTextBox, _InputWidgetMixin, _SecureWidgetMixin], {
constraints: {},
reset: function () {
this._set("value", this.resetValue);
},
not sure what this means and why I need to move this and if it's a Dojo thing or plain JS thing to worry about and why.
I would say this is a dojo thing, it's to do with how dojo implements a class-like system. To understand why this is important, take a look at this recent question : Dojo instances of same widgets are not saparated.
As mentioned there, arrays and objects are shared across instances, so if you don't want this behaviour, you should initialize them in your constructor. Dojo docs on declare
Your constraints will be a shared object across all instances of xxxTextBox unless in your constructor you assign a new object to constraints:
this.constraints = {};
This is because the constraints value is part of a prototype object and therefore this is a javascript thing. Once you assign this.constraints you obscure the reference to the shared constraints with the objects own local copy.
I want to write some Javascript classes which extend DOM nodes (so that I can then insert instances of my class directly into the DOM), but am having difficulty finding out which class/prototype I should inherit from.
E.g.:
function myExtendedElement() {
this.superclass = ClassA;
this.superclass();
delete this.superclass;
}
But what should ClassA be?
It's not a good idea to do this.
First of all, to inherit from DOM element, you need to have access to that element's prototype. The problem is that not all browsers provide access to prototypes of DOM elements. Newer Gecko and WebKit -based clients, for example, expose some of these prototypes as global objects - HTMLDivElement, HTMLElement, Element, Node, etc.
For example, plain DIV element usually has a prototype chain similar to:
HTMLDivElement.prototype -> HTMLElement.prototype -> Element.prototype
-> Node.prototype -> Object.prototype -> null
You can access any of them and extend or inherit from as desired. But again, even though you can, I strongly advise not to.
When browser doesn't expose these prototypes, you're pretty much out of luck. You can try retrieving them by following constructor property of DOM element itself -
document.createElement('div').constructor;
- but then there's no guarantee that element has constructor property (e.g. IE6 doesn't) and even if it does, that this property references "correct" object. If, after all, constructor does reference correct object, there's still no guarantee that this objects is allowed to be augmented at all. The truth is that host objects are allowed to implement completely bizarre behavior and do not even have to follow rules that native JS objects follow (you can find dozens of such examples in real life).
Second reason you want to avoid inheriting from DOM element prototypes is that mechanism of such inheritance is not really specified anywhere; it could be quirky, unpredictable and overall fragile and unreliable.
Yes, you can create a constructor that would initialize objects with proper prototype chain (i.e. having DOM prototype in it):
function MyDivElement(){}
MyDivElement.prototype = HTMLDivElement.prototype;
var myDiv = new MyDivElement();
typeof myDiv.appendChild; // "function"
- but this is as much as it goes, and usefulness of this whole approach becomes limited by having certain methods in prototype and nothing else -
typeof myDivElement.nodeName; // "undefined"
myDivElement.innerHTML = '<span>foo<\/span>';
myDivElement.childNodes; // Error
Until some standard specifies exact mechanism for inheriting from DOM prototypes (and browsers actually implement that mechanism), it's best to leave them alone, and perhaps try alternative approach - e.g. wrapper or decorator patterns rather than prototype one :)
Old Q but there's a better answer than "Do" or "Don't" now that IE6 is mostly defunct. First of all prototyping core ECMA endpoint-inheritance constructors like 'Array' is pretty harmless and useful if you do it properly and test to avoid breaking existing methods. Definitely stay away from Object and think real hard before messing with Function, however.
If you're sharing code between a lot of people/authors, or dealing with DOM uncertainty, however, it's typically better to create adapter/wrapper objects with a new factory method to use in an inheritance-scheme.
In this case I wrote document.createExtEl to create wrapped DOM elements whose accessible properties are all available via prototype.
Using the following, your "superclass" for divs would be HTMLExtDivElement (in this case globally available - ew, but it's just an example). All references to the original HTMLElement instance's available properties live inside the wrapper's prototype. Note: some old IE properties can't be passed as references or even accessed without throwing errors (awesome), which is what the try/catch is for.
You could normalize common properties by adding logic to put missing or standardized properties in right after the loop wraps instance-available properties but I'll leave that to you.
Now for the love of Pete, don't ever use my code to write some cascading 16-times inheritance foolishness and then implement in some ironically popular library we're all forced to deal with or I will hunt you down and loudly quote "Design Patterns" at you while throwing rotten fruit.
//Implementation just like document.createElement()
//document.createExtEl('div').tagName === 'DIV'
document.createExtEl = ( function(){ //returns a function below
var htmlTags = ['div','a'], //... add all the element tags you care to make extendable here
constructorMap = {},
i = htmlTags.length;
while(i--){
thisTag = htmlTags[i].toLowerCase();
constructorMap[ thisTag ] = function(){
var elObj = document.createElement(thisTag),
thisProto = this.constructor.prototype,
constructorName = 'HTMLExt' + thisTag.charAt(0).toUpperCase() + thisTag.slice(1) + 'Element';
alert(constructorName);
window[constructorName] = this.constructor; //adds a global reference you can access the new constructor from.
for(var x in elObj){ try{ thisProto[x] = elObj[x]; } catch(e){} }
}
}
//all of the above executes once and returned function accesses via closure
return function(tagName){
return new constructorMap[tagName.toLowerCase()]();
}
} )()
//Now in the case of a superclass/constructor for div, you could use HTMLExtDivElement globally
In 2020, you can easily create a custom element by extending HTML elements.
class AppDrawer extends HTMLElement {...}
window.customElements.define('app-drawer', AppDrawer);
// Or use an anonymous class if you don't want a named constructor in current scope.
window.customElements.define('app-drawer', class extends HTMLElement {...});
More info and reference: Custom Elements
You can simply add new functions to the DOM prototypes, eg.
Element.prototype.myNameSpaceSomeFunction = function(...){...}
Then myNameSpaceSomeFunction will exist on all elements.
I've found a hack that works... at least, it enables me to access the extended object properties via the DOM element and vice versa. But it's hardly elegant.
var DOMelement = document.getElementById('myID'); // or $('#myID')[0]; in jQuery
DOMelement.extended = new extensionClass(this);
function extensionClass(element) {
this.element = element;
...
}