【问题标题】:When should you use a constructor in JavaScript?什么时候应该在 JavaScript 中使用构造函数?
【发布时间】:2013-06-17 23:33:04
【问题描述】:

例如,我有一个构建Car 对象的函数。

function Car() {
    var honkCount = 0;
    var honkHorn = function () {
        honkCount++;
        $results.html('HONK!<br />');
    };
    return {
        get honkCount() {
            return honkCount;
        },
        honk: honkHorn
    }
}

var car = new Car();var car = Car(); 似乎没有太大区别,我有点困惑。

【问题讨论】:

  • 那不是构造函数,构造函数使用 this.something 在使用新前缀调用时影响返回的对象。
  • 看起来像你那里的工厂模式
  • 简要回答:如果您从“构造函数”返回任何内容(this 除外),它就不再是构造函数(或作为构造函数有用)。正如您所注意到的,使用 new 或不使用不会有太大的不同。
  • @BenjaminGruenbaum:不,就在它下面说“构造函数的“原型”属性的值是用于实现继承和共享属性的原型对象”,如果您返回文字。 [].slice() 还为您提供了一个全新的 Array 对象,但没有人认为它是构造函数。仅仅因为它有两个轮子并不能使它成为哈雷......

标签: javascript function oop object constructor


【解决方案1】:

简短的回答

在使用 new 运算符和在返回对象时删除它之间存在 no big difference

引用"JavaScript Garden"

如果被调用的函数没有显式的 return 语句,那么它会隐式地返回 this 的值——新对象。在显式返回语句的情况下,函数返回该语句指定的值,但前提是返回值是一个对象。

语言规范告诉我们:

如果 Type(result) 是 Object 则返回结果。

返回对象。

[[construct]] 算法中指定构造函数的完成方式。


语言规范简介

但是,对于您雄心勃勃的类型 - 让我们一起探索语言规范中的原因!我们怎么能自己想出来?

这就是为什么,我们正在评估 new NewExpression,其中 newExpression 是您的函数。我通过检查新关键字在索引中的作用到达那里。

第一:

  1. 设 ref 为计算 NewExpression 的结果。

这是函数调用

然后:

  1. 让构造函数为 GetValue(ref)。

GetValue 里面的哪个去:

返回调用GetBindingValue(见10.2.1)具体方法的结果,基类将GetReferencedName(V)和IsStrictReference(V)作为参数传递。

这会返回函数本身(基于this

如果 Type(constructor) 不是 Object,则抛出 TypeError 异常。

函数是JS中的对象,所以一切都很好。

如果构造函数没有实现 [[Construct]] 内部方法,则抛出 TypeError 异常。

这会检查它是否是一个函数。所有函数都有一个construct方法(把一个函数看成构造函数,你可以尝试评估(function(){}).constructor看看。

返回在构造函数上调用 [[Construct]] 内部方法的结果,不提供任何参数(即一个空的参数列表)。

太棒了!让我们看看[[construct]] 做了什么。它在13.3.2 中定义,它说了很多事情。大奖是这样的:

令 result 为调用 F 的 [[Call]] 内部属性的结果,提供 obj 作为 this 值,并提供传入 [[Construct]] 的参数列表作为 args。

如果 Type(result) 是 Object 则返回结果。 返回 obj。

叮叮叮!

所以在内部,规范说如果你的函数返回一个对象,构造函数会返回它而不是创建的对象。

注意(一个很小的区别是,当您不在严格模式下时,使用 new 可能是 catch a bug


奖金:Here is a nice explanation on constructors from JavaScript garden

【讨论】:

  • (提示,我有一个小小的谎言来删除另一个动作(解析函数中的参数),因为你正在使用参数列表调用你的构造函数(在你的情况下为空)它评估为@ 987654335@ 而不是 `NewExpression.`,除此之外它完全相同:))
【解决方案2】:

var car = new Car();var car = Car(); 似乎没有太大区别,我有点困惑。

你是对的,它们都是一样的,只是因为你从函数中返回了一个对象。 From the MDN documentation

3。构造函数返回的对象成为整个 new 表达式的结果。如果构造函数没有显式返回对象,则使用在步骤 1 中创建的对象。 (通常构造函数不返回值,但如果他们想覆盖正常的对象创建过程,他们可以选择这样做。)

正如文档中所说,通常构造函数不会显式返回值。

【讨论】:

  • 我觉得应该叫singleton paattern
  • @millimoose,我想我是对的,看看this articlehere
  • @SheikhHeera——但使用这种“模式”,您可以创建多个实例。 module pattern 更像是一个单例模式。
  • 谢谢,我很困惑,但你能把这个(OP的问题)命名为模式吗?
  • @BenjaminGruenbaum 模块示例与单例完全相同。单例示例只是一种复杂的方式。
【解决方案3】:

有人在javascript中复制了一个简单的闭包示例。将 new 与函数一起使用的原因是创建一个闭包,但这是一个无用的示例。这是使用闭包的更好方法。请注意,honkCount 只能访问 honkHorn 和 get_honkCount 这两种方法:

function Car() {
    var honkCount = 0;
    this.honkHorn = function () {
        honkCount++;
        $results.html('HONK!<br />');
    };
    this.get_honkCount = function() {
            return honkCount;
    };
}

var car1 = new Car();
var car2 = new Car();
car1.honkHorn();
car1.honkHorn();
car2.honkHorn();

alert('car1 honks: ' + car1.get_honkCount());
alert('car2 honks: ' + car2.get_honkCount());

因此,现在您看到两个对象作为实例化的私有变量明显地跟踪喇叭,这要归功于 honkCount 的 new 和 javascript 闭包。

【讨论】:

  • 与 OP 正在做的事情相比,这没有任何优势。如果您将方法放在原型上,您将获得继承、通用方法,并且不必在每次创建实例时分配 N*2(函数对象 + 原型对象,也是指向函数对象的指针)额外的对象。
  • 是的,你是对的,我在每次实例化时再创建两个函数对象,但我在代码中使用了很多原型继承。所以我的方式在这种情况下是一个优势。如果您不关心继承,只想添加带有私有变量的事件处理程序,那么 OP 方式就非常好。请记住,如果方法没有引用闭包,则在任何一种情况下都不能对函数 Car 进行垃圾收集。
  • 在此代码中,您没有使用原型继承,它不能与原型继承一​​起使用。 OPs 代码已经创建了具有不同闭包上下文的对象,这就是为什么与 OP 所做的相比没有优势。
  • 我没有在这段代码中使用原型继承,但假设你做了以下事情:function Bus() {} jQuery.extend(true,Bus.prototype,new Car());现在我可以做一些有趣的事情了: var myBus = new Bus(); myBus.honkHorn();
  • 我的困惑实际上是试图在构造函数创建的对象上定义 getter 和 setter。它们很容易在对象文字中定义,所以我返回了一个带有定义的对象文字。我现在意识到,通过从构造函数返回一个对象,它会使构造函数变得毫无意义,而 new 关键字在这一点上没有任何区别。我理解闭包很好,我只是被创建属性绊倒了,只是让自己感到困惑。不过我现在一切都很好。谢谢你的回答:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-16
  • 2019-04-01
相关资源
最近更新 更多