两者之间有什么区别

var A = function () {
    this.x = function () {
        //do something
    };
};

and

var A = function () { };
A.prototype.x = function () {
    //do something
};

当前回答

让我给你一个我在JavaScript培训课程中学到的更全面的答案。

大多数答案已经提到了这一点,即当原型化时,功能与所有(未来)实例共享。而在类中声明函数将为每个实例创建一个副本。

总的来说,没有对错之分,更多的是品味或设计决定,取决于您的需求。然而,原型是用于以面向对象的方式进行开发的技术,我希望您会在答案的结尾看到。

你的问题显示了两种模式。我将尝试再解释两个,并尝试解释相关的差异。随意编辑/扩展。在所有示例中,它都是关于一个具有位置并可以移动的汽车对象。

对象装饰器图案

不确定这种模式现在是否仍然适用,但它确实存在。知道这一点很好。您只需将对象和属性传递给decorator函数。装饰器返回带有属性和方法的对象。

var carlike = function(obj, loc) {
    obj.loc = loc;
    obj.move = function() {
        obj.loc++;
    };
    return obj;
};

var amy = carlike({}, 1);
amy.move();
var ben = carlike({}, 9);
ben.move();

功能类别

JavaScript中的函数是一个专门的对象。除了被调用外,函数还可以像其他任何对象一样存储财产。

在本例中,Car是一个函数(也称为对象),可以按照您的习惯进行调用。它有一个属性方法(这是一个带有移动函数的对象)。当调用Car时,会调用extend函数,这会产生一些魔力,并使用方法中定义的方法扩展Car函数(think对象)。

这个例子虽然不同,但最接近问题中的第一个例子。

var Car = function(loc) {
    var obj = {loc: loc};
    extend(obj, Car.methods);
    return obj;
};

Car.methods = {
    move : function() {
        this.loc++;
    }
};

var amy = Car(1);
amy.move();
var ben = Car(9);
ben.move();

原型类

前两种模式允许讨论使用技术来定义共享方法或使用在构造函数主体中内联定义的方法。在这两种情况下,每个实例都有自己的移动功能。

原型模式不适合同样的检查,因为通过原型委托共享功能是原型模式的目标。正如其他人所指出的,预计它的内存占用量会更好。

然而,有一点值得注意:每个原型对象都有一个方便的属性构造函数,它指向它所附加的函数(think对象)。

关于最后三行:

在本例中,Car链接到原型对象,该对象通过构造函数链接到Car本身,即Car.prototype.constructor是Car本身。这允许您确定哪个构造函数构建了某个对象。

amy.constructor的查找失败,因此被委托给Car.prototype,它确实具有构造函数属性。所以我的结构是Car。

此外,amy是Car的一个例子。instanceof运算符的工作方式是查看右操作数的原型对象(Car)是否可以在左操作数的模型(amy)链中的任何位置找到。

var Car = function(loc) {
    var obj = Object.create(Car.prototype);
    obj.loc = loc;
    return obj;
};

Car.prototype.move = function() {
        this.loc++;
};

var amy = Car(1);
amy.move();
var ben = Car(9);
ben.move();

console.log(Car.prototype.constructor);
console.log(amy.constructor);
console.log(amy instanceof Car);

一些开发人员一开始可能会感到困惑。参见以下示例:

var Dog = function() {
  return {legs: 4, bark: alert};
};

var fido = Dog();
console.log(fido instanceof Dog);

instanceof运算符返回false,因为在fido的原型链中的任何位置都找不到Dog的原型。fido是一个用对象文字创建的简单对象,即它只委托给object.prototype。

伪经典模式

这实际上只是简化形式的原型模式的另一种形式,对于用Java编程的人来说更为熟悉,因为它使用了新的构造函数。

它确实和原型模式一样,它只是原型模式的句法糖份。

然而,主要区别在于JavaScript引擎中实现的优化仅在使用伪经典模式时适用。把伪经典模式想象成原型模式的更快版本;两个示例中的对象关系相同。

var Car = function(loc) {
    this.loc = loc;
};

Car.prototype.move = function() {
        this.loc++;
};

var amy = new Car(1);
amy.move();
var ben = new Car(9);
ben.move();

最后,实现面向对象的编程应该不会太难。有两个部分。

定义原型(链)中常见财产/方法的一个部分。

在另一节中,您放置了区分对象的定义(示例中的loc变量)。

这就是允许我们在JavaScript中应用超类或子类等概念的原因。

可以随意添加或编辑。一旦完成,我可能会把它变成一个社区维基。

其他回答

正如其他答案中所讨论的,这确实是一个性能考虑因素,因为原型中的函数与所有实例化共享,而不是为每个实例化创建的函数。

我制作了一个jsperf来展示这一点。实例化类所需的时间有着巨大的差异,尽管只有在创建许多实例时,它才真正相关。

http://jsperf.com/functions-in-constructor-vs-prototype

原型是类的模板;这适用于它的所有未来实例。而这是对象的特定实例。

每个对象都链接到原型对象。当试图访问不存在的属性时,JavaScript将在对象的原型对象中查找该属性,如果该属性存在,则返回该属性。

函数构造函数的prototype属性引用使用new时使用该函数创建的所有实例的原型对象。


在第一个示例中,您将向使用a函数创建的每个实例添加属性x。

var A = function () {
    this.x = function () {
        //do something
    };
};

var a = new A();    // constructor function gets executed
                    // newly created object gets an 'x' property
                    // which is a function
a.x();              // and can be called like this

在第二个示例中,您将向原型对象添加一个属性,所有使用a创建的实例都指向该属性。

var A = function () { };
A.prototype.x = function () {
    //do something
};

var a = new A();    // constructor function gets executed
                    // which does nothing in this example

a.x();              // you are trying to access the 'x' property of an instance of 'A'
                    // which does not exist
                    // so JavaScript looks for that property in the prototype object
                    // that was defined using the 'prototype' property of the constructor

总之,在第一个示例中,函数的副本被分配给每个实例。在第二个示例中,所有实例共享函数的单个副本。

想想静态类型的语言,原型上的东西是静态的,而这上面的东西是与实例相关的。

有什么区别?=>很多。

我认为,这个版本用于实现封装,即数据隐藏。它有助于处理私有变量。

让我们看一下以下示例:

var AdultPerson = function() {

  var age;

  this.setAge = function(val) {
    // some housekeeping
    age = val >= 18 && val;
  };

  this.getAge = function() {
    return age;
  };

  this.isValid = function() {
    return !!age;
  };
};

现在,原型结构可以应用如下:

不同的成年人有不同的年龄,但所有成年人都享有相同的权利。所以,我们使用原型而不是这个添加它。

AdultPerson.prototype.getRights = function() {
  // Should be valid
  return this.isValid() && ['Booze', 'Drive'];
};

现在让我们看看实现。

var p1 = new AdultPerson;
p1.setAge(12); // ( age = false )
console.log(p1.getRights()); // false ( Kid alert! )
p1.setAge(19); // ( age = 19 )
console.log(p1.getRights()); // ['Booze', 'Drive'] ( Welcome AdultPerson )

var p2 = new AdultPerson;
p2.setAge(45);    
console.log(p2.getRights()); // The same getRights() method, *** not a new copy of it ***

希望这有帮助。