Biến tĩnh trong JavaScript


716

Làm cách nào để tạo biến tĩnh trong Javascript?


chúng ta có thể định nghĩa nhãn hoặc thẻ html khác với thuộc tính kiểu "disaly: none" và đặt giá trị biến cho giá trị này và thao tác trên giá trị này. Chúng ta đừng khó khăn.
Asghar

Giải pháp đơn giản nhất mà tôi tìm thấy: không định nghĩa một biến tĩnh trong lớp. Khi bạn muốn sử dụng một biến tĩnh, chỉ cần xác định nó ở đó và sau đó, ví dụ someFunc = () => { MyClass.myStaticVariable = 1; }. Sau đó, chỉ cần tạo một phương thức tĩnh để trả về thành viên tĩnh, vd static getStatic() { return MyClass.myStaticVariable; }. Sau đó, bạn chỉ có thể gọi MyClass.getStatic()từ bên ngoài lớp để có được dữ liệu tĩnh!
Pixel

Câu trả lời:


863

Nếu bạn đến từ một ngôn ngữ hướng đối tượng được gõ tĩnh (như Java, C ++ hoặc C #), tôi giả sử rằng bạn đang cố gắng tạo một biến hoặc phương thức được liên kết với một "loại" nhưng không phải là một thể hiện.

Một ví dụ sử dụng cách tiếp cận "cổ điển", với các hàm xây dựng có thể giúp bạn nắm bắt các khái niệm về OO JavaScript cơ bản:

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được định nghĩa trong đối tượng MyClass (là một hàm) và không liên quan gì đến các thể hiện được tạo của nó, JavaScript coi các hàm là các đối tượng hạng nhất , vì vậy là một đối tượng, bạn có thể gán các thuộc tính cho một hàm.

CẬP NHẬT: ES6 giới thiệu khả năng khai báo các lớp thông qua classtừ khóa. Đó là cú pháp đường trên thừa kế dựa trên nguyên mẫu hiện có.

Các statictừ khóa cho phép bạn dễ dàng xác định các thuộc tính tĩnh hoặc các phương pháp trong một lớp học.

Hãy xem ví dụ trên được triển khai với các lớp 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"


5
Có lẽ privilegedMethodkhông tương đương với một phương thức riêng tư trong OO vì có vẻ như nó có thể được gọi trên một thể hiện của MyClass? Bạn có nghĩa là nó đặc quyền bởi vì nó có thể truy cập privateVariable?
Dónal

3
Không thể this.constructorđược sử dụng để truy cập các biến tĩnh từ "phương thức cá thể"? Nếu có, nó đáng để thêm nó vào câu trả lời.
Ciro Santilli 冠状 病毒 审查 事件

1
Bạn cũng có thể đề cập đến các hàm tĩnh trong ví dụ của bạn.
David Coleues

18
xin chào, tôi không chắc chắn mình đồng ý với dòng này // Biến tĩnh được chia sẻ bởi tất cả các trường hợp 'MyClass.staticProperty = "baz";' với tôi rằng bạn có thể tìm thấy baz từ 'myInstance.staticProperty' mà tất nhiên bạn không thể.
fullstacklife 19/03/2015

5
Có lẽ nó nên đọc MyClass.prototype.staticProperty = "baz";hoặc chính xác hơn đối với các nguyên tắc OO, thuộc tính tĩnh thực sự phải được định nghĩa là một hàm ẩn danh MyClass.prototype.staticProperty = function () {return staticVar;}và để tất cả các cá thể truy cập vào một biến duy nhất cũng có thể được thay đổi bằng một setter.
lindsaymacvean

535

Bạn có thể lợi dụng thực tế là các hàm JS cũng là các đối tượng - có nghĩa là chúng có thể có các thuộc tính.

Ví dụ: trích dẫn ví dụ được đưa ra trên bài viết (hiện đã biến mất) Các biến tĩnh trong 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);
}

Nếu bạn gọi chức năng đó nhiều lần, bạn sẽ thấy bộ đếm đang được tăng lên.

Và đây có lẽ là một giải pháp tốt hơn nhiều so với việc biến đổi không gian tên toàn cầu bằng một biến toàn cục.


Và đây là một giải pháp khả thi khác, dựa trên việc đóng cửa: Thủ thuật sử dụng các biến tĩnh trong 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.

Điều này mang lại cho bạn cùng loại kết quả - ngoại trừ, lần này, giá trị gia tăng được trả về, thay vì hiển thị.


50
như một phím tắt, bạn chỉ có thể làm countMyself.counter = countMyself.counter || initial_value;nếu biến tĩnh sẽ không bao giờ là falsey (false, 0, null hoặc chuỗi rỗng)
Kip

3
Hơi ngắn và rõ ràng hơn: (function () {var id = 0; function uniqueID () {return id ++;};}) ();
Tom Robinson

3
Bộ đếm trong đóng cửa rất nhanh hơn trong lớp trong Firefox. jsperf.com/static-count-in- class
Sony Santos

Sử dụng ===để typeofkiểm tra nếu không bạn sẽ nhận được một số ép buộc kỳ lạ đang diễn ra.
sương

@SonySantos Thử nghiệm của bạn cho thấy điều ngược lại với Firefox 40
bartolo-otrit

96

Bạn thực hiện thông qua IIFE (biểu thức hàm được gọi ngay lập tức):

var incr = (function () {
    var i = 1;

    return function () {
        return i++;
    }
})();

incr(); // returns 1
incr(); // returns 2

21
Tôi muốn nói rằng đây là cách thành ngữ nhất để làm điều đó trong JavaScript. Quá tệ, nó không nhận được quá nhiều sự ủng hộ nhờ các phương pháp khác có thể hợp lý hơn với những người đến từ các ngôn ngữ khác.

1
Tôi muốn viết lại bằng cách sử dụng 'đóng cửa' thay vì chỉ 'IIFE'.
zendka

39

bạn có thể sử dụng argument.callee để lưu trữ các biến "tĩnh" (điều này cũng hữu ích trong hàm ẩn danh):

function () {
  arguments.callee.myStaticVar = arguments.callee.myStaticVar || 1;
  arguments.callee.myStaticVar++;
  alert(arguments.callee.myStaticVar);
}

3
Theo như tôi có thể hiểu, phương pháp này có một lợi thế (chỉ có một?) So với cách của pascal MARTIN: bạn có thể sử dụng nó cho các hàm ẩn danh. Một ví dụ về điều này sẽ rất tuyệt
Dan

27
arguments.calleebị phản đối
Câu hỏi Quolonel

Tôi lúc nào cũng chế giễu JS, nhưng calleedường như là một điều tốt đẹp để có. Tôi tự hỏi tại sao hack họ quyết định từ chối điều này ...: |
user2173353

35

Tôi đã thấy một vài câu trả lời tương tự, nhưng tôi muốn đề cập rằng bài đăng này mô tả nó tốt nhất, vì vậy tôi muốn chia sẻ nó với bạn.

Đây là một số mã được lấy từ nó, mà tôi đã sửa đổi để có được một ví dụ hoàn chỉnh, hy vọng sẽ mang lại lợi ích cho cộng đồng vì nó có thể được sử dụng làm mẫu thiết kế cho các lớp.

Nó cũng trả lời câu hỏi của bạn:

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 + ' ...');
};

Cho ví dụ đó, bạn có thể truy cập các thuộc tính / hàm tĩnh như sau:

// access static properties/functions
console.log(Podcast.FILE_EXTENSION);   // 'mp3'
Podcast.download('Astronomy cast');    // 'Downloading Astronomy cast ...'

Và các thuộc tính / hàm đối tượng đơn giản là:

// access object properties/functions
var podcast = new Podcast();
podcast.title = 'The Simpsons';
console.log(podcast.toString());       // Title: The Simpsons
console.log(podcast.immutableProp());  // 123

Lưu ý rằng trong podcast.immutableProp (), chúng tôi có một bao đóng : Tham chiếu đến _somePrivateVariable được giữ bên trong hàm.

Bạn thậm chí có thể định nghĩa getters và setters . Hãy xem đoạn mã này ( dnguyên mẫu của đối tượng mà bạn muốn khai báo thuộc tính ylà biến riêng tư không hiển thị bên ngoài hàm tạo):

// getters and setters
var d = Date.prototype;
Object.defineProperty(d, "year", {
    get: function() {return this.getFullYear() },
    set: function(y) { this.setFullYear(y) }
});

Nó xác định thuộc tính d.yearthông qua getvà các setchức năng - nếu bạn không chỉ định set, thì thuộc tính chỉ đọc và không thể sửa đổi (lưu ý rằng bạn sẽ không gặp lỗi nếu bạn cố gắng đặt nó, nhưng nó không có hiệu lực). Mỗi thuộc tính có các thuộc tính writable, configurable(cho phép thay đổi sau khi khai báo) và enumerable(cho phép sử dụng nó làm điều tra viên), theo mặc định false. Bạn có thể đặt chúng qua definePropertytham số thứ 3, vd enumerable: true.

Điều gì cũng hợp lệ là cú pháp này:

// getters and setters - alternative syntax
var obj = { a: 7, 
            get b() {return this.a + 1;}, 
            set c(x) {this.a = x / 2}
        };

trong đó xác định thuộc tính có thể đọc / ghi được a, thuộc tính chỉ đọc bvà thuộc tính chỉ ghi c, thông qua thuộc tính đóa có thể được truy cập.

Sử dụng:

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

Ghi chú:

Để tránh hành vi không mong muốn trong trường hợp bạn quên newtừ khóa, tôi khuyên bạn nên thêm các chức năng sau vào chức năng Podcast:

// instantiation helper
function Podcast() {
    if(false === (this instanceof Podcast)) {
        return new Podcast();
    }
// [... same as above ...]
};

Bây giờ cả hai cảnh báo sau sẽ hoạt động như mong đợi:

var podcast = new Podcast(); // normal usage, still allowed
var podcast = Podcast();     // you can omit the new keyword because of the helper

Câu lệnh 'mới' tạo ra một đối tượng mới và sao chép tất cả các thuộc tính và phương thức, tức là

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"

Cũng lưu ý rằng , trong một số trường hợp, có thể hữu ích khi sử dụng returncâu lệnh trong hàm Podcasttạo để trả về một hàm bảo vệ đối tượng tùy chỉnh mà lớp bên trong dựa vào nhưng cần phải được phơi bày. Điều này được giải thích thêm trong chương 2 (Đối tượng) của loạt bài viết.

Bạn có thể nói điều đó abkế thừa từ Podcast. Bây giờ, điều gì sẽ xảy ra nếu bạn muốn thêm một phương thức vào Podcast áp dụng cho tất cả chúng sau đó abđã được cung cấp? Trong trường hợp này, sử dụng .prototypenhư sau:

Podcast.prototype.titleAndLink = function() {
    return this.title + " [" + this.link + "]";
};

Bây giờ gọi abmột lần nữa:

console.log(a.titleAndLink()); // "a [http://www.astronomycast.com]"
console.log(b.titleAndLink()); // "An Astronomy Cast [http://www.astronomycast.com]"

Bạn có thể tìm thêm chi tiết về nguyên mẫu ở đây . Nếu bạn muốn làm thừa kế nhiều hơn, tôi khuyên bạn nên xem xét điều này .


Các bài viết tôi đã đề cập ở trên rất khuyến khích để đọc, chúng cũng bao gồm các chủ đề sau:

  1. Chức năng
  2. Các đối tượng
  3. Nguyên mẫu
  4. Thực thi mới trên các hàm xây dựng
  5. Tời kéo
  6. Tự động chèn dấu chấm phẩy
  7. Thuộc tính và phương thức tĩnh

Lưu ý rằng chèn dấu chấm phẩy tự động "tính năng" của JavaScript (như được đề cập trong 6.) thường rất có trách nhiệm gây ra các vấn đề lạ trong mã của bạn. Do đó, tôi thà coi nó là một lỗi hơn là một tính năng.

Nếu bạn muốn đọc thêm, đây là một bài viết MSDN khá thú vị về các chủ đề này, một số trong số chúng được mô tả ở đó cung cấp nhiều chi tiết hơn nữa.

Điều thú vị để đọc là tốt (cũng bao gồm các chủ đề được đề cập ở trên) là những bài viết từ Hướng dẫn JavaScript MDN :

Nếu bạn muốn biết cách mô phỏng outcác tham số c # (như trong DateTime.TryParse(str, out result)) trong JavaScript, bạn có thể tìm mã mẫu ở đây.


Những người bạn đang làm việc với IE (không có giao diện điều khiển cho JavaScript trừ khi bạn mở các công cụ dành cho nhà phát triển bằng cách sử dụng F12và mở tab bảng điều khiển) có thể thấy đoạn mã sau hữu ích. Nó cho phép bạn sử dụng console.log(msg);như được sử dụng trong các ví dụ trên. Chỉ cần chèn nó trước Podcastchức năng.

Để thuận tiện cho bạn, đây là đoạn mã trên trong một đoạn mã hoàn chỉnh:


Ghi chú:

  • Một số mẹo hay, gợi ý và đề xuất hay về lập trình JavaScript nói chung bạn có thể tìm thấy ở đây (thực tiễn tốt nhất về JavaScript)ở đó ('var' so với 'let') . Cũng đề nghị là bài viết này về các kiểu chữ ngầm (cưỡng chế) .

  • Một cách thuận tiện để sử dụng các lớp và biên dịch chúng thành JavaScript là TypeScript. Đây là một sân chơi nơi bạn có thể tìm thấy một số ví dụ cho bạn thấy nó hoạt động như thế nào. Ngay cả khi bạn hiện không sử dụng TypeScript, bạn vẫn có thể xem vì bạn có thể so sánh TypeScript với kết quả JavaScript trên chế độ xem song song. Hầu hết các ví dụ đều đơn giản, nhưng cũng có một ví dụ Raytracer mà bạn có thể thử ngay lập tức. Tôi đặc biệt khuyên bạn nên xem xét các ví dụ "Sử dụng lớp", "Sử dụng kế thừa" và "Sử dụng Generics" bằng cách chọn chúng trong hộp tổ hợp - đây là những mẫu đẹp bạn có thể sử dụng ngay trong JavaScript. Bản đánh máy được sử dụng với Angular.

  • Để đạt được đóng gói các biến cục bộ, hàm vv trong JavaScript, tôi khuyên bạn nên sử dụng một mẫu như sau (JQuery sử dụng cùng một kỹ thuật):

<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:&nbsp;</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>

Tất nhiên, bạn có thể - và nên - đặt mã tập lệnh vào một *.jstệp riêng ; đây chỉ là nội tuyến để giữ cho ví dụ ngắn.

Các hàm tự gọi (còn được gọi là IIFE = Biểu thức hàm được gọi ngay lập tức) được mô tả chi tiết hơn ở đây .


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

28

Cập nhật câu trả lời:

Trong ECMAScript 6 , bạn có thể tạo các hàm tĩnh bằng statictừ khóa:

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

Các lớp ES6 không giới thiệu bất kỳ ngữ nghĩa mới nào cho thống kê. Bạn có thể làm điều tương tự trong ES5 như thế này:

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

Bạn có thể gán cho một thuộc tính Foovì trong hàm JavaScript là các đối tượng.


Foo.bar;trả về hàm được gán cho nó, không phải chuỗi được trả về bởi hàm như nhận xét của bạn.

Bạn có thể thêm một số thông tin về cách đặt (ghi đè) một giá trị tĩnh trong cả hai ví dụ này không?
Héo

1
@ Trong cả hai trường hợp, thuộc tính "tĩnh" chỉ là một thuộc tính trên hàm, vì vậy bạn đặt nó và ghi đè lên nó giống như bất kỳ thuộc tính nào khác trong JavaScript. Trong cả hai trường hợp, bạn có thể thiết lập các barthuộc tính của Foođể 3như thế này:Foo.bar = 3;
Max Heiber


16

Ví dụ và giải thích sau đây là từ cuốn sách Professional JavaScript dành cho nhà phát triển web Phiên bản 2 của Nicholas Zakas. Đây là câu trả lời tôi đang tìm kiếm vì vậy tôi nghĩ rằng nó sẽ hữu ích để thêm nó ở đây.

(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

Các Person tạo trong ví dụ này có quyền truy cập vào tên biến riêng, cũng như các phương thức getName()setName(). Sử dụng mẫu này, biến tên trở thành tĩnh và sẽ được sử dụng trong số tất cả các trường hợp. Điều này có nghĩa là gọi setName()một trường hợp ảnh hưởng đến tất cả các trường hợp khác. Gọi setName()hoặc tạo một thể hiện mới Personđặt biến tên thành một giá trị mới. Điều này khiến tất cả các trường hợp trả về cùng một giá trị.


ngoại hình nhà xây dựng + nguyên mẫu (lai)
Ganesh Kumar

2
Điều này đặt đối tượng Person trong không gian tên toàn cầu. Không phải là một giải pháp tôi muốn giới thiệu.
Ghola

Tôi không nghĩ rằng đây là một biến tĩnh thực sự bởi vì nó được khởi tạo khác nhau với mỗi đối tượng mới. Một đối tượng tĩnh phải nhất quán trên tất cả các đối tượng kế thừa từ nguyên mẫu mẹ?
lindsaymacvean 10/11/2015

1
@Ghola Ý định ở đây là giải thích cách tạo biến tĩnh. Không gian tên phù hợp và tránh toàn cầu là một chủ đề riêng biệt có thể đã thêm vào sự phức tạp của câu trả lời. Người dùng tùy thuộc vào cách xác định cách gắn hàm tạo mà không gây ô nhiễm. Nếu nó đủ tốt cho Nicholas Zakas, thì nó đủ tốt cho tôi.
Nate

@lindsaymacvean Đó là một biến tĩnh vì giá trị đơn được chia sẻ trên tất cả các trường hợp. Giá trị thay đổi là OK. Nếu một thể hiện thay đổi giá trị, tất cả các thể hiện sẽ bị ảnh hưởng. Không có khả năng nó sẽ được sử dụng chính xác như ví dụ trên. Cho phép giá trị được đặt trong khi khởi tạo chỉ là để hiển thị nó có thể. Một trường hợp sử dụng nhiều khả năng sẽ chỉ có getter và setter hoặc ít nhất là kiểm tra để đảm bảo rằng nó được đặt thành một cái gì đó không phải là không xác định.
Nate

15

Nếu bạn đang sử dụng cú pháp lớp mới thì bây giờ bạn có thể làm như sau:

    class MyClass {
      static get myStaticVariable() {
        return "some static variable";
      }
    }

    console.log(MyClass.myStaticVariable);

    aMyClass = new MyClass();
    console.log(aMyClass.myStaticVariable, "is undefined");

Điều này thực sự tạo ra một biến tĩnh trong JavaScript.


Điều này rất hữu ích khi xây dựng các lớp tiện ích tĩnh!
Xúi giục

1
Nhưng bây giờ câu hỏi là làm thế nào để bạn duy trì một giá trị và cho phép thay đổi nó với một setter. Một đóng cửa sẽ là cần thiết hoặc một thuộc tính MyClassđược xác định bên ngoài cấu trúc lớp.
trincot


8

Nếu bạn muốn khai báo các biến tĩnh để tạo các hằng trong ứng dụng của mình thì tôi thấy theo cách tiếp cận đơn giản nhất

ColorConstants = (function()
{
    var obj = {};
    obj.RED = 'red';
    obj.GREEN = 'green';
    obj.BLUE = 'blue';
    obj.ALL = [obj.RED, obj.GREEN, obj.BLUE];
    return obj;
})();

//Example usage.
var redColor = ColorConstants.RED;

8

Về phần classgiới thiệu của ECMAScript 2015. Các câu trả lời khác không hoàn toàn rõ ràng.

Dưới đây là một ví dụ cho thấy cách tạo var tĩnh staticVarvới ClassName. vartổng hợp:

class MyClass {
    constructor(val) {
        this.instanceVar = val;
        MyClass.staticVar = 10;
    }
}

var class1 = new MyClass(1);
console.log(class1.instanceVar);      // 1
console.log(class1.constructor.staticVar); // 10

// New instance of MyClass with another value
var class2 = new MyClass(3);
console.log(class1.instanceVar);      // 1
console.log(class2.instanceVar);      // 3

Để truy cập biến tĩnh, chúng tôi sử dụng .constructor tính trả về tham chiếu đến hàm xây dựng đối tượng đã tạo lớp. Chúng ta có thể gọi nó trên hai trường hợp đã tạo:

MyClass.staticVar = 11;
console.log(class1.constructor.staticVar); // 11
console.log(class2.constructor.staticVar); // 11 <-- yes it's static! :)

MyClass.staticVar = 12;
console.log(class1.constructor.staticVar); // 12
console.log(class2.constructor.staticVar); // 12

7

Có những câu trả lời tương tự khác, nhưng không ai trong số chúng khá hấp dẫn tôi. Đây là những gì tôi đã kết thúc với:

var nextCounter = (function () {
  var counter = 0;
  return function() {
    var temp = counter;
    counter += 1;
    return temp;
  };
})();

7

Ngoài phần còn lại, hiện tại có một dự thảo ( đề xuất giai đoạn 2 ) về Đề xuất ECMA giới thiệu static các lĩnh vực công cộng trong các lớp học. ( lĩnh vực tư nhân đã được xem xét )

Sử dụng ví dụ từ đề xuất, staticcú pháp được đề xuất sẽ như thế này:

class CustomDate {
  // ...
  static epoch = new CustomDate(0);
}

và tương đương với những điều sau đây mà những người khác đã nhấn mạnh:

class CustomDate {
  // ...
}
CustomDate.epoch = new CustomDate(0);

Sau đó bạn có thể truy cập nó thông qua CustomDate.epoch.

Bạn có thể theo dõi các đề xuất mới trong proposal-static-class-features.


Hiện tại, babel hỗ trợ tính năng này với plugin thuộc tính lớp chuyển đổi mà bạn có thể sử dụng. Ngoài ra, mặc dù vẫn đang trong quá trình, V8đang thực hiện nó .


6

Bạn có thể tạo một biến tĩnh trong JavaScript như thế này bên dưới. Đây countlà biến tĩnh.

var Person = function(name) {
  this.name = name;
  // first time Person.count is undefined, so it is initialized with 1
  // next time the function is called, the value of count is incremented by 1
  Person.count = Person.count ? Person.count + 1 : 1;
}

var p1 = new Person('User p1');
console.log(p1.constructor.count);   // prints 1
var p2 = new Person('User p2');
console.log(p2.constructor.count);   // prints 2

Bạn có thể gán giá trị cho biến tĩnh bằng cách sử dụng Personhàm hoặc bất kỳ trường hợp nào:

// set static variable using instance of Person
p1.constructor.count = 10;         // this change is seen in all the instances of Person
console.log(p2.constructor.count); // prints 10

// set static variable using Person
Person.count = 20;
console.log(p1.constructor.count); // prints 20

Đây là một trong những cách tiếp cận tốt để khai báo biến tĩnh và truy cập nó trong JavaScript.
ArunDhwaj IIITH

5

Nếu bạn muốn tạo một biến tĩnh toàn cầu:

var my_id = 123;

Thay thế biến bằng dưới đây:

Object.defineProperty(window, 'my_id', {
    get: function() {
            return 123;
        },
    configurable : false,
    enumerable : false
});

4

Điều gần nhất trong JavaScript với một biến tĩnh là một biến toàn cục - đây đơn giản là một biến được khai báo bên ngoài phạm vi của một hàm hoặc đối tượng bằng chữ:

var thisIsGlobal = 1;

function foo() {
    var thisIsNot = 2;
}

Một điều khác bạn có thể làm là lưu trữ các biến toàn cục bên trong một đối tượng theo nghĩa đen như sau:

var foo = { bar : 1 }

Và sau đó truy cập các variabels như thế này : foo.bar.


cái này giúp tôi tải lên nhiều tập tin ..... var foo = {counter: 1}; chức năng moreFiles () {fileName = "File" + foo.count; foo.count = foo.count + 1;
veer7

4

Để cô đọng tất cả các khái niệm lớp ở đây, hãy kiểm tra điều này:

var Test = function() {
  // "super private" variable, accessible only here in constructor. There are no real private variables
  //if as 'private' we intend variables accessible only by the class that defines the member and NOT by child classes
  var test_var = "super private";

  //the only way to access the "super private" test_var is from here
  this.privileged = function(){
    console.log(test_var);
  }();

  Test.test_var = 'protected';//protected variable: accessible only form inherited methods (prototype) AND child/inherited classes

  this.init();
};//end constructor

Test.test_var = "static";//static variable: accessible everywhere (I mean, even out of prototype, see domready below)

Test.prototype = {

 init:function(){
   console.log('in',Test.test_var);
 }

};//end prototype/class


//for example:
$(document).ready(function() {

 console.log('out',Test.test_var);

 var Jake = function(){}

 Jake.prototype = new Test();

 Jake.prototype.test = function(){
   console.log('jake', Test.test_var);
 }

 var jake = new Jake();

 jake.test();//output: "protected"

});//end domready

Chà, một cách khác để xem xét các thực tiễn tốt nhất trong những điều này, là chỉ xem cách coffeescript dịch các khái niệm này.

#this is coffeescript
class Test
 #static
 @prop = "static"

 #instance
 constructor:(prop) ->
   @prop = prop
   console.log(@prop)

 t = new Test('inst_prop');

 console.log(Test.prop);


//this is how the above is translated in plain js by the CS compiler
  Test = (function() {
    Test.prop = "static";

    function Test(prop) {
     this.prop = prop;
     console.log(this.prop);
    }

    return Test;

  })();

  t = new Test('inst_prop');

  console.log(Test.prop);

4

Trong các biến JavaScript là tĩnh theo mặc định. Ví dụ :

var x = 0;

function draw() {
    alert(x); //
    x+=1;
}

setInterval(draw, 1000);

Giá trị của x được tăng thêm 1 cứ sau 1000 mili giây
Nó sẽ in 1,2,3 trở đi


2
Đó là một trường hợp khác. Ví dụ của bạn là về phạm vi.
challet

4

Có một cách tiếp cận khác, giải quyết các yêu cầu của tôi sau khi duyệt chủ đề này. Nó phụ thuộc vào chính xác những gì bạn muốn đạt được với một "biến tĩnh".

Phiên thuộc tính toàn cầuStorage hoặc localStorage cho phép dữ liệu được lưu trữ trong vòng đời của phiên hoặc trong một khoảng thời gian dài hơn không xác định cho đến khi được xóa rõ ràng, tương ứng. Điều này cho phép dữ liệu được chia sẻ giữa tất cả các cửa sổ, khung, bảng tab, cửa sổ bật lên, v.v. của trang / ứng dụng của bạn và mạnh hơn nhiều so với "biến tĩnh / toàn cầu" đơn giản trong một phân đoạn mã.

Nó tránh mọi rắc rối với phạm vi, thời gian tồn tại, ngữ nghĩa, động lực v.v ... của các biến toàn cầu cấp cao nhất, ví dụ như Window.myglobal. Không biết hiệu quả của nó như thế nào, nhưng điều đó không quan trọng đối với lượng dữ liệu khiêm tốn, được truy cập ở mức khiêm tốn.

Dễ dàng truy cập dưới dạng "sessionStorage.mydata = anything" và được truy xuất tương tự. Xem "JavaScript: Hướng dẫn dứt khoát, Phiên bản thứ sáu", David Flanagan, ISBN: 980-0-596-80552-4, Chương 20, phần 20.1. Điều này có thể dễ dàng tải xuống dưới dạng PDF bằng cách tìm kiếm đơn giản hoặc trong đăng ký O'Reilly Safaribooks của bạn (đáng giá vàng của nó).


2

Hàm / lớp của hàm chỉ cho phép hàm tạo duy nhất cho phạm vi đối tượng của nó. Function Hoisting, declarations & expressions

  • Các hàm được tạo với hàm tạo Hàm không tạo các bao đóng cho bối cảnh tạo của chúng; chúng luôn được tạo ra trong phạm vi toàn cầu.

      var functionClass = function ( ) {
            var currentClass = Shape;
            _inherits(currentClass, superClass);
            function functionClass() { superClass.call(this); // Linking with SuperClass Constructor.
                // Instance Variables list.
                this.id = id;   return this;
            }
        }(SuperClass)

Đóng cửa - bản sao của đóng là chức năng với dữ liệu được bảo quản.

  • Mỗi bản sao của bao đóng được tạo cho một hàm với các giá trị hoặc tham chiếu miễn phí của riêng chúng, Bất cứ khi nào bạn sử dụng hàm bên trong hàm khác, một bao đóng được sử dụng.
  • Một bao đóng trong JavaScript giống như duy trì một bản sao của tất cả các biến cục bộ của hàm cha của nó bởi các hàm bên trong.

      function closureFun( args ) {
            // Local variable that ends up within closure
            var num = args;
            num++;
            return function() { console.log(num); }
        }
        var closure1 = closureFun( 5 );
        var closure2 = closureFun( 777 );
        closure1(); // 5
        closure2(); // 777
        closure2(); // 778
        closure1(); // 6

Các lớp chức năng ES5 : sử dụng Object.defineProperty (O, P, Attribution)

Phương thức Object.defineProperty () định nghĩa một thuộc tính mới trực tiếp trên một đối tượng hoặc sửa đổi một thuộc tính hiện có trên một đối tượng và trả về đối tượng.

Tạo một số phương pháp bằng cách sử dụng `` , để mỗi lần có thể hiểu các lớp hàm một cách dễ dàng.

'use strict';
var Shape = function ( superClass ) {
    var currentClass = Shape;
    _inherits(currentClass, superClass); // Prototype Chain - Extends

    function Shape(id) { superClass.call(this); // Linking with SuperClass Constructor.
        // Instance Variables list.
        this.id = id;   return this;
    }
    var staticVariablesJOSN = { "parent_S_V" : 777 };
    staticVariable( currentClass, staticVariablesJOSN );

    // Setters, Getters, instanceMethods. [{}, {}];
    var instanceFunctions = [
        {
            key: 'uniqueID',
            get: function get() { return this.id; },
            set: function set(changeVal) { this.id = changeVal; }
        }
    ];
    instanceMethods( currentClass, instanceFunctions );

    return currentClass;
}(Object);

var Rectangle = function ( superClass ) {
    var currentClass = Rectangle;

    _inherits(currentClass, superClass); // Prototype Chain - Extends

    function Rectangle(id, width, height) { superClass.call(this, id); // Linking with SuperClass Constructor.

        this.width = width;
        this.height = height;   return this;
    }

    var staticVariablesJOSN = { "_staticVar" : 77777 };
    staticVariable( currentClass, staticVariablesJOSN );

    var staticFunctions = [
        {
            key: 'println',
            value: function println() { console.log('Static Method'); }
        }
    ];
    staticMethods(currentClass, staticFunctions);

    var instanceFunctions = [
        {
            key: 'setStaticVar',
            value: function setStaticVar(staticVal) {
                currentClass.parent_S_V = staticVal;
                console.log('SET Instance Method Parent Class Static Value : ', currentClass.parent_S_V);
            }
        }, {
            key: 'getStaticVar',
            value: function getStaticVar() {
                console.log('GET Instance Method Parent Class Static Value : ', currentClass.parent_S_V);
                return currentClass.parent_S_V;
            }
        }, {
            key: 'area',
            get: function get() {
                console.log('Area : ', this.width * this.height);
                return this.width * this.height;
                }
        }, {
            key: 'globalValue',
            get: function get() {
                console.log('GET ID : ', currentClass._staticVar);
                return currentClass._staticVar;
            },
            set: function set(value) {
                currentClass._staticVar = value;
                console.log('SET ID : ', currentClass._staticVar);
            }
        }
    ];
    instanceMethods( currentClass, instanceFunctions );

    return currentClass;
}(Shape);

// ===== ES5 Class Conversion Supported Functions =====
function defineProperties(target, props) {
    console.log(target, ' : ', props);
    for (var i = 0; i < props.length; i++) {
        var descriptor = props[i];
        descriptor.enumerable = descriptor.enumerable || false;
        descriptor.configurable = true;
        if ("value" in descriptor) descriptor.writable = true;
        Object.defineProperty(target, descriptor.key, descriptor);
    }
}
function staticMethods( currentClass, staticProps ) {
    defineProperties(currentClass, staticProps);
};
function instanceMethods( currentClass, protoProps ) {
    defineProperties(currentClass.prototype, protoProps);
};
function staticVariable( currentClass, staticVariales ) {
    // Get Key Set and get its corresponding value.
    // currentClass.key = value;
    for( var prop in staticVariales ) {
        console.log('Keys : Values');
        if( staticVariales.hasOwnProperty( prop ) ) {
            console.log(prop, ' : ', staticVariales[ prop ] );
            currentClass[ prop ] = staticVariales[ prop ];
        }
    }
};
function _inherits(subClass, superClass) {
    console.log( subClass, ' : extends : ', superClass );
    if (typeof superClass !== "function" && superClass !== null) {
        throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
    }
    subClass.prototype = Object.create(superClass && superClass.prototype, 
            { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } });
    if (superClass)
        Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
}

Đoạn mã dưới đây là để kiểm tra về Mỗi phiên bản có bản sao riêng của các thành viên thể hiện và các thành viên tĩnh chung.

var objTest = new Rectangle('Yash_777', 8, 7);
console.dir(objTest);

var obj1 = new Rectangle('R_1', 50, 20);
Rectangle.println(); // Static Method
console.log( obj1 );    // Rectangle {id: "R_1", width: 50, height: 20}
obj1.area;              // Area :  1000
obj1.globalValue;       // GET ID :  77777
obj1.globalValue = 88;  // SET ID :  88
obj1.globalValue;       // GET ID :  88  

var obj2 = new Rectangle('R_2', 5, 70);
console.log( obj2 );    // Rectangle {id: "R_2", width: 5, height: 70}
obj2.area;              // Area :  350    
obj2.globalValue;       // GET ID :  88
obj2.globalValue = 999; // SET ID :  999
obj2.globalValue;       // GET ID :  999

console.log('Static Variable Actions.');
obj1.globalValue;        // GET ID :  999

console.log('Parent Class Static variables');
obj1.getStaticVar();    // GET Instance Method Parent Class Static Value :  777
obj1.setStaticVar(7);   // SET Instance Method Parent Class Static Value :  7
obj1.getStaticVar();    // GET Instance Method Parent Class Static Value :  7

Các cuộc gọi phương thức tĩnh được thực hiện trực tiếp trên lớp và không thể gọi được trong các phiên bản của lớp. Nhưng bạn có thể đạt được các cuộc gọi cho các thành viên tĩnh từ bên trong một thể hiện.

Sử dụng cú pháp:

   this.constructor.staticfunctionName();
class MyClass {
    constructor() {}
    static staticMethod() {
        console.log('Static Method');
    }
}
MyClass.staticVar = 777;

var myInstance = new MyClass();
// calling from instance
myInstance.constructor.staticMethod();
console.log('From Inside Class : ',myInstance.constructor.staticVar);

// calling from class
MyClass.staticMethod();
console.log('Class : ', MyClass.staticVar);

Các lớp ES6: Các lớp ES2015 là một đường đơn giản so với mẫu OO dựa trên nguyên mẫu. Có một hình thức khai báo thuận tiện duy nhất làm cho các mẫu lớp dễ sử dụng hơn và khuyến khích khả năng tương tác. Các lớp hỗ trợ kế thừa dựa trên nguyên mẫu, siêu gọi, thể hiện và phương thức tĩnh và hàm tạo.

Ví dụ : giới thiệu bài viết trước của tôi.


2

Có 4 cách để mô phỏng các biến tĩnh cục bộ trong Javascript.

Phương pháp 1: Sử dụng các thuộc tính đối tượng chức năng (được hỗ trợ trong các trình duyệt cũ)

function someFunc1(){
    if( !('staticVar' in someFunc1) )
        someFunc1.staticVar = 0 ;
    alert(++someFunc1.staticVar) ;
}

someFunc1() ; //prints 1
someFunc1() ; //prints 2
someFunc1() ; //prints 3

Phương pháp 2: Sử dụng bao đóng, biến thể 1 (được hỗ trợ trong các trình duyệt cũ)

var someFunc2 = (function(){
    var staticVar = 0 ;
    return function(){
        alert(++staticVar) ;
    }
})()

someFunc2() ; //prints 1
someFunc2() ; //prints 2
someFunc2() ; //prints 3

Phương pháp 3: Sử dụng bao đóng, biến thể 2 (cũng được hỗ trợ trong các trình duyệt cũ)

var someFunc3 ;
with({staticVar:0})
    var someFunc3 = function(){
        alert(++staticVar) ;
    }

someFunc3() ; //prints 1
someFunc3() ; //prints 2
someFunc3() ; //prints 3

Phương pháp 4: Sử dụng bao đóng, biến thể 3 (yêu cầu hỗ trợ cho EcmaScript 2015)

{
    let staticVar = 0 ;
    function someFunc4(){
        alert(++staticVar) ;
    }
}

someFunc4() ; //prints 1
someFunc4() ; //prints 2
someFunc4() ; //prints 3

2

Bạn có thể xác định các hàm tĩnh trong JavaScript bằng statictừ khóa:

class MyClass {
  static myStaticFunction() {
    return 42;
  }
}

MyClass.myStaticFunction(); // 42

Khi viết bài này, bạn vẫn không thể định nghĩa các thuộc tính tĩnh (ngoài các hàm) trong lớp. Các thuộc tính tĩnh vẫn là đề xuất Giai đoạn 3 , có nghĩa là chúng chưa phải là một phần của JavaScript. Tuy nhiên, không có gì ngăn bạn chỉ đơn giản là gán cho một lớp như bạn sẽ làm với bất kỳ đối tượng nào khác:

class MyClass {}

MyClass.myStaticProperty = 42;

MyClass.myStaticProperty; // 42

Lưu ý cuối cùng: hãy cẩn thận về việc sử dụng các đối tượng tĩnh có tính kế thừa - tất cả các lớp được kế thừa đều chia sẻ cùng một bản sao của đối tượng .


1

Trong JavaScript, không có thuật ngữ hoặc từ khóa tĩnh, nhưng chúng ta có thể đưa dữ liệu đó trực tiếp vào đối tượng hàm (như trong bất kỳ đối tượng nào khác).

function f() {
    f.count = ++f.count || 1 // f.count is undefined at first
    alert("Call No " + f.count)
}

f(); // Call No 1

f(); // Call No 2

1

Tôi sử dụng các biến hàm tĩnh rất nhiều và thật xấu hổ, JS không có cơ chế tích hợp sẵn cho điều đó. Tôi thường thấy mã nơi các biến và hàm được xác định trong phạm vi bên ngoài mặc dù chúng chỉ được sử dụng bên trong một hàm. Điều này là xấu xí, dễ bị lỗi và chỉ yêu cầu rắc rối ...

Tôi đã đưa ra phương pháp sau:

if (typeof Function.prototype.statics === 'undefined') {
  Function.prototype.statics = function(init) {
    if (!this._statics) this._statics = init ? init() : {};
    return this._statics;
  }
}

Điều này thêm phương thức 'statics' vào tất cả các hàm (vâng, chỉ cần thư giãn về nó), khi được gọi, nó sẽ thêm một đối tượng trống (_statics) vào đối tượng hàm và trả về nó. Nếu một hàm init được cung cấp, _statics sẽ được đặt thành kết quả init ().

Sau đó bạn có thể làm:

function f() {
  const _s = f.statics(() => ({ v1=3, v2=somefunc() });

  if (_s.v1==3) { ++_s.v1; _s.v2(_s.v1); }
} 

So sánh điều này với IIFE là câu trả lời đúng khác, điều này có nhược điểm là thêm một nhiệm vụ và một nếu trên mỗi hàm gọi và thêm thành viên '_statics' vào hàm, tuy nhiên có một vài ưu điểm: các đối số có tại đỉnh không nằm trong hàm bên trong, sử dụng 'tĩnh' trong mã chức năng bên trong là rõ ràng với '_s.' tiền tố, và nói chung là đơn giản hơn để xem và hiểu.


1

Tóm lược:

Trong ES6/ ES 2015, classtừ khóa đã được giới thiệu với một statictừ khóa đi kèm . Hãy nhớ rằng đây là đường cú pháp trên mô hình thừa kế nguyên mẫu mà javavscript là hiện thân. Các statictừ khóa hoạt động theo cách sau cho các phương pháp:

class Dog {

  static bark () {console.log('woof');}
  // classes are function objects under the hood
  // bark method is located on the Dog function object
  
  makeSound () { console.log('bark'); }
  // makeSound is located on the Dog.prototype object

}

// to create static variables just create a property on the prototype of the class
Dog.prototype.breed = 'Pitbull';
// So to define a static property we don't need the `static` keyword.

const fluffy = new Dog();
const vicky = new Dog();
console.log(fluffy.breed, vicky.breed);

// changing the static variable changes it on all the objects
Dog.prototype.breed = 'Terrier';
console.log(fluffy.breed, vicky.breed);


2
Ông đang yêu cầu một biến tĩnh, không phải là hàm tĩnh.
Konrad Höffner

1

Tôi đã sử dụng nguyên mẫu và cách nó hoạt động:

class Cat extends Anima {
  constructor() {
    super(Cat.COLLECTION_NAME);
  }
}

Cat.COLLECTION_NAME = "cats";

hoặc sử dụng một getter tĩnh:

class Cat extends Anima {
  constructor() {
    super(Cat.COLLECTION_NAME);
  }

  static get COLLECTION_NAME() {
    return "cats"
  }
}

0

Vars cấp cửa sổ là giống như thống kê theo nghĩa là bạn có thể sử dụng tham chiếu trực tiếp và chúng có sẵn cho tất cả các phần của ứng dụng của bạn


3
Một mô tả tốt hơn nhiều về các bình như vậy là 'toàn cầu', thay vì tĩnh.
Patrick M

0

Không có thứ gọi là biến tĩnh trong Javascript. Ngôn ngữ này là đối tượng dựa trên nguyên mẫu được định hướng, do đó không có các lớp, nhưng các nguyên mẫu từ đó các đối tượng "sao chép" chính chúng.

Bạn có thể mô phỏng chúng với các biến toàn cục hoặc với nguyên mẫu (thêm một thuộc tính vào nguyên mẫu):

function circle(){
}
circle.prototype.pi=3.14159

Phương pháp này hoạt động, nhưng bạn đang làm phiềnFunction.prototype
Dan

@Dan: Theo hiểu biết của tôi thì điều này sẽ chỉ dành cho vòng tròn chứ không phải Chức năng. Ít nhất đó là những gì Chrome cố gắng nói với tôi: function circle() {}| circle.prototype| circle.prototype.pi = 3.14| circle.prototype| Function.prototype| Function.__proto__(nếu đó là những gì bạn muốn nói)
Aktau

0

Làm việc với các trang web MVC sử dụng jQuery, tôi muốn đảm bảo các hành động AJAX trong các trình xử lý sự kiện nhất định chỉ có thể được thực thi khi yêu cầu trước đó đã hoàn thành. Tôi sử dụng biến đối tượng jqXHR "tĩnh" để đạt được điều này.

Cho nút sau:

<button type="button" onclick="ajaxAction(this, { url: '/SomeController/SomeAction' })">Action!</button>

Tôi thường sử dụng IIFE như thế này cho trình xử lý nhấp chuột của mình:

var ajaxAction = (function (jqXHR) {
    return function (sender, args) {
        if (!jqXHR || jqXHR.readyState == 0 || jqXHR.readyState == 4) {
            jqXHR = $.ajax({
                url: args.url,
                type: 'POST',
                contentType: 'application/json',
                data: JSON.stringify($(sender).closest('form').serialize()),
                success: function (data) {
                    // Do something here with the data.
                }
            });
        }
    };
})(null);

0

Nếu bạn muốn sử dụng nguyên mẫu thì có một cách

var p = function Person() {
    this.x = 10;
    this.y = 20;
}
p.prototype.counter = 0;
var person1 = new p();
person1.prototype = p.prototype;
console.log(person1.counter);
person1.prototype.counter++;
var person2 = new p();
person2.prototype = p.prototype;
console.log(person2.counter);
console.log(person1.counter);

Làm điều này bạn sẽ có thể truy cập vào biến đếm từ bất kỳ trường hợp nào và mọi thay đổi trong thuộc tính sẽ được phản ánh ngay lập tức !!

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.