JavaScript中的类与静态方法

时间:2011-10-08 02:58:38

标签: javascript oop

我知道这会奏效:

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
  3. 还有其他方法吗?我不知道这样做是否正确。您是否在JavaScript代码中使用了类方法或静态方法?

15 个答案:

答案 0 :(得分:373)

首先,请记住JavaScript主要是prototypal language,而不是class-based language 1 Foo不是一个类,它是一个函数,它是一个对象。您可以使用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 was a "Future Reserved Word" in the ECMAScript 5 specification,但ES6引入了使用class关键字定义类的功能。

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

3:primitive values - 包括undefinednull,布尔值,数字和字符串 - 技术上不是对象,因为它们是低级语言实现。布尔值,数字和字符串仍然与原型链相互作用,就像它们是对象一样,所以为了这个答案的目的,即使它们不完整,也更容易将它们视为“对象”。

答案 1 :(得分:65)

您可以按照以下方式实现:

function Foo() {};

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

您现在可以调用" talk"功能如下:

Foo.talk();

您可以这样做,因为在JavaScript中,函数也是对象。 " zzzzBov"我也回答了这个问题,但这是一个冗长的阅读。

答案 2 :(得分:35)

从实例调用静态方法:

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

答案 3 :(得分:30)

这是一个很好的例子来演示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

答案 4 :(得分:25)

此外,现在可以使用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'

答案 5 :(得分:10)

我使用命名空间:

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 :(得分:5)

ES6现在支持class&amp; static关键字如魅力:

class Foo {
    constructor() {}

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

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

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

}

答案 7 :(得分:2)

如果你必须在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/

答案 8 :(得分:1)

当您尝试调用Foo.talk时,JS会尝试通过talk搜索函数__proto__,当然,无法找到它。

Foo.__proto__Function.prototype

答案 9 :(得分:1)

仅需补充说明。使用类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中的静态方法将可枚举设置为 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
);

答案 10 :(得分: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

答案 11 :(得分: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");

答案 12 :(得分:0)

静态方法调用直接在类上进行,并且在类的实例上不可调用。静态方法经常被用来 创建效用函数

非常清晰的描述

Taken Directly from mozilla.org

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

答案 13 :(得分: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

答案 14 :(得分:0)

在函数或类对象以及它们的实例上实现了方法和属性的树方式。

  1. 关于类(或函数)本身:Foo.method()Foo.prop。这些是静态方法或属性
  2. 关于它的原型Foo.prototype.method()Foo.prototype.prop。创建时,实例将通过原型女巫 {method:function(){...}, prop:...} 继承这些对象。所以 foo 对象将接收作为原型的 Foo.prototype 对象的副本。
  3. 在实例本身:方法或属性被添加到对象本身。 foo={method:function(){...}, prop:...}

this 关键字将根据上下文进行不同的表示和操作。在静态方法中,它将代表类本身(毕竟 witch 是 Function 的一个实例:class Foo {} 相当于 let Foo = new Function({})

使用 ECMAScript 2015,在今天看来实现得很好,可以更清楚地看到类(静态)方法和属性、实例方法和属性以及自己的方法和属性之间的区别。因此,您可以创建三个名称相同但不同的方法或属性,因为它们适用于不同的对象,方法中的 this 关键字将分别适用于类对象自身和实例对象,由原型或由它自己。

class Foo {
  constructor(){super();}
  
  static prop = "I am static" // see 1.
  static method(str) {alert("static method"+str+" :"+this.prop)} // see 1.
  
  prop="I am of an instance"; // see 2.
  method(str) {alert("instance method"+str+" : "+this.prop)} // see 2.
}

var foo= new Foo();
foo.prop = "I am of own";  // see 3.
foo.func = function(str){alert("own method" + str + this.prop)} // see 3.