JavaScript中的类与静态方法

我知道这是可行的:

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代码中使用类方法还是静态方法?

224366 次浏览

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

我建议大多数时候忽略__proto__,因为它的跨浏览器支持很差,而是专注于学习prototype是如何工作的。

如果你有一个从function2创建的对象实例,并且你以任何方式访问它的一个成员(方法、属性、属性、常量等),访问将沿着原型层次结构向下流动,直到它(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中,所有东西都是__abc0对象。

一切是一个对象。

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

这就是为什么你可以用Foo.prototype访问Foo的原型。

你还可以在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规范中是一个"Future Reserved Word",但是ES6引入了使用class关键字定义类的能力。

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

3: 原始值—其中包括undefinednull,布尔值,数字和string —在技术上不是对象,因为它们是低级语言实现。布尔值、数字和字符串仍然与原型链交互,就像它们是对象一样,因此为了回答这个问题,更容易将它们视为“对象”,尽管它们不完全是。

从一个实例调用一个静态方法:

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

你可以通过以下方式实现:

function Foo() {};


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

你现在可以调用"talk"函数如下:

Foo.talk();

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

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

我使用命名空间:

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();

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

ES6现在支持class &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";
}


}

当你尝试调用Foo.talk时,JS尝试从talk__proto__搜索函数,当然,它找不到。

Foo.__proto__Function.prototype

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

在你的例子中,如果你想Foo.talk():

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


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

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


另一种方式,My way定义为静态类:

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

当我面对这样的情况时,我是这样做的:

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方法as Logger.info("my Msg", "Tag"); < / p >

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

非常清晰的描述

直接取自mozilla.org

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

只是额外的注释。使用类ES6,当我们创建静态方法时,javacscript引擎设置的描述符属性与老式的“静态”方法略有不同

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中,静态方法可列举的被设置为

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

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


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

静态方法在ES6中被视为其他类的私有属性(名称,长度,构造函数),除了静态方法仍然是可写的,因此描述符可写的被设置为真正的 { writable: true }。这也意味着我们可以推翻它

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


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

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

在函数或类对象以及它们的实例上实现方法和属性有很多方法。

  1. 关于课堂(或函数)本身:Foo.method()Foo.prop。这些是静态方法或属性
  2. 关于原型: Foo.prototype.method()Foo.prototype.prop。创建时,实例将通过{method:function(){...}, prop:...}的原型继承这些对象。因此喷火对象将接收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.