JavaScript中关键字"new"的含义是什么?

8

我读过关于JavaScript中"new"关键字的主题(What is the 'new' keyword in JavaScript?),但是我还是有些困惑;下面我们来看这个例子:

var foo = function() {
    return {
        setA: function(a) {
            this.a = a;
        },
        readA: function() {
            console.log(this.a);
        }
    };
};

现在,让我们来看看这两段代码:

第一段:

var bob1 = foo();
bob1.setA(10);
bob1.readA();

二:

var bob2 = new foo();
bob2.setA(10);
bob2.readA();

我看不出有什么区别。那么使用关键字 "new" 有什么好处呢?

3
相关:构造函数与工厂函数的区别该链接讨论了JavaScript中构造函数和工厂函数之间的区别。构造函数是一种特殊类型的函数,用于创建对象,通常使用“new”关键字调用。而工厂函数则是一种函数,它不使用“new”关键字并返回一个新的对象。使用构造函数时,如果你没有使用“new”关键字来调用它,它就不会返回一个对象并且它所设置的属性也无法被正确地继承。然而,使用工厂函数可以避免这些问题,并且更加灵活,因为它们只是普通的函数,它们可以接受参数并且可以直接返回任何类型的值。总之,在选择使用构造函数或工厂函数时,需要考虑到你要创建的对象的复杂性和可扩展性,以及你是否需要在将来对其进行修改或扩展。 - Qantas 94 Heavy
1
@3506238:楼主已经阅读过了。 - Qantas 94 Heavy
顺便说一句,你之前阅读的帖子有很多有用的信息,你只需要向下滚动一些就可以看到一些更容易理解的答案。例如,这个:https://dev59.com/h3I-5IYBdhLWcg3w18V3#1646957 - Qantas 94 Heavy
1
区别在于函数的范围不同。你看不到任何区别,因为你没有引用函数范围内的“this”。你只引用了返回对象的范围内的“this”,这两种情况下的“this”是相同的。 - Molecular Man
@Bergi,是的。我的意思是当作为构造函数和普通函数使用时,this引用不同的对象,但我无法选出正确的词语。 - Molecular Man
显示剩余6条评论
3个回答

4
如果您的函数直接返回对象,则不需要使用new运算符。 new关键字的作用不仅如此。
举个例子:
function Animal(kind, name) {
   this.kind = kind;
   this.name = name;
}

Animal.prototype.walk = function() {
    console.log('Walking');
}

那么您正在进行的是:
 var animal = new Animal();

JavaScript引擎将会执行以下操作。
  var o = Object.create(Animal.prototype)
  Animal.apply(o, arguments);
  return o;

Object.create 会继承 Animal 函数的prototype对象,因此 animal 对象将拥有它自己的属性和继承的属性。


3

i'm still in the fog about new; let's talk about this example :

var foo = function() {
    return {
        setA: function(a) {
            this.a = a;
        },
        readA: function() {
            console.log(this.a);
        }
    };
};
我们不应该谈论这个例子。无论我们是否在此函数中使用new都没有区别,因为 new的工作方式如下所示:
  1. 创建一个新对象,继承自foo.prototype
  2. 将构造函数foo与指定的参数一起调用,并将this绑定到新创建的对象上。
  3. 由构造函数返回的对象成为整个new表达式的结果。如果构造函数没有显式地返回对象,则使用在步骤1中创建的对象。(通常构造函数不会返回值,但是如果它们想要覆盖正常的对象创建过程,则可以选择这样做。)
这里需要注意第3步。您正在创建和返回一个对象(对象字面量),因此将分配给bob1。通常情况下,构造函数不会返回任何内容,隐式创建的新实例(在函数内部作为this可用)成为结果。 new foo()foo()都只将对象字面量分配给bob变量 - 结果没有区别。在以下示例中情况不同:
function foo() {
    this.setA = function(a) {
        this.a = a;
    };
    this.readA = function() {
        console.log(this.a);
    };
    // return this; is implicit
}
var bob = new foo; // OK
var bob = foo(); // horrible error

1
如果构造函数foo返回一个对象,那么new foo()与直接调用函数foo()是相同的。我们可以通过检查ECMAScript对new的行为来证明这一点:

返回在constructor[即构造函数]上调用[[Construct]]内部方法的结果...

函数的[[Construct]]内部方法是一个特殊的包装器,用于调用函数的[[Call]]内部方法(即函数的正常行为)。让我们看看[[Construct]]的结尾,以了解此包装器的行为:

8) 将F [由new调用的函数] 的[[Call]]内部属性调用的结果赋值给result,将obj作为this值提供,并将传递到[[Construct]]的参数列表作为args提供。

9) 如果result的类型为对象,则返回result

10) 返回obj

在您的情况下,您的构造函数foo返回一个对象,因此[[Construct]]的第9步(因此反过来也是new foo())返回该对象。但我们可以看到,在步骤10中,[[Construct]] 可能返回称为obj的其他值,该值等于构造函数内部的this值。 让我们倒带一下,看看这是怎么回事:

1)将obj设为新创建的本地ECMAScript对象。

...

4)使用参数"prototype"调用F的[[Get]]内部属性,将返回值赋给proto

5)如果proto的类型是Object,则将obj的[[Prototype]]内部属性设为proto

这里我们可以看到 new 的真正威力:如果构造函数不返回一个对象(因此允许由 new 启动的 [[Construct]] 操作返回 obj),那么原型继承就会发生。 obj[[Prototype]] (也称为 obj.__proto__)被设置为构造方法的 prototype 属性。这意味着,如果 foo 不返回一个对象(而是修改了 this),并且设置了 foo.prototype.someProp,那么从 var instance = new foo() 返回的实例将可以访问 instance.someProp

因此,编写代码的另一种方式可能是:

var foo = function() { };
foo.prototype.setA = function(a) { this.a = a; };
foo.prototype.setA = function(a) { console.log(this.a); };

在这种情况下,new foo() 生成的对象原型链包括 foo.prototype,而 foo() 不包括。这样做的好处是使用更少的内存:所有这里的 foo 实例共享通用的原型方法,而不是每个实例都携带自己的独立函数。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接