JavaScript对象中的构造函数

Cli*_*ote 401 javascript oop

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

Nic*_*ick 408

使用原型:

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

Box.prototype.getColor = function()
{
    return this.color;
};
Run Code Online (Sandbox Code Playgroud)

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

function Box(col)
{
   var color = col;

   this.getColor = function()
   {
       return color;
   };
}
Run Code Online (Sandbox Code Playgroud)

用法:

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

var greenBox = new Box("green");
alert(greenBox.getColor()); // will alert green
Run Code Online (Sandbox Code Playgroud)

  • `var`生成一个私有变量.`this`是一个公共变量 (49认同)
  • @CamiloMartin虽然并非总是必要,但使变量"私有"(或者在这种情况下,不可命名)可以是防止外部代码依赖于类的实现细节的有用方法.即使只是指示该类的哪些元素是公共/私有对外部用户也是有用的. (6认同)
  • @Jeach是的.我提供了一个隐藏"颜色"的替代片段.我建议你使用的主要是个人偏好(保护与简单) (4认同)
  • @BorisB,是的 - 这会在Box对象上定义color和getColor,否则你会在通常的范围内分配变量. (3认同)
  • @AlanKis(至少在一些Javascript引擎中)堆栈跟踪在匿名函数的情况下甚至不会提到`Foo`,而在后一种情况下它会知道它被称为`Foo`.非常有用的调试. (3认同)
  • 我真的不明白你为什么要在构造函数中创建getter.这种技术的问题,而不是使用原型,是每次你创建一个这种类型的对象,你复制功能,使用更多的内存,更慢,等等.原型是你真正创建类的方式. (3认同)
  • @CamiloMartin数据隐藏在您自己处理大型项目时甚至非常有用.经过一段时间后,您可能不记得是否应该从另一个范围访问该变量. (2认同)
  • 第一种方法(`this.getColor`)与第二种方法(`Box.prototype.getColor`)之间的细微差别.前者在Box _instance_上定义了一个新属性,后者在其_prototype_上定义.所以`(new Box('blue')).hasOwnProperty('getColor')`用第一种方法返回`true`,但是`(new box('green')).hasOwnProperty('getColor')`返回`false `第二个.当遵循常见模式来迭代对象的属性时,这变得很重要,例如:[循环JavaScript对象](http://stackoverflow.com/questions/684672/loop-through-javascript-object?rq=1) (2认同)

Bli*_*ixt 248

这是我有时在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;
})();
Run Code Online (Sandbox Code Playgroud)

我被问及使用这种模式的继承,所以这里有:

// 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;
})();
Run Code Online (Sandbox Code Playgroud)

以及使用它的一个例子:

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
Run Code Online (Sandbox Code Playgroud)

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

需要注意的一点是需要影子实例属性.实际上,您可以使该inherit函数遍历作为函数的所有实例属性(使用hasOwnProperty),并自动添加super_<method name>属性.这将允许您调用this.super_get_name()而不是将其存储在临时值中并使用它来调用它call.

对于原型上的方法,您不需要担心上述情况,但如果您想访问超类的原型方法,则可以调用this.constructor.super.prototype.methodName.如果你想减少冗长,你当然可以增加便利性.:)

  • 只是关于`cls.prototype`部分的注释:"跨实例共享"仅用于读取值(调用`announce`).如果将`myClassInstance.announce`设置为另一个值,它会在`myClassInstance`中创建一个新属性,因此它只适用于该对象,而不适用于该类的其他实例.分配给"MyClass.prototype.announce"会影响所有实例. (7认同)
  • @Blixt - 广泛使用了你的例子 - 谢谢! (3认同)
  • 谢谢!非常喜欢!你能用这种方法展示类继承的例子吗? (2认同)
  • @DmitrijGolubev,Brad Dwyer和Nathan C. Tresch:我已经添加了继承,但它变得非常复杂,所以我通常会建议你使用更简单的解决方案,除非你需要在JavaScript中使用这样的硬核继承(这实际上只是一个原型语言). (2认同)

Jon*_*Jon 166

在我看来,大多数人都给出了getter和setter的例子而不是构造函数,即http://en.wikipedia.org/wiki/Constructor_(object-oriented_programming).

lunched-dan更接近但是这个例子在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
Run Code Online (Sandbox Code Playgroud)

如果要分配公共属性,则可以将构造函数定义为:

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
Run Code Online (Sandbox Code Playgroud)

  • 这怎么不是#1答案?只有Jon创建了一个带参数的构造函数. (45认同)
  • @AndersonGreen您可以向Box添加一个参数,然后将其作为函数参数传递给私有构造函数. (2认同)

小智 23

那么"构造函数"属性的意义何在?无法弄清楚它可能有用的地方,任何想法?

构造函数属性的要点是提供一些假装JavaScript的方法.您无法做的事情之一是在创建对象的构造函数后更改它.情况很复杂.

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


bit*_*her 16

示例: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>
Run Code Online (Sandbox Code Playgroud)

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

<script>
//============================================================
// Register Namespace
//------------------------------------------------------------
var Shape = Shape||{};
Shape.Rectangle = Shape.Rectangle||{};
// In previous example, Rectangle was defined in the constructor.
</script>
Run Code Online (Sandbox Code Playgroud)

示例类:

<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>
Run Code Online (Sandbox Code Playgroud)

示例实例化:

<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>
Run Code Online (Sandbox Code Playgroud)

注意函数定义为AB =函数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>
Run Code Online (Sandbox Code Playgroud)

  • 你能解释一下它是如何工作的吗? (3认同)

Sat*_*are 10

这是一个构造函数:

function MyClass() {}
Run Code Online (Sandbox Code Playgroud)

当你这样做

var myObj = new MyClass();
Run Code Online (Sandbox Code Playgroud)

MyClass 执行,并返回该类的新对象.


bob*_*bob 8

这种模式对我很有帮助.使用此模式,您可以在单独的文件中创建类,并根据需要将它们加载到整个应用程序中.

// Namespace
// (Creating new if not instantiated yet, otherwise, use existing and just add to it)
var myApp = myApp || {};

// "Package" 
// Similar to how you would establish a package in other languages
(function() {

// "Class"
var MyClass = function(params) {
    this.initialize(params);
}

    // "Private Static" vars 
    //    - Only accessible to functions in this class.
    //    - Doesn't get wiped out when we create a new instance.
    var countInstances = 0;
    var allInstances = [];

    // "Private Static" functions 
    //    - Same as above, but it's a function accessible 
    //      only to other functions in this class.
    function doSomething(){
    }

    // "Public Static" vars
    //    - Everyone has access.
    //    - Doesn't get wiped out when we create a new instance.
    MyClass.counter = 0;

    // "Public Static" functions
    //    - Same as above, but anyone can call this "static method".
    //    - Kinda like a singleton class situation.
    MyClass.foobar = function(){
    }

    // Public properties and methods are built into the "prototype"
    //    - This is how each instance can become unique unto itself.
    //    - Establishing "p" as "local" (Static Private) variable 
    //      simply so we don't have to keep typing "MyClass.prototype" 
    //      for each property and function.
var p = MyClass.prototype;

    // "Public" vars
    p.id = null;
    p.firstname = null;
    p.lastname = null;

    // "Private" vars
    //    - Only used by "this" instance.
    //    - There isn't "true" privacy for each 
    //      instance so we have to fake it. 
    //    - By tradition, we indicate "privacy"  
    //      by prefixing it with an underscore. 
    //    - So technically, anyone can access, but we simply 
    //      don't tell anyone about it (e.g. in your API)
    //      so no one knows about it :)
    p._foo = null;

    p.initialize = function(params){
        this.id = MyClass.counter++;
        this.firstname = params.firstname;
        this.lastname = params.lastname;
        MyClass.counter++;
        countInstances++;
        allInstances.push(this);
    }

    p.doAlert = function(theMessage){
        alert(this.firstname + " " + this.lastname + " said: " + theMessage + ". My id:" + this.id + ".  Total People:" + countInstances + ". First Person:" + allInstances[0].firstname + " " + allInstances[0].lastname);
    }


// Assign class to app
myApp.MyClass = MyClass;

// Close the "Package"
}());

// Usage example:
var bob = new myApp.MyClass({   firstname   :   "bob",
                                lastname    :   "er"
                            });

bob.doAlert("hello there");
Run Code Online (Sandbox Code Playgroud)


ana*_*ria 8

我发现本教程非常有用.大多数jQuery插件都使用这种方法.

http://www.htmlgoodies.com/html5/tutorials/create-an-object-oriented-javascript-class-constructor.html#fbid=OVYAQL_TDpK

var Class = function(methods) {   
    var klass = function() {    
        this.initialize.apply(this, arguments);          
    };  

    for (var property in methods) { 
       klass.prototype[property] = methods[property];
    }

    if (!klass.prototype.initialize) klass.prototype.initialize = function(){};      

    return klass;    
};
Run Code Online (Sandbox Code Playgroud)

现在,

var Person = Class({ 
    initialize: function(name, age) {
        this.name = name;
        this.age  = age;
    },
    toString: function() {
        return "My name is "+this.name+" and I am "+this.age+" years old.";
    }
}); 

var alice = new Person('Alice', 26);
alert(alice.name); //displays "Alice"
alert(alice.age); //displays "26"
alert(alice.toString()); //displays "My name is Alice and I am 26 years old" in most browsers.
//IE 8 and below display the Object's toString() instead! "[Object object]"
Run Code Online (Sandbox Code Playgroud)

  • 每当我看到人们使用`klass`时,我都会畏缩 (10认同)

Bru*_*uno 8

是的,你可以在类声明中定义一个构造函数,如下所示:

class Rectangle {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
}
Run Code Online (Sandbox Code Playgroud)


Hen*_*zia 6

我想我会发布我用javascript闭包做的事,因为还没有人使用闭包.

var user = function(id) {
  // private properties & methods goes here.
  var someValue;
  function doSomething(data) {
    someValue = data;
  };

  // constructor goes here.
  if (!id) return null;

  // public properties & methods goes here.
  return {
    id: id,
    method: function(params) {
      doSomething(params);
    }
  };
};
Run Code Online (Sandbox Code Playgroud)

欢迎对此解决方案提出意见和建议.:)


Chi*_*imz 5

也许它变得更简单了,但以下是我在 2017 年提出的想法:

class obj {
  constructor(in_shape, in_color){
    this.shape = in_shape;
    this.color = in_color;
  }

  getInfo(){
    return this.shape + ' and ' + this.color;
  }
  setShape(in_shape){
    this.shape = in_shape;
  }
  setColor(in_color){
    this.color = in_color;
  }
}
Run Code Online (Sandbox Code Playgroud)

在使用上面的类时,我有以下内容:

var newobj = new obj('square', 'blue');

//Here, we expect to see 'square and blue'
console.log(newobj.getInfo()); 

newobj.setColor('white');
newobj.setShape('sphere');

//Since we've set new color and shape, we expect the following: 'sphere and white'
console.log(newobj.getInfo());
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,构造函数接受两个参数,我们设置对象的属性。我们还通过使用这些函数来改变对象的颜色和形状setter,并证明在getInfo()这些改变之后调用时它的变化仍然存在。

有点晚了,但我希望这会有所帮助。我已经通过mocha单元测试对此进行了测试,并且运行良好。