I wish to be able to extend any object given in an .extend(obj) function. So far I have it working except for when an object literal is being passed in.
example:
class myClass {
static extend(obj) {
Object.assign(Object.getPrototypeOf(obj), myClass.prototype);
myClass.call(obj);
}
sayHello() {
return 'hello!'
}
}
This works fine when the extend function is called from within a constructor like so:
function foo() {
myClass.extend(this);
}
const bar = new foo();
bar.sayHello();
However when I pass in an object literal which is already created the methods from myClass.prototype are not available.
const foo = {};
myClass.extend(foo);
foo.sayHello(); // this is not available.
Is there a way to check the last case and assign the prototype to the object itself instead of it's prototype so that the last scenario will also work?
static extend() {
if (/* obj is an object literal */) {
Object.assign(obj, myClass.prototype);
} else {
// first example
}
This works fine when the extend function is called from within a constructor like so:
It shouldn't work fine, and when I ran it I got the error Class constructor myClass cannot be invoked without 'new'. That error is because of the statement myClass.call(obj);. Only if I change the class to an ES5 constructor function does it work.
However when I pass in an object literal which is already created the methods from myClass.prototype are not available.
They were for me.
function myClass() {}
myClass.extend = function(obj) {
Object.assign(Object.getPrototypeOf(obj), myClass.prototype);
myClass.call(obj);
}
myClass.prototype.sayHello = function() {
return 'hello!'
}
const foo = {};
myClass.extend(foo);
foo.sayHello(); // "hello!"
Related
class Foo {
constructor(bar) {
this.bar = bar
}
getBar() {
return this.bar
}
}
function unmethodize(f) {
return function(object, ...args) {
return f.apply(object, args)
}
}
const unmethodizedGetBar = unmethodize(Foo.prototype.getBar)
function test() {
foos = [new Foo(1), new Foo(2), new Foo(3)]
return foos.map(unmethodizedGetBar)
}
I know about foos.map(foo => foo.getBar()) etc.
I simply want a version of getBar that takes the "this" object as its first parameter. Does it already exist somewhere, or must I create it via unmethodize or some such?
Does it already exist somewhere?
No, you'll have to create it yourself. The getBar in your class defines only a method that expects a this argument.
If you don't want to use an arrow function or write your own unmethodize function, you can however achieve this with builtins only:
const unmethodize = Function.bind.bind(Function.call);
foos.map(Function.call.bind(Foo.prototype.getBar))
foos.map(Function.call, Foo.prototype.getBar)
But seriously just use the arrow function :-)
I can do this in JavaScript:
var output = String(result);
And I can do this with the same object that is referenced using String:
var character = String.fromCharCode(10);
String can be used as a function to construct an object and members can be called on it without using it as a constructor. How do I make an object usable in both these ways? What is this called?
You are talking about class methods.
function Foo() {
this.bar = 3
}
Foo.baz = function() {
console.log('hi');
}
or in ES 2015
class Foo {
static baz () {
console.log('hi');
}
}
Response to the comments
You can define a static method in the constructor function because the constructor function is necessarily in scope:
function Foo() {
Foo.method = function () {
// do stuff
}
}
There are a couple of problems with this approach though:
Foo.method('stringy string'); // TypeError: cannot read property 'method' of undefined
Because the static method is defined in the constructor, it won't be there until the constructor function runs at least once:
const foo = new Foo();
Foo.method('stringy string'); // now we're good
which leads to another problem, now we're wastefully reassigning that method every time the constructor runs. You can avoid that with a conditional check:
function Foo() {
if (!Foo.method) Foo.method = function....
}
But that's a lot of weird stuff just to avoid defining the class method after the constructor, and it still doesn't solve the first problem.
You can make a class with static methods:
class Foo {
constructor(bar) {
this.bar = bar;
}
length() {
return this.bar.length;
}
static fromThing(thing) {
return new Foo(thing.bar);
}
}
Foo.fromThing() is analogous to String.fromCharCode()
Simply
function MyClass () {
this.val = 1;
}
MyClass.staticMethod = function () {/* code here */};
I'm working on building a collection of prototype helper methods inside a wrapper. However for ease of use, I'd like to be able to call the object as both a new instance and single global instance under the same call.
For example, with jQuery, you can call both "$" and "$()" which can be used differently http://learn.jquery.com/using-jquery-core/dollar-object-vs-function/:
So given the bellow as simple example, how could I do something similar?
(function () {
var myWrapper = function (foo) {
return new helper(foo);
};
var helper = function (foo) {
this[0] = foo;
return this;
}
helper.prototype = {
putVar: function(foo) {
this[0] = foo;
}
}
if(!window.$) {
window.$ = myWrapper;
}
})();
// create an new instace;
var instance = $("bar");
console.log(instance);
// call a prototype method
instance.putVar("foo");
console.log(instance);
// call a prototype method using the same call without new instance
// this doesnt work :(
$.putVar("foo");
// however this will work
window.myLib = $("foo");
myLib.putVar("bar");
http://jsfiddle.net/2ywsunb4/
If you want to call $.putVar, you should define putVar like this:
myWrapper.putVar = function (foo) {
console.log('Work');
}
In your code, the instance and myLib are the same thing, they are both instances created by you.
If you want to call both $.putVar and $(...).putVar, you should add the code I show you above. That means you have to define two putVar functions, one is used like a 'instance' method, while the other one is used like a 'static' method.
If you search through jQuery source code, you will see two each functions defined. That's why you can all both $.each(...) and $(...).each for different usages.
I am creating an AJAX API for a web service and I want to be able to call jQuery-like accessors.
jQuery seems to be able to execute 'jQuery' as a function, but also use it to directly access the object that is the result of the function EG:
jQuery();
jQuery.each({});
This is the trick that I can't seem to pull off:
myAPI('foo'); //output: 'foo'
myAPI('foo').changeBar(); //output: 'foo' 1
myAPI.changeBar(); //Error: not a function
I have seen the answers to similar questions, which are helpful, but don't really answer my question.
#8734115 - Really interesting, but you can't access the methods that were set by f.prototype.
#2953314 - Uses Multiple operations to create object instead of a single function.
here is my code:
(function(window) {
var h = function(foo) {
// The h object is actually just the init constructor 'enhanced'
return new h.fn.init(foo);
};
/**
* Methods defined at protoype.
*/
h.fn = h.prototype = {
constructor: h,
init: function(foo) {
console.log(foo);
return this;
},
splice : function () {},
length : 0,
bar : 0,
changeBar : function() {
this.bar++;
return this.bar;
}
};
h.fn.init.prototype = h.fn;
//Publish
window.myAPI =h;
}( window));
I'm sure I'm missing something simple :(
What jQuery is doing there is using jQuery as both a function and as a pseudo-namespace. That is, you can call jQuery: var divs = jQuery("div"); and you can use properties on it, e.g.: jQuery.each(...);.
This is possible because in JavaScript, functions are first-class objects, and so you can add arbitrary properties to them:
function foo() {
alert("Foo!");
}
foo.bar = function() {
alert("Bar!");
};
foo(); // "Foo!"
foo.bar(); // "Bar!"
That's literally all there is to it.
Within the call to bar, this will be the foo function (because this is determined entirely by how a function is called, not where it's defined). jQuery doesn't use this to refer to itself (usually it uses this to refer to DOM elements, sometimes to other things like array elements; when referring to itself, since it's a single thing, it just uses jQuery).
Now, you might want to ensure that your functions have proper names (whereas the function I assigned to bar above is anonymous — the property has a name, but the function does not). In that case, you might get into the module pattern:
var foo = (function() {
function foo() {
alert("Foo!");
}
function foo_bar() {
alert("Bar!");
}
foo.bar = foo_bar;
return foo;
})();
foo(); // "Foo!"
foo.bar(); // "Bar!"
That pattern also has the advantage that you can have private data and functions held within the scoping function (the big anonymous function that wraps everything else) that only your code can use.
var foo = (function() {
function foo() {
reallyPrivate("Foo!");
}
function foo_bar() {
reallyPrivate("Bar!");
}
function reallyPrivate(msg) {
alert(msg);
}
foo.bar = foo_bar;
return foo;
})();
foo(); // "Foo!"
foo.bar(); // "Bar!"
reallyPrivate("Hi"); // Error, `reallyPrivate` is undefined outside of the scoping function
In your code, you're assigning things to the prototype property of the function. That only comes into play when the function is called as a constructor function (e.g., via new). When you do that, the object created by new receives the function's prototype property as its underlying prototype. But that's a completely different thing, unrelated to what jQuery does where it's both a function and a pseudo-namespace.
You do not need any of that weirdness, to use stuff like $.each
you just attach functions to the function object instead
of the prototype object:
function Constructor() {
if (!(this instanceof Constructor)) {
return new Constructor();
}
}
Constructor.prototype = {
each: function() {
return "instance method";
}
};
Constructor.each = function() {
return "static method";
};
var a = Constructor();
a.each(); //"instance method"
Constructor.each(); //"static method"
I use the following function for creating new objects.
function newObj(o) {
var params = Array.prototype.slice.call(arguments,1);
function F() {}
F.prototype = o;
var obj = new F();
if(params.length) {
obj.init.apply(obj,params);
}
return obj;
}
And it works well most of the time. However one of my base "classes" is now defined as inheriting from another base class
SPZ.EditablePuzzle = function () {
// function and variable definitions
return {
///some methods and properties
}
}();
SPZ.EditablePuzzle.prototype = SPZ.Puzzle;
Now when I use newObj() to create a new SPZ.EditablePuzzle the init function is not defined even though it is defined in SPZ.Puzzle and I make sure EditablePuzzle runs after Puzzle
Why won't my newObj function find the init function? Shouldn't it automatically look in the prototype as soon as it fails to find it in the object itself?
I suspect the inheritance is not well set. try doing
SPZ.EditablePuzzle.prototype = new SPZ.Puzzle;
Might solve this problem, though I am not sure.
Concerning the problem:
function Foo() {
}
Foo.prototype.init = function() {
console.log('bla');
};
function FooBar() {
}
FooBar.prototype = Foo; // looks fishy...
var kitten = new FooBar();
console.log(kitten.init); // yields undefined, uh what?
The problem is, that in this case Foo itself gets assigned to the prototype property, when in fact you wanted to do:
FooBar.prototype = Foo.prototype
See the difference? Foo has no init property as it is defined on the prototype object.