簡體   English   中英

JavaScript 中的類與靜態方法

[英]Class vs. static method in 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 代碼中使用類方法還是靜態方法?

首先,請記住 JavaScript 主要是一種原型語言,而不是基於類的語言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.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 規范中的“未來保留字” ,但 ES6 引入了使用class關鍵字定義類的能力。

2:本質上是一個由構造函數創建的類實例,但是有很多細微差別我不想誤導你

3:原始值——包括undefinednull 、布爾值、數字和字符串——在技術上不是對象,因為它們是低級語言實現。 布爾值、數字和字符串仍然像對象一樣與原型鏈交互,因此就本答案而言,即使它們不完全是“對象”,也更容易將它們視為“對象”。

你可以實現它如下:

function Foo() {};

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

您現在可以調用“talk”功能,如下所示:

Foo.talk();

您可以這樣做,因為在 JavaScript 中,函數也是對象。

從實例調用靜態方法:

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/redardo7/sjsClass

這是一個很好的例子來演示 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

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

我使用命名空間:

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

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";
    }

}

如果您必須在 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/

只是補充說明。 使用 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
);

當您嘗試調用Foo.talk ,JS 會嘗試通過__proto__搜索函數talk ,當然找不到。

Foo.__proto__Function.prototype

靜態方法調用直接在類上進行,不能在類的實例上調用。 靜態方法通常用於創建效用函數

描述很清楚

直接取自 mozilla.org

Foo 需要綁定到你的類然后當你創建一個新實例時你可以調用 myNewInstance.foo() 如果你導入你的類你可以調用一個靜態方法

在你的情況下,如果你想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

當我遇到這種情況時,我做了這樣的事情:

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

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 (ie 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對象將接收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.

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM