Làm cách nào để bỏ đặt một biến JavaScript?


592

Tôi có một biến toàn cục trong JavaScript (thực ra là một thuộc windowtính, nhưng tôi không nghĩ nó có vấn đề) đã được tạo bởi tập lệnh trước nhưng tôi không muốn một tập lệnh khác chạy sau để thấy giá trị của nó hoặc thậm chí nó còn xác định.

Tôi đã đặt some_var = undefinedvà nó hoạt động cho mục đích thử nghiệm typeof some_var == "undefined"nhưng tôi thực sự không nghĩ rằng đó là cách đúng đắn để thực hiện.

Bạn nghĩ sao?

Câu trả lời:


454

Các deletenhà điều hành loại bỏ một tài sản từ một đối tượng. Nó không thể loại bỏ một biến. Vì vậy, câu trả lời cho câu hỏi phụ thuộc vào cách xác định biến toàn cục hoặc thuộc tính.

(1) Nếu nó được tạo bằng var, nó không thể bị xóa.

Ví dụ:

var g_a = 1; //create with var, g_a is a variable 
delete g_a; //return false
console.log(g_a); //g_a is still 1

(2) Nếu nó được tạo mà không có var, nó có thể bị xóa.

g_b = 1; //create without var, g_b is a property 
delete g_b; //return true
console.log(g_b); //error, g_b is not defined

Giải thích kỹ thuật

1. Sử dụng var

Trong trường hợp này, tham chiếu g_ađược tạo trong cái mà đặc tả ECMAScript gọi là " Biến môi trường " được gắn với phạm vi hiện tại - đây có thể là bối cảnh thực thi chức năng trong trường hợp sử dụng varbên trong một hàm (mặc dù nó có thể phức tạp hơn một chút khi bạn xem xét let) hoặc trong trường hợp mã "toàn cầu", Var biếnEn Môi trường được gắn vào đối tượng toàn cầu (thường window).

Các tham chiếu trong Biến môi trường thường không thể xóa được - quy trình chi tiết trong ECMAScript 10.5 giải thích điều này một cách chi tiết, nhưng đủ để nói rằng trừ khi mã của bạn được thực thi trong evalngữ cảnh (mà hầu hết các bảng điều khiển phát triển dựa trên trình duyệt sử dụng), thì các biến được khai báo varkhông thể bị xóa

2. Không sử dụng var

Khi cố gắng gán giá trị cho tên mà không sử dụng vartừ khóa, Javascript sẽ cố gắng xác định vị trí tham chiếu được đặt tên trong thông số ECMAScript gọi là " LexicalEn Môi trường " và sự khác biệt chính là LexicalE Môi trường được lồng vào nhau - đó là một LexicalEn Môi trường có cha mẹ ( những gì ECMAScript đặc tả các cuộc gọi "tài liệu tham khảo môi trường bên ngoài") và khi javscript thất bại trong việc xác định vị trí tài liệu tham khảo trong một LexicalEenvironment , có vẻ trong các phụ huynh LexicalEnvironment (như chi tiết trong 10.3.110.2.2.1 ). Mức đầu LexicalEnvironment là " môi trường toàn cầu", và điều đó bị ràng buộc với đối tượng toàn cầu trong đó các tham chiếu của nó là thuộc tính của đối tượng toàn cầu. Vì vậy, nếu bạn cố gắng truy cập một tên không được khai báo bằng vartừ khóa trong phạm vi hiện tại hoặc bất kỳ phạm vi bên ngoài nào, Javascript cuối cùng sẽ tìm nạp một thuộc tính của windowđối tượng để phục vụ như tham chiếu đó. Như chúng ta đã học trước đây, các thuộc tính trên các đối tượng có thể bị xóa.

Ghi chú

  1. Điều quan trọng cần nhớ là các varkhai báo bị "treo" - tức là chúng luôn được coi là đã xảy ra khi bắt đầu phạm vi mà chúng nằm trong - mặc dù không phải là khởi tạo giá trị có thể được thực hiện trong một varcâu lệnh - đó là để lại ở đó . Vì vậy, trong đoạn mã sau, alà một tham chiếu từ Var biếnEn Môi trường chứ không phải thuộc windowtính và giá trị của nó sẽ 10ở cuối mã:

    function test() { a = 5; var a = 10; }

  2. Các cuộc thảo luận ở trên là khi "chế độ nghiêm ngặt" không được kích hoạt. Các quy tắc tra cứu hơi khác một chút khi sử dụng "chế độ nghiêm ngặt" và các tham chiếu từ vựng đã giải quyết các thuộc tính cửa sổ mà không có "chế độ nghiêm ngặt" sẽ đưa ra các lỗi "biến không khai báo" trong "chế độ nghiêm ngặt". Tôi thực sự không hiểu nơi này được chỉ định, nhưng đó là cách trình duyệt hoạt động.


8
Những gì bạn nói là một quan niệm sai lầm phổ biến nhưng thực sự không chính xác - trong Javascript không có "biến toàn cục". Các biến được định nghĩa không có phạm vi rõ ràng (chẳng hạn như sử dụng varbên ngoài hàm) là các thuộc tính của "đối tượng toàn cầu", trong trình duyệt web là window. Vì vậy - var a = 1; delete window.a; console.log(a);sẽ xóa thành công biến và khiến dòng cuối cùng phát sinh lỗi tham chiếu.
Guss

7
@Guss, mã của bạn var a = 1; delete window.a; console.log(a);hiển thị 1.
Dayong

5
Tôi đang sử dụng Google Chrome v36. Tôi đã thử nghiệm trên các trình duyệt khác. Có vẻ như nó không nhất quán trình duyệt chéo. Chrome và Opera hiển thị 1, trong khi Firefox, Safari và IE 11 trên máy tính của tôi bị lỗi.
Dayong

3
Ok, sai lầm của tôi. Xem ecma-i Intl.org/ecma-262/5.1/#sec-10.5 (điểm phụ 2 và 8.c.ii): Khi chạy thử nghiệm của tôi trong bảng điều khiển dành cho nhà phát triển, nó thường được coi là "bối cảnh tệ hại" (mặc dù có thể không có trong Chrome), do đó, nó sẽ phát sinh lỗi. Cùng một mã trong bối cảnh toàn cầu của một tài liệu thực sẽ xuất ra 1chính xác trong tất cả các trình duyệt. Chạy trong các tài liệu thực, ví dụ mã của bạn là chính xác. Tôi đã chọn câu trả lời của bạn là chính xác, nhưng tôi đánh giá cao nó nếu bạn có thể chỉnh sửa nó để bao gồm giải thích window.a = 1; delete window.a;và có thể là cơ chế. Tôi cũng có thể làm như vậy nếu bạn không phiền.
Guss

2
@KlaiderKlai có. Các biến phạm vi chức năng được tạo và hủy mỗi khi hàm được thực thi. Có lẽ đóng cửa là một ngoại lệ.
Dayong

278

Câu trả lời của @ scunlife sẽ hoạt động, nhưng về mặt kỹ thuật thì phải như vậy

delete window.some_var; 

xóa được coi là không có tác dụng khi mục tiêu không phải là thuộc tính đối tượng. ví dụ,

(function() {
   var foo = 123;
   delete foo; // wont do anything, foo is still 123
   var bar = { foo: 123 };
   delete bar.foo; // foo is gone
}());

Nhưng vì các biến toàn cục thực sự là thành viên của đối tượng cửa sổ, nên nó hoạt động.

Khi các chuỗi nguyên mẫu có liên quan, việc sử dụng xóa sẽ phức tạp hơn vì nó chỉ xóa thuộc tính khỏi đối tượng đích chứ không phải nguyên mẫu. ví dụ,

function Foo() {}
Foo.prototype = { bar: 123 };
var foo = new Foo();
// foo.bar is 123
foo.bar = 456;
// foo.bar is now 456
delete foo.bar;
// foo.bar is 123 again.

Vì vậy, hãy cẩn thận.

EDIT: Câu trả lời của tôi có phần không chính xác (xem phần "Quan niệm sai" ở cuối). Liên kết giải thích tất cả các thông tin chi tiết, nhưng tóm tắt là có thể có sự khác biệt lớn giữa các trình duyệt và tùy thuộc vào đối tượng bạn đang xóa. delete object.somePropNói chung nên an toàn miễn là object !== window. Tôi vẫn sẽ không sử dụng nó để xóa các biến được khai báo varmặc dù bạn có thể trong các trường hợp phù hợp.


14
cảm ơn @jedierikb vì đã liên kết đến bài viết thú vị đó. cụ thể hơn cho phần này < perfectionkills.com/understanding-delete/#misconceptions > của bài viết mà nơi các bang tác giả tuyên bố rằng noah của "delete được coi là một không-op" là khá chính xác cùng với một exlpanation tuyệt vời tại sao nó là không chính xác . (Đừng bắn sứ giả!)
Rob Wells

2
Liên quan đến câu cuối cùng của câu trả lời sửa đổi, tình huống duy nhất mà bạn có thể xóa các biến được khai báo varlà khi biến được khai báo với eval.
Stephen Booher

1
Trong trường hợp này , câu lệnh xóa dường như không làm gì cả. Những gì đang xảy ra ở đây?
Anderson Green

@ AndersonGreen Các biến toàn cầu được dán nhãn decal được tạo bằng cờ DontDelete nên không thể xóa . Mã đó hành xử chính xác như mong đợi.
RobG

35

Nếu bạn đang ngầm khai báo biến mà không có var, cách thích hợp sẽ là sử dụng delete foo.

Tuy nhiên, sau khi bạn xóa nó, nếu bạn cố gắng sử dụng điều này trong một thao tác, chẳng hạn như thêm ReferenceErrorsẽ bị ném vì bạn không thể thêm một chuỗi vào một định danh không xác định, không xác định. Thí dụ:

x = 5;
delete x
alert('foo' + x )
// ReferenceError: x is not defined

Trong một số trường hợp, có thể an toàn hơn khi gán nó thành false, null hoặc không xác định để nó được khai báo và sẽ không ném loại lỗi này.

foo = false

Lưu ý rằng trong ECMAScript null, false, undefined, 0, NaN, hoặc ''tất cả sẽ đánh giá để false. Chỉ cần chắc chắn rằng bạn không sử dụng các !==toán tử mà thay vào đó !=khi kiểm tra kiểu cho boolean và bạn không muốn kiểm tra danh tính (vì vậy nullsẽ == falsefalse == undefined).

Cũng lưu ý rằng deletekhông "xóa" các tham chiếu mà chỉ là các thuộc tính trực tiếp trên đối tượng, ví dụ:

bah = {}, foo = {}; bah.ref = foo;

delete bah.ref;
alert( [bah.ref, foo ] )
// ,[object Object] (it deleted the property but not the reference to the other object)

Nếu bạn đã khai báo một biến với varbạn không thể xóa nó:

(function() {
    var x = 5;
    alert(delete x)
    // false
})();

Trong tê giác:

js> var x
js> delete x
false

Bạn cũng không thể xóa một số thuộc tính được xác định trước như Math.PI:

js> delete Math.PI
false

Có một số trường hợp ngoại lệ kỳ lạ deletenhư với bất kỳ ngôn ngữ nào, nếu bạn quan tâm đầy đủ, bạn nên đọc:


Cảm ơn câu trả lời đầy đủ với tất cả các chi tiết. Tôi đã đánh dấu nó cho điều này, nhưng tôi đã chấp nhận câu trả lời của Nô-ê vì tôi tin rằng đối với một câu hỏi đơn giản, sự ngắn gọn quan trọng hơn là hoàn thành. Một lần nữa - cảm ơn vì công việc tuyệt vời mà bạn đã làm cho câu trả lời này.
Guss

30
some_var = null;

//or remove it..
delete some_var;

11
Điều này không hoạt động nếu phạm vi của mã này là một chức năng. Xem câu trả lời của @ noah để có giải pháp chính xác.
Roatin Marth

1
Cảm ơn câu trả lời, nhưng tôi đã chấp nhận câu trả lời của Nô-ê vì nó giải thích rõ hơn về những cạm bẫy của delete.
Guss

3
không phải lo lắng ... Tôi đã đưa ra một câu trả lời đơn giản "nhanh n bẩn" - @noah đã thêm tất cả các chi tiết cho các trường hợp "khác" do đó anh ta cũng xứng đáng được ghi nhận. ;-)
scunliffe

7
Điều này LAF không đúng. deletechỉ làm việc cho một tài sản. Đặt nó nullbiến vẫn tồn tại.
Derek 朕 會

1
Câu trả lời này đủ tốt cho trường hợp rất có thể bạn kiểm tra bằng "if (some_var) {..}"
BearCode

16

TLDR: đơn giản định nghĩa các biến (không có var, let, const) có thể bị xóa với delete. Nếu bạn sử dụng var, let, const- họ có thể không bị xóa không có deletecũng không phải vớiReflect.deleteProperty .

Chrome 55:

simpleVar = "1";
"1"
delete simpleVar;
true
simpleVar;
VM439:1 Uncaught ReferenceError: simpleVar is not defined
    at <anonymous>:1:1
(anonymous) @ VM439:1
var varVar = "1";
undefined
delete varVar;
false
varVar;
"1"
let letVar = "1";
undefined
delete letVar;
true
letVar;
"1"
const constVar="1";
undefined
delete constVar;
true
constVar;
"1"
Reflect.deleteProperty (window, "constVar");
true
constVar;
"1"
Reflect.deleteProperty (window, "varVar");
false
varVar;
"1"
Reflect.deleteProperty (window, "letVar");
true
letVar;
"1"

FF Nightly 53.0a1 cho thấy hành vi tương tự.


Câu trả lời của bạn là đúng về mặt kỹ thuật, vì vậy bạn nhận được một điểm, nhưng tất cả mọi thứ bạn viết đều được bao phủ bởi câu trả lời đã chọn với nhiều chi tiết và tham chiếu đến thông số kỹ thuật ECMAScript - trong tương lai sẽ rất hữu ích để xem lại câu trả lời hiện có trước khi đăng.
Guss

5
Đã đồng ý. Nhưng chỉ đề cập đến vartrường hợp. Như đối với tôi nó là thú vị để thử nghiệm và chia sẻ letconstcác trường hợp là tốt. Tuy nhiên, cảm ơn đã lưu ý. Sẽ cố gắng để được cụ thể hơn vào lần tới.
Serj.by

4

ECMAScript 2015 cung cấp API Reflect. Có thể xóa thuộc tính đối tượng với Reflect.deleteProperty () :

Reflect.deleteProperty(myObject, 'myProp');
// it is equivalent to:
delete myObject.myProp;
delete myObject['myProp'];

Để xóa tài sản của windowđối tượng toàn cầu :

Reflect.deleteProperty(window, 'some_var');

Trong một số trường hợp, các thuộc tính không thể bị xóa (khi thuộc tính không thể cấu hình được) và sau đó hàm này trả về false(cũng như xóa toán tử ). Trong các trường hợp khác trả về true:

Object.defineProperty(window, 'some_var', {
    configurable: false,
    writable: true,
    enumerable: true,
    value: 'some_val'
});

var frozen = Object.freeze({ myProperty: 'myValue' });
var regular = { myProperty: 'myValue' };
var blank = {};

console.log(Reflect.deleteProperty(window, 'some_var')); // false
console.log(window.some_var); // some_var

console.log(Reflect.deleteProperty(frozen, 'myProperty')); // false
console.log(frozen.myProperty); // myValue

console.log(Reflect.deleteProperty(regular, 'myProperty')); // true
console.log(regular.myProperty); // undefined

console.log(Reflect.deleteProperty(blank, 'notExistingProperty')); // true
console.log(blank.notExistingProperty); // undefined

Có một sự khác biệt giữa deletePropertychức năng và deletetoán tử khi chạy ở chế độ nghiêm ngặt:

'use strict'

var frozen = Object.freeze({ myProperty: 'myValue' });

Reflect.deleteProperty(frozen, 'myProperty'); // false
delete frozen.myProperty;
// TypeError: property "myProperty" is non-configurable and can't be deleted

4

Các biến, ngược lại với các thuộc tính đơn giản, có thuộc tính [[Có thể cấu hình]] , nghĩa là không thể xóa một biến thông qua toán tử xóa . Tuy nhiên, có một bối cảnh thực hiện mà quy tắc này không ảnh hưởng. Đây là bối cảnh eval : có thuộc tính [[Có thể cấu hình]] không được đặt cho các biến.



3

Ngoài những gì mọi người đã viết, cũng lưu ý rằng delete trả về boolean. Nó có thể cho bạn biết nếu xóa thành công hay không.

Thử nghiệm trên Chrome, mọi thứ ngoại trừ letđều có thể thực hiện được. Khi deletetrả lại truenó thực sự loại bỏ chúng:

implicit_global = 1;
window.explicit_global = 1;
function_set = function() {};
function function_dec() { };
var declared_variable = 1;
let let_variable = 1;

delete delete implicit_global; // true, tested on Chrome 52
delete window.explicit_global; // true, tested on Chrome 52
delete function_set; // true, tested on Chrome 52
delete function_dec; // true, tested on Chrome 52
delete declared_variable; // true, tested on Chrome 52
delete let_variable; // false, tested on Chrome 78

Không phải lúc nào cũng đúng. Đặc biệt là trong Chrome. Firefox trả lại mọi thứ chính xác. Không kiểm tra trong bất kỳ trình duyệt nào khác. Đối với letvars và constvars, nó đang trả về true, điều đó có nghĩa là biến đó bị xóa nhưng không phải vậy. Bạn có thể kiểm tra nó trong cả Chrome và FF. FF dường như trả về giá trị chính xác trong khi Chrome thì không. Vì vậy, không chắc chắn bạn có thể thực sự dựa vào nó. Hãy xem:let letVar = "1"; undefined delete letVar; true letVar "1" typeof letVar; "string" const constVar="1"; undefined delete constVar; true constVar; "1" typeof constVar; "string"
Serj.by

1
Như jedierikb đã đề cập dưới đây, có một bài viết hoàn hảo của kangax perfectionkills.com/under Hiểu-delete mà chủ yếu mô tả lý do tại sao và cách thức deletevận hành. Nhưng nó không mô tả lý do tại sao nghĩa đen đối diện với các chức năng. Thật đáng tiếc. Tuy nhiên, liên quan đến các biến, mọi thứ bắt đầu rõ ràng hơn nhiều.
Serj.by

2

Bạn không thể xóa một biến nếu bạn khai báo nó (với var x;) tại lần sử dụng đầu tiên. Tuy nhiên, nếu biến x của bạn xuất hiện lần đầu tiên trong tập lệnh mà không cần khai báo, thì bạn có thể sử dụng toán tử xóa (xóa x;) và biến của bạn sẽ bị xóa, rất giống với việc xóa một phần tử của mảng hoặc xóa thuộc tính của đối tượng .


1

Tôi hơi bối rối. Nếu tất cả những gì bạn muốn là cho một giá trị biến không chuyển sang tập lệnh khác thì không cần phải xóa biến khỏi phạm vi. Đơn giản chỉ cần vô hiệu hóa biến sau đó kiểm tra rõ ràng nếu nó là hoặc không null. Tại sao phải trải qua những rắc rối trong việc xóa biến khỏi phạm vi? Mục đích nào mà máy chủ này vô hiệu hóa không thể?

foo = null;
if(foo === null) or if(foo !== null)

Yêu cầu là tập lệnh thứ tự, không thuộc quyền kiểm soát của tôi, sẽ không thấy biến đó tồn tại - cụ thể là đối với trường hợp OP, tập lệnh đích có hành vi cho nullgiá trị mà tôi không muốn kích hoạt.
Guss

Không có "phụ trợ" đã bị lạm dụng trong quá trình sản xuất câu hỏi này. Đây chỉ là một vài tập lệnh trên một trang web mà tôi không kiểm soát được bất cứ điều gì ngoại trừ tập lệnh này.
Guss

Cả hai tập lệnh trong cùng một tài liệu hoặc trong các tài liệu riêng biệt mà người này gọi người kia để tải? Bạn đã đề cập đến kịch bản lệnh và kịch bản đích. Nếu đó là vấn đề của một biến được chuyển đến một tập lệnh khác thông qua biến get / post, thì tôi sẽ xóa nó trên phần phụ trợ trước khi bất kỳ javascript nào nhúng tay vào nó. Một ví dụ về điều này trong php sẽ là một cái gì đó như. <?php if(isset($_POST['somevariable']) unset($_POST['somevariable']); if(isset($_GET['somevariable']) unset($_GET['somevariable']); ?>
designdrumm

Tôi hiểu rồi. Chà, nếu có kiểm tra và số dư cho null thì đặt nó thành một giá trị, tập lệnh đích sẽ không làm gì có vẻ hợp lý hơn sau đó xóa một biến khỏi phạm vi, nhưng bạn sẽ có câu trả lời của mình, vì vậy tôi sẽ để con ngựa nằm. Cảm ơn câu trả lời của bạn.
designdrumm

Một câu hỏi nhanh. Sẽ có một tập lệnh được gọi sau tập lệnh của bạn sẽ không nằm trong tầm kiểm soát của bạn nhưng vẫn sẽ cần biến này? Nếu vậy, thì việc xóa biến khỏi phạm vi là một ý tưởng tồi.
designdrumm
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.