关于oop:JavaScript:Class.method vs. Class.prototype.method

JavaScript: Class.method vs. Class.prototype.method

以下两个声明有什么区别?

1
2
Class.method = function () { /* code */ }
Class.prototype.method = function () { /* code using this.values */ }

可以将第一个语句看作静态方法的声明,将第二个语句看作实例方法的声明吗?


是的,第一个函数与该构造函数函数的对象实例没有关系,您可以将其视为"静态方法"。

在javascript中,函数是第一类对象,这意味着您可以像对待任何对象一样对待它们,在本例中,您只是向函数对象添加一个属性。

第二个函数在扩展构造函数函数原型时,它将可用于使用new关键字创建的所有对象实例,并且该函数中的上下文(this关键字)将引用调用它的实际对象实例。

考虑这个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// constructor function
function MyClass () {
  var privateVariable; // private member only available within the constructor fn

  this.privilegedMethod = function () { // it can access private members
    //..
  };
}

// A 'static method', it's just like a normal function
// it has no relation with any 'MyClass' object instance
MyClass.staticMethod = function () {};

MyClass.prototype.publicMethod = function () {
  // the 'this' keyword refers to the object instance
  // you can access only 'privileged' and 'public' members
};

var myObj = new MyClass(); // new object instance

myObj.publicMethod();
MyClass.staticMethod();


当您创建MyClass的多个实例时,内存中仍然只有一个PublicMethod实例,但如果是PrivilegedMethod,您将最终创建大量实例,StaticMethod与对象实例没有任何关系。

这就是原型节省内存的原因。

此外,如果更改父对象的属性,则子对象的相应属性是否尚未更改,它将被更新。


对于视觉学习者,在定义没有.prototype的函数时

1
2
3
4
5
6
7
8
9
10
ExampleClass = function(){};
ExampleClass.method = function(customString){
             console.log((customString !== undefined)?
                          customString :
                         "called from func def.");}
ExampleClass.method(); // >> output: `called from func def.`  

var someInstance = new ExampleClass();
someInstance.method('Called from instance');
    // >> error! `someInstance.method is not a function`

使用相同的代码,如果添加了.prototype

1
2
3
4
5
6
7
8
9
10
ExampleClass.prototype.method = function(customString){
             console.log((customString !== undefined)?
                          customString :
                         "called from func def.");}
ExampleClass.method();  
      // > error! `ExampleClass.method is not a function.`  

var someInstance = new ExampleClass();
someInstance.method('Called from instance');
                 // > output: `Called from instance`

为了让它更清楚,

1
2
3
4
5
6
7
8
9
10
11
ExampleClass = function(){};
ExampleClass.directM = function(){}  //M for method
ExampleClass.prototype.protoM = function(){}

var instanceOfExample = new ExampleClass();

ExampleClass.directM();     ? works
instanceOfExample.directM();   x Error!

ExampleClass.protoM();     x Error!
instanceOfExample.protoM();  ? works

****注意对于上面的示例,someInstance.method()不会作为执行,exampleClass.method()导致错误&无法继续执行。但是为了便于理解和说明,我保留了这个顺序。

chrome developer consoleJS Bin产生的结果单击上面的jsbin链接以逐步完成代码。用ctrl+/切换注释部分


是的,第一个是static method,也叫class method,第二个是instance method

考虑下面的例子,以便更详细地理解它。

在ES5中

1
2
3
4
5
6
7
8
9
10
11
12
function Person(firstName, lastName) {
   this.firstName = firstName;
   this.lastName = lastName;
}

Person.isPerson = function(obj) {
   return obj.constructor === Person;
}

Person.prototype.sayHi = function() {
   return"Hi" + this.firstName;
}

在上述代码中,isPerson是静态方法,而sayHiPerson的实例方法。

下面是如何从Person构造函数创建对象。

var aminu = new Person("Aminu","Abubakar");

采用静态方法isPerson

Person.isPerson(aminu); // will return true

使用实例方法sayHi

aminu.sayHi(); // will return"Hi Aminu"

在ES6中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Person {
   constructor(firstName, lastName) {
      this.firstName = firstName;
      this.lastName = lastName;
   }

   static isPerson(obj) {
      return obj.constructor === Person;
   }

   sayHi() {
      return `Hi ${this.firstName}`;
   }
}

看看如何使用static关键字声明静态方法isPerson

创建Person类的对象。

const aminu = new Person("Aminu","Abubakar");

采用静态方法isPerson

Person.isPerson(aminu); // will return true

使用实例方法sayHi

aminu.sayHi(); // will return"Hi Aminu"

注意:两个例子本质上是相同的,javascript仍然是一种无类语言。ES6中引入的class主要是对现有的基于原型的继承模型的一种语法上的优势。