![](http://habrastorage.org/getpro/habr/avatars/45b/a39/080/45ba3908025464c61b3ad3ebcf6aa351.png)
John Rezig's self-invoking constructor and reflection on why this decision did not take root
- Tutorial
![](https://habrastorage.org/getpro/habr/post_images/9ee/819/661/9ee8196616b225063f58ad1ce21a50bf.jpg)
However, since I don’t see it at all in the search resultson Habrahabr by the word "Resig", one involuntarily has to think that no one bothered to translate (or at least retell) this useful blog post over the past four years - so I have to retransmit the blog post by Rezig on my own before I fulfill my main intention : Reflect out loud why the method proposed by Rezig to solve the problem he indicated has not yet become widespread. And I will retell. (The retelling itself would already be useful to the reader, even if I didn’t add anything to him. And I’ll add.)
On December 6, 2007, Rezig examined what happens when the “new” operation is used in the javascript to create an object (in languages with classes, we would say “class instance”):
function User(first, last){
this.name = first + " " + last;
}
var user = new User("John", "Resig");
Rezig rightly noted that for beginners in javascript it is not quite obvious that the appearance of “this” in the function code indicates that we have an object constructor. (I’ll add in parentheses myself: if the function is located in the bowels of a library, this circumstance also needs to be documented - otherwise the library user will not differ much from the beginner: not everyone reads the source code with the body of the function, especially since it is often used in a minified, unreadable way.)
Therefore, Rezig reasoned, sooner or later
var name = "Resig";
var user = User("John", name);
// здесь переменная «user» не определена
// БОЛЕЕ ТОГО: значение «name» теперь ужé не «Resig»!
if ( name == "John Resig" ) {
// фигассе!…
}
Nevertheless, Resig pointed out further, calling the constructor is useful. It has the advantage that prototype inheritance (getting the properties of an object from a prototype), that is, calling a real constructor, works much faster than getting the same properties as an object “constructed” by calling a simple function:
// Вот это работает быстро:
function User(){}
User.prototype = { /* …куча свойств… */ };
// А вот это работает медленно:
function User(){
return { /* …куча свойств… */ };
}
Rezig made a natural conclusion from here that it would be nice to compose a function every time that, on the one hand, could work as a constructor (providing quick prototype inheritance), and on the other hand, could be called
Fortunately, Resig continued, everyone these problems lend themselves to a simple solution using conditional notation of the function body:
function User(first, last){
if ( this instanceof User ) {
// мы находимся внутри конструктора:
this.name = first + " " + last;
} else {
// мы находимся внутри обычной функции:
return new User(first, last);
}
}
Operator « the instanceof » here serve as the primary means by which to detect whether the involved
function test(){
alert( this instanceof test );
}
test(); // сработает как alert( false );
new test(); // сработает как alert( true );
Having found this solution and making sure it is working, Rezig said: now let's wrap this solution in a generalized means of creating constructors of "classes", which could be used whenever there is a need for such functions. For this purpose, John Rezig posted this piece of free code:
// makeClass - By John Resig (MIT Licensed)
function makeClass(){
return function(args){
if ( this instanceof arguments.callee ) {
if ( typeof this.init == "function" )
this.init.apply( this, args.callee ? args : arguments );
} else
return new arguments.callee( arguments );
};
}
The previous example, to use this code, must be rewritten so that the body of the previous constructor becomes the body of the “ init ” method in the prototype:
var User = makeClass();
User.prototype.init = function(first, last){
this.name = first + " " + last;
};
var user = User("John", "Resig");
user.name // выдаёт «John Resig»
The logic of the work of " makeClass " John Rezig also explained in sufficient detail. Function « makeClass () » not a designer, and the designer creates - this constructor is returned
The retelling of John Rezig's thoughtful blog post is now over; now I can finally talk about where he himself
An unpleasant element of his
It seems to me that this dislike of
Another reason for the hostility to the self-invoking constructor is,
I have seen this more than once.
I remember that in May of this (2011) year in the JavaScript FAQ compiled by azproduction , it was said:
- It is better, more familiar and ideological to create objects through new. Designers should be capitalized.
- I prefer to be based on conventions and do not check this inside the constructor - I called the constructor without new and therefore has flown to the globals - which means "the fool himself." And in no case do I encourage an error with new - some people check if this is a global means the user called the constructor without new and create an object inside the constructor and return it - this is an encouragement to the error and an ideologically incorrect approach.
(End of quote.)
I also remember the case of Vladimir Agafonkin, the creator of the beautiful Leaflet library for displaying maps, which was mentioned more than once on Habrahabr. In August of this (2011) year, he received a request for a merger at Github , the author of which at the beginning of each designer suggested putting something like this code:
if ( !(this instanceof arguments.callee) ){
return new arguments.callee(arguments);
}
Agafonkin answered him:
“Keeping novice JS authors from making mistakes is very useful, but I don’t like the idea of such a dull language that allows incorrect syntax instead of telling the user that he was wrong.
- Instead of creating an instance of the object even without the “new”, it seems to me better to do something like throw new Error ("You forgot to put the new keyword before the class constructor.") .
- And one more thing: I read somewhere that arguments.callee is now considered malicious, and it’s safer to explicitly write the class name.
(End of quote.)
The author of the request then went, read
Which of the Leaflet users will read at least the “ Quick Start Guide ” will probably notice that the global object defined by this library is called (obviously, for brevity)
Sometimes I want to think that John Rezig would have been forward-thinking if he had refrained
function User(first, last){
if ( this instanceof User ) {
// мы находимся внутри конструктора:
this.name = first + " " + last;
} else {
// мы находимся внутри обычной функции:
return new User(first, last);
}
}
But only, of course, in order not to create an extra
function User(first, last){
if (!(this instanceof User)) return new User(first, last);
// здесь и далее мы гарантированно находимся внутри конструктора
this.name = first + " " + last;
// …и далее следует всё остальное тело конструктора…
}
And we must pay tribute to the JavaScript FAQ compiled by
It is easy and pleasant to follow such a good example. It is also more understandable than the constructor constructor - including for javascript optimizers more clearly.
If you want to finally see a similar positive example from life, then look
var Reader = exports.Reader = function (data) {
if (!(this instanceof Reader))
return new Reader(data);
this._data = data;
this._offset = 0;
}
The conclusion is simple: study the works of John Rezig, obey his advice, act on his instructions. But only to a certain limit of complexity.
Appendage. By the end of July 2012, Vladimir