4

Nếu bạn đã lập trình bằng JavaScript trong bất kỳ khoảng thời gian nào, có lẽ bạn đã gặp phải một số vấn đề về phạm vi biến JavaScript. Bạn thậm chí có thể sửa chúng. Nhưng, bạn có thể ngăn chặn các vấn đề này nếu bạn hiểu rõ hơn lý do tại sao các vấn đề này tồn tại ở nơi đầu tiên.

Mục tiêu của tôi, thông qua một loạt các bài đăng trên blog về chủ đề này, là làm cho tất cả chúng ta lập trình JavaScript tốt hơn. JavaScript không còn là một món đồ chơi. Những người sống sót trong hệ sinh thái JavaScript mới sẽ là những người hiểu tại sao JavaScript hoạt động theo cách đó.

Tôi sẽ tiếp cận chủ đề này như một loạt các câu đố. Điều này sẽ chỉ ra vấn đề, và sau đó chúng ta có thể thảo luận tại sao vấn đề tồn tại và phải làm gì với nó.

Câu đố 1


var a = 'abc';  

function foo()
 {
     console.log(a);

     var a = 'xyz';
     console.log(a);
  }

 foo();

Cho mã ở trên, giá trị của adòng 5 là gì và giá trị của adòng 8 là gì?

(Chơi nhạc Jeopardy tại đây)

ĐƯỢC. Hết giờ rồi. Bạn nghĩ sao?

Nếu bạn nói rằng dòng 5 có giá trị 'abc', bạn sẽ sai. Nhưng, tôi hoàn toàn hiểu tại sao bạn sẽ nghĩ như vậy. Tôi nghĩ mọi người sẽ đồng ý rằng giá trị ở dòng 8 bây giờ là 'xyz'. Vì vậy, chúng tôi sẽ bỏ qua điều đó.

Tại sao không phải là giá trị ở dòng 5 'abc'?

Tời kéo

Điều đầu tiên chúng ta cần hiểu về các biến là bất kể chúng được khai báo ở đâu, khai báo luôn luôn được 'nâng lên' trên đỉnh của khối phạm vi mà biến được khai báo. Việc gán xảy ra khi chúng ta viết mã.

Vì vậy, hãy viết lại mã của chúng tôi để nó trông giống như những gì đang thực sự xảy ra.

var a = 'abc';

function foo()
{
var a;
  console.log(a);

    a = 'xyz';
    console.log(a);
 }

 foo();

Viết theo cách này, rõ ràng  a là không xác định hoặc null ở dòng 6. Đó là gì?

Không xác định hay Null?

Điều này luôn luôn làm tôi bối rối. Chủ yếu là vì trong mọi ngôn ngữ khác mà tôi làm việc cùng, nếu một biến được khai báo nhưng không được gán, nó hầu như luôn luôn là null. Lần duy nhất nó sẽ không được xác định là nếu tôi đã không khai báo nó.

Trong JavaScript, mọi thứ đều khác nhau.

Trong một ngôn ngữ được gõ mạnh, chúng ta biết nhiều hơn về các loại biến của mình khi biến được khai báo. Vì vậy, nếu chúng ta khai báo một biến là một loại đối tượng, nó được gán null theo mặc định. Nhưng, các loại giá trị bị loại bỏ không phải lúc nào cũng không có giá trị.

Trong JavaScript, chúng tôi không biết loại biến cho đến khi nó được chỉ định. Vì vậy, tất cả những gì chúng ta biết khi chúng ta khai báo một biến với từ khóa var là có một biến. Nhưng, loại biến không xác định. Do đó, bất kỳ biến nào chưa được chỉ định sẽ không được xác định không phải là null.

Câu đố 2

Hãy di chuyển một số mã xung quanh một chút.

var a = 'abc';

function foo(){
a = 'xyz';
}

foo();

console.log(a);

Giá trị của  a dòng 9 là gì?

Tôi hy vọng đây là một câu đố dễ dàng hơn để giải quyết. Lưu ý rằng chúng tôi gọi  foo() ở dòng 7, vì vậy khi chúng tôi trở về từ foo() a bây giờ sẽ giữ giá trị của 'xyz' vì chúng tôi đã không tái phân phối biến bên trong  foo(). Do phạm vi biến, đã  a gán biến được khai báo ở dòng 1.

Câu đố 3

Một lần nữa, hãy di chuyển một số mã xung quanh.

function foo(){
a = 'xyz';
}

foo();

console.log(a);

Lưu ý rằng trong trường hợp này, chúng tôi đã không khai báo biến nào  a cả. Chúng tôi vừa gán 'xyz' cho nó bên trong  foo(). Vậy, giá trị của  a dòng 7 là gì?

Lựa chọn của chúng tôi là gì?

  1. Mã sẽ không chạy.
  2. a === 'xyz'
  3. a không định nghĩa được.

Nếu bạn nghĩ rằng mã sẽ không chạy, bạn sẽ sai và có lẽ bạn đã không được mã hóa bằng JavaScript rất lâu. Mã sẽ chạy.

Vì vậy, câu hỏi tiếp theo bạn phải tự hỏi là nơi nào sẽ ađược xác định? Vì chúng tôi chưa khai báo, nên nó phải tự động được khai báo ở một nơi nào đó. Đó là bên trong foo()hoặc một nơi nào khác?

Câu trả lời là một nơi khác. Quy tắc là thế này, nếu biến chưa được khai báo trong phạm vi nó đang được sử dụng, biến được khai báo là biến toàn cục. Trong trình duyệt, điều này tạo ra một thuộc tính treo trên đối tượng cửa sổ.

Và vì vậy, câu trả lời hợp lệ duy nhất là a === ‘xyz’ở dòng 7.

Đừng tự bắn vào chân mình

Như bạn có thể tưởng tượng, nếu bạn không cẩn thận, bạn thực sự có thể gặp rắc rối. Điều thú vị về máy tính, họ làm chính xác  những gì họ được nói. Nó thực sự không quan trọng những gì bạn nghĩ nó nên làm.

Nhưng, có một cách để ngăn chặn một số vấn đề ở trên.

Bằng cách thêm "use strict";dưới dạng một dòng trong mã của bạn, nhiều lỗi phổ biến mà chúng tôi mắc phải trong khi lập trình bằng JavaScript sẽ được đưa ra dưới dạng ngoại lệ.

Một thứ khác bạn nên đặt trong kho vũ khí của mình là một công cụ như jsHint mà bạn có thể nhận được bằng cách sử dụng WebEssentials trong phòng thu trực quan. Điều này sẽ cho bạn biết khi bạn đã làm những điều có thể không đúng.

Nhân tiện, sử dụng jsHint thay vì jsLint. jsLint là cách quá quan điểm. Ví dụ, tôi hiểu quan điểm của ông Crawford về việc buộc sử dụng câu lệnh ngắt trong khối chuyển đổi / trường hợp. Nhưng thực sự? Tôi sẽ có thể tắt nó ở những nơi không có tuyên bố phá vỡ chính xác là những gì tôi muốn. jsHint cung cấp cho bạn sự linh hoạt này.

|