Phạm vi từ vựng là gì?


682

Giới thiệu ngắn gọn về phạm vi từ vựng là gì?


89
Trong podcast 58, Joel khuyến khích những câu hỏi như thế này vì anh ấy muốn SO trở thành nơi để trả lời, ngay cả khi chúng đã được trả lời ở những nơi khác. Đây là một câu hỏi hợp lệ, mặc dù người ta có thể đặt nó lịch sự hơn một chút.
Ralph M. Rickenbach

5
@rahul Tôi hiểu đó là một câu hỏi cũ. Nhưng tôi chắc chắn ngay cả trong năm 2009, SO dự kiến ​​những người hỏi sẽ nỗ lực cơ bản để giải quyết nó. Khi nó đứng, nó không cho thấy bất kỳ nỗ lực nào cả. Có thể, đó là lý do tại sao nó bị hạ thấp bởi nhiều người?
PP

13
Có thể người hỏi không (hoặc không) thông thạo tiếng Anh khi viết câu hỏi này
Martin

27
Câu hỏi là lịch sự, anh ấy chỉ nói những gì anh ấy muốn. Bạn được tự do trả lời. Không cần nhân chứng quá khổ ở đây.
Markus Siebene Rich

25
Tôi nghĩ những câu hỏi như thế này rất hay vì nó xây dựng nội dung cho SO. IMO, người quan tâm nếu câu hỏi không có nỗ lực ... câu trả lời sẽ có nội dung tuyệt vời và đó là điều quan trọng trên bảng tin này.
Jwan622

Câu trả lời:


685

Tôi hiểu họ thông qua các ví dụ. :)

Đầu tiên, phạm vi từ vựng (còn được gọi là phạm vi tĩnh ), theo cú pháp giống như C:

void fun()
{
    int x = 5;

    void fun2()
    {
        printf("%d", x);
    }
}

Mỗi cấp độ bên trong có thể truy cập các cấp độ bên ngoài của nó.

Có một cách khác, được gọi là phạm vi động được sử dụng bởi lần triển khai đầu tiên của Lisp , một lần nữa theo cú pháp giống như C:

void fun()
{
    printf("%d", x);
}

void dummy1()
{
    int x = 5;

    fun();
}

void dummy2()
{
    int x = 10;

    fun();
}

Ở đây funcó thể truy cập xvào dummy1hoặc dummy2, hoặc bất xkỳ chức năng nào được gọi funvới xkhai báo trong đó.

dummy1();

sẽ in 5,

dummy2();

sẽ in 10.

Cái đầu tiên được gọi là tĩnh bởi vì nó có thể được suy ra tại thời gian biên dịch và cái thứ hai được gọi là động vì phạm vi bên ngoài là động và phụ thuộc vào lệnh gọi chuỗi của các hàm.

Tôi tìm thấy phạm vi tĩnh dễ dàng hơn cho mắt. Hầu hết các ngôn ngữ đã đi theo cách này cuối cùng, ngay cả Lisp (có thể làm cả hai, phải không?). Phạm vi động giống như chuyển các tham chiếu của tất cả các biến sang hàm được gọi.

Như một ví dụ về lý do tại sao trình biên dịch không thể suy ra phạm vi động bên ngoài của hàm, hãy xem xét ví dụ cuối cùng của chúng tôi. Nếu chúng ta viết một cái gì đó như thế này:

if(/* some condition */)
    dummy1();
else
    dummy2();

Chuỗi cuộc gọi phụ thuộc vào một điều kiện thời gian chạy. Nếu đó là sự thật, thì chuỗi cuộc gọi trông như sau:

dummy1 --> fun()

Nếu điều kiện sai:

dummy2 --> fun()

Phạm vi bên ngoài của funtrong cả hai trường hợp là người gọi cộng người gọi của người gọi và vân vân .

Chỉ cần đề cập rằng ngôn ngữ C không cho phép các hàm lồng nhau cũng như phạm vi động.


19
Tôi cũng muốn chỉ ra một hướng dẫn rất dễ hiểu mà tôi vừa tìm thấy. Ví dụ của Arak là tốt, nhưng có thể quá ngắn đối với người cần nhiều ví dụ hơn (thực tế, so sánh với các ngôn ngữ khác ..). Hãy xem. Điều quan trọng là phải hiểu điều này , vì từ khóa đó sẽ dẫn chúng ta hiểu phạm vi từ vựng. howtonode.org/what-is-this
CppLearner

9
Đây là một câu trả lời tốt. Nhưng câu hỏi được gắn thẻ JavaScript. Vì vậy, tôi nghĩ rằng điều này không nên được đánh dấu là câu trả lời được chấp nhận. Phạm vi từ vựng cụ thể trong JS là khác nhau
Boyang

6
Câu trả lời cực kỳ tốt. Cảm ơn bạn. @Boyang tôi không đồng ý. Tôi không phải là một lập trình viên Lisp, nhưng thấy ví dụ Lisp hữu ích, vì đó là một ví dụ về phạm vi động, mà bạn không nhận được trong JS.
dudewad

4
Ban đầu tôi nghĩ ví dụ này là mã C hợp lệ và bị nhầm lẫn liệu có phạm vi động trong C. Có lẽ từ chối trách nhiệm ở cuối có thể được chuyển sang trước ví dụ mã không?
Yangshun Tay

2
Đây vẫn là một câu trả lời rất hữu ích nhưng tôi nghĩ @Boyang là chính xác. Câu trả lời này đề cập đến 'cấp độ', nằm dọc theo các dòng phạm vi khối mà C có. JavaScript theo mặc định không có phạm vi cấp khối, do đó, bên trong một forvòng lặp là vấn đề điển hình. Phạm vi từ điển cho JavaScript chỉ ở cấp độ chức năng trừ khi ES6 lethoặc constđược sử dụng.
icc97

274

Hãy thử định nghĩa ngắn nhất có thể:

Lexical Scoping định nghĩa cách các tên biến được giải quyết trong các hàm lồng nhau: các hàm bên trong chứa phạm vi của các hàm cha ngay cả khi hàm cha đã trả về .

Đó là tất cả để có nó!


21
Phần cuối cùng: "ngay cả khi hàm cha đã trả về" được gọi là Đóng.
Juanma Menendez

1
Hiểu phạm vi từ vựng & đóng cửa chỉ trong một tình cảm. Cảm ơn!!
Dungeon

63
var scope = "I am global";
function whatismyscope(){
   var scope = "I am just a local";
   function func() {return scope;}
   return func;
}

whatismyscope()()

Đoạn mã trên sẽ trả về "Tôi chỉ là người địa phương". Nó sẽ không trả lại "Tôi là một người toàn cầu". Bởi vì hàm func () tính nơi được xác định ban đầu nằm trong phạm vi của chức năng phân tích hàm.

Nó sẽ không bận tâm từ bất cứ thứ gì nó được gọi (phạm vi toàn cầu / từ bên trong một chức năng khác), đó là lý do tại sao giá trị phạm vi toàn cầu tôi là toàn cầu sẽ không được in.

Điều này được gọi là phạm vi từ vựng trong đó "các hàm được thực thi bằng chuỗi phạm vi có hiệu lực khi chúng được xác định " - theo Hướng dẫn định nghĩa JavaScript.

Phạm vi từ điển là một khái niệm rất rất mạnh mẽ.

Hi vọng điêu nay co ich..:)


3
nó giải thích rất hay tôi muốn thêm một điều nữa nếu bạn viết hàm func () {return this.scope;} thì nó sẽ trả về "Tôi là toàn cầu" chỉ cần sử dụng từ khóa này và phạm vi của bạn sẽ được thay đổi
Rajesh Kumar Bhawsar

41

Phạm vi từ vựng (tĩnh AKA) đề cập đến việc xác định phạm vi của một biến chỉ dựa trên vị trí của nó trong kho văn bản của mã. Một biến luôn đề cập đến môi trường cấp cao nhất của nó. Thật tốt khi hiểu nó liên quan đến phạm vi động.


41

Phạm vi xác định khu vực, nơi các chức năng, biến và như vậy có sẵn. Ví dụ, tính khả dụng của một biến được xác định trong ngữ cảnh của nó, giả sử hàm, tệp hoặc đối tượng, chúng được định nghĩa. Chúng ta thường gọi các biến cục bộ này.

Phần từ vựng có nghĩa là bạn có thể rút ra phạm vi từ việc đọc mã nguồn.

Phạm vi từ điển còn được gọi là phạm vi tĩnh.

Phạm vi động xác định các biến toàn cục có thể được gọi hoặc được tham chiếu từ bất kỳ đâu sau khi được xác định. Đôi khi chúng được gọi là biến toàn cục, mặc dù các biến toàn cục trong hầu hết các ngôn ngữ chương trình có phạm vi từ vựng. Điều này có nghĩa, nó có thể được bắt nguồn từ việc đọc mã mà biến có sẵn trong ngữ cảnh này. Có thể người ta phải tuân theo một mệnh đề sử dụng hoặc bao gồm mệnh đề hoặc định nghĩa, nhưng mã / trình biên dịch biết về biến ở nơi này.

Ngược lại, trong phạm vi động, bạn tìm kiếm trong hàm cục bộ, sau đó bạn tìm kiếm trong hàm gọi là hàm cục bộ, sau đó bạn tìm kiếm trong hàm gọi hàm đó, v.v., lên ngăn xếp cuộc gọi. "Động" đề cập đến sự thay đổi, trong đó ngăn xếp cuộc gọi có thể khác nhau mỗi khi một chức năng nhất định được gọi và do đó, chức năng có thể đạt các biến khác nhau tùy thuộc vào nơi nó được gọi. (xem tại đây )

Để xem một ví dụ thú vị cho phạm vi động xem tại đây .

Để biết thêm chi tiết xem tại đâyở đây .

Một số ví dụ trong Delphi / Object Pascal

Delphi có phạm vi từ vựng.

unit Main;
uses aUnit;  // makes available all variables in interface section of aUnit

interface

  var aGlobal: string; // global in the scope of all units that use Main;
  type 
    TmyClass = class
      strict private aPrivateVar: Integer; // only known by objects of this class type
                                    // lexical: within class definition, 
                                    // reserved word private   
      public aPublicVar: double;    // known to everyboday that has access to a 
                                    // object of this class type
    end;

implementation

  var aLocalGlobal: string; // known to all functions following 
                            // the definition in this unit    

end.

Delphi gần nhất có phạm vi động là cặp hàm RegisterClass () / GetClass (). Đối với việc sử dụng của nó xem ở đây .

Giả sử thời gian mà RegisterClass ([TmyClass]) được gọi để đăng ký một lớp nhất định không thể dự đoán được bằng cách đọc mã (nó được gọi trong phương thức nhấp vào nút được gọi bởi người dùng), mã gọi GetClass ('TmyClass') sẽ nhận được một kết quả hay không Cuộc gọi đến RegisterClass () không phải nằm trong phạm vi từ vựng của đơn vị sử dụng GetClass ();

Một khả năng khác cho phạm vi động là các phương thức ẩn danh (bao đóng) trong Delphi 2009, vì chúng biết các biến của hàm gọi của chúng. Nó không đi theo con đường gọi từ đó một cách đệ quy và do đó không hoàn toàn năng động.


2
Trên thực tế private có thể truy cập trong toàn bộ đơn vị nơi lớp được xác định. Đây là lý do tại sao "Strict private" được giới thiệu trong D2006.
Marco van de Voort

2
+1 cho ngôn ngữ đơn giản (trái ngược với cả ngôn ngữ phức tạp và ví dụ không có nhiều mô tả)
Pops

36

Tôi yêu các câu trả lời đầy đủ, không liên quan đến ngôn ngữ từ những người như @Arak. Vì câu hỏi này được gắn thẻ JavaScript , nên tôi muốn ghi chú trong một số ghi chú rất cụ thể cho ngôn ngữ này.

Trong JavaScript, các lựa chọn của chúng tôi cho phạm vi là:

  • nguyên trạng (không điều chỉnh phạm vi)
  • từ vựng var _this = this; function callback(){ console.log(_this); }
  • ràng buộc callback.bind(this)

Điều đáng chú ý, tôi nghĩ rằng JavaScript không thực sự có phạm vi động . .bindđiều chỉnh thistừ khóa, và đó là gần, nhưng về mặt kỹ thuật không giống nhau.

Dưới đây là một ví dụ minh họa cả hai cách tiếp cận. Bạn làm điều này mỗi khi bạn đưa ra quyết định về cách phạm vi các cuộc gọi lại để điều này áp dụng cho các lời hứa, xử lý sự kiện và hơn thế nữa.

Từ điển

Đây là những gì bạn có thể hạn Lexical Scopingcủa cuộc gọi lại trong JavaScript:

var downloadManager = {
  initialize: function() {
    var _this = this; // Set up `_this` for lexical access
    $('.downloadLink').on('click', function () {
      _this.startDownload();
    });
  },
  startDownload: function(){
    this.thinking = true;
    // Request the file from the server and bind more callbacks for when it returns success or failure
  }
  //...
};

Giới hạn

Một cách khác để phạm vi là sử dụng Function.prototype.bind:

var downloadManager = {
  initialize: function() {
    $('.downloadLink').on('click', function () {
      this.startDownload();
    }.bind(this)); // Create a function object bound to `this`
  }
//...

Những phương pháp này, theo như tôi biết, tương đương với hành vi.


Sử dụng bindkhông ảnh hưởng đến phạm vi.
Ben Aston

12

Phạm vi từ vựng: Các biến được khai báo bên ngoài hàm là các biến toàn cục và hiển thị ở mọi nơi trong chương trình JavaScript. Các biến được khai báo bên trong một hàm có phạm vi hàm và chỉ hiển thị với mã xuất hiện bên trong hàm đó.


12

IBM định nghĩa nó là:

Phần của một chương trình hoặc phân đoạn đơn vị áp dụng khai báo. Một định danh được khai báo trong một thường trình được biết đến trong thói quen đó và trong tất cả các thói quen lồng nhau. Nếu một thói quen lồng nhau khai báo một mục có cùng tên, thì mục bên ngoài không có sẵn trong thói quen lồng nhau.

Ví dụ 1:

function x() {
    /*
    Variable 'a' is only available to function 'x' and function 'y'.
    In other words the area defined by 'x' is the lexical scope of
    variable 'a'
    */
    var a = "I am a";

    function y() {
        console.log( a )
    }
    y();

}
// outputs 'I am a'
x();

Ví dụ 2:

function x() {

    var a = "I am a";

    function y() {
         /*
         If a nested routine declares an item with the same name,
         the outer item is not available in the nested routine.
         */
        var a = 'I am inner a';
        console.log( a )
    }
    y();

}
// outputs 'I am inner a'
x();

8

Phạm vi từ vựng có nghĩa là trong một nhóm các hàm lồng nhau, các hàm bên trong có quyền truy cập vào các biến và các tài nguyên khác trong phạm vi cha của chúng . Điều này có nghĩa là các hàm con bị ràng buộc về mặt từ vựng với bối cảnh thực thi của cha mẹ chúng. Phạm vi từ điển đôi khi cũng được gọi là phạm vi tĩnh .

function grandfather() {
    var name = 'Hammad';
    // 'likes' is not accessible here
    function parent() {
        // 'name' is accessible here
        // 'likes' is not accessible here
        function child() {
            // Innermost level of the scope chain
            // 'name' is also accessible here
            var likes = 'Coding';
        }
    }
}

Điều bạn sẽ nhận thấy về phạm vi từ vựng là nó hoạt động về phía trước, có nghĩa là tên có thể được truy cập bởi bối cảnh thực thi của trẻ em. Nhưng nó không hoạt động ngược với cha mẹ của nó, có nghĩa là biến likesnày không thể được truy cập bởi cha mẹ của nó.

Điều này cũng cho chúng ta biết rằng các biến có cùng tên trong các bối cảnh thực hiện khác nhau được ưu tiên từ trên xuống dưới của ngăn xếp thực thi. Một biến, có tên tương tự với biến khác, trong hàm trong cùng (bối cảnh trên cùng của ngăn xếp thực thi) sẽ có quyền ưu tiên cao hơn.

Lưu ý rằng điều này được lấy từ đây .


8

Trong ngôn ngữ đơn giản, phạm vi từ vựng là một biến được xác định bên ngoài phạm vi của bạn hoặc phạm vi trên được tự động có sẵn trong phạm vi của bạn, điều đó có nghĩa là bạn không cần phải vượt qua nó ở đó.

Thí dụ:

let str="JavaScript";

const myFun = () => {
    console.log(str);
}

myFun();

// Kết quả: JavaScript


2
Câu trả lời ngắn nhất và tốt nhất cho tôi với một ví dụ. Có thể đã được thêm rằng các hàm mũi tên ES6 'giải quyết vấn đề với bind. Với họ, bindkhông còn cần thiết nữa. Để biết thêm thông tin về thay đổi này, hãy kiểm tra stackoverflow.com/a/34361380/11127383
Daniel Danielecki

4

Có một phần quan trọng của cuộc trò chuyện xung quanh phạm vi từ vựngđộng bị thiếu: một lời giải thích rõ ràng về thời gian tồn tại của biến trong phạm vi - hoặc khi biến có thể được truy cập.

Phạm vi động chỉ tương ứng rất lỏng lẻo với phạm vi "toàn cầu" theo cách mà chúng ta thường nghĩ về nó (lý do tôi đưa ra so sánh giữa hai là nó đã được đề cập - và tôi không đặc biệt thích cách giải thích của bài báo được liên kết ); có lẽ tốt nhất là chúng ta không nên so sánh giữa toàn cầu và động - mặc dù được cho là, theo bài báo được liên kết, "... [nó] hữu ích như là một thay thế cho các biến có phạm vi toàn cầu."

Vì vậy, trong tiếng Anh đơn giản, sự khác biệt quan trọng giữa hai cơ chế phạm vi là gì?

Phạm vi từ vựng đã được xác định rất rõ trong suốt các câu trả lời ở trên: các biến có phạm vi từ vựng có sẵn - hoặc, có thể truy cập - ở cấp độ cục bộ của hàm được xác định.

Tuy nhiên - vì nó không phải là trọng tâm của OP - phạm vi năng động đã không nhận được nhiều sự chú ý và sự chú ý mà nó nhận được có nghĩa là nó có thể cần thêm một chút (đó không phải là một lời chỉ trích các câu trả lời khác, mà là "oh, câu trả lời đó khiến chúng tôi ước có thêm một chút nữa "). Vì vậy, đây là một chút nữa:

Phạm vi động có nghĩa là một biến có thể truy cập được vào chương trình lớn hơn trong suốt vòng đời của lệnh gọi hàm - hoặc, trong khi hàm đang thực thi. Thực sự, Wikipedia thực sự làm một công việc tốt với lời giải thích về sự khác biệt giữa hai. Vì vậy, để không làm xáo trộn nó, đây là văn bản mô tả phạm vi động:

... [I] n phạm vi động (hoặc phạm vi động), nếu phạm vi của tên biến là một hàm nhất định, thì phạm vi của nó là khoảng thời gian trong đó hàm đang thực thi: trong khi hàm đang chạy, tên biến tồn tại và bị ràng buộc với biến của nó, nhưng sau khi hàm trả về, tên biến không tồn tại.


3

Phạm vi từ vựng có nghĩa là một hàm tìm kiếm các biến trong bối cảnh nơi nó được xác định và không nằm trong phạm vi ngay lập tức xung quanh nó.

Nhìn vào cách phạm vi từ vựng hoạt động trong Lisp nếu bạn muốn biết thêm chi tiết. Câu trả lời được lựa chọn của Kyle Cronin trong các biến động và biến động trong Common Lisp rõ ràng hơn nhiều so với các câu trả lời ở đây.

Thật trùng hợp, tôi chỉ học về điều này trong một lớp Lisp và nó cũng được áp dụng trong JavaScript.

Tôi đã chạy mã này trong bảng điều khiển của Chrome.

// JavaScript               Equivalent Lisp
var x = 5;                //(setf x 5)
console.debug(x);         //(print x)
function print_x(){       //(defun print-x ()
    console.debug(x);     //    (print x)
}                         //)
(function(){              //(let
    var x = 10;           //    ((x 10))
    console.debug(x);     //    (print x)
    print_x();            //    (print-x)
})();                     //)

Đầu ra:

5
10
5

3

Phạm vi từ vựng trong JavaScript có nghĩa là một biến được xác định bên ngoài hàm có thể truy cập được bên trong hàm khác được xác định sau khai báo biến. Nhưng điều ngược lại là không đúng sự thật; các biến được định nghĩa bên trong một hàm sẽ không thể truy cập được bên ngoài hàm đó.

Khái niệm này được sử dụng nhiều trong các bao đóng trong JavaScript.

Hãy nói rằng chúng tôi có mã dưới đây.

var x = 2;
var add = function() {
    var y = 1;
    return x + y;
};

Bây giờ, khi bạn gọi add () -> cái này sẽ in 3.

Vì vậy, hàm add () đang truy cập vào biến toàn cục xđược xác định trước khi thêm hàm phương thức. Điều này được gọi là do phạm vi từ vựng trong JavaScript.


Hãy xem xét rằng đoạn mã dành cho ngôn ngữ có phạm vi động. Nếu add()hàm được gọi ngay sau đoạn mã đã cho, nó cũng sẽ in 3. Phạm vi từ vựng không chỉ đơn giản là hàm có thể truy cập các biến toàn cục bên ngoài ngữ cảnh cục bộ. Vì vậy, mã ví dụ thực sự không giúp thể hiện phạm vi từ vựng nghĩa là gì. Hiển thị phạm vi từ vựng trong mã thực sự cần một ví dụ ngược lại hoặc ít nhất là một lời giải thích về các cách hiểu khác về mã.
C Perkins

2

Phạm vi từ vựng đề cập đến từ vựng của mã định danh (ví dụ: biến, hàm, v.v.) hiển thị từ vị trí hiện tại trong ngăn xếp thực thi.

- global execution context
    - foo
    - bar
    - function1 execution context
        - foo2
        - bar2
        - function2 execution context
            - foo3
            - bar3

foobarluôn nằm trong từ vựng của các định danh khả dụng vì chúng là toàn cầu.

Khi function1được thực thi, nó có quyền truy cập vào một từ vựng của foo2, bar2, foo, và bar.

Khi function2được thực thi, nó có quyền truy cập vào một từ vựng của foo3, bar3, foo2, bar2, foo, và bar.

Lý do các hàm toàn cầu và / hoặc bên ngoài không có quyền truy cập vào một định danh hàm bên trong là do việc thực thi chức năng đó chưa xảy ra và do đó, không có định danh nào của nó được phân bổ vào bộ nhớ. Hơn nữa, một khi bối cảnh bên trong thực thi, nó sẽ bị xóa khỏi ngăn xếp thực thi, nghĩa là tất cả các định danh của nó đã được thu gom rác và không còn khả dụng.

Cuối cùng, đây là lý do tại sao một bối cảnh thực thi lồng nhau có thể LUÔN truy cập vào bối cảnh thực thi của tổ tiên và do đó tại sao nó có quyền truy cập vào một từ vựng lớn hơn của các định danh.

Xem:

Đặc biệt cảm ơn @ robr3rd vì đã giúp đơn giản hóa định nghĩa trên.


1

Đây là một góc độ khác nhau cho câu hỏi này mà chúng ta có thể nhận được bằng cách lùi lại một bước và xem xét vai trò của phạm vi trong khuôn khổ giải thích lớn hơn (chạy một chương trình). Nói cách khác, hãy tưởng tượng rằng bạn đang xây dựng một trình thông dịch (hoặc trình biên dịch) cho một ngôn ngữ và chịu trách nhiệm tính toán đầu ra, đưa ra một chương trình và một số đầu vào cho nó.

Giải thích liên quan đến việc theo dõi ba điều:

  1. Trạng thái - cụ thể là các biến và vị trí bộ nhớ được tham chiếu trên heap và stack.

  2. Hoạt động trên trạng thái đó - cụ thể là, mọi dòng mã trong chương trình của bạn

  3. Các môi trường trong đó một cho hoạt động chạy - cụ thể là, các dự báo của nhà nước về phẫu thuật.

Một trình thông dịch bắt đầu ở dòng mã đầu tiên trong một chương trình, tính toán môi trường của nó, chạy dòng trong môi trường đó và nắm bắt hiệu ứng của nó đối với trạng thái của chương trình. Sau đó, nó tuân theo luồng điều khiển của chương trình để thực thi dòng mã tiếp theo và lặp lại quy trình cho đến khi chương trình kết thúc.

Cách bạn tính toán môi trường cho bất kỳ hoạt động nào là thông qua một bộ quy tắc chính thức được xác định bởi ngôn ngữ lập trình. Thuật ngữ "ràng buộc" thường được sử dụng để mô tả ánh xạ trạng thái tổng thể của chương trình tới một giá trị trong môi trường. Lưu ý rằng theo "trạng thái tổng thể", chúng tôi không có nghĩa là trạng thái toàn cầu, mà là tổng số của mọi định nghĩa có thể tiếp cận, tại bất kỳ thời điểm nào trong quá trình thực thi).

Đây là khuôn khổ trong đó vấn đề phạm vi được xác định. Bây giờ đến phần tiếp theo của những lựa chọn của chúng tôi.

  • Là người triển khai trình thông dịch, bạn có thể đơn giản hóa nhiệm vụ của mình bằng cách làm cho môi trường càng gần với trạng thái của chương trình. Theo đó, môi trường của một dòng mã sẽ được xác định đơn giản bởi môi trường của dòng mã trước đó với các hiệu ứng của hoạt động đó được áp dụng cho nó, bất kể dòng trước đó có phải là gán, gọi hàm, trả về từ hàm, hoặc một cấu trúc điều khiển như vòng lặp while.

Đây là ý chính của phạm vi động , trong đó môi trường mà bất kỳ mã nào chạy vào đều bị ràng buộc với trạng thái của chương trình như được xác định bởi bối cảnh thực thi của nó.

  • Hoặc , bạn có thể nghĩ về một lập trình viên sử dụng ngôn ngữ của bạn và đơn giản hóa nhiệm vụ của họ là theo dõi các giá trị mà một biến có thể thực hiện. Có quá nhiều con đường và quá nhiều sự phức tạp liên quan đến lý luận về kết quả của toàn bộ quá trình thực hiện trong quá khứ. Phạm vi từ vựng giúp thực hiện điều này bằng cách giới hạn môi trường hiện tại ở phần trạng thái được xác định trong khối hiện tại, hàm hoặc đơn vị phạm vi khác và cha mẹ của nó (tức là khối bao quanh đồng hồ hiện tại hoặc hàm được gọi là hàm hiện tại).

Nói cách khác, với phạm vi từ vựng , môi trường mà bất kỳ mã nào nhìn thấy đều bị ràng buộc với trạng thái được liên kết với một phạm vi được xác định rõ ràng trong ngôn ngữ, chẳng hạn như một khối hoặc một hàm.


0

Câu hỏi cổ xưa, nhưng đây là tôi đảm nhận nó.

Phạm vi từ vựng (tĩnh) đề cập đến phạm vi của một biến trong mã nguồn .

Trong một ngôn ngữ như JavaScript, nơi các hàm có thể được truyền qua và được gắn và gắn lại vào các đối tượng linh tinh, bạn có thể có mặc dù phạm vi đó sẽ phụ thuộc vào việc ai gọi hàm đó vào thời điểm đó, nhưng không. Thay đổi phạm vi theo cách đó sẽ là phạm vi động và JavaScript không thực hiện điều đó, ngoại trừ có thể vớithis tham chiếu đối tượng.

Để minh họa điểm:

var a='apple';

function doit() {
    var a='aardvark';
    return function() {
        alert(a);
    }
}

var test=doit();
test();

Trong ví dụ này, biến ađược định nghĩa trên toàn cầu, nhưng bị che khuất trong doit()hàm. Hàm này trả về một hàm khác, như bạn thấy, dựa vàoa biến ngoài phạm vi của chính nó.

Nếu bạn chạy cái này, bạn sẽ thấy rằng giá trị được sử dụng aardvark, không phải giá trị appleđó, mặc dù nó nằm trong phạm vi củatest() hàm, không nằm trong phạm vi từ vựng của hàm ban đầu. Nghĩa là, phạm vi được sử dụng là phạm vi như nó xuất hiện trong mã nguồn, không phải là phạm vi mà hàm thực sự được sử dụng.

Thực tế này có thể có hậu quả gây phiền nhiễu. Ví dụ: bạn có thể quyết định rằng việc tổ chức các chức năng của mình một cách dễ dàng hơn và sau đó sử dụng chúng khi đến lúc, chẳng hạn như trong trình xử lý sự kiện:

var a='apple',b='banana';

function init() {
  var a='aardvark',b='bandicoot';
  document.querySelector('button#a').onclick=function(event) {
    alert(a);
  }
  document.querySelector('button#b').onclick=doB;
}

function doB(event) {
  alert(b);
}

init();
<button id="a">A</button>
<button id="b">B</button>

Mẫu mã này làm một trong mỗi. Bạn có thể thấy rằng do phạm vi từ vựng, nút Asử dụng biến trong, nút whileB thì không. Bạn có thể kết thúc các chức năng lồng nhau nhiều hơn bạn muốn.

Nhân tiện, trong cả hai ví dụ, bạn cũng sẽ nhận thấy rằng các biến có phạm vi từ vựng bên trong vẫn tồn tại mặc dù hàm hàm chứa đã chạy khóa học của nó. Điều này được gọi là bao đóng và đề cập đến quyền truy cập của hàm lồng nhau vào các biến ngoài, ngay cả khi hàm ngoài đã kết thúc. JavaScript cần phải đủ thông minh để xác định xem các biến đó có còn cần thiết hay không và nếu không, rác có thể thu thập chúng hay không.


-1

Tôi thường học bằng ví dụ, và đây là một vài điều:

const lives = 0;

function catCircus () {
    this.lives = 1;
    const lives = 2;

    const cat1 = {
        lives: 5,
        jumps: () => {
            console.log(this.lives);
        }
    };
    cat1.jumps(); // 1
    console.log(cat1); // { lives: 5, jumps: [Function: jumps] }

    const cat2 = {
        lives: 5,
        jumps: () => {
            console.log(lives);
        }
    };
    cat2.jumps(); // 2
    console.log(cat2); // { lives: 5, jumps: [Function: jumps] }

    const cat3 = {
        lives: 5,
        jumps: () => {
            const lives = 3;
            console.log(lives);
        }
    };
    cat3.jumps(); // 3
    console.log(cat3); // { lives: 5, jumps: [Function: jumps] }

    const cat4 = {
        lives: 5,
        jumps: function () {
            console.log(lives);
        }
    };
    cat4.jumps(); // 2
    console.log(cat4); // { lives: 5, jumps: [Function: jumps] }

    const cat5 = {
        lives: 5,
        jumps: function () {
            var lives = 4;
            console.log(lives);
        }
    };
    cat5.jumps(); // 4
    console.log(cat5); // { lives: 5, jumps: [Function: jumps] }

    const cat6 = {
        lives: 5,
        jumps: function () {
            console.log(this.lives);
        }
    };
    cat6.jumps(); // 5
    console.log(cat6); // { lives: 5, jumps: [Function: jumps] }

    const cat7 = {
        lives: 5,
        jumps: function thrownOutOfWindow () {
            console.log(this.lives);
        }
    };
    cat7.jumps(); // 5
    console.log(cat7); // { lives: 5, jumps: [Function: thrownOutOfWindow] }
}

catCircus();

-1

Chủ đề này liên quan chặt chẽ với bindchức năng tích hợp và được giới thiệu trong Chức năng mũi tên ECMAScript 6 . Điều đó thực sự gây phiền nhiễu, bởi vì đối với mọi phương thức "lớp" (hàm thực sự) mới mà chúng tôi muốn sử dụng, chúng tôi phải thực hiện bindđiều này để có quyền truy cập vào phạm vi.

JavaScript theo mặc định không đặt phạm vi thischức năng của nó (nó không đặt bối cảnh trên this). Theo mặc định, bạn phải nói rõ ràng bối cảnh bạn muốn có.

Các hàm mũi tên tự động được gọi là phạm vi từ vựng (có quyền truy cập vào định nghĩa của biến trong khối chứa của nó). Khi sử dụng các chức năng mũi tên, nó sẽ tự động liên kết thisđến vị trí nơi chức năng mũi tên được xác định ở vị trí đầu tiên và bối cảnh của chức năng mũi tên này này là khối chứa.

Xem cách nó hoạt động trong thực tế trên các ví dụ đơn giản nhất dưới đây.

Trước Hàm mũi tên (không có phạm vi từ vựng theo mặc định):

const programming = {
  language: "JavaScript",
  getLanguage: function() {
    return this.language;
  }
}

const globalScope = programming.getLanguage;
console.log(globalScope()); // Output: undefined

const localScope = programming.getLanguage.bind(programming);
console.log(localScope()); // Output: "JavaScript"

Với các hàm mũi tên (phạm vi từ vựng theo mặc định):

const programming = {
  language: "JavaScript",
  getLanguage: function() {
    return this.language;
  }
}

const arrowFunction = () => {
    console.log(programming.getLanguage());
}

arrowFunction(); // Output: "JavaScript"
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.