函数 F() { if (!(this instanceof F)) { return new F() }; ... }

IT技术 javascript
2021-03-12 18:31:48

构造的用法是什么:function F() { if (!(this instanceof F)) { return new F() }; ... }

我在一个pty.jsfor Node.js 中找到了这个这是原始代码:

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;
}
3个回答

这意味着如果在没有new运算符的情况下调用函数,它将自动返回一个新实例。

例如,如果您没有此保护措施,并且执行了此操作...

var t = Terminal();

...然后thiswhile 执行Terminal()将指向window(或您的全局对象,花哨的非浏览器家伙/gal),绝对不是您想要的。

通过确定它this实际上是 的一个实例Terminal,然后我们可以继续。否则,保护返回一个新对象。

然后我们可以简单地使用这两种形式......

var t = Terminal(); // Will be same as `new Terminal()`
这种结构的使用是否被认为是“糟糕的编程”,或者这种保护+允许这种用法是否可以?
2021-05-11 18:31:48
+1 无论您是否包含“自动”一词。;-D
2021-05-17 18:31:48
@BrendanAshworth 我不会这么说。jQuery 对 做同样的事情$.Deferred(),并在文档中指出这样做是可以的,无论值多少钱。
2021-05-20 18:31:48

这只是为了确保即使在F没有new.

当您调用Fwith 时new,该函数中this是新实例。

然后,如果this不是F( !(this instanceof F))的实例,则表示F未使用new. 在这种情况下,F调用自身,现在使用new.

除了此线程中的精彩解释之外,看看幕后发生的事情也很有趣。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将指向一个新创建的实例,它终端类型!

如果你想获得更多的技术,本身instance并没有constructor属性。相反,它(通过原型链*)链接到一个私有对象**(由运行时创建),该对象具有constructor指向终端函数属性。该私有对象由函数通过属性指向prototypeD.Crockford伪代码表示如下:

Terminal.prototype = {constructor: Terminal};

同样,这在您使用new.

* 如果没有找到属性,对象会在属性指向的对象上查找__proto__

**(想象一个_Terminal你不能通过名字访问的对象