JavaScript类定义之间有什么区别?

3

如您所知,JavaScript在语法方面非常灵活,并且是一种面向对象的语言。但我的问题是,在JavaScript中定义函数的方式有哪些流行的方法?

只有JavaScript提供了多种方式来帮助了解其他人的代码,还是基于性能原因提供了多种方式?

1. 使用函数

function Apple (type) {
    this.type = type;
    this.color = "red";
    this.getInfo = getAppleInfo;
    function getAppleInfo() {
        return this.color + ' ' + this.type + ' apple';
    }
}

2. 使用对象字面量

var apple = {
    type: "macintosh",
    color: "red",
    getInfo: function () {
        return this.color + ' ' + this.type + ' apple';
    }
}

3. 使用函数实现单例模式

var apple = new function() {
    this.type = "macintosh";
    this.color = "red";
    this.getInfo = function () {
        return this.color + ' ' + this.type + ' apple';
    };
}

6
我推荐阅读Addy Osmani所著的《JavaScript设计模式》,可以从 http://addyosmani.com/resources/essentialjsdesignpatterns/book/ 免费获取。该书详细解释了不同模式背后的原理。我认为,在JavaScript中最流行的模式是“随机而缺乏远见”,但其实不必如此!;) - michaelward82
4个回答

2

原型

首先,我必须告诉你,我不是专家,你可能会在这个主题上找到更好(完整)的答案。

这3种方法之间的主要区别在于原型。方法1 [使用函数] 可以让你绑定原型对象,这些对象不会与其他对象共享。

你可以向Apple对象以及Object对象添加原型方法。例如:

//init
Object.prototype.hello = 'Hello';
Apple.prototype.world = ' World';

//Method 1
alert(apple.hello + apple.world); //Hello World

//Method 2
alert(apple.hello + apple.world); //Helloundefined

//Method 3
alert(apple.hello + apple.world); //Helloundefined

可重用性

如果你想要多个相同对象的实例,没有第一种方法会很麻烦。比如你想要两个不同的苹果,你需要复制/粘贴并更改属性(除了第一种方法)。

//Method 1
var macintosh = new Apple('macintosh');

var otherApple = new Apple('Heisenberg')

//Method 2
var macintosh = {
    type: "macintosh",
    color: "red",
    getInfo: function () {
        return this.color + ' ' + this.type + ' apple';
    }
}

var otherApple = {
    type: "I'm not good with apple's name",
    color: "red",
    getInfo: function () {
        return this.color + ' ' + this.type + ' apple';
    }
}

//Method 3
var macintosh = new (function(type) {
    this.type = type;
    this.color = "red";
    this.getInfo = function () {
        return this.color + ' ' + this.type + ' apple';
    };
})('macintosh');

var otherApple = new (function(type) {
    this.type = type;
    this.color = "red";
    this.getInfo = function () {
        return this.color + ' ' + this.type + ' apple';
    };
})('Still not better');

变量作用域

在方法1和方法3中,您可以拥有局部变量。这些元素在对象外部不可访问。

当您在对象内部具有事件处理程序函数时,这是非常有用的。在这些函数中,您会失去this引用。幸运的是,您可以将其保存在局部变量中。以timeout为例,不要使用.bind()之类的东西来欺骗自己:

//Method 2
var apple = {
    getMe : 'YES!',
    setTimer : function(){
        setTimeout(this.goGetHim, 500);
    },
    goGetHim : function(){
        //alert(this.getMe); Do not work
        alert(apple.getMe); //Kinda lame
    }
}

// Method 1, 3
yourinitfn(){
    var self = this
    this.getMe : 'YES!',
        this.setTimer : function(){
            setTimeout(this.goGetHim, 500);
        },
            this.goGetHim : function(){
                alert(self.getMe); //YES!
            }
}

apple.self;//Undefined

鉴别

最后一件我能想到的是鉴别。基本上,你可以很容易地通过方法#1知道这个物体是一个苹果:

//Method 1
alert(apple instanceof Object); //True
alert(apple instanceof Apple); //True

//Method 2
alert(apple instanceof Object); //True
alert(apple instanceof Apple); //False

//Method 2
alert(apple instanceof Object); //True
alert(apple instanceof Apple); //False

必须有其他优势

如果有人能在这些主题上找到其他优势,我将不胜感激:

  • 内存
  • 性能
  • 可读性
  • 编程语言兼容性(例如:通过 JSON 将对象转换为 PHP)
  • 其他我想不到的东西...

最后说明

我从未使用过也永远不会使用单例函数来创建对象。我曾经在某个地方读到过(现在找不到参考资料了),使用 new function 是一种不好的做法并且会严重影响性能。请注意,我可能在这里是错误的...


1
简单来说,对于你的问题“哪种方法流行?”我必须说我在实际应用中看到了各种不同的方法和组合。没有标准的方式(因此会有很多漏洞)。
之所以有多种方法,是因为JS不是面向对象的语言。它是一种基于对象的脚本语言,最初并不是用于大规模应用和库的。它允许你用五行代码实现令人惊叹的效果,因为其灵活的语法,但当需要大规模的健壮性和可读性时,就会变成噩梦。
个人而言,我编写了一个JavaScript库,试图像C++一样将清晰自然的语法带入JS中,先声明后定义,并且可以轻松使用静态、私有、受保护、虚拟等成员。 OoJs可以在GitHub上找到。这是语法的基本示例:
;( function class_Template( namespace )
{
    'use strict'; // recommended

    if( namespace[ "Template" ] ) return    // protect against double inclusions

        namespace.Template = Template
    var Static             = TidBits.OoJs.setupClass( namespace, "Template", "BaseClass" )

    // Static data members, private by default
    //
    Static.privateStaticDM   = 0
    Static.protectedStaticDM = 0
    Static.publicStaticDM    = 0

    Static.Private
    (
          "privateStaticDM"     //< can do for consistency, but private is the default
        ,  privateStaticMethod  //  accesslevel for data members
    )

    Static.Protected
    (
          "protectedStaticDM"
        ,  protectedStaticMethod
    )

    Static.Public
    (
          "publicStaticDM"
        ,  publicStaticMethod
    )

    // constructor
    //
    function Template( parameter )
    {
        // Data members
        //
        this.privateInstanceDM   = parameter
        this.protectedInstanceDM = 0
        this.publicInstanceDM    = 0

        this.Private
        (
              "privateInstanceDM"
            ,  init

            ,  this.Virtual
               (
                    virtualPrivateInstanceMethod
               )
        )

        this.Protected
        (
              "protectedInstanceDM"
            ,  protectedInstanceMethod
        )

        var iFace = this.Public
        (
              "publicInstanceDM"
            ,  publicInstanceMethod
        )

        this.init() // if you have to do something else

        return iFace
    }

    // all your method definitions go here
    //
    function init(){}
    function privateStaticMethod(){}

})( window )

0
使用函数 vs 使用对象字面量,意味着你需要调用它来获取一个实例。
var apple = new Apple("Macintosh");

使用构造函数创建的对象与使用对象字面量创建的对象相等。然而,使用构造函数允许我们在多个实例之间共享一些值进行优化,主要是方法:

function getInfo() {
    return this.color + ' ' + this.type + ' apple';
}
function Apple (type) {
    this.type = type;
    this.color = "red";
    this.getInfo = getAppleInfo;
}
var apple1 = new Apple("Macintosh"),
    apple2 = new Apple("Golden Delicious");
apple1.getInfo === apple2.getInfo // only one function object!

然而,使用工厂函数返回对象文字也可以实现这个功能。但是使用构造函数和new关键字的额外好处在于实例共享其原型对象,从中它们能够继承属性。我们可以简化为:

function Apple (type) {
    this.type = type;
    this.color = "red";
}
Apple.prototype.getInfo = function getInfo() {
    return this.color + ' ' + this.type + ' apple';
};
// now, having
var apple2 = new Apple("Golden Delicious");
apple2.getInfo === Apple.prototype.getInfo // still only one function object
apple2.getInfo() // and we can access it directly on instances without explicitly
                 // having created a property on them!
apple2 instanceof Apple // is also a feature

请参阅JavaScript中“prototype”与“this”的使用?以获取更多详细信息。

3 Singleton using a function

var apple = new function() {

永远不要使用这个。它会掩盖构造函数的存在,这会在你不知情的情况下允许构造其他非单例实例。


0

你的选项1和3本质上是相同的。为了创建一个实例,选项1需要使用new进行调用。选项3则直接这样做。但这有点奇怪,因为如果只有一个对象,最好使用选项2(对象字面量)。

你还缺少另一种常见的选项,可以称之为对象工厂。在这种情况下,你可以将选项2封装在一个函数中,以便能够实例化多个对象副本。

你还缺少使用函数原型的任何用途,这对于使用方法实例化类的性能来说非常重要。例如:

var Apple = function () {
    this.type = "macintosh";
    this.color = "red";
};
Apple.prototype = {
    getInfo: function () {
        return this.color + ' ' + this.type + ' apple';
    }
};

var myApple = new Apple();

在这种情况下,只有一个getInfo方法的副本存在,而在您的示例中,每次实例化苹果时都会重新创建该方法。此外,使用原型可以利用原型继承的能力。有一些新版本的JavaScript中引入的替代方法,可能会让人感到困惑,但它们最终是相同的。
如果我需要一个没有任何方法的对象,或者如果我正在使用先前定义的函数作为我的方法(例如,不是每次实例化对象时创建新的函数表达式),那么我会使用您的Option 2。否则,我会使用原型和构造函数。
我建议阅读Reg Braithwaite的《Javascript Allongé》,它是免费在线的,真正让我感受到了如何充分利用Javascript函数和对象模型的感觉。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接