【问题标题】:How to make a class in JavaScript?如何在 JavaScript 中创建一个类?
【发布时间】:2012-10-24 05:10:41
【问题描述】:

在 JavaScript 中有很多方法可以做同样的事情。然而,我已经掌握了一些方法,坦率地说,有些方法我不明白。谁能帮我澄清一些事情? (我最初是在 PHP 中学习 OOP。)

所以可以这样制作一个类:

var object = new class(constructparams) {
    var private_members; // Can be accessed from within the code inside this definition only.
    this.public_members; // Can be accessed from everywhere.

    var private_functions = function() {}
    this.public_functions = function() {}
}

object.prototype.semi_public_members = function() {
    // Will be public, but can only access public members and methods.
    // E. g. private_members; is not available here.
}

到目前为止,这一切都正确吗?

然后有人喜欢自执行匿名函数的方法来创建命名空间。这样做有什么意义,当你在上面做同样的事情时,提供一个命名空间?

最后你有我不明白的对象文字符号。

var object = { // Something strange in here }

里面发生了什么?是 JSON 吗?它是如何使用的,我该如何使用它。使用这种方式而不是使用我描述的方法有什么好处?为什么你要原型而不是第一次正确地制作类?

【问题讨论】:

  • 从技术上讲,JavaScript 没有类。但是,人们经常像使用对象一样使用对象。你的最后一个例子只是一个对象字面量的赋值。

标签: javascript json


【解决方案1】:

通过示例解释构造对象中不同事物的行为:

// Defined as a variable from an anonymous function
// so that there is scope closure over variables
// shared across all instances and the prototype.
// If this isn't important, you don't need to close
// scope around it, so define directly
var ConstructedObject = (function constructorCreator () {
    // Define any variables/methods to be shared across
    // all instances but not polluting the namespace
    var sharedVariable = 'foo';

    // Next the actual constructor
    function ConstructedObject () {
        // Variables here are normally used to help
        // each instance and will be kept in memory as
        // long as the instance exists
        var instanceVariable = 'bar';
        // instance-specific properties get defined
        // using the "this" keyword, these are the
        // properties expected to be changed across
        // each different instance
        this.instanceProperty = true;
        this.instanceMethod = function () { return instanceVariable; };
        this.changeInstanceVar = function () { instanceVariable = 'foo'; };
            // you do have access to the shared
            // variables here if you need them.
    }
    // After the constructor, you set up the
    // prototype, if any. This is an object of shared
    // properties and methods to be inherited by every
    // instance made by the constructor, and it also
    // inherits the prototype's prototype, too.
    // Lets use a literal object for simplicity.
    ConstructedObject.prototype = {
        // Accessing the instance to which a method
        // applies is done using the "this" keyword,
        // similar to in the constructor
        sharedMethod : function () { return [sharedVariable, this.instanceMethod(),this.instanceProperty]; },
        changeSharedVar : function () { sharedVariable = 'bar'; }
        // properties may also be defined
    };
    // Finally, the constructor is returned so it
    // can be kept alive outside of the anonymous
    // function used to create it
    return ConstructedObject;
// and the anonymous function is called to execute
// what we've done so far
})();

执行上述代码后,您将拥有一个构造函数,该构造函数创建具有特定于实例的变量和共享变量的对象。现在让我们通过创建两个实例并在一些更改前后比较它们来看看它们的行为。

// First create the two instances
var myObjA = new ConstructedObject(),
    myObjB = new ConstructedObject();
// Now compare them, the sharedMethod method we
// used in the prototype offers an easy way to
// do this
console.log( myObjA.sharedMethod(), myObjB.sharedMethod() );
// ["foo", "bar", true] ["foo", "bar", true]
// Next lets change the different variables in
// myObjB so we can see what happens, again the
// change* methods defined before let us do this
// easily
myObjB.changeInstanceVar();
myObjB.changeSharedVar();
// For completeness, lets also change the property
// on myObjB.
myObjB.instanceProperty = false;
// Now when we compare them again, we see that our
// changes to the myObjB instance have only changed
// the shared variables of myObjA
console.log( myObjA.sharedMethod(), myObjB.sharedMethod() );
// ["bar", "bar", true] ["bar", "foo", false]

为了便于查看,这里将两个记录的语句放在一起

//     myObjA               myObjB
["foo", "bar", true] ["foo", "bar", true]
["bar", "bar", true] ["bar", "foo", false]

【讨论】:

  • 感谢您的回复!这是一个非常可靠的答案。但是我可以问为什么要使用这种方法而不是使用我在示例中使用的方法吗?在我看来,它们都提供相同的功能。是不是这样占用内存少?
  • 我假设你说的是整个事情都被封闭在一个匿名函数中?在第一条评论中,我试图解释这一点;它是通过在同一范围内捕获所有共享变量来创建对共享变量的闭包,而不会污染window(或您之前所在的任何范围)。如果你不需要这种方式的共享变量,你不需要将构造函数/原型包装在匿名函数中。
  • 如果存在内存泄漏(当你完成它们但希望你的对象保持不变时不清除大变量)或者如果你正在做巨大的一切都在实例上的这些对象的数量 - 如果它是共享的,则实例只需要一个引用,如果它在实例上,它需要在创建时为其分配新内存。 @StudentofHogwarts
  • 嗯,但是在同一个闭包中对所有内容进行原型设计而不是仅使用 this.methodname 创建方法有什么好处?
  • 在谷歌上无休止的搜索之后,我终于找到了一个解释原型设计基础的教程。我刚刚知道 它可以工作,而不是如何。现在这很有意义!再次感谢!
【解决方案2】:

我认为有些概念似乎遗漏了,但我会尽量回答。

所以可以像这样制作一个类...到目前为止这一切都正确吗?

它很接近,但并不完全正确。创建构造函数不需要new,只在创建“类”的新实例时才需要。

function Klass() { ... } // capitalized name as convention for constructors
var myKlass = new Klass(); //<-- Need "new" here

公共方法可以附加到实例或原型上。当您将其附加到原型时,该方法将在所有实例之间共享,从而节省一些内存。

Klass.prototype = {
  publicSharedMethod: function() {
    var self = this;
  }
}

然后有人喜欢自动执行匿名函数...什么是 重点...

模块模式(或自执行功能)方法有点不同,因为您通常不会处理原型,尽管您可以。它只是一个返回带有属性和方法的文字对象的函数,但当您不需要该对象的实例时,我主要将它用于单例:

var Singleton = (function() {
  var private = 'foo';
  var public = 'baz';
  var publicMethod = function() { ... }
  // Expose public methods and properties
  return {
    public: public
    publicMethod: publicMethod
  }
}());

最后你有我没有的对象文字符号 明白。

这只是 JavaScript 中的一个常规对象,类似于 PHP 所说的“关联数组”。文字对象语法是 JSON 的基础,但 JSON 在格式方面有更多限制; JavaScript 更宽容,因此您可以使用不带引号的属性和方法。

你为什么要原型而不是正确地制作类 第一次?

这里的重点是要理解JavaScript 不是传统的面向对象语言,所以没有类,你不应该考虑类。但是原型非常强大,我会说比类更强大。网上有很多关于如何使用原型复制类的示例,但反之则不然。

【讨论】:

  • 嗯,所以你基本上是说原型是静态成员?
  • 而 Klass() 只是一个不起作用的对象,因为构造函数尚未在其上运行以填充数据?因此,如果您有一个带有警报方法的 Klass(),它会在不被实例化的情况下工作吗?
  • 我喜欢 OOP 的做事方式,因为它不会污染全局命名空间,并且它会将事物分成模块,从而迫使您保持事物井井有条且非意大利面。您将如何概述结构?如果您需要多个实例,请使用我的方法,如果您需要一个实例,请使用原型?
  • 是的,我会说它们类似于静态成员。 Klass 可以是函数或构造函数,这完全取决于您如何调用它。如果你使用new,那么它是一个构造函数,否则它是一个函数,这就是为什么建议总是将你的构造函数大写以避免这种混淆。
  • 如果你想运行一个方法,你需要一个实例。
【解决方案3】:
var object = new function () {
    //
}

var myObject = new object();

【讨论】:

  • 好吧,我只是做了一个快捷方式:P
【解决方案4】:

不,这是不正确的。构造函数应该和对象的创建分开:

function myClass(constructparam1) {
  this.public_member = constructparam1; // Can be accessed from everywhere.
}

方法应该在构造函数的原型中,而不是实例中:

myClass.prototype.semi_public_members = function() {
  // Will be public
}

使用new关键字调用构造函数来创建实例:

var obj = new myClass(1337);

构造函数内部的局部变量只能在该函数内部访问。如果你的类中没有局部变量,你需要一个围绕它的函数来创建一个闭包,以便原型中的方法可以访问变量:

var myClass = (function(){

  var local_variable;

  function constructor(constructparam1) {
    this.public_member = constructparam1; // Can be accessed from everywhere.
  }

  constructor.prototype.semi_public_members = function() {
    // Will be public
    alert(local_variable); // can access private variables
  }

  return constructor;
})();

对象文字只是一次性创建对象的一种简单但有限的方法。它们没有原型,因此如果您希望它们具有方法,则必须将它们分配给属性:

var obj = {

  public_member: 1337,

  semi_public_members: function(){
    alert(this.public_member);
  }

};

【讨论】:

  • 那么在你的最后一个例子中,{}里面的东西就像做原型一样?在上面的示例中,您是否在最后公开的闭包内“生成”了一个名为构造函数的对象,以便您可以拥有一个对象并仍然访问私有成员?
  • @StudentofHogwarts:类似于使用原型,但方法是对象本身的成员,而不是原型的成员。如果您创建多个实例,则每个实例都将拥有自己的函数对象副本,而不是共享它们。在上面的示例中,constructor 是从封闭函数返回的,因此变量 myClass 包含构造函数,并且可以像第一个示例中一样使用。如果您创建多个实例,它们将共享局部变量,因此这可能不是如何做到这一点的最佳示例。
猜你喜欢
  • 2011-03-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-11-26
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多