With ES6, JavaScript has proper class syntax, allowing you to declare them either as class A {} or const A = class {}. The latter are class expressions.
I'm working on a project where everything is customizable. The user will create a new file with the same name except with a prefix, and anything in their file with overwrite that of the original. However, it seems as though you cannot do this with the constructor of a class expression.
const A = class {};
A.constructor = function(name) {
this.name = name;
}
You would expect the above to work, (or even A.prototype.constructor) but it doesn't.
How does one properly define a constructor in this manner? I can't have it within the class declaration.
class syntax creates a constructor function and fills in the object assigned to its prototype property. There isn't a "class" separate from the constructor function. In your example, A is a reference to the constructor function created by the class {} expression. While you can replace the value of the constructor property on the object assigned to A.prototype, that won't change what function A refers to, and so won't change what happens via new A.
You can set A.prototype.constructor, and then give class A a constructor that defers to the one defined on the prototype if it's not the same function anymore, forwarding any arguments that were passed. That creates markedly unusual classes (normally, A.prototype.constructor === A is true), but does what you need it to do.
const A = class {
constructor(...args) {
let Constructor = A.prototype.constructor
if (Constructor !== A) {
Constructor.apply(this, args);
}
}
};
(A.prototype.constructor = function(name) {
this.name = name;
}).prototype = A
console.log(
new A('It works!')
)
Edit: Another option, which avoids messing with A.prototype.constructor, would be to have a static Constructor property defined on A that you can change to your liking:
const A = class {
static Constructor = Function.prototype
constructor(...args) {
A.Constructor.apply(this, args);
}
};
A.Constructor = function(name) {
this.name = name;
}
console.log(
new A('It works!')
)
Related
What is the difference between the following two declarations?
Class.method = function () { /* code */ }
Class.prototype.method = function () { /* code using this.values */ }
Is it okay to think of the first statement as a declaration of a static method, and the second statement as a declaration of an instance method?
Yes, the first function has no relationship with an object instance of that constructor function, you can consider it like a 'static method'.
In JavaScript functions are first-class objects, that means you can treat them just like any object, in this case, you are only adding a property to the function object.
The second function, as you are extending the constructor function prototype, it will be available to all the object instances created with the new keyword, and the context within that function (the this keyword) will refer to the actual object instance where you call it.
Consider this example:
// constructor function
function MyClass () {
var privateVariable; // private member only available within the constructor fn
this.privilegedMethod = function () { // it can access private members
//..
};
}
// A 'static method', it's just like a normal function
// it has no relation with any 'MyClass' object instance
MyClass.staticMethod = function () {};
MyClass.prototype.publicMethod = function () {
// the 'this' keyword refers to the object instance
// you can access only 'privileged' and 'public' members
};
var myObj = new MyClass(); // new object instance
myObj.publicMethod();
MyClass.staticMethod();
Yes, the first one is a static method also called class method, while the second one is an instance method.
Consider the following examples, to understand it in more detail.
In ES5
function Person(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
Person.isPerson = function(obj) {
return obj.constructor === Person;
}
Person.prototype.sayHi = function() {
return "Hi " + this.firstName;
}
In the above code, isPerson is a static method, while sayHi is an instance method of Person.
Below, is how to create an object from Person constructor.
var aminu = new Person("Aminu", "Abubakar");
Using the static method isPerson.
Person.isPerson(aminu); // will return true
Using the instance method sayHi.
aminu.sayHi(); // will return "Hi Aminu"
In ES6
class Person {
constructor(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
static isPerson(obj) {
return obj.constructor === Person;
}
sayHi() {
return `Hi ${this.firstName}`;
}
}
Look at how static keyword was used to declare the static method isPerson.
To create an object of Person class.
const aminu = new Person("Aminu", "Abubakar");
Using the static method isPerson.
Person.isPerson(aminu); // will return true
Using the instance method sayHi.
aminu.sayHi(); // will return "Hi Aminu"
NOTE: Both examples are essentially the same, JavaScript remains a classless language. The class introduced in ES6 is primarily a syntactical sugar over the existing prototype-based inheritance model.
When you create more than one instance of MyClass , you will still only have only one instance of publicMethod in memory but in case of privilegedMethod you will end up creating lots of instances and staticMethod has no relationship with an object instance.
That's why prototypes save memory.
Also, if you change the parent object's properties, is the child's corresponding property hasn't been changed, it'll be updated.
For visual learners, when defining the function without .prototype
ExampleClass = function(){};
ExampleClass.method = function(customString){
console.log((customString !== undefined)?
customString :
"called from func def.");}
ExampleClass.method(); // >> output: `called from func def.`
var someInstance = new ExampleClass();
someInstance.method('Called from instance');
// >> error! `someInstance.method is not a function`
With same code, if .prototype is added,
ExampleClass.prototype.method = function(customString){
console.log((customString !== undefined)?
customString :
"called from func def.");}
ExampleClass.method();
// > error! `ExampleClass.method is not a function.`
var someInstance = new ExampleClass();
someInstance.method('Called from instance');
// > output: `Called from instance`
To make it clearer,
ExampleClass = function(){};
ExampleClass.directM = function(){} //M for method
ExampleClass.prototype.protoM = function(){}
var instanceOfExample = new ExampleClass();
ExampleClass.directM(); ✓ works
instanceOfExample.directM(); x Error!
ExampleClass.protoM(); x Error!
instanceOfExample.protoM(); ✓ works
****Note for the example above, someInstance.method() won't be executed as,
ExampleClass.method() causes error & execution cannot continue.
But for the sake of illustration & easy understanding, I've kept this sequence.****
Results generated from chrome developer console & JS Bin
Click on the jsbin link above to step through the code.
Toggle commented section with ctrl+/
A. Static Method:
Class.method = function () { /* code */ }
method() here is a function property added to an another function (here Class).
You can directly access the method() by the class / function name. Class.method();
No need for creating any object/instance (new Class()) for accessing the method(). So you could call it as a static method.
B. Prototype Method (Shared across all the instances):
Class.prototype.method = function () { /* code using this.values */ }
method() here is a function property added to an another function protype (here Class.prototype).
You can either directly access by class name or by an object/instance (new Class()).
Added advantage - this way of method() definition will create only one copy of method() in the memory and will be shared across all the object's/instance's created from the Class
C. Class Method (Each instance has its own copy):
function Class () {
this.method = function () { /* do something with the private members */};
}
method() here is a method defined inside an another function (here Class).
You can't directly access the method() by the class / function name. Class.method();
You need to create an object/instance (new Class()) for the method() access.
This way of method() definition will create a unique copy of the method() for each and every objects created using the constructor function (new Class()).
Added advantage - Bcos of the method() scope it has the full right to access the local members(also called private members) declared inside the constructor function (here Class)
Example:
function Class() {
var str = "Constructor method"; // private variable
this.method = function () { console.log(str); };
}
Class.prototype.method = function() { console.log("Prototype method"); };
Class.method = function() { console.log("Static method"); };
new Class().method(); // Constructor method
// Bcos Constructor method() has more priority over the Prototype method()
// Bcos of the existence of the Constructor method(), the Prototype method
// will not be looked up. But you call it by explicity, if you want.
// Using instance
new Class().constructor.prototype.method(); // Prototype method
// Using class name
Class.prototype.method(); // Prototype method
// Access the static method by class name
Class.method(); // Static method
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!"
In the code I am using a named class "Car". Now if I try to use like var carNew = new Car("ferrari");, then it's throwing me an error. So what is the use of named class expression in ES6?
'use strict';
var car = class Car{
constructor(model){
this.model = model;
}
}
var carNew = new car("ferrari");
console.log(carNew); //car {model: "ferrari"}
console.log(carNew.constructor == car); //true
In case of class expressions you can use class name internally, but not outside of the class:
const Foo = class Self {
static greetingPrefix() {
return 'Hello, ';
}
greeting() {
return Self.greetingPrefix() + 'World';
}
};
const foo = new Foo();
console.log(foo.greeting()); // "Hello, World"
console.log(Self); // throws "Uncaught ReferenceError: Self is not defined"
With the following code
var car = class Car{ /* ... */ }
var carNew = new Car("ferrari"); throws an error, why?
Car has not entered the scope because you've written a class expression, similar to using function in a function expression
var foo = function Bar() {};
foo; // defined
Bar; // undefined
var carNew = new car("ferrari"); works, why?
For the same reasoning as above, the car identifier is defined in your scope and points to the class expression
What is use of named or unnamed class expression [...]?
Lets look back at function again. So now think, if Bar wasn't defined in the scope we were working in, where was it defined?
Well, obviously we have foo.name === 'Bar', but we could also do
var foo = function Bar() {console.log(Bar)};
foo(); // Bar logged, we see Bar === foo here
Great, so we can reference Bar inside the function.
It is exactly the same with class, we can reference Car from within the class expression itself, letting us do recursive behaviours or copy static references to instances etc.
I've added such a reference to make it easy to see in the code below
var car = class Car {
constructor(model) {
this.model = model;
this.foo = Car; // the big C Car that was ReferenceErroring outside
}
};
var bar = new car();
console.log(bar.foo); // logs class Car (=== car)
The same use as of named / not named functions. Look at the functions behavior:
var a = function b() { console.log('b') }
>> undefined
a()
>> b
b()
>> Uncaught ReferenceError: b is not defined(…)
This is very similar to your case.
Where and why you would need to use it? Usually the only use case for functions is to name the functions called once which gives you kind of self-documenting ability:
(function bootstrap() {
//
})();
Here the function name says that your function bootstraps your app.
So I can imagine another case for the class now: you can use it for singleton classes which are used to create one object only:
var a = new (class Car{
constructor(model){
this.model = model;
}
})('ferrari');
console.log(a.model);
Here you just name the class to know what it is doing, however the name won't be accessible elsewhere.
It thrown an error because Car variable is not declared.
From JavaScript | MDN documentation :
An important difference between function declarations and class
declarations is that function declarations are hoisted and class
declarations are not. You first need to declare your class and then
access it, otherwise code like the following will throw a
ReferenceError
So, to use :
var newCar = Car('ferrari');
You have to define the Car variable :
var Car = class Car{
constructor(model){
this.model = model;
}
}
Notice here that we use Car and not car
Concerning unamed and named class expression, here is an example from JavaScript | MDN documentation :
A class expression is another way to define a class. Class expressions
can be named or unnamed. The name given to a named class expression is
local to the class's body.
// unnamed
var Polygon = class {
constructor(height, width) {
this.height = height;
this.width = width;
}
};
// named
var Polygon = class Polygon {
constructor(height, width) {
this.height = height;
this.width = width;
}
};
Read ES6 classes - JavaScript | MDN for more information
Class expressions
Similarly to functions, there are two kinds of class definitions, two ways to define a class: class declarations and class expressions.
Also similarly to functions, the identifier of a class expression is only visible within the expression:
const MyClass = class Me {
getClassName() {
return Me.name;
}
};
const inst = new MyClass();
console.log(inst.getClassName()); // Me
console.log(Me.name); // ReferenceError: Me is not defined
I have this class where I have a private property and a public method for access:
Person = function () {
this.Name = "asd";
var _public = new Object();
_public.Name = function (value) {
if (value == undefined) { //Get
return this.Name
} else {
this.Name = value; //Set
}
};
return _public;
};
I want to force the context in _public.Name for access a this.Name.
I know the technique of closure, but I want to see if I can force a context.
I found a technique to do it, extend object Function:
Function.prototype.setScope = function (scope) {
var f = this;
return function () {
f().apply(scope);
}
}
And my class becomes:
Person = function () {
this.Name = "asd";
var _public = new Object();
_public.Name = function (value) {
if (value == undefined) {
return this.Name
} else {
this.Name = value;
}
}.setScope(this);
return _public;
};
So I can force correctly the context, but I can not pass value and can not, however, return this.Name.
Not
f().apply(scope);
just
f.apply(scope);
(No () after f.) You want to use the apply function on the function f object, not call the function f and access apply on its return value.
To also pass on the arguments that your function in setScope receives, add this:
f.apply(scope, arguments);
arguments is an implicit argument to all functions, which is a pseudo-array of the actual arguments passed to the function at runtime. apply accepts any array-like thing as its second parameter to specify the arguments to use when calling the underlying function.
I'd also have it return the return value:
return f.apply(scope, arguments);
So setScope becomes:
Function.prototype.setScope = function (scope) {
var f = this;
return function () {
return f.apply(scope, arguments);
}
}
Live example
Note that the usual name for this function, and the name it has in the new ECMAScript5 standard, is bind (Section 15.3.4.5; ECMAScript5's bind also lets you curry arguments, which isn't done by this implementation). setScope is a particularly unfortunate name, because it doesn't set the scope, it sets the context.
Having said all that, there's no reason you need setScope in your Person constructor. You can just do this:
Person = function () {
var self = this;
this.Name = "asd";
var _public = new Object();
_public.Name = function (value) {
if (value == undefined) {
return self.Name;
} else {
self.Name = value;
}
};
return _public;
};
Live example
But using bind (aka setScope) can be useful in places where you don't want a new closure over the context in which you're doing it.
Off-topic: The way you're specifying Person will break certain things people might expect to work, such as:
var p = new Person();
alert(p instanceof Person); // Expect "true", but in your case will be "false"
...because you're replacing the object new created for you, but returning a different object out of your constructor (which overrides the default).
Rather than creating a new object and returning that in your constructor, allow the object constructed for you by new to be the object (and thus the Person relationship is maintained), but you can still get truly private variables and use accessors:
function Person() {
// Private variable
var name = "asd";
// Accessor function
this.Name = function(value) {
if (typeof value === "undefined") {
return name;
}
name = value;
};
}
Live example
As you can see, this is dramatically simpler, and it preserves the instanceof relationship. Note that we're not qualifying our references to name within Name at all, and so we're using the local variable in the constructor call in which our Name function, which closes over it, was created.
I've also taken the liberty there of giving the constructor function a name, because I'm not a fan of anonymous functions. I should have given the accessor a name as well:
function Person() {
// Private variable
var name = "asd";
// Accessor function
this.Name = Person_Name;
function Person_Name(value) {
if (typeof value === "undefined") {
return name;
}
name = value;
}
}
Off-topic 2: The overwhelming convention in JavaScript code is to use initial caps on function names only for constructor functions (like Person), and not on other kinds of functions (like Name). You're free to do whatever you like, of course, but I thought I'd mention the convention, as it makes it easier for other people to read your code.
Worth noting: All of these techniques result in every single Person object having its own copy of the accessor function. If there are going to be a lot of these objects, that could be a memory issue. If there are only going to be a few, that's fine.
First thing, I think the correct way to go about this is the "closure" method, as the syntax is easier and simpler to understand and makes more sense and most object oriented code written in Javascript is written that way. Another thing to note is that in your method, the "private" member can be accessed from outside by accessing Person.Name (instead of (new Person()).Name).
That being said, it seems that you want something like Prototype.JS's bind method, which allows you to bind a function reference as a method call to a specific object, and also passes all the arguments correctly (including allowing preloaded arguments).
Look at Prototype.JS source for the complete implementation, but a simple implementation of this semantic might look like this:
Function.prototype.bind = function(context) {
var callee = this;
var args = Array.prototype.slice.call(arguments,1);
return function() {
var newargs = args.concat(Array.prototype.slice.call(arguments,0));
return callee.apply(context, newargs);
};
};
It is difficult to understand what you are trying to achieve. But if I guess that you are trying to create a Person class with a name method to get/set the person's name, here is my suggestion:
function Person() {
this._name = undefined; // not required but is better than assigning a fake name
return this;
}
Person.prototype.name = function( _name ) {
if ( _name === undefined ) return this._name; // get
return this._name = _name; // set
}
Note that I have defined the name function with a lower case first letter. This is standard practice in JavaScript where only constructors are usually capitalized. To use this class you do:
person = new Person();
person.name( "Ermes Enea Colella" );
alert( person.name ); // displays "Ermes Enea Colella"
There is no need to bind any context with this method, so you may be looking for something else. If you can clarify your need, I'll be happy to edit my answer.
I hope this helps.
Is the keyword (or method?) prototype in jquery kind of like extension methods?
i.e. all classes will have this functionality available to it going forward?
This is part of javascript and not specific to jquery.
the prototype property defines methods and properties shared by all objects of that type.
e.g.
function MyClass()
{
}
myClass.prototype.myMethod = function()
{
alert("hello world");
}
var myObject = new MyClass();
myObject.myMethod();
All instances of MyClass will have (share) the method myMethod().
Note that methods on the prototype do not have the same visibility as methods declared within the constructor.
For example:
function Dog(name, color)
{
this.name = name;
this.getColor = function()
{
return color;
}
}
Dog.prototype.alertName = function {
alert(this.name);
}
Dog.prototype.alertColor = function {
//alert(color); //fails. can't see color.
//alert(this.color); //fails. this.color was never defined
alert(this.getColor()); //succeeds
}
var fluffy = new Dog("Fluffy","brown");
prototype is not a jQuery keyword; it is a Javascript keyword. It is used to add public functions to objects in a way such that they will exist every time you create a new instance of that object.
http://www.javascriptkit.com/javatutors/proto.shtml
http://www.devarticles.com/c/a/JavaScript/Object-Oriented-JavaScript-Using-the-Prototype-Property/