Related
I realize that, strictly speaking, this is not subclassing the array type, but will this work in the way one might expect, or am I still going to run into some issues with .length and the like? Are there any drawbacks that I would not have if normal subclassing were an option?
function Vector()
{
var vector = [];
vector.sum = function()
{
sum = 0.0;
for(i = 0; i < this.length; i++)
{
sum += this[i];
}
return sum;
}
return vector;
}
v = Vector();
v.push(1); v.push(2);
console.log(v.sum());
I'd wrap an array inside a proper vector type like this:
window.Vector = function Vector() {
this.data = [];
}
Vector.prototype.push = function push() {
Array.prototype.push.apply(this.data, arguments);
}
Vector.prototype.sum = function sum() {
for(var i = 0, s=0.0, len=this.data.length; i < len; s += this.data[i++]);
return s;
}
var vector1 = new Vector();
vector1.push(1); vector1.push(2);
console.log(vector1.sum());
Alternatively you can build new prototype functions on arrays and then just use normal arrays.
If you are consistent with naming the arrays so they all start with a lowercase v for example or something similar that clearly mark them aw vector and not normal arrays, and you do the same on the vector specific prototype functions, then it should be fairly easy to keep track of.
Array.prototype.vSum = function vSum() {
for(var i = 0, s=0.0, len=this.length; i < len; s += this[i++]);
return s;
}
var vector1 = [];
vector1.push(1); vector1.push(2);
console.log(vector1.vSum());
EDIT -- I originally wrote that you could subclass an Array just like any other object, which was wrong. Learn something new every day. Here is a good discussion
http://perfectionkills.com/how-ecmascript-5-still-does-not-allow-to-subclass-an-array/
In this case, would composition work better? i.e. just create a Vector object, and have it backed by an array. This seems to be the path you are on, you just need to add the push and any other methods to the prototype.
Nowadays you could use subclassing with ES6 classes:
class Vector extends Array {
sum(){
return this.reduce((total, value) => total + value)
}
}
let v2 = new Vector();
v2.push(1);
v2.push(2);
console.log(v2.sum());
console.log(v2.length);
v2.length = 0;
console.log(v2.length);
console.log(v2);
Just another example of the wrapper. Having some fun with .bind.
var _Array = function _Array() {
if ( !( this instanceof _Array ) ) {
return new _Array();
};
};
_Array.prototype.push = function() {
var apContextBound = Array.prototype.push,
pushItAgainst = Function.prototype.apply.bind( apContextBound );
pushItAgainst( this, arguments );
};
_Array.prototype.pushPushItRealGood = function() {
var apContextBound = Array.prototype.push,
pushItAgainst = Function.prototype.apply.bind( apContextBound );
pushItAgainst( this, arguments );
};
_Array.prototype.typeof = (function() { return ( Object.prototype.toString.call( [] ) ); }());
#hvgotcodes answer has an awesome link. I just wanted to summerize the conclusion here.
Wrappers. Prototype chain injection
This seems to be the best method to extend array from the article.
wrappers can be used ... in which object’s prototype chain is augmented, rather than object itself.
function SubArray() {
var arr = [ ];
arr.push.apply(arr, arguments);
arr.__proto__ = SubArray.prototype;
return arr;
}
SubArray.prototype = new Array;
// Add custom functions here to SubArray.prototype.
SubArray.prototype.last = function() {
return this[this.length - 1];
};
var sub = new SubArray(1, 2, 3);
sub instanceof SubArray; // true
sub instanceof Array; // true
Unfortunally for me, this method uses arr.__proto__, unsupported in IE 8-, a browser I have to support.
Wrappers. Direct property injection.
This method is a little slower than the above, but works in IE 8-.
Wrapper approach avoids setting up inheritance or emulating length/indices relation. Instead, a factory-like function can create a plain Array object, and then augment it directly with any custom methods. Since returned object is an Array one, it maintains proper length/indices relation, as well as [[Class]] of “Array”. It also inherits from Array.prototype, naturally.
function makeSubArray() {
var arr = [ ];
arr.push.apply(arr, arguments);
// Add custom functions here to arr.
arr.last = function() {
return this[this.length - 1];
};
return arr;
}
var sub = makeSubArray(1, 2, 3);
sub instanceof Array; // true
sub.length; // 3
sub.last(); // 3
There is a way that looks and feels like prototypical inheritance, but it's different in only one way.
First lets take a look at one of the standard ways of implementing prototypical inheritance in javascript:
var MyClass = function(bar){
this.foo = bar;
};
MyClass.prototype.awesomeMethod = function(){
alert("I'm awesome")
};
// extends MyClass
var MySubClass = function(bar){
MyClass.call(this, bar); // <- call super constructor
}
// which happens here
MySubClass.prototype = Object.create(MyClass.prototype); // prototype object with MyClass as its prototype
// allows us to still walk up the prototype chain as expected
Object.defineProperty(MySubClass.prototype, "constructor", {
enumerable: false, // this is merely a preference, but worth considering, it won't affect the inheritance aspect
value: MySubClass
});
// place extended/overridden methods here
MySubClass.prototype.superAwesomeMethod = function(){
alert("I'm super awesome!");
};
var testInstance = new MySubClass("hello");
alert(testInstance instanceof MyClass); // true
alert(testInstance instanceof MySubClass); // true
The next example just wraps up the above structure to keep everything clean. And there is a slight tweak that seems at first glance to perform a miracle. However, all that is really happening is each instance of the subclass is using not the Array prototype as a template for construction, but rather an instance of an Array - so the prototype of the subclass comes hooked onto the end of a fully loaded object which passes the ducktype of an array - which it then copies. If you still see something strange here and it bothers you, I'm not sure that I can explain it better - so maybe how it works is a good topic for another question. :)
var extend = function(child, parent, optionalArgs){ //...
if(parent.toString() === "function "+parent.name+"() { [native code] }"){
optionalArgs = [parent].concat(Array.prototype.slice.call(arguments, 2));
child.prototype = Object.create(new parent.bind.apply(null, optionalArgs));
}else{
child.prototype = Object.create(parent.prototype);
}
Object.defineProperties(child.prototype, {
constructor: {enumerable: false, value: child},
_super_: {enumerable: false, value: parent} // merely for convenience (for future use), its not used here because our prototype is already constructed!
});
};
var Vector = (function(){
// we can extend Vector prototype here because functions are hoisted
// so it keeps the extend declaration close to the class declaration
// where we would expect to see it
extend(Vector, Array);
function Vector(){
// from here on out we are an instance of Array as well as an instance of Vector
// not needed here
// this._super_.call(this, arguments); // applies parent constructor (in this case Array, but we already did it during prototyping, so use this when extending your own classes)
// construct a Vector as needed from arguments
this.push.apply(this, arguments);
}
// just in case the prototype description warrants a closure
(function(){
var _Vector = this;
_Vector.sum = function sum(){
var i=0, s=0.0, l=this.length;
while(i<l){
s = s + this[i++];
}
return s;
};
}).call(Vector.prototype);
return Vector;
})();
var a = new Vector(1,2,3); // 1,2,3
var b = new Vector(4,5,6,7); // 4,5,6,7
alert(a instanceof Array && a instanceof Vector); // true
alert(a === b); // false
alert(a.length); // 3
alert(b.length); // 4
alert(a.sum()); // 6
alert(b.sum()); // 22
Soon we'll have class and the ability to extend native classes in ES6 but that may be a another year yet. In the mean time I hope this helps someone.
function SubArray(arrayToInitWith){
Array.call(this);
var subArrayInstance = this;
subArrayInstance.length = arrayToInitWith.length;
arrayToInitWith.forEach(function(e, i){
subArrayInstance[i] = e;
});
}
SubArray.prototype = Object.create(Array.prototype);
SubArray.prototype.specialMethod = function(){alert("baz");};
var subclassedArray = new SubArray(["Some", "old", "values"]);
So I was searching for a way to deep clone an object in javascript and found this solution :
function keepCloning(objectpassed) {
if (objectpassed === null || typeof objectpassed !== 'object') {
return objectpassed;
}
// give temporary-storage the original obj's constructor
var temporary_storage = objectpassed.constructor();
for (var key in objectpassed) {
temporary_storage[key] = keepCloning(objectpassed[key]);
}
return temporary_storage;
}
var employeeDetailsOriginal = { name: 'Manjula', age: 25, Profession: 'Software Engineer' };
var employeeDetailsDuplicate = (keepCloning(employeeDetailsOriginal));
employeeDetailsOriginal.name = "NameChanged";
console.log(employeeDetailsOriginal);
console.log(employeeDetailsDuplicate);
My question was that shouldn't we be using new with the constructor?
var temporary_storage = new objectpassed.constructor();
Then I realised that the object passed is made using object literals{} and have constructor as Object();
I made a first class constructor function Person()
function Person(name, age, profession){
this.name=name;
this.age=age;
this.profession=profession;
}
var employeeDetailsOriginal = new Person('Manjula', 25,'Software Engineer');
var employeeDetailsDuplicate (keepCloning(employeeDetailsOriginal));
Now when I used the keepCloning method, it threw an error that temporary_storage is undefined meaning that
objectpassed.constructor();
must have returned undefined!
So I want to know that do we or do we not use the new keyword before constructor?
I googled it but didn't found any satisfactory explanation!
2) In the same context of the question
function A(){
}
var a = new A(); //1
var a1= A.prototype.constructor(); //2
var a1 = new A.prototype.constructor(); //3
Which of the (2) and (3) is the exactly similar method to (1) for constructing an object of A?
new constructor();
is equal to
(function(){
const obj = Object.create(constructor.prototype);
constructor.call(obj);
return obj;
})()
So with new it returns a new object, without it just calls the constructor function, and as it returns nothing its undefined. So yes its mandatory.
new A() === new A.prototype.constructor();
A() === A.prototype.constructor();
I'm new to prototyping and instantiations and therefore had a question :
How can I create a function that constructs a new array that also has some properties added with prototype but without modifying the default Array function ?
For example :
function Cool_Object() {
this = new Array() // Construct new array.
//This is only for the example. I know you can't do that.
}
Cool_Object.prototype.my_method = function() {
// Some method added
};
So, if you call :
var myObject = new Cool_Object();
myObject would be an array and have a method called "my_method" (which actually calls a function).
But the default Array object would be intact.
Thanks in advance !
You've got it a bit backwards. Just use Array.prototype as your custom object's prototype.
function Cool_Object() {
this.my_method = function () {
return 42;
}
}
Cool_Object.prototype = Array.prototype;
var foo = new Cool_Object();
foo.my_method(); // 42
foo.push(13);
foo[0]; // 13
You can get both Array.prototype and my_method on Cool_Object's prototype, without modifying Array.prototype, by introducing an intermediate type:
function Even_Cooler() {}
Even_Cooler.prototype = Array.prototype;
function Cool_Object() {}
Cool_Object.prototype = new Even_Cooler();
Cool_Object.prototype.my_method = function () {
return 42;
}
You can't just assign to this, it doesn't work and throws a ReferenceError. Just make Cool_Object extend Array.
One way to do that:
var Cool_Object = Object.create(Array.prototype);
Cool_Object.my_method = function() {
// Some method added
};
Then create further objects with
var obj = Object.create(Cool_Object);
Use an array as the function's prototype, so that your new type "inherits" from Array, and then introduce new methods in the prototype:
function CustomArray() {}
CustomArray.prototype = [];
// introduce a new method to your custom array type
CustomArray.prototype.total = function() {
return this.reduce(function(ret, el) {
return ret+el;
}, 0);
};
// introduce another new method to your custom array type
CustomArray.prototype.arithmetiMean = function() {
return this.total()/this.length;
};
Alternately you could introduce those methods in new instances:
function CustomArray() {
// introduce a new method to your custom array object
this.total = function() {
return this.reduce(function(ret, el) {
return ret+el;
}, 0);
};
// introduce another new method to your custom array object
this.arithmetiMean = function() {
return this.total()/this.length;
};
}
CustomArray.prototype = [];
var arr = new CustomArray();
arr.push(1); // push is an array-standard method
arr.push(2);
arr.push(3);
arr.push(4);
arr.push(5);
arr.push(6);
arr.push(7);
arr.push(8);
arr.push(9);
arr.push(10);
console.log(arr.arithmetiMean());
function PseudoArray() {
};
PseudoArray.prototype = Object.defineProperties(Object.create(Array.prototype), {
constructor: {value:PseudoArray}
})
Adding this for reference, since Object.create is supported in most browsers these days, a good way to make your own array object would be like this:
function MyCustomArray(){
}
MyCustomArray.prototype = $.extend(Object.create(Array.prototype), {
/* example of creating own method */
evenonly : function(){
return this.filter(function(value){return (value % 2 == 0);});
},
/* example for overwriting existing method */
push : function(value){
console.log('Quit pushing me around!');
return Array.prototype.push.call(this, value);
}
});
var myca = new MyCustomArray();
myca instanceof MyCustomArray /*true*/
myca instanceof Array /*true*/
myca instanceof Object /*true*/
myca.push(1); /*Quit pushing me around!*/
myca.push(2); /*Quit pushing me around!*/
myca.push(3); /*Quit pushing me around!*/
myca.push(4); /*Quit pushing me around!*/
myca.push(5); /*Quit pushing me around!*/
myca.push(6); /*Quit pushing me around!*/
myca.length; /*6*/
myca.evenonly() /*[2, 4, 6]*/
Using jQuery's $.extend, because it's convenient to keep code structured, but there's no need for it, you could do this instead:
MyCustomArray.prototype = Object.create(Array.prototype);
MyCustomArray.prototype.push = function(){...}
I much prefer defining the methods on the prototype rather than putting them inside the constructor. It's cleaner and saves your custom array object from being cluttered with unnecessary functions.
I like that in javascript, I can create a function, and then add further methods and attributes to that function
myInstance = function() {return 5}
myInstance.attr = 10
I would like to create a class to generate these objects. I assume I have to inherit from the Function base class.
In other words, I would like to:
var myInstance = new myFunctionClass()
var x = myInstance()
// x == 5
But I don't know how to create the myFunctionClass. I have tried the following, but it does not work:
var myFunctionClass = function() {Function.call(this, "return 5")}
myFunctionClass.prototype = new Function()
myInstance = new myFunctionClass()
myInstance()
// I would hope this would return 5, but instead I get
// TypeError: Property 'myInstance' of object #<Object> is not a function
I also tried the more complicated (and more proper?) inheritance method found here: How to "properly" create a custom object in JavaScript?, with no more luck. I have also tried using the util.inherits(myFunctionClass, Function) found in node.js. Still no luck
I have exhausted Google, and therefore feel that I must be missing something fundamental or obvious. Help would be greatly appreciated.
Your trying to inherit from Function. This is a right pain to do. I suggest you do the following instead
Live Example
var Proto = Object.create(Function.prototype);
Object.extend(Proto, {
constructor: function (d) {
console.log("construct, argument : ", d);
this.d = d;
// this is your constructor logic
},
call: function () {
console.log("call", this.d);
// this get's called when you invoke the "function" that is the instance
return "from call";
},
method: function () {
console.log("method");
// some method
return "return from method";
},
// some attr
attr: 42
});
You want to create a prototype object that forms the basis of your "class". It has your generic methods/attributes. It also has a constructor that gets invoked on object construction and a call method that gets invoked when you call the function
var functionFactory = function (proto) {
return function () {
var f = function () {
return f.call.apply(f, arguments);
};
Object.keys(proto).forEach(function (key) {
f[key] = proto[key];
});
f.constructor.apply(f, arguments);
return f;
}
}
A function factory takes a prototype object and returns a factory for it. The returned function when called will give you a new function object that "inherits" from your prototype object.
var protoFactory = functionFactory(proto);
var instance = protoFactory();
Here you create your factory and then create your instance.
However this isn't proper prototypical OO. we are just shallow copying properties of a prototype into a new object. So changes to the prototype will not reflect back to the original object.
If you want real prototypical OO then you need to use a hack.
var f = function () {
// your logic here
};
f.__proto__ = Proto;
Notice how we use the non-standard deprecated .__proto__ and we are mutating the value of [[Prototype]] at run-time which is considered evil.
JS does not allow a constructor to return a function, even though functions are objects. So you cant have an instantiation of a prototype that is itself executable. (Am I right in this? please correct if I'm not, it's an interesting question).
Though you could do a factory function:
var makeCoolFunc = function() {
var f = function() { return 5 };
f.a = 123;
f.b = 'hell yes!'
return f;
};
var func = makeCoolFunc();
var x = func();
You can extend Function and pass the wanted function body as String to the super constructor. The context of the function can be accessed with arguments.callee.
Example for an observable Attribute class:
export default class Attribute extends Function {
constructor(defaultValue){
super("value", "return arguments.callee.apply(arguments);");
this.value = defaultValue;
this.defaultValue = defaultValue;
this.changeListeners = [];
}
apply([value]){
if(value!==undefined){
if(value!==this.value){
var oldValue = this.value;
this.value=value;
this.changeListeners.every((changeListener)=>changeListener(oldValue, value));
}
}
return this.value;
}
clear(){
this.value=undefined;
}
reset(){
this.value=this.defaultValue;
}
addChangeListener(listener){
this.changeListeners.push(listener);
}
removeChangeListener(listener){
this.changeListeners.remove(listener);
}
clearChangeListeners(){
this.changeListeners = [];
}
}
Example usage:
import Attribute from './attribute.js';
var name= new Attribute();
name('foo'); //set value of name to 'foo'
name.addChangeListener((oldValue, newValue)=>{
alert('value changed from ' +oldValue+ ' to ' +newValue);
});
alert(name()); //show value of name: 'foo'
name('baa'); //set value of name to new value 'baa' and trigger change listener
I know the possibility to call a function with an array of arguments with apply(obj,args);
Is there a way to use this feature when creating a new instance of a function?
I mean something like this:
function A(arg1,arg2){
var a = arg1;
var b = arg2;
}
var a = new A.apply([1,2]); //create new instance using an array of arguments
I hope you understand what i mean... ^^^
Thanks for your help!
Solved!
I got the right answer. To make the answer fit to my question:
function A(arg1,arg2) {
var a = arg1;
var b = arg2;
}
var a = new (A.bind.apply(A,[A,1,2]))();
var wrapper = function(f, args) {
return function() {
f.apply(this, args);
};
};
function Constructor() {
this.foo = 4;
}
var o = new (wrapper(Constructor, [1,2]));
alert(o.foo);
We take a function and arguments and create a function that applies the arguments to that function with the this scope.
Then if you call it with the new keyword it passes in a new fresh this and returns it.
The important thing is the brackets
new (wrapper(Constructor, [1,2]))
Calls the new keyword on the function returned from the wrapper, where as
new wrapper(Constructor, [1,2])
Calls the new keyword on the wrapper function.
The reason it needs to be wrapped is so that this that you apply it to is set with the new keyword. A new this object needs to be created and passed into a function which means that you must call .apply(this, array) inside a function.
Live example
Alternatively you could use ES5 .bind method
var wrapper = function(f, args) {
var params = [f].concat(args);
return f.bind.apply(f, params);
};
See example
with ECMAscript 5 you can:
function createInstanceWithArguments (fConstructor, aArgs) {
var foo = Object.create(fConstructor.prototype);
fConstructor.apply(foo, aArgs);
return foo;
}
#Raynos answer works well, except that the non-ES5 version is missing the constructor's prototype after instantiation.
Here's my updated cApply method:
var cApply = function(c) {
var ctor = function(args) {
c.apply(this, args);
};
ctor.prototype = c.prototype;
return ctor;
};
Which can be used like this:
var WrappedConstructor = cApply(Constructor);
var obj = new WrappedConstructor([1,2,3]);
// or inline like this.
var obj2 = new (cApply(Constructor))([1,2,3]);
JSFiddle for reference.
You can curry the functions:
function A(arg1, arg2) {
// ...
}
function A12() {
A(1, 2);
}
You could even build a curry factory:
function A_curry() {
var args = arguments;
return function () {
A.apply(null, args);
};
}
You can use Object.create() to build the new instance, and call the constructor on the instance after that:
var args = [1,2,3];
var instance = Object.create(MyClass.prototype);
MyClass.apply(instance, args);
or in a single line:
var instance = MyClass.apply(Object.create(MyClass.prototype), args);
but don't forget return this; in MyClass by this single line solution.
If you don't have Object.create(), then it is very simple to write one:
Object.create = function (source){
var Surrogate = function (){};
Surrogate.prototype = source;
return new Surrogate();
};
You can optionally write an Function.newInstance() or a Function.prototype.newInstance() function:
Function.newInstance = function (fn, args){
var instance = Object.create(fn.prototype);
fn.apply(instance, args);
return instance;
};
so var instance = Function.newInstance(MyClass, args);.
note: It is not recommended to override native classes.
What if you have your object class name stored in a variable called className ?
var className = 'A'
According to this answer here it all becomes very clean and simple using ECMAScipt5's Function.prototype.bind method.
With an array of arguments:
new ( Function.prototype.bind.apply( className, arguments ) );
Or with an argument list:
new ( Function.prototype.bind.call( className, argument1, argument2, ... ) );
A single line, no need for a wrapper.
I just had the same issue and also wanted to preserve prototype properties of the target object. After looking at the answers, just came up with a rather simple solution. Thought I might as well share it for any future seeker :)
// Define a pseudo object that takes your arguments as an array
var PseudoObject = function (args) {
TargetObject.apply(this, args);
};
// if you want to inherit prototype as well
PseudoObject.prototype = new TargetObject();
// Now just use the PseudoObject to instantiate a object of the the OtherObject
var newObj = new PseudoObject([1, 2, 3]);
It can now be done using the spread operator:
let a = new A(...[1,2]);
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax#examples