在JavaScript中使用“原型”与“this”?

2022-08-29 22:26:24

两者之间有什么区别

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

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

答案 1

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

在查看差异之前,应注意以下事项:

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

所以这里有一些有问题的片段:

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

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

在以下情况下:

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

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

在第二行中,A.prototype.x 被分配了对函数的引用。如果 x 属性不存在,这将创建该属性,如果存在,则为其赋值。因此,与表达式中涉及对象的x属性的第一个示例的差异。

另一个例子如下。它类似于第一个(也许是你想问的):

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

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

要检查 A 是否具有 x 属性,请执行以下操作:

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

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

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

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

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

在本例中,分配了调用右侧函数的返回值。在这里,由于这不是在调用中设置的,因此它将引用全局对象并且是有效的 。由于该函数不返回任何内容,因此的值将为 。Athis.xwindow.xAundefined

这两种方法之间的这些差异也体现在你对JSON进行序列化和反序列化Javascript对象时。在对象的原型上定义的方法在序列化对象时不会序列化,例如,当您只想序列化对象的数据部分而不是它的方法时,这可能很方便:

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不是一种低级语言。将原型设计或其他继承模式视为显式更改内存分配方式的一种方式可能不是很有价值。


答案 2

正如其他人在第一个版本中所说的那样,使用“this”会导致类A的每个实例都有自己独立的函数方法“x”副本。而使用“prototype”将意味着类A的每个实例将使用相同的方法“x”的副本。

下面是一些代码来显示这种细微的差异:

// x is a method assigned to the object using "this"
var A = function () {
    this.x = function () { alert('A'); };
};
A.prototype.updateX = function( value ) {
    this.x = function() { alert( value ); }
};

var a1 = new A();
var a2 = new A();
a1.x();  // Displays 'A'
a2.x();  // Also displays 'A'
a1.updateX('Z');
a1.x();  // Displays 'Z'
a2.x();  // Still displays 'A'

// Here x is a method assigned to the object using "prototype"
var B = function () { };
B.prototype.x = function () { alert('B'); };

B.prototype.updateX = function( value ) {
    B.prototype.x = function() { alert( value ); }
}

var b1 = new B();
var b2 = new B();
b1.x();  // Displays 'B'
b2.x();  // Also displays 'B'
b1.updateX('Y');
b1.x();  // Displays 'Y'
b2.x();  // Also displays 'Y' because by using prototype we have changed it for all instances

正如其他人所提到的,选择一种方法或另一种方法有各种原因。我的样本只是为了清楚地展示差异。