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


当前回答

在使用上面Blixt的伟大模板时,我发现它不能很好地使用多级继承(MyGrandChildClass扩展MyChildClass扩展MyClass)——它循环调用第一个父类的构造函数。这里有一个简单的解决方法-如果你需要多级继承,而不是使用this.constructor.super。调用(这,姓氏);使用chainSuper()。调用(这,姓氏);链函数定义如下:

function chainSuper(cls) {
  if (cls.__depth == undefined) cls.__depth = 1; else cls.__depth++;
  var depth = cls.__depth;
  var sup = cls.constructor.super;
  while (depth > 1) {
    if (sup.super != undefined) sup = sup.super;
    depth--;
  }
  return sup;
}

其他回答

这里我们需要注意一点,它是一种无类语言,但是我们可以通过使用java script中的函数来实现。实现这一点最常见的方法是我们需要在java脚本中创建一个函数,并使用new关键字来创建一个对象,并使用此关键字来定义属性和方法。下面是示例。

// Function constructor

   var calculator=function(num1 ,num2){
   this.name="This is function constructor";
   this.mulFunc=function(){
      return num1*num2
   };

};

var objCal=new calculator(10,10);// This is a constructor in java script
alert(objCal.mulFunc());// method call
alert(objCal.name);// property call

//Constructors With Prototypes

var calculator=function(){
   this.name="Constructors With Prototypes";
};

calculator.prototype.mulFunc=function(num1 ,num2){
 return num1*num2;
};
var objCal=new calculator();// This is a constructor in java script
alert(objCal.mulFunc(10,10));// method call
alert(objCal.name); // property call

下面是一个模板,我有时会在JavaScript中使用类似oop的行为。如您所见,您可以使用闭包模拟私有(静态和实例)成员。新的MyClass()将返回一个对象,该对象仅具有分配给这个对象和“类”的原型对象中的属性。

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

正如您所看到的,类之间正确地交互(它们共享来自MyClass的静态id, announce方法使用正确的get_name方法,等等)。

需要注意的一点是需要对实例属性进行阴影处理。您实际上可以让继承函数遍历所有实例属性(使用hasOwnProperty)是函数,并自动魔术般地添加一个super_<方法名>属性。这将允许您调用This .super_get_name(),而不是将其存储在临时值中并使用call调用它。

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

使用原型:

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

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

隐藏“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 objectA = {
    color: ''; 
    callColor : function(){
        console.log(this.color);
    }
    this.callColor(); 
}
var newObject = new objectA(); 

例如: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>

注意函数定义为A.B = 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>