JavaScript 对象中的构造函数

IT技术 javascript oop
2021-02-02 05:59:52

JavaScript 类/对象可以有构造函数吗?它们是如何创建的?

6个回答

使用原型:

function Box(color) // Constructor
{
    this.color = color;
}

Box.prototype.getColor = function()
{
    return this.color;
};

隐藏“颜色”(有点类似于私有成员变量):

function Box(col)
{
   var color = col;

   this.getColor = function()
   {
       return color;
   };
}

用法:

var blueBox = new Box("blue");
alert(blueBox.getColor()); // will alert blue

var greenBox = new Box("green");
alert(greenBox.getColor()); // will alert green
var制作一个私有变量。this制作一个公共变量
2021-03-20 05:59:52
@AlanKis(至少在某些 Javascript 引擎中)堆栈跟踪将在匿名函数情况下甚至不提及Foo,而在后一种情况下它会知道它Foo正在被调用。对调试很有帮助。
2021-03-26 05:59:52
@CamiloMartin 尽管并非总是必要,但将变量设为“私有”(或在本例中为不可命名)可能是防止外部代码依赖于类的实现细节的有用方法。即使只是指示类的哪些元素是公共/私有的,也可能对外部用户有用。
2021-04-03 05:59:52
@BorisB,是的,您这样做了 - 这在 Box 对象上定义了颜色和 getColor,否则您将在通常的范围内分配变量。
2021-04-06 05:59:52
@Jeach 是的。我提供了一个隐藏的替代片段color我建议您使用哪种主要取决于个人喜好(保护与简单)
2021-04-07 05:59:52

这是我有时用于 JavaScript 中类似 OOP 行为的模板。如您所见,您可以使用闭包模拟私有(静态和实例)成员。什么new MyClass()将返回是只分配给该属性的对象this对象,并在prototype该对象“类”。

var MyClass = (function () {
    // private static
    var nextId = 1;

    // constructor
    var cls = function () {
        // private
        var id = nextId++;
        var name = 'Unknown';

        // public (this instance only)
        this.get_id = function () { return id; };

        this.get_name = function () { return name; };
        this.set_name = function (value) {
            if (typeof value != 'string')
                throw 'Name must be a string';
            if (value.length < 2 || value.length > 20)
                throw 'Name must be 2-20 characters long.';
            name = value;
        };
    };

    // public static
    cls.get_nextId = function () {
        return nextId;
    };

    // public (shared across instances)
    cls.prototype = {
        announce: function () {
            alert('Hi there! My id is ' + this.get_id() + ' and my name is "' + this.get_name() + '"!\r\n' +
                  'The next fellow\'s id will be ' + MyClass.get_nextId() + '!');
        }
    };

    return cls;
})();

有人问我使用这种模式继承,所以这里是:

// It's a good idea to have a utility class to wire up inheritance.
function inherit(cls, superCls) {
    // We use an intermediary empty constructor to create an
    // inheritance chain, because using the super class' constructor
    // might have side effects.
    var construct = function () {};
    construct.prototype = superCls.prototype;
    cls.prototype = new construct;
    cls.prototype.constructor = cls;
    cls.super = superCls;
}

var MyChildClass = (function () {
    // constructor
    var cls = function (surName) {
        // Call super constructor on this instance (any arguments
        // to the constructor would go after "this" in call(…)).
        this.constructor.super.call(this);

        // Shadowing instance properties is a little bit less
        // intuitive, but can be done:
        var getName = this.get_name;

        // public (this instance only)
        this.get_name = function () {
            return getName.call(this) + ' ' + surName;
        };
    };
    inherit(cls, MyClass); // <-- important!

    return cls;
})();

以及一个使用它的例子:

var bob = new MyClass();
bob.set_name('Bob');
bob.announce(); // id is 1, name shows as "Bob"

var john = new MyChildClass('Doe');
john.set_name('John');
john.announce(); // id is 2, name shows as "John Doe"

alert(john instanceof MyClass); // true

如您所见,这些类正确地相互交互(它们共享来自 的静态 id MyClassannounce方法使用正确的get_name方法等)

需要注意的一件事是需要隐藏实例属性。您实际上可以使inherit函数遍历所有作为函数的实例属性(使用hasOwnProperty),并自动添加一个super_<method name>属性。这将让您调用this.super_get_name()而不是将其存储在临时值中并使用call.

对于原型上的方法,您无需担心上述问题,如果您想访问超类的原型方法,只需调用this.constructor.super.prototype.methodName. 如果你想让它不那么冗长,你当然可以添加便利属性。:)

谢谢!非常喜欢!你能用这种方法展示类继承的例子吗?
2021-03-21 05:59:52
@DmitrijGolubev、Brad Dwyer 和 Nathan C. Tresch:我已经添加了继承,但它变得非常复杂,所以我通常会建议您使用更简单的解决方案,除非您需要 JavaScript 中的这种核心继承(这实际上只是一个原型语言)。
2021-04-03 05:59:52
关于该cls.prototype部分的说明:“跨实例共享”仅用于读取值(调用announce)。如果您设置myClassInstance.announce为另一个值,它会在 中创建一个新属性myClassInstance,因此它仅适用于该对象,而不适用于该类的其他实例。分配到MyClass.prototype.announce将影响所有实例。
2021-04-06 05:59:52
没问题,很高兴能帮到你!:)
2021-04-08 05:59:52
@guiomie 这是一个“公共静态”方法,因此您可以在构造函数(“类”)而不是实例上调用它: MyClass.get_nextId()
2021-04-10 05:59:52

在我看来,你们中的大多数人都在举例说明 getter 和 setter 而不是构造函数,即http://en.wikipedia.org/wiki/Constructor_(object-oriented_programming)

午餐丹更接近,但该示例在 jsFiddle 中不起作用。

此示例创建一个私有构造函数,该函数仅在对象创建期间运行。

var color = 'black';

function Box()
{
   // private property
   var color = '';

   // private constructor 
   var __construct = function() {
       alert("Object Created.");
       color = 'green';
   }()

   // getter
   this.getColor = function() {
       return color;
   }

   // setter
   this.setColor = function(data) {
       color = data;
   }

}

var b = new Box();

alert(b.getColor()); // should be green

b.setColor('orange');

alert(b.getColor()); // should be orange

alert(color); // should be black

如果你想分配公共属性,那么构造函数可以这样定义:

var color = 'black';

function Box()
{
   // public property
   this.color = '';

   // private constructor 
   var __construct = function(that) {
       alert("Object Created.");
       that.color = 'green';
   }(this)

   // getter
   this.getColor = function() {
       return this.color;
   }

   // setter
   this.setColor = function(color) {
       this.color = color;
   }

}

var b = new Box();

alert(b.getColor()); // should be green

b.setColor('orange'); 

alert(b.getColor()); // should be orange

alert(color); // should be black
@Rap Jon 的构造函数示例没有参数,因为它是Box()函数 :) 。但是这个例子以及其他答案中的例子可以很容易地扩展到接受参数。
2021-03-14 05:59:52
这怎么不是#1答案?只有 Jon 创建了一个带参数的构造函数。
2021-03-22 05:59:52
@AndersonGreen 您可以向 Box 添加一个参数,然后将其作为函数参数传递给私有构造函数。
2021-04-07 05:59:52
不需要“私有构造函数”。只需在Box函数中进行构建,就可以开始了(它仍然是“私有的”)。Javascript 中的“私有”仅表示可通过词法范围访问;无需分配给成员。另外:此代码是错误的。它创建了一个全局__construct变量,这很糟糕。var应该用来限制__construct.
2021-04-07 05:59:52
有什么方法可以让我们使用这种构造函数的范式来获得继承的例子吗?
2021-04-10 05:59:52

那么“构造函数”属性的重点是什么?无法弄清楚它在哪里有用,有什么想法吗?

构造函数属性的重点是提供某种方式来假装 JavaScript 具有类。不能做的一件事是在创建对象后更改其构造函数。情况很复杂。

几年前我写了一篇相当全面的文章:http : //joost.zeekat.nl/constructors-thinked-mildly-confusing.html

重点是使用“new”关键字。"d = new Drofto()" 创建一个空对象并运行 Drofto 函数,所述新对象的边界为 "this"。Drofto 函数可以自由地返回任何东西,但习惯上将返回的东西视为 Drofto 类的成员。
2021-03-13 05:59:52

这里的例子:http : //jsfiddle.net/FZ5nC/

试试这个模板:

<script>
//============================================================
// Register Namespace
//------------------------------------------------------------
var Name = Name||{};
Name.Space = Name.Space||{};

//============================================================
// Constructor - MUST BE AT TOP OF FILE
//------------------------------------------------------------
Name.Space.ClassName = function Name_Space_ClassName(){}

//============================================================
// Member Functions & Variables
//------------------------------------------------------------
Name.Space.ClassName.prototype = {
  v1: null
 ,v2: null
 ,f1: function Name_Space_ClassName_f1(){}
}

//============================================================
// Static Variables
//------------------------------------------------------------
Name.Space.ClassName.staticVar = 0;

//============================================================
// Static Functions
//------------------------------------------------------------
Name.Space.ClassName.staticFunc = function Name_Space_ClassName_staticFunc(){
}
</script>

如果要定义静态类,则必须调整命名空间:

<script>
//============================================================
// Register Namespace
//------------------------------------------------------------
var Shape = Shape||{};
Shape.Rectangle = Shape.Rectangle||{};
// In previous example, Rectangle was defined in the constructor.
</script>

示例类:

<script>
//============================================================
// Register Namespace
//------------------------------------------------------------
var Shape = Shape||{};

//============================================================
// Constructor - MUST BE AT TOP OF FILE
//------------------------------------------------------------
Shape.Rectangle = function Shape_Rectangle(width, height, color){
    this.Width = width;
    this.Height = height;
    this.Color = color;
}

//============================================================
// Member Functions & Variables
//------------------------------------------------------------
Shape.Rectangle.prototype = {
  Width: null
 ,Height: null
 ,Color: null
 ,Draw: function Shape_Rectangle_Draw(canvasId, x, y){
    var canvas = document.getElementById(canvasId);
    var context = canvas.getContext("2d");
    context.fillStyle = this.Color;
    context.fillRect(x, y, this.Width, this.Height);
 }
}

//============================================================
// Static Variables
//------------------------------------------------------------
Shape.Rectangle.Sides = 4;

//============================================================
// Static Functions
//------------------------------------------------------------
Shape.Rectangle.CreateSmallBlue = function Shape_Rectangle_CreateSmallBlue(){
    return new Shape.Rectangle(5,8,'#0000ff');
}
Shape.Rectangle.CreateBigRed = function Shape_Rectangle_CreateBigRed(){
    return new Shape.Rectangle(50,25,'#ff0000');
}
</script>

实例化示例:

<canvas id="painting" width="500" height="500"></canvas>
<script>
alert("A rectangle has "+Shape.Rectangle.Sides+" sides.");

var r1 = new Shape.Rectangle(16, 12, "#aa22cc");
r1.Draw("painting",0, 20);

var r2 = Shape.Rectangle.CreateSmallBlue();
r2.Draw("painting", 0, 0);

Shape.Rectangle.CreateBigRed().Draw("painting", 10, 0);
</script>

注意函数定义为 AB = function A_B()。这是为了使您的脚本更易于调试。打开 Chrome 的 Inspect Element 面板,运行此脚本,并展开调试回溯:

<script>
//============================================================
// Register Namespace
//------------------------------------------------------------
var Fail = Fail||{};

//============================================================
// Static Functions
//------------------------------------------------------------
Fail.Test = function Fail_Test(){
    A.Func.That.Does.Not.Exist();
}

Fail.Test();
</script>
可能是因为它给问题增加了不必要的复杂程度。由于迂腐的名称间距和静态类声明,很难在您的帖子中找到答案。不要误会我的意思,这是很好的信息,但如果您试图了解理解,它肯定比帮助更令人困惑。我在 JS 方面有一半的能力,我几乎不明白你在这里做什么,或者为什么它与“我如何构造函数?”相关。
2021-03-12 05:59:52
添加了示例。还添加了有关改进调试输出的信息。
2021-03-16 05:59:52
感谢您的洞察力,Bmo。这是一篇很长的文章,但这是因为如果构造函数没有绑定到定义良好的对象和静态类实现,我不明白它的用法。在学习 C++ 或 Java 时,您必须学习如何实现类以及如何实现构造函数。自从我偶然发现这种编写 javascript 的方法以来,Web 开发变得更加有趣,我只是想分享一下。我将小提琴移到顶部,以便更容易找到。我希望这能澄清任何混淆。
2021-03-20 05:59:52
@Bmo 你是认真的,关于命名空间的两行很难在下面找到构造函数,特别是考虑到注释构造函数 - 必须在文件顶部?非常欢迎提供带有命名空间的示例,javascript 开发社区盲目地忽略命名空间,导致在名称冲突时难以发现错误。令人遗憾的是,js 开发人员认为如果他们从互联网上的帖子中复制一段文本并执行类似于他们需要的工作,他们的工作就完成了。
2021-03-30 05:59:52
一般而言,js 和 Web 开发的主要问题是,大多数开发人员忽略了该行业 50 多年来创造的所有实践,并认为如果他们可以进行 ajax 调用,他们就是王者。很遗憾看到花了这么多年才开始在 javascript 中使用众所周知的模式和实践。
2021-04-06 05:59:52