Variabel statis dalam JavaScript
Bagaimana saya bisa membuat variabel statis di Javascript?
Jawaban #1
Jika Anda berasal dari bahasa berorientasi objek yang statis dan berbasis kelas (seperti Java, C++ atau C#), saya berasumsi bahwa Anda mencoba membuat variabel atau metode yang terkait dengan "tipe" tetapi bukan dengan sebuah instance.
Sebuah contoh menggunakan pendekatan "klasik", dengan fungsi konstruktor mungkin bisa membantu Anda memahami konsep dasar OO JavaScript:
function MyClass () { // constructor function
var privateVariable = "foo"; // Private variable
this.publicVariable = "bar"; // Public variable
this.privilegedMethod = function () { // Public Method
alert(privateVariable);
};
}
// Instance method will be available to all instances but only load once in memory
MyClass.prototype.publicMethod = function () {
alert(this.publicVariable);
};
// Static variable shared by all instances
MyClass.staticProperty = "baz";
var myInstance = new MyClass();
staticProperty didefinisikan di objek MyClass (yang merupakan sebuah fungsi) dan tidak ada hubungannya dengan instance yang dibuatnya, JavaScript memperlakukan fungsi sebagai objek kelas satu, jadi karena merupakan objek, Anda dapat menetapkan properti ke sebuah fungsi.
PEMBAHARUAN: ES6 memperkenalkan kemampuan untuk mendeklarasikan kelas melalui kata kunci class. Ini adalah sintaks gula di atas pewarisan berbasis prototipe yang sudah ada.
Kata kunci static memungkinkan Anda untuk dengan mudah mendefinisikan properti atau metode statis dalam sebuah kelas.
Mari kita lihat contoh di atas yang diimplementasikan dengan kelas ES6:
class MyClass {
// class constructor, equivalent to
// the function body of a constructor
constructor() {
const privateVariable = 'private value'; // Private variable at the constructor scope
this.publicVariable = 'public value'; // Public property
this.privilegedMethod = function() {
// Public Method with access to the constructor scope variables
console.log(privateVariable);
};
}
// Prototype methods:
publicMethod() {
console.log(this.publicVariable);
}
// Static properties shared by all instances
static staticProperty = 'static value';
static staticMethod() {
console.log(this.staticProperty);
}
}
// We can add properties to the class prototype
MyClass.prototype.additionalMethod = function() {
console.log(this.publicVariable);
};
var myInstance = new MyClass();
myInstance.publicMethod(); // "public value"
myInstance.additionalMethod(); // "public value"
myInstance.privilegedMethod(); // "private value"
MyClass.staticMethod(); // "static value"Jawaban #2
Anda mungkin memanfaatkan fakta bahwa fungsi JS juga merupakan objek -- yang berarti mereka dapat memiliki properti.
Sebagai contoh, mengutip contoh yang diberikan pada artikel (sekarang hilang) Variabel statis dalam Javascript:
function countMyself() {
// Check to see if the counter has been initialized
if ( typeof countMyself.counter == 'undefined' ) {
// It has not... perform the initialization
countMyself.counter = 0;
}
// Do something stupid to indicate the value
alert(++countMyself.counter);
}
Jika Anda memanggil fungsi itu beberapa kali, Anda akan melihat bahwa penghitung sedang ditingkatkan.
Dan ini mungkin solusi yang jauh lebih baik daripada mencemari namespace global dengan variabel global.
Berikut adalah solusi lain yang mungkin, berdasarkan penutupan: Trik menggunakan variabel statis dalam javascript:
var uniqueID = (function() {
var id = 0; // This is the private persistent value
// The outer function returns a nested function that has access
// to the persistent value. It is this nested function we're storing
// in the variable uniqueID above.
return function() { return id++; }; // Return and increment
})(); // Invoke the outer function after defining it.
Yang menghasilkan jenis hasil yang sama -- kecuali, kali ini, nilai yang ditingkatkan dikembalikan, bukan ditampilkan.
Jawaban #3
Anda melakukannya melalui IIFE (ekspresi fungsi yang langsung dipanggil):
var incr = (function () {
var i = 1;
return function () {
return i++;
}
})();
incr(); // returns 1
incr(); // returns 2
Jawaban #4
Saya telah melihat beberapa jawaban serupa, tetapi saya ingin menyebutkan bahwa postingan ini menjelaskannya dengan baik, jadi saya ingin membagikannya kepada Anda.
Berikut beberapa kode yang diambil darinya, yang telah saya modifikasi untuk mendapatkan contoh lengkap yang semoga bermanfaat bagi komunitas karena dapat digunakan sebagai template desain untuk kelas.
Ini juga menjawab pertanyaan Anda:
function Podcast() {
// private variables
var _somePrivateVariable = 123;
// object properties (read/write)
this.title = 'Astronomy Cast';
this.description = 'A fact-based journey through the galaxy.';
this.link = 'http://www.astronomycast.com';
// for read access to _somePrivateVariable via immutableProp
this.immutableProp = function() {
return _somePrivateVariable;
}
// object function
this.toString = function() {
return 'Title: ' + this.title;
}
};
// static property
Podcast.FILE_EXTENSION = 'mp3';
// static function
Podcast.download = function(podcast) {
console.log('Downloading ' + podcast + ' ...');
};
Dengan contoh tersebut, Anda dapat mengakses properti/fungsi statis sebagai berikut:
// access static properties/functions
console.log(Podcast.FILE_EXTENSION); // 'mp3'
Podcast.download('Astronomy cast'); // 'Downloading Astronomy cast ...'
Dan properti/fungsi objek cukup seperti:
// access object properties/functions
var podcast = new Podcast();
podcast.title = 'The Simpsons';
console.log(podcast.toString()); // Title: The Simpsons
console.log(podcast.immutableProp()); // 123
Catatan bahwa dalam podcast.immutableProp(), kita memiliki sebuah closure: Referensi ke _somePrivateVariable disimpan di dalam fungsi.
Anda bahkan bisa mendefinisikan getter dan setter. Lihat potongan kode ini (di mana d adalah prototipe objek yang ingin Anda deklarasikan propertinya, y adalah variabel privat yang tidak terlihat di luar konstruktor):
// getters and setters
var d = Date.prototype;
Object.defineProperty(d, "year", {
get: function() {return this.getFullYear() },
set: function(y) { this.setFullYear(y) }
});
Ini mendefinisikan properti d.year melalui fungsi get dan set - jika Anda tidak menentukan set, maka properti tersebut hanya-baca dan tidak dapat diubah (perlu diingat Anda tidak akan mendapatkan kesalahan jika mencoba mengaturnya, tetapi tidak ada efeknya). Setiap properti memiliki atribut writable, configurable (memungkinkan untuk diubah setelah deklarasi) dan enumerable (memungkinkan digunakan sebagai enumerator), yang secara default adalah false. Anda dapat mengaturnya melalui defineProperty pada parameter ketiga, misalnya enumerable: true.
Yang juga valid adalah sintaks ini:
// getters and setters - alternative syntax
var obj = { a: 7,
get b() {return this.a + 1;},
set c(x) {this.a = x / 2}
};
yang mendefinisikan properti yang dapat dibaca/ditulis a, properti hanya-baca b dan properti hanya-tulis c, melalui mana properti a dapat diakses.
Penggunaan:
console.log(obj.a); console.log(obj.b); // output: 7, 8
obj.c=40;
console.log(obj.a); console.log(obj.b); // output: 20, 21
Catatan:
Untuk menghindari perilaku yang tak terduga jika Anda lupa kata kunci new, saya sarankan Anda menambahkan hal berikut pada fungsi Podcast:
// instantiation helper
function Podcast() {
if(false === (this instanceof Podcast)) {
return new Podcast();
}
// [... same as above ...]
};
Sekarang kedua instansiasi berikut akan bekerja sebagaimana mestinya:
var podcast = new Podcast(); // normal usage, still allowed
var podcast = Podcast(); // you can omit the new keyword because of the helper
Pernyataan 'new' membuat objek baru dan menyalin semua properti serta metode, yaitu.
var a=new Podcast();
var b=new Podcast();
a.title="a"; b.title="An "+b.title;
console.log(a.title); // "a"
console.log(b.title); // "An Astronomy Cast"
Perlu dicatat juga, bahwa dalam beberapa situasi, mungkin berguna untuk menggunakan pernyataan return dalam fungsi konstruktor Podcast untuk mengembalikan objek kustom yang melindungi fungsi-fungsi yang secara internal digunakan oleh kelas tetapi perlu diungkapkan. Hal ini dijelaskan lebih lanjut di bab 2 (Objek) dari seri artikel tersebut.
Anda bisa mengatakan bahwa a dan b mewarisi dari Podcast. Sekarang, bagaimana jika Anda ingin menambahkan metode ke Podcast yang berlaku untuk semuanya setelah a dan b telah diinstansiasi? Dalam kasus ini, gunakan .prototype seperti berikut:
Podcast.prototype.titleAndLink = function() {
return this.title + " [" + this.link + "]";
};
Sekarang panggil a dan b lagi:
console.log(a.titleAndLink()); // "a [http://www.astronomycast.com]"
console.log(b.titleAndLink()); // "An Astronomy Cast [http://www.astronomycast.com]"
Anda dapat menemukan lebih banyak detail tentang prototipe di sini. Jika Anda ingin melakukan lebih banyak pewarisan, saya sarankan untuk mempelajari ini.
Seri artikel yang saya sebutkan di atas sangat direkomendasikan untuk dibaca, mereka juga mencakup topik-topik berikut:
- Fungsi
- Objek
- Prototipe
- Menerapkan New pada Fungsi Konstruktor
- Pengangkatan
- Penyisipan Tanda Titik Koma Otomatis
- Properti dan Metode Statis
Perhatikan bahwa penyisipan titik koma otomatis "fitur" dari JavaScript (seperti disebutkan di poin 6.) sangat sering menjadi penyebab masalah aneh dalam kode Anda. Oleh karena itu, saya lebih suka menganggapnya sebagai bug daripada sebagai fitur.
Jika Anda ingin membaca lebih lanjut, berikut adalah artikel MSDN yang cukup menarik tentang topik-topik ini, beberapa di antaranya yang dijelaskan di sana bahkan memberikan lebih banyak detail.
Yang menarik untuk dibaca juga (juga mencakup topik-topik yang disebutkan di atas) adalah artikel-artikel dari Panduan JavaScript MDN berikut:
- Pengantar kembali ke JavaScript
- Bekerja dengan Objek
Jika Anda ingin tahu cara meniru parameter out di C# (seperti di DateTime.TryParse(str, out result)) di JavaScript, Anda dapat menemukan contoh kode di sini.
Bagi Anda yang bekerja dengan IE (yang tidak memiliki konsol untuk JavaScript kecuali Anda membuka alat pengembang menggunakan F12 dan membuka tab konsol) mungkin akan menemukan cuplikan berikut berguna. Ini memungkinkan Anda menggunakan console.log(msg); seperti yang digunakan dalam contoh di atas. Cukup masukkan sebelum fungsi Podcast.
Untuk kenyamanan Anda, berikut adalah kode di atas dalam satu potongan kode lengkap:
let console = { log: function(msg) {
let canvas = document.getElementById("log"), br = canvas.innerHTML==="" ? "" : "<br/>";
canvas.innerHTML += (br + (msg || "").toString());
}};
console.log('For details, see the explaining text');
function Podcast() {
// with this, you can instantiate without new (see description in text)
if (false === (this instanceof Podcast)) {
return new Podcast();
}
// private variables
var _somePrivateVariable = 123;
// object properties
this.title = 'Astronomy Cast';
this.description = 'A fact-based journey through the galaxy.';
this.link = 'http://www.astronomycast.com';
this.immutableProp = function() {
return _somePrivateVariable;
}
// object function
this.toString = function() {
return 'Title: ' + this.title;
}
};
// static property
Podcast.FILE_EXTENSION = 'mp3';
// static function
Podcast.download = function(podcast) {
console.log('Downloading ' + podcast + ' ...');
};
// access static properties/functions
Podcast.FILE_EXTENSION; // 'mp3'
Podcast.download('Astronomy cast'); // 'Downloading Astronomy cast ...'
// access object properties/functions
var podcast = new Podcast();
podcast.title = 'The Simpsons';
console.log(podcast.toString()); // Title: The Simpsons
console.log(podcast.immutableProp()); // 123
// getters and setters
var d = Date.prototype;
Object.defineProperty(d, "year", {
get: function() {
return this.getFullYear()
},
set: function(y) {
this.setFullYear(y)
}
});
// getters and setters - alternative syntax
var obj = {
a: 7,
get b() {
return this.a + 1;
},
set c(x) {
this.a = x / 2
}
};
// usage:
console.log(obj.a); console.log(obj.b); // output: 7, 8
obj.c=40;
console.log(obj.a); console.log(obj.b); // output: 20, 21
var a=new Podcast();
var b=new Podcast();
a.title="a"; b.title="An "+b.title;
console.log(a.title); // "a"
console.log(b.title); // "An Astronomy Cast"
Podcast.prototype.titleAndLink = function() {
return this.title + " [" + this.link + "]";
};
console.log(a.titleAndLink()); // "a [http://www.astronomycast.com]"
console.log(b.titleAndLink()); // "An Astronomy Cast [http://www.astronomycast.com]"<div id="log"></div>Catatan:
Beberapa tips, petunjuk, dan rekomendasi yang baik tentang pemrograman JavaScript secara umum dapat Anda temukan di sini (praktik terbaik JavaScript) dan di sana ('var' versus 'let'). Juga direkomendasikan artikel ini tentang konversi tipe implisit (coercion).
Cara yang nyaman untuk menggunakan kelas dan mengompilasinya ke dalam JavaScript adalah TypeScript. Berikut adalah playground di mana Anda bisa menemukan beberapa contoh yang menunjukkan cara kerjanya. Bahkan jika Anda tidak menggunakan TypeScript saat ini, Anda tetap dapat melihatnya karena Anda bisa membandingkan TypeScript dengan hasil JavaScript secara berdampingan. Sebagian besar contoh sederhana, tetapi ada juga contoh Raytracer yang bisa Anda coba langsung. Saya sangat menyarankan untuk melihat khususnya contoh "Using Classes", "Using Inheritance" dan "Using Generics" dengan memilihnya di combobox - ini adalah template bagus yang bisa langsung Anda gunakan di JavaScript. TypeScript digunakan dengan Angular.
Untuk mencapai enkapsulasi dari variabel lokal, fungsi, dll dalam JavaScript, saya menyarankan untuk menggunakan pola seperti berikut (JQuery menggunakan teknik yang sama):
<html>
<head></head>
<body><script>
'use strict';
// module pattern (self invoked function)
const myModule = (function(context) {
// to allow replacement of the function, use 'var' otherwise keep 'const'
// put variables and function with local module scope here:
var print = function(str) {
if (str !== undefined) context.document.write(str);
context.document.write("<br/><br/>");
return;
}
// ... more variables ...
// main method
var _main = function(title) {
if (title !== undefined) print(title);
print("<b>last modified: </b>" + context.document.lastModified + "<br/>");
// ... more code ...
}
// public methods
return {
Main: _main
// ... more public methods, properties ...
};
})(this);
// use module
myModule.Main("<b>Module demo</b>");
</script></body>
</html>Tentu saja, kamu bisa - dan seharusnya - menaruh kode skrip di file *.js terpisah; ini hanya ditulis langsung agar contohnya singkat.
Fungsi yang memanggil dirinya sendiri (juga dikenal sebagai IIFE=Ekspresi Fungsi yang Dipanggil Segera) dijelaskan lebih rinci di sini.
Jawaban #5
Anda dapat menggunakan arguments.callee untuk menyimpan variabel "statis" (ini juga berguna dalam fungsi anonim):
function () {
arguments.callee.myStaticVar = arguments.callee.myStaticVar || 1;
arguments.callee.myStaticVar++;
alert(arguments.callee.myStaticVar);
}
Jawaban #6
Jawaban yang diperbarui:
Dalam ECMAScript 6, Anda dapat membuat fungsi statis menggunakan kata kunci static:
class Foo {
static bar() {return 'I am static.'}
}
//`bar` is a property of the class
Foo.bar() // returns 'I am static.'
//`bar` is not a property of instances of the class
var foo = new Foo()
foo.bar() //-> throws TypeError
Kelas ES6 tidak memperkenalkan semantik baru untuk statis. Anda bisa melakukan hal yang sama di ES5 seperti ini:
//constructor
var Foo = function() {}
Foo.bar = function() {
return 'I am static.'
}
Foo.bar() // returns 'I am static.'
var foo = new Foo()
foo.bar() // throws TypeError
Anda dapat menetapkan ke properti dari Foo karena di JavaScript fungsi adalah objek.
Jawaban #7
function Person(){
if(Person.count == undefined){
Person.count = 1;
}
else{
Person.count ++;
}
console.log(Person.count);
}
var p1 = new Person();
var p2 = new Person();
var p3 = new Person();
Jawaban #8
Contoh dan penjelasan berikut berasal dari buku Professional JavaScript for Web Developers Edisi ke-2 oleh Nicholas Zakas. Ini adalah jawaban yang saya cari, jadi saya pikir akan berguna untuk menambahkannya di sini.
(function () {
var name = '';
Person = function (value) {
name = value;
};
Person.prototype.getName = function () {
return name;
};
Person.prototype.setName = function (value) {
name = value;
};
}());
var person1 = new Person('Nate');
console.log(person1.getName()); // Nate
person1.setName('James');
console.log(person1.getName()); // James
person1.name = 'Mark';
console.log(person1.name); // Mark
console.log(person1.getName()); // James
var person2 = new Person('Danielle');
console.log(person1.getName()); // Danielle
console.log(person2.getName()); // Danielle
Konstruktor Person dalam contoh ini memiliki akses ke variabel privat name, begitu juga metode getName() dan setName(). Menggunakan pola ini, variabel name menjadi statis dan akan digunakan di semua instansi. Ini berarti memanggil setName() pada satu instansi memengaruhi semua instansi lainnya. Memanggil setName() atau membuat instansi Person baru menetapkan variabel name ke nilai baru. Hal ini menyebabkan semua instansi mengembalikan nilai yang sama.
Jawaban #9
Jika Anda menggunakan sintaks kelas baru maka sekarang Anda dapat melakukan hal berikut:
class MyClass {
static get myStaticVariable() {
return "some static variable";
}
}
console.log(MyClass.myStaticVariable);
aMyClass = new MyClass();
console.log(aMyClass.myStaticVariable, "is undefined");Ini secara efektif membuat variabel statis di JavaScript.
Jawaban #10
Ada 5 cara untuk meniru variabel statis lokal fungsi di Javascript.
Metode 1: Menggunakan properti objek fungsi (didukung di browser lama)
function someFunc1(){
if( !('staticVar' in someFunc1) )
someFunc1.staticVar = 0 ;
alert(++someFunc1.staticVar) ;
}
someFunc1() ; //prints 1
someFunc1() ; //prints 2
someFunc1() ; //prints 3
Metode 2: Menggunakan closure, varian 1 (didukung di browser lama)
var someFunc2 = (function(){
var staticVar = 0 ;
return function(){
alert(++staticVar) ;
}
})()
someFunc2() ; //prints 1
someFunc2() ; //prints 2
someFunc2() ; //prints 3
Metode 3: Menggunakan closure, varian 2 (juga didukung di browser lama)
var someFunc3 ;
with({staticVar:0})
var someFunc3 = function(){
alert(++staticVar) ;
}
someFunc3() ; //prints 1
someFunc3() ; //prints 2
someFunc3() ; //prints 3
Metode 4: Menggunakan closure, varian 3 (memerlukan dukungan untuk EcmaScript 2015)
{
let staticVar = 0 ;
function someFunc4(){
alert(++staticVar) ;
}
}
someFunc4() ; //prints 1
someFunc4() ; //prints 2
someFunc4() ; //prints 3
Metode 5 untuk mode ketat
'use strict'
{
let staticVar = 0 ;
var someFunc5 = function(){
alert(++staticVar) ;
} ;
}
someFunc5() ; //prints 1
someFunc5() ; //prints 2
someFunc5() ; //prints 3
