JavaScript中的类与静态方法


262

我知道这会起作用:

function Foo() {};
Foo.prototype.talk = function () {
    alert('hello~\n');
};

var a = new Foo;
a.talk(); // 'hello~\n'

但是如果我想打电话

Foo.talk() // this will not work
Foo.prototype.talk() // this works correctly

我找到一些Foo.talk工作方法,

  1. Foo.__proto__ = Foo.prototype
  2. Foo.talk = Foo.prototype.talk

还有其他方法吗?我不知道这样做是否正确。您在JavaScript代码中使用类方法还是静态方法?


14
Foo.talk = function ...
过早的优化,

1
@downvoterstepintothelight Foo.walk = function() {}由于它不在原型链上,因此不会影响其实例。是否有跨浏览器的方法来[[prototype]]指出函数的指向prototype
Lostyzd 2011年

3
可能我不知道您想要什么,因为类方法不会通过定义影响实例
过早的优化,

@downvoterstepintothelight我怀疑method,在像python这样的语言中,实例是否可以调用其class方法,区别是this指针。
lostyzd

Answers:


410

首先,请记住JavaScript主要是一种原型语言,而不是基于类的语言1Foo不是一个类,而是一个函数,这是一个对象。您可以使用关键字该函数实例化一个对象,该new关键字将允许您使用标准OOP语言创建类似于类的内容。

我建议__proto__大部分时间都忽略它,因为它对跨浏览器的支持不佳,而应该专注于了解其prototype工作原理。

如果您有通过函数创建的对象的实例 2并且以任何方式访问其成员之一(方法,属性,属性,常量等),则访问将沿原型层次结构进行,直到(a)找到成员,或(b)找不到其他原型。

层次结构从被调用的对象开始,然后搜索其原型对象。如果原型对象具有原型,则重复该操作,如果不存在原型,undefined则返回该对象。

例如:

foo = {bar: 'baz'};
console.log(foo.bar); // logs "baz"

foo = {};
console.log(foo.bar); // logs undefined

function Foo(){}
Foo.prototype = {bar: 'baz'};
f = new Foo();
console.log(f.bar);
// logs "baz" because the object f doesn't have an attribute "bar"
// so it checks the prototype
f.bar = 'buzz';
console.log( f.bar ); // logs "buzz" because f has an attribute "bar" set

在我看来,您已经至少已经对这些“基本”部分有所了解,但是为了确保它们,我需要使其明确。

在JavaScript中,一切都是对象3

一切都是对象。

function Foo(){} 不仅定义了一个新函数,它还定义了一个新的函数对象,可以使用 Foo

这就是为什么您可以使用来访问Foo的原型的原因Foo.prototype

你也可以做的是设置更多的功能Foo

Foo.talk = function () {
  alert('hello world!');
};

可以使用以下方式访问此新功能:

Foo.talk();

我希望到目前为止,您已经注意到函数对象上的函数与静态方法之间的相似性。

可以将其f = new Foo();视为创建类实例,Foo.prototype.bar = function(){...}为该类定义共享方法以及为该类Foo.baz = function(){...}定义公共静态方法。


ECMAScript 2015为此类声明引入了多种语法糖,以使其更易于实现,同时也更易于阅读。因此,前面的示例可以写成:

class Foo {
  bar() {...}

  static baz() {...}
}

它可以bar被称为:

const f = new Foo()
f.bar()

并被baz称为:

Foo.baz()

1:class是ECMAScript 5规范中的“未来保留字”,但是ES6引入了使用class关键字定义类的功能。

2:本质上是由构造函数创建的类实例,但是有很多细微的差别我不想误导你

3:原始值(包括undefined,,null布尔值,数字和字符串)在技术上不是对象,因为它们是低级语言实现。布尔值,数字和字符串仍然像它们是对象一样与原型链交互,因此,出于此答案的目的,即使它们并非完全相同,也更容易将它们视为“对象”。


1
@lostyzd-好,他们可以通过访问它Foo.talk()。如果需要,可以在构造函数中分配它:this.talk = Foo.talk-或如您所注意到的,通过分配Foo.prototype.talk = Foo.talk。但是我不确定这是一个好主意-原则上,实例方法应该特定于实例。
nrabinowitz 2011年

2
@Doug Avery,Foo.talk()只是在调用一个命名空间函数。您将在类似于Java / C#之类的OOP语言中调用静态方法的情况下使用它。用例的一个很好的例子是类似的函数Array.isArray()
zzzzBov 2011年

7
PS null是对象的typeof空== '对象'
mvladk

1
您所缺少的基本要点是静态方法被继承。Foo.talk = function ()...子类无法使用自己的类名。可以通过“扩展”子类来解决此问题,但我仍在寻找更优雅的方法。

1
@nus,只有某些语言允许继承静态方法。如果需要继承,则不应使用静态方法。
zzzzBov

67

您可以如下实现:

function Foo() {};

Foo.talk = function() { alert('I am talking.'); };

您现在可以按以下方式调用“通话”功能:

Foo.talk();

您可以这样做,因为在JavaScript中,函数也是对象。


37

从实例调用静态方法:

function Clazz() {};
Clazz.staticMethod = function() {
    alert('STATIC!!!');
};

Clazz.prototype.func = function() {
    this.constructor.staticMethod();
}

var obj = new Clazz();
obj.func(); // <- Alert's "STATIC!!!"

简单的Javascript类项目:https//github.com/reduardo7/sjsClass


13
这不是静态调用。var obj = new Clazz(); 创建Clazz 的新实例。但是,Clazz.staticMethod()可以在没有其他所有内容的情况下获得结果。
mpemburn,2014年

5
@mpemburn:Eduardo的回答也是正确的。他向您展示的不仅是您可以通过“外部”调用静态方法,Clazz.staticMethod而且还向您展示了如何从实例化对象内链接到这些静态方法。这在像Node.js这样的环境中特别有用,在使用enquire的环境中,您可能无法直接访问原始构造函数。我唯一要添加的是this.constructor.staticMethod.apply(this, arguments);
Mauvis Ledford

1
绝对constructor: (a) -> @constructor.add @出色,甚至可以在咖啡脚本构造函数中使用:(几乎可以,无论如何)
Orwellophile,2016年

31

这是一个很好的示例,演示了Javascript如何与静态/实例变量和方法一起使用。

function Animal(name) {
    Animal.count = Animal.count+1||1;// static variables, use function name "Animal"
    this.name = name; //instance variable, using "this"
}

Animal.showCount = function () {//static method
    alert(Animal.count)
}

Animal.prototype.showName=function(){//instance method
    alert(this.name);
}

var mouse = new Animal("Mickey");
var elephant = new Animal("Haddoop");

Animal.showCount();  // static method, count=2
mouse.showName();//instance method, alert "Mickey"
mouse.showCount();//Error!! mouse.showCount is not a function, which is different from  Java

好点:不能通过访问静态函数可能很奇怪this
TrapII

感谢您的解决方案,这就是我一直在寻找在哪种情况下可以使用this关键字的原因
santhosh

30

此外,现在可以使用classstatic

'use strict'

class Foo {
 static talk() {
     console.log('talk')
 };

 speak() {
     console.log('speak')
 };

};

会给

var a = new Foo();
Foo.talk();  // 'talk'
a.talk();    // err 'is not a function'
a.speak();   // 'speak'
Foo.speak(); // err 'is not a function'

这是最好的答案,因为示例值一千字。但是,它没有解释为什么a.talk()不起作用。公认的答案说,原型链应该找到它,对吗?但事实并非如此
Pynchia

11

我使用名称空间:

var Foo = {
     element: document.getElementById("id-here"),

     Talk: function(message) {
            alert("talking..." + message);
     },

     ChangeElement: function() {
            this.element.style.color = "red";
     }
};

并使用它:

Foo.Talk("Testing");

要么

Foo.ChangeElement();

6

ES6现在支持classstatic关键字,如超级魅力:

class Foo {
    constructor() {}

    talk() {
        console.log("i am not static");
    }

    static saying() {
        console.log(this.speech);
    }

    static get speech() {
        return "i am static method";
    }

}

我一直在寻找这样的答案。静态方法可以调用非静态方法/变量吗?
Tomasz Mularczyk

1
@Tomasz静态方法不会将“ this”设置为类的任何实例,而是类本身。因此,当然,静态方法可以调用实例方法,但前提是它必须以某种方式访问​​实例,例如“静态staticMethod(){new Foo()。talk(); }“
JHH

3

如果必须在ES5中编写静态方法,我为此找到了一个不错的教程:

//Constructor
var Person = function (name, age){
//private properties
var priv = {};

//Public properties
this.name = name;
this.age = age;

//Public methods
this.sayHi = function(){
    alert('hello');
}
}


// A static method; this method only 
// exists on the class and doesn't exist  
// on child objects
Person.sayName = function() {
   alert("I am a Person object ;)");  
};

参见@ https://abdulapopoola.com/2013/03/30/static-and-instance-methods-in-javascript/


2

只是附加说明。使用类ES6,当我们创建静态方法时。.Javacsript引擎将描述符属性设置为与老式“静态”方法不同的一点点

function Car() {

}

Car.brand = function() {
  console.log('Honda');
}

console.log(
  Object.getOwnPropertyDescriptors(Car)
);

它将brand()的内部属性(描述符属性)设置为

..
brand: [object Object] {
    configurable: true,
    enumerable: true,
    value: ..
    writable: true

}
..

相比

class Car2 {
   static brand() {
     console.log('Honda');
   }
}

console.log(
  Object.getOwnPropertyDescriptors(Car2)
);

将brand()的内部属性设置为

..
brand: [object Object] {
    configurable: true,
    enumerable: false,
    value:..
    writable: true
  }

..

看到ES6中的静态方法将enumerable设置为false

这意味着您不能使用for-in循环来检查对象

for (let prop in Car) {
  console.log(prop); // brand
}

for (let prop in Car2) {
  console.log(prop); // nothing here
}

ES6中的静态方法与其他人的类私有属性(名称,长度,构造函数)一样,只是静态方法仍然可写,因此描述符writable设置为true { writable: true }。这也意味着我们可以覆盖它

Car2.brand = function() {
   console.log('Toyota');
};

console.log(
  Car2.brand() // is now changed to toyota
);


1

静态方法调用直接在类上进行,而不能在类的实例上进行调用。静态方法通常用于创建效用函数

很清楚的描述

直接取自mozilla.org

Foo需要绑定到您的类,然后在创建新实例时可以调用myNewInstance.foo()如果导入类,则可以调用静态方法


0

当我面对这种情况时,我做了这样的事情:

Logger = {
    info: function (message, tag) {
        var fullMessage = '';        
        fullMessage = this._getFormatedMessage(message, tag);
        if (loggerEnabled) {
            console.log(fullMessage);
        }
    },
    warning: function (message, tag) {
        var fullMessage = '';
        fullMessage = this._getFormatedMessage(message, tag);
        if (loggerEnabled) {
            console.warn(fullMessage);`enter code here`
        }
    },
    _getFormatedMessage: function () {}
};

所以现在我可以将info方法称为 Logger.info("my Msg", "Tag");


我一直在这样做,但基本上只是命名空间。它不允许您使用实例变量创建实例吗?
dcsan

0

就您而言,如果您想Foo.talk()

function Foo() {};
// But use Foo.talk would be inefficient
Foo.talk = function () {
    alert('hello~\n');
};

Foo.talk(); // 'hello~\n'

但这是一种低效的实现方式,使用prototype效果更好。


另一种方式,我的方式被定义为静态类:

var Foo = new function() {
  this.talk = function () {
    alert('hello~\n');
    };
};

Foo.talk(); // 'hello~\n'

上面的静态类不需要使用,prototype因为它将仅作为静态用法构造一次。

https://github.com/yidas/js-design-patterns/tree/master/class


@jvitoroc谢谢!

0

Javascript没有实际的类,而是使用原型继承系统,其中对象通过其原型链从其他对象“继承”。最好通过代码本身来解释:

function Foo() {};
// creates a new function object

Foo.prototype.talk = function () {
    console.log('hello~\n');
};
// put a new function (object) on the prototype (object) of the Foo function object

var a = new Foo;
// When foo is created using the new keyword it automatically has a reference 
// to the prototype property of the Foo function

// We can show this with the following code
console.log(Object.getPrototypeOf(a) === Foo.prototype); 

a.talk(); // 'hello~\n'
// When the talk method is invoked it will first look on the object a for the talk method,
// when this is not present it will look on the prototype of a (i.e. Foo.prototype)

// When you want to call
// Foo.talk();
// this will not work because you haven't put the talk() property on the Foo
// function object. Rather it is located on the prototype property of Foo.

// We could make it work like this:
Foo.sayhi = function () {
    console.log('hello there');
};

Foo.sayhi();
// This works now. However it will not be present on the prototype chain 
// of objects we create out of Foo

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.