Có sự khác biệt nào giữa việc khai báo một biến không:
var a=0; //1
...cách này:
a=0; //2
...hoặc là:
window.a=0; //3
trong phạm vi toàn cầu?
Có sự khác biệt nào giữa việc khai báo một biến không:
var a=0; //1
...cách này:
a=0; //2
...hoặc là:
window.a=0; //3
trong phạm vi toàn cầu?
Câu trả lời:
Vâng, có một vài sự khác biệt, mặc dù về mặt thực tế, chúng thường không phải là lớn.
Có một cách thứ tư, và kể từ ES2015 (ES6) có thêm hai cách nữa. Tôi đã thêm cách thứ tư vào cuối, nhưng đã chèn các cách ES2015 sau # 1 (bạn sẽ thấy lý do tại sao), vì vậy chúng tôi có:
var a = 0; // 1
let a = 0; // 1.1 (new with ES2015)
const a = 0; // 1.2 (new with ES2015)
a = 0; // 2
window.a = 0; // 3
this.a = 0; // 4
# 1 var a = 0;
Điều này tạo ra một biến toàn cục cũng là một thuộc tính của đối tượng toàn cầu , mà chúng ta truy cập như windowtrên các trình duyệt (hoặc thông qua thisphạm vi toàn cầu, trong mã không nghiêm ngặt). Không giống như một số tài sản khác, tài sản không thể được gỡ bỏ thông qua delete.
Theo thuật ngữ đặc tả, nó tạo ra một định danh ràng buộc trên Bản ghi môi trường đối tượng cho môi trường toàn cầu . Điều đó làm cho nó trở thành một tài sản của đối tượng toàn cầu bởi vì đối tượng toàn cầu là nơi các ràng buộc định danh cho đối tượng Môi trường toàn cầu được ghi lại. Đây là lý do tại sao tài sản không thể xóa được: Nó không chỉ là một thuộc tính đơn giản, nó là một ràng buộc định danh.
Liên kết (biến) được xác định trước khi dòng mã đầu tiên chạy (xem "Khi varxảy ra" bên dưới).
Lưu ý rằng trên IE8 trở về trước, thuộc tính được tạo trên windowkhông thể đếm được (không hiển thị trong các for..inbáo cáo). Trong IE9, Chrome, Firefox và Opera, nó là vô số.
# 1.1 let a = 0;
Điều này tạo ra một biến toàn cục không phải là một thuộc tính của đối tượng toàn cầu. Đây là một điều mới kể từ ES2015.
Trong các thuật ngữ đặc tả, nó tạo ra một định danh ràng buộc trên Bản ghi môi trường khai báo cho môi trường toàn cầu thay vì Bản ghi môi trường đối tượng . Môi trường toàn cầu độc đáo ở chỗ có một sự chia rẽ Môi trường Ghi, một cho tất cả những thứ cũ mà đi trên đối tượng toàn cầu (các đối tượng môi trường Record) và một cho tất cả các công cụ mới ( let, const, và các chức năng được tạo ra bởi class) mà không làm đi vào đối tượng toàn cầu.
Liên kết được tạo trước khi bất kỳ mã từng bước nào trong khối kèm theo của nó được thực thi (trong trường hợp này, trước khi bất kỳ mã toàn cầu nào chạy), nhưng nó không thể truy cập theo bất kỳ cách nào cho đến khi thực thi từng bước đạt được letcâu lệnh. Khi thực hiện đạt đến letcâu lệnh, biến có thể truy cập. (Xem phần "Khi nào letvà constxảy ra" bên dưới.)
# 1.2 const a = 0;
Tạo một hằng số toàn cầu, không phải là một tài sản của đối tượng toàn cầu.
consthoàn toàn giống như letngoại trừ việc bạn phải cung cấp bộ khởi tạo ( = valuephần) và bạn không thể thay đổi giá trị của hằng số khi nó được tạo. Dưới vỏ bọc, nó chính xác như thế letnhưng với một cờ trên ràng buộc định danh cho biết giá trị của nó không thể thay đổi. Sử dụng constba điều cho bạn:
# 2 a = 0;
Điều này tạo ra một tài sản trên đối tượng toàn cầu ngầm . Vì nó là một tài sản bình thường, bạn có thể xóa nó. Tôi khuyên bạn không nên làm điều này, nó có thể không rõ ràng với bất cứ ai đọc mã của bạn sau này. Nếu bạn sử dụng chế độ nghiêm ngặt của ES5, thực hiện việc này (gán cho biến không tồn tại) là một lỗi. Đó là một trong nhiều lý do để sử dụng chế độ nghiêm ngặt.
Và thật thú vị, một lần nữa trên IE8 trở về trước, tài sản được tạo ra không thể đếm được (không hiển thị trong các for..inbáo cáo). Điều đó thật kỳ quặc, đặc biệt được đưa ra # 3 dưới đây.
# 3 window.a = 0;
Điều này tạo ra một thuộc tính trên đối tượng toàn cầu một cách rõ ràng, sử dụng windowtoàn cục đề cập đến đối tượng toàn cầu (trên trình duyệt; một số môi trường không có trình duyệt có biến toàn cục tương đương, chẳng hạn như globaltrên NodeJS). Vì nó là một tài sản bình thường, bạn có thể xóa nó.
Thuộc tính này là vô số , trên IE8 trở về trước và trên mọi trình duyệt khác mà tôi đã thử.
#4 this.a = 0;
Chính xác như # 3, ngoại trừ chúng tôi đang tham khảo đối tượng toàn cầu thông qua thisthay vì toàn cầu window. Tuy nhiên, điều này sẽ không hoạt động ở chế độ nghiêm ngặt, bởi vì trong mã toàn cầu ở chế độ nghiêm ngặt, thiskhông có tham chiếu đến đối tượng toàn cầu ( undefinedthay vào đó nó có giá trị ).
Ý tôi là gì khi "xóa" hoặc "xóa" a? Chính xác là: Xóa thuộc tính (hoàn toàn) thông qua deletetừ khóa:
window.a = 0;
display("'a' in window? " + ('a' in window)); // displays "true"
delete window.a;
display("'a' in window? " + ('a' in window)); // displays "false"
deleteloại bỏ hoàn toàn một tài sản từ một đối tượng. Bạn không thể làm điều đó với các thuộc tính được thêm vào windowgián tiếp thông qua var, deletesẽ âm thầm bị bỏ qua hoặc ném một ngoại lệ (tùy thuộc vào việc triển khai JavaScript và liệu bạn có ở chế độ nghiêm ngặt không).
Cảnh báo : IE8 một lần nữa (và có lẽ sớm hơn và IE9-IE11 ở chế độ "tương thích" bị hỏng): Nó sẽ không cho phép bạn xóa các thuộc tính của windowđối tượng, ngay cả khi bạn được phép. Tồi tệ hơn, nó ném một ngoại lệ khi bạn thử ( thử trải nghiệm này trong IE8 và trong các trình duyệt khác). Vì vậy, khi xóa khỏi windowđối tượng, bạn phải phòng thủ:
try {
delete window.prop;
}
catch (e) {
window.prop = undefined;
}
Điều đó cố gắng xóa tài sản, và nếu một ngoại lệ được ném ra, nó sẽ làm điều tốt nhất tiếp theo và đặt thuộc tính undefined.
Điều này chỉ áp dụng cho windowđối tượng và chỉ (theo như tôi biết) đối với IE8 trở về trước (hoặc IE9-IE11 ở chế độ "tương thích" bị hỏng). Các trình duyệt khác đều ổn với việc xóa các windowthuộc tính, tuân theo các quy tắc ở trên.
varxảy raCác biến được xác định thông qua vartuyên bố được tạo ra trước khi bất kỳ đang từng bước trong bối cảnh thực hiện được điều hành, và do đó tài sản tồn tại tốt trước các vartuyên bố.
Điều này có thể gây nhầm lẫn, vì vậy hãy xem:
display("foo in window? " + ('foo' in window)); // displays "true"
display("window.foo = " + window.foo); // displays "undefined"
display("bar in window? " + ('bar' in window)); // displays "false"
display("window.bar = " + window.bar); // displays "undefined"
var foo = "f";
bar = "b";
display("foo in window? " + ('foo' in window)); // displays "true"
display("window.foo = " + window.foo); // displays "f"
display("bar in window? " + ('bar' in window)); // displays "true"
display("window.bar = " + window.bar); // displays "b"
Ví dụ trực tiếp:
Như bạn có thể thấy, biểu tượng foođược xác định trước dòng đầu tiên, nhưng biểu tượng barthì không. Trường hợp var foo = "f";câu lệnh là, thực sự có hai điều: xác định ký hiệu, xảy ra trước khi dòng mã đầu tiên được chạy; và thực hiện một nhiệm vụ cho biểu tượng đó, xảy ra khi dòng nằm trong luồng từng bước. Điều này được gọi là " varcẩu" vì var foophần được di chuyển ("Palăng") lên trên cùng của phạm vi, nhưng foo = "f"phần được để lại ở vị trí ban đầu. (Xem Nghèo bị hiểu lầmvar trên blog nhỏ thiếu máu của tôi.)
letvà constxảy raletvà constkhác với varmột vài cách Cách liên quan đến câu hỏi là mặc dù ràng buộc mà họ xác định được tạo ra trước khi bất kỳ mã từng bước nào chạy, nhưng nó không thể truy cập được cho đến khi đạt được câu lệnh lethoặc constcâu lệnh.
Vì vậy, trong khi điều này chạy:
display(a); // undefined
var a = 0;
display(a); // 0
Điều này đưa ra một lỗi:
display(a); // ReferenceError: a is not defined
let a = 0;
display(a);
Hai cách khác letvà constkhác với var, không thực sự phù hợp với câu hỏi, là:
varluôn luôn áp dụng cho toàn bộ bối cảnh thực hiện (trong cả mã toàn cầu, hoặc trong cả mã chức năng trong hàm mà nó xuất hiện), nhưng letvà constchỉ áp dụng trong phạm vi khối nơi họ xuất hiện. Đó là, varcó chức năng (hoặc toàn cầu) phạm vi, nhưng letvà constcó phạm vi khối.
Lặp lại var atrong cùng một bối cảnh là vô hại, nhưng nếu bạn có let a(hoặc const a), có một let ahoặc một const ahoặc một var alỗi là một cú pháp.
Dưới đây là một ví dụ chứng minh điều đó letvà constcó hiệu lực ngay lập tức trong khối của họ trước khi bất kỳ mã nào trong khối đó chạy, nhưng không thể truy cập được cho đến khi câu lệnh lethoặc const:
var a = 0;
console.log(a);
if (true)
{
console.log(a); // ReferenceError: a is not defined
let a = 1;
console.log(a);
}
Lưu ý rằng lần thứ hai console.logkhông thành công, thay vì truy cập atừ bên ngoài khối.
window)Đối windowtượng được rất, rất lộn xộn với các thuộc tính. Bất cứ khi nào có thể, mạnh mẽ đề nghị không thêm vào mớ hỗn độn. Thay vào đó, hãy gói các biểu tượng của bạn trong một gói nhỏ và xuất tối đa một biểu tượng cho windowđối tượng. (Tôi thường không xuất bất kỳ ký hiệu nào cho windowđối tượng.) Bạn có thể sử dụng một hàm để chứa tất cả mã của mình để chứa các ký hiệu của bạn và hàm đó có thể ẩn danh nếu bạn muốn:
(function() {
var a = 0; // `a` is NOT a property of `window` now
function foo() {
alert(a); // Alerts "0", because `foo` can access `a`
}
})();
Trong ví dụ đó, chúng tôi xác định một hàm và thực hiện nó ngay lập tức ( ()ở cuối).
Một hàm được sử dụng theo cách này thường được gọi là hàm phạm vi . Các hàm được xác định trong hàm phạm vi có thể truy cập các biến được xác định trong hàm phạm vi vì chúng đóng trên dữ liệu đó (xem: Đóng không phức tạp trên blog nhỏ thiếu máu của tôi).
window['a']=0để làm rõ rằng tôi đang sử dụng cửa sổ làm bản đồ không? có gì windowđặc biệt khi một số trình duyệt không cho phép điều này và buộc tôi sử dụng window.a?
window.a = 0;chỉ hoạt động trong môi trường trình duyệt và chỉ theo quy ước. Liên kết đối tượng toàn cầu với một biến có tên windowkhông có trong ES Spec và do đó sẽ không hoạt động, ví dụ, V8 hoặc Node.js, trong khi this.a = 0;(khi được gọi trong bối cảnh thực thi toàn cầu) sẽ hoạt động trong mọi môi trường do thông số kỹ thuật đó chỉ định rằng phải có một đối tượng toàn cầu. Nếu gói mã của bạn trong IIFE như trong phần Off-topic , bạn có thể chuyển thisdưới dạng tham số có tên windowhoặc globalđể có được tham chiếu trực tiếp đến đối tượng toàn cục.
var a = 0;tự động trở thành một thuộc tính của đối tượng toàn cầu. Nếu tôi khai báo var b = 0;trong một khai báo hàm, nó cũng sẽ là một thuộc tính của một số đối tượng cơ bản?
Giữ cho nó đơn giản:
a = 0
Đoạn mã trên cung cấp một biến phạm vi toàn cầu
var a = 0;
Mã này sẽ đưa ra một biến được sử dụng trong phạm vi hiện tại và theo nó
window.a = 0;
Điều này thường giống như biến toàn cục.
a dưới các phạm vi hiện tại. Bạn có thể. Ngoài ra, việc bạn sử dụng "biến toàn cục" là một chút - hai nơi bạn nói "biến toàn cầu" không toàn cầu hơn nơi bạn không nói.
<title>Index.html</title>
<script>
var varDeclaration = true;
noVarDeclaration = true;
window.hungOnWindow = true;
document.hungOnDocument = true;
</script>
<script src="external.js"></script>
/* external.js */
console.info(varDeclaration == true); // could be .log, alert etc
// returns false in IE8
console.info(noVarDeclaration == true); // could be .log, alert etc
// returns false in IE8
console.info(window.hungOnWindow == true); // could be .log, alert etc
// returns true in IE8
console.info(document.hungOnDocument == true); // could be .log, alert etc
// returns ??? in IE8 (untested!) *I personally find this more clugy than hanging off window obj
Có một đối tượng toàn cầu mà tất cả các vars được treo theo mặc định? ví dụ: 'globalals.noVar tuyên bố'
window.*khai báo. Tuyên bố này có vẻ an toàn nhất đối với việc sao chép mã của bạn và cũng rõ ràng.
Bassed trên câu trả lời tuyệt vời của TJ Crowder : ( Off-topic: Tránh lộn xộnwindow )
Đây là một ví dụ về ý tưởng của anh ấy:
Html
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="init.js"></script>
<script type="text/javascript">
MYLIBRARY.init(["firstValue", 2, "thirdValue"]);
</script>
<script src="script.js"></script>
</head>
<body>
<h1>Hello !</h1>
</body>
</html>
init.js (Dựa trên câu trả lời này )
var MYLIBRARY = MYLIBRARY || (function(){
var _args = {}; // private
return {
init : function(Args) {
_args = Args;
// some other initialising
},
helloWorld : function(i) {
return _args[i];
}
};
}());
script.js
// Here you can use the values defined in the html as if it were a global variable
var a = "Hello World " + MYLIBRARY.helloWorld(2);
alert(a);
Đây là plnkr . Hy vọng nó sẽ giúp!
Trong phạm vi toàn cầu không có sự khác biệt về ngữ nghĩa.
Nhưng bạn thực sự nên tránh a=0vì bạn đặt giá trị cho một biến không được khai báo.
Đồng thời sử dụng các bao đóng để tránh chỉnh sửa phạm vi toàn cầu
(function() {
// do stuff locally
// Hoist something to global scope
window.someGlobal = someLocal
}());
Luôn luôn sử dụng các bao đóng và luôn luôn nâng lên phạm vi toàn cầu khi nó hoàn toàn cần thiết. Bạn nên sử dụng xử lý sự kiện không đồng bộ cho hầu hết các giao tiếp của bạn.
Như @AvianMoncellor đã đề cập, có một lỗi IE với var a = foo chỉ khai báo toàn cầu cho phạm vi tệp. Đây là một vấn đề với trình thông dịch bị hỏng khét tiếng của IE. Lỗi này nghe có vẻ quen thuộc nên có lẽ đúng.
Vì vậy, dính vào window.globalName = someLocalpointer
deletea var).
var. Chúng chỉ là những cơ chế hoàn toàn khác nhau có cùng kết quả thực tế. :-)
varnhảy đến điểm dừng của phạm vi.