Javascript 使用原型相对于直接在构造函数中定义方法的优势?

Javascript 使用原型相对于直接在构造函数中定义方法的优势?,javascript,oop,prototype,Javascript,Oop,Prototype,我想知道使用其中任何一种方法是否比使用另一种方法有任何优势,我应该走哪条路 建造商方法: 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 =

我想知道使用其中任何一种方法是否比使用另一种方法有任何优势,我应该走哪条路

建造商方法:

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 () {

    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 () {}
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
// 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!"); }
请注意如何更改应用于这两个实例的方法?这是因为
ins1
ins2
共享相同的
calc()
函数。为了在构造过程中创建公共方法,必须将新方法分配给已创建的每个实例,这是一项棘手的任务。这是因为
ins1
ins2
将拥有各自单独创建的
calc()
函数

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

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

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
// 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!"); }

原型方法的优点是效率。所有
Class
对象(我指的是通过调用
Class
构造函数创建的对象)之间共享一个
calc()
函数对象。另一种方法(在构造函数中分配方法)为每个
对象创建一个新的函数对象,在调用
构造函数时使用更多内存并花费更多的处理时间。但是,这种方法确实有一个优点:
calc()
方法可以访问构造函数中的局部变量,您可以利用它:

function Class() {
    var calcCallCount = 0;

    this.calc = function (a, b) {
        ++calcCallCount;
        alert("Calc called " + calcCallCount + " times");
        return a + b;
    };
};
关于
var Class=function(){…}
function Class(){…}
的比较,我通常更喜欢后者,因为这意味着函数有一个名称,在调试时可能很有用。另一个区别是后一个版本(函数声明)被提升,这意味着它在定义它的范围内的任何地方都可用,而不仅仅是在定义之后。但是,我们更愿意在任何地方使用前者(函数表达式)

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);  
};
原型方法的优点:

  • 当您通过prototype定义方法时,它们将在所有类实例中共享。因此,如果在构造函数中定义方法,则此类实例的总大小小于;有一些测试表明,通过原型的方法定义如何减少html页面的总大小,从而提高加载速度

  • 通过prototype定义的方法的另一个优点是,当您使用继承的类时,您可以重写这些方法,并且在派生类的重写方法中,您可以使用相同的名称调用基类的方法,但是对于构造函数中定义的方法,您不能这样做


  • 啊,这让事情变得更清楚了:)我没有意识到效率的不同——知道这一点非常有用。提升效果也是如此——确实很棘手。谢谢你这么好的回答,我从中学到了很多!非常古老的问题,但不知何故,它遵循了一个链接并在这里绊倒了——我认为如果你保持论点的数量一致,这个例子会更有说服力(只是为了证明它使用的是
    a+b
    。这确实是一个小问题,但它可以帮助读者识别您所关注的差异,并排除他可能正在阅读的其他因素(例如:如果您有第三个参数,第一次调用会发生什么情况)。这个例子很简单,希望程序员足够优秀,不会被小的差异所困扰。当然,使用函数模式也可以达到同样的效果,从而避免模仿经典语言的继承模式。这部分代码在做什么:
    Class.prototype.calc=function(){var args=Array.prototype.slice.apply(arguments),res=0,c;
    由于一段时间过去了,我们现在有了ES6+,我想指出,arrow函数定义是
    var Class=function(){…}的缩写
    ,因此也没有被提升。谢谢你的回答,我很感激,现在我更加意识到资源堆栈溢出是多么伟大。您好,您所说的继承类是什么意思?我认为这不是正确的术语,因为javascript没有类的概念。当您说派生类的重写方法时,您是指另一个对象,谁的原型是你的对象?我迷路了。你能编辑或解释一下吗?Alexandr你能用一个例子解释一下#2吗?蒂姆,也谢谢你的回答!Re
    Class=function(){…}
    ,即在全局/窗口范围内定义,就名称而言,我对这种方法没有任何调试问题,尽管可以理解,这种方法似乎没有出现任何问题。不确定这种方法与您的两种方法之间是否有任何其他区别。@NickWiggill:自从我写这篇answe之后,内置浏览器开发工具已经取得了长足的进步r,他们现在在从上下文推断合适的函数名方面做得更好了,所以我同意,现在调试的容易程度已经不再是一个问题。进行了js Perf测试以可视化性能差异。@DaveVoyles: