我想知道使用这些方法是否有什么优点,我应该走哪条路?

构造函数的方法:

var Class = function () {

    this.calc = function (a, b) {
        return a + b;
    };

};

原型的方法:

var Class = function () {};

Class.prototype.calc = function (a, b) {
    return a + b;
};

我不喜欢这样,使用原型,方法定义从类中分离出来,我不知道是否有任何特定的原因,我应该使用这种方法而不仅仅是第一种方法。

另外,使用函数字面量来定义“类”比仅仅使用函数定义有什么好处:

var Class = function () {};

vs

function Class () {};

谢谢!


当前回答

通过原型链继承的方法可以对所有实例进行通用更改,例如:

function Class () {}
Class.prototype.calc = function (a, b) {
    return a + b;
}

// Create 2 instances:
var ins1 = new Class(),
    ins2 = new Class();

// Test the calc method:
console.log(ins1.calc(1,1), ins2.calc(1,1));
// -> 2, 2

// Change the prototype method
Class.prototype.calc = function () {
    var args = Array.prototype.slice.apply(arguments),
        res = 0, c;

    while (c = args.shift())
        res += c;

    return res; 
}

// Test the calc method:
console.log(ins1.calc(1,1,1), ins2.calc(1,1,1));
// -> 3, 3

注意如何更改应用于两个实例的方法?这是因为ins1和ins2共享同一个calc()函数。为了使用构造过程中创建的公共方法来实现这一点,您必须将新方法分配给已创建的每个实例,这是一项棘手的任务。这是因为ins1和ins2有它们自己单独创建的calc()函数。

在构造函数内部创建方法的另一个副作用是性能较差。每次构造函数运行时都必须创建每个方法。原型链上的方法只创建一次,然后由每个实例“继承”。另一方面,公共方法可以访问“私有”变量,这在继承方法中是不可能的。

至于你的函数Class() {} vs var Class = function(){}问题,前者在执行前被“提升”到当前作用域的顶部。对于后者,提升的是变量声明,而不是赋值。例如:

// Error, fn is called before the function is assigned!
fn();
var fn = function () { alert("test!"); } 

// Works as expected: the fn2 declaration is hoisted above the call
fn2();
function fn2() { alert("test!"); }

其他回答

通过原型链继承的方法可以对所有实例进行通用更改,例如:

function Class () {}
Class.prototype.calc = function (a, b) {
    return a + b;
}

// Create 2 instances:
var ins1 = new Class(),
    ins2 = new Class();

// Test the calc method:
console.log(ins1.calc(1,1), ins2.calc(1,1));
// -> 2, 2

// Change the prototype method
Class.prototype.calc = function () {
    var args = Array.prototype.slice.apply(arguments),
        res = 0, c;

    while (c = args.shift())
        res += c;

    return res; 
}

// Test the calc method:
console.log(ins1.calc(1,1,1), ins2.calc(1,1,1));
// -> 3, 3

注意如何更改应用于两个实例的方法?这是因为ins1和ins2共享同一个calc()函数。为了使用构造过程中创建的公共方法来实现这一点,您必须将新方法分配给已创建的每个实例,这是一项棘手的任务。这是因为ins1和ins2有它们自己单独创建的calc()函数。

在构造函数内部创建方法的另一个副作用是性能较差。每次构造函数运行时都必须创建每个方法。原型链上的方法只创建一次,然后由每个实例“继承”。另一方面,公共方法可以访问“私有”变量,这在继承方法中是不可能的。

至于你的函数Class() {} vs var Class = function(){}问题,前者在执行前被“提升”到当前作用域的顶部。对于后者,提升的是变量声明,而不是赋值。例如:

// Error, fn is called before the function is assigned!
fn();
var fn = function () { alert("test!"); } 

// Works as expected: the fn2 declaration is hoisted above the call
fn2();
function fn2() { alert("test!"); }

The advantage of the prototype approach is efficiency. There is one calc() function object shared between all Class objects (by which I mean objects created by calling the Class constructor). The other way (assigning methods within the constructor) creates a new function object for every Class object, using more memory and taking more processing time when calling the Class constructor. However, this approach does have an advantage: the calc() method has access to local variables within the constructor, which you can use to your advantage:

function Class() {
    var calcCallCount = 0;

    this.calc = function (a, b) {
        ++calcCallCount;
        alert("Calc called " + calcCallCount + " times");
        return a + b;
    };
};

关于var Class = function(){…}与函数类(){…},我通常更喜欢后者,因为它意味着函数有一个名称,这在调试时很有用。另一个区别是后一个版本(函数声明)是提升的,这意味着它在定义它的作用域中的任何地方都可用,而不仅仅是在定义之后。然而,有些人更喜欢在任何地方使用前者(函数表达式)。

var YourClass = function(){
  var privateField = "somevalue";
  this.publicField = "somevalue";
  this.instanceMethod1 = function(){
     //you may access both private and public field from here:
     //in order to access public field, you must use "this":
     alert(privateField + "; " + this.publicField);
  };
}

YourClass.prototype.instanceMethod2 = function(){
  //you may access only public field 2 from this method, but not private fields:
  alert(this.publicField);
  //error: drawaback of prototype methods:
  alert(privateField);  
};

原型法的优点:

When you define methods via prototype, they are shared among all YourClass instances. As a result the total size of such instances is < than if you define methods in constructor; There are tests that show how method definition via prototype decrease the total size of html page and as a result a speed of its loading. another advantage of methods, defined via prototype - is when you use inherited classes, you may override such methods and in the overriden method of the derived class you may invoke the method of base class with the same name, but with methods defined in constructor, you cannot do this.