Функция F() {if (! (этот экземпляр F)) {return new F()};...}
Каково использование конструкции: function F() { if (!(this instanceof F)) { return new F() }; ... }
?
Я нашел это в pty.js
для Node. Вот исходный код:
function Terminal(file, args, opt) {
if (!(this instanceof Terminal)) {
return new Terminal(file, args, opt);
}
var self = this
, env
, cwd
, name
, cols
, rows
, term;
-------------------SKIP-----------------------------------
Terminal.total++;
this.socket.on('close', function() {
Terminal.total--;
self._close();
self.emit('exit', null);
});
env = null;
}
Ответы
Ответ 1
Это означает, что если функция была вызвана без оператора new
, она автоматически вернет новый экземпляр.
Например, если у вас не было этой защиты и сделал это...
var t = Terminal();
... тогда this
при выполнении Terminal()
будет указывать на window
(или ваш глобальный объект, причудливый не-браузерный парень/галлон), определенно не то, что вы хотите.
Определив, что this
на самом деле является экземпляром Terminal
, мы можем продолжить. В противном случае защита возвращает новый объект.
Тогда мы можем просто использовать обе формы...
var t = Terminal(); // Will be same as `new Terminal()`
Ответ 2
Это просто, чтобы убедиться, что он будет работать, даже если F
вызывается без new
.
Когда вы вызываете F
с new
, в этой функции this
находится новый экземпляр.
Затем, если this
не является экземпляром F
(!(this instanceof F)
), это означает, что F
не вызывается с помощью new
. В этом случае F
вызывает себя, теперь с new
.
Ответ 3
В дополнение к великим объяснениям в этой теме интересно посмотреть, что происходит под капотом. Спецификация ECMAScript (где основан Javascript) определяет глобальный объект. Это выполняется по-разному в разных средах исполнения. В вашем типичном браузере это объект window
, а в Node.js - это объект root
. Каждая функция, определенная "в дикой природе" (не привязанная к создаваемому пользователем объекту), станет свойством глобального объекта. В Node.js вы можете попробовать:
> function Test() {};
> root.Test
[Function: Test]
Теперь переменная this
указывает на объект, членом которого является функция. Итак, в приведенном выше примере:
> function Test() {
... console.log(this === root);
... };
> Test()
true
То же самое касается вашей функции Terminal
. Если вы запустите его, this
укажет на глобальный объект, который, конечно, не экземпляр Terminal
!
При вызове функции с помощью оператора new
возвращается объект, который будет иметь доступ к свойству с именем constructor
, которое будет ссылаться на эту функцию. Это нечто эквивалентное:
> var instance = {};
> instance.constructor = Terminal;
> instance.constructor();
Итак, когда условие терпит неудачу, и функция терминала запускается через строку new Terminal()
, this
указывает на вновь созданный экземпляр, который типа Terminal!
Если вы хотите получить больше технических данных, instance
не имеет свойства constructor
. Он привязан (через цепочку прототипов *) к частному объекту ** (созданному во время выполнения), который имеет свойство constructor
, указывающее на функцию терминала. Этот частный объект привязан к функцией через свойство prototype
. D.Crockford представляет это в псевдокоде следующим образом:
Terminal.prototype = {constructor: Terminal};
Опять же, это только, когда вы вызываете функцию с помощью new
.
* Если свойство не найдено, объект будет искать его в объекте, на который указывает свойство __proto__
.
** (представьте себе что-то вроде объекта с именем _Terminal
, к которому вы не можете получить доступ по имени)