两者之间有什么区别

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

and

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

当前回答

使用this而不是prototype的最终问题是,当重写方法时,基类的构造函数仍将引用重写的方法。考虑一下:

BaseClass = function() {
    var text = null;

    this.setText = function(value) {
        text = value + " BaseClass!";
    };

    this.getText = function() {
        return text;
    };

    this.setText("Hello"); // This always calls BaseClass.setText()
};

SubClass = function() {
    // setText is not overridden yet,
    // so the constructor calls the superclass' method
    BaseClass.call(this);

    // Keeping a reference to the superclass' method
    var super_setText = this.setText;
    // Overriding
    this.setText = function(value) {
        super_setText.call(this, "SubClass says: " + value);
    };
};
SubClass.prototype = new BaseClass();

var subClass = new SubClass();
console.log(subClass.getText()); // Hello BaseClass!

subClass.setText("Hello"); // setText is already overridden
console.log(subClass.getText()); // SubClass says: Hello BaseClass!

对比:

BaseClass = function() {
    this.setText("Hello"); // This calls the overridden method
};

BaseClass.prototype.setText = function(value) {
    this.text = value + " BaseClass!";
};

BaseClass.prototype.getText = function() {
    return this.text;
};

SubClass = function() {
    // setText is already overridden, so this works as expected
    BaseClass.call(this);
};
SubClass.prototype = new BaseClass();

SubClass.prototype.setText = function(value) {
    BaseClass.prototype.setText.call(this, "SubClass says: " + value);
};

var subClass = new SubClass();
console.log(subClass.getText()); // SubClass says: Hello BaseClass!

如果你认为这不是一个问题,那么这取决于你是否能在没有私人变量的情况下生活,以及你是否有足够的经验在看到泄漏时知道泄漏。此外,必须将构造函数逻辑放在方法定义之后是不方便的。

var A = function (param1) {
    var privateVar = null; // Private variable

    // Calling this.setPrivateVar(param1) here would be an error

    this.setPrivateVar = function (value) {
        privateVar = value;
        console.log("setPrivateVar value set to: " + value);

        // param1 is still here, possible memory leak
        console.log("setPrivateVar has param1: " + param1);
    };

    // The constructor logic starts here possibly after
    // many lines of code that define methods

    this.setPrivateVar(param1); // This is valid
};

var a = new A(0);
// setPrivateVar value set to: 0
// setPrivateVar has param1: 0

a.setPrivateVar(1);
//setPrivateVar value set to: 1
//setPrivateVar has param1: 0

对比:

var A = function (param1) {
    this.setPublicVar(param1); // This is valid
};
A.prototype.setPublicVar = function (value) {
    this.publicVar = value; // No private variable
};

var a = new A(0);
a.setPublicVar(1);
console.log(a.publicVar); // 1

其他回答

每个对象都链接到原型对象。当试图访问不存在的属性时,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

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

第一个示例仅更改该对象的接口。第二个示例更改该类的所有对象的接口。

这些例子有非常不同的结果。

在研究差异之前,应注意以下几点:

构造函数的原型提供了一种通过实例的私有[[prototype]]属性在实例之间共享方法和值的方法。函数的this是通过函数的调用方式或使用bind(此处未讨论)来设置的。如果在对象上调用函数(例如myObj.method()),则方法中的函数将引用该对象。如果这不是通过调用或使用bind设置的,则默认为全局对象(浏览器中的窗口)或严格模式,保持未定义。JavaScript是一种面向对象的语言,即大多数值都是对象,包括函数。(字符串、数字和布尔值不是对象。)

以下是有问题的片段:

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

在这种情况下,变量A被分配了一个引用函数的值。当使用A()调用该函数时,函数的this不会被调用设置,因此它默认为全局对象,表达式this.x是有效的window.x。结果是,对右侧函数表达式的引用被分配给window.x。

在以下情况下:

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

发生了非常不同的事情。在第一行中,变量A被分配给一个函数的引用。在JavaScript中,默认情况下,所有函数对象都有一个prototype属性,因此没有单独的代码来创建a.prototype对象。

在第二行中,为A.prototype.x分配了对函数的引用。如果x属性不存在,这将创建一个x属性,如果存在,则分配一个新值。因此,与第一个示例的不同之处在于,表达式中包含了对象的x属性。

另一个例子如下。这与第一个类似(也许你想问的是什么):

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

在本例中,在函数表达式之前添加了新运算符,以便将函数作为构造函数调用。当使用new调用时,函数的this被设置为引用一个新Object,该Object的私有[[Prototype]]属性被设置为参考构造函数的公共原型。因此,在赋值语句中,将在这个新对象上创建x属性。当作为构造函数调用时,函数默认返回其this对象,因此不需要单独返回this;陈述

要检查A是否具有x属性:

console.log(A.x) // function () {
                 //   //do something
                 // };

这是一种不常见的new用法,因为引用构造函数的唯一方法是通过A.constructor

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

实现类似结果的另一种方法是使用立即调用的函数表达式:

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

在本例中,A指定了调用右侧函数的返回值。在这里,由于在调用中未设置此项,因此它将引用全局对象,而此.x是有效的window.x。由于函数不返回任何内容,因此A的值将为undefined。

如果要将Javascript对象序列化为JSON或从JSON反序列化,这两种方法之间的差异也会很明显。序列化对象时,对象原型上定义的方法不会被序列化,例如,如果您只想序列化对象的数据部分,而不想序列化其方法,这会很方便:

var A = function () { 
    this.objectsOwnProperties = "are serialized";
};
A.prototype.prototypeProperties = "are NOT serialized";
var instance = new A();
console.log(instance.prototypeProperties); // "are NOT serialized"
console.log(JSON.stringify(instance)); 
// {"objectsOwnProperties":"are serialized"} 

相关问题:

JavaScript是一种原型语言,这意味着什么?JavaScript中函数的作用域是什么?“this”关键字是如何工作的?

Sidenote:这两种方法之间可能没有任何显著的内存节省,但是使用原型共享方法和财产可能比每个拥有自己副本的实例使用更少的内存。

JavaScript不是低级语言。将原型设计或其他继承模式视为显式改变内存分配方式的一种方式可能不是很有价值。

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

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

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

有什么区别?=>很多。

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

让我们看一下以下示例:

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 ***

希望这有帮助。