677

I have a global variable in JavaScript (actually a window property, but I don't think it matters) which was already populated by a previous script, but I don't want another script that will run later to see its value or that it was even defined.

I've put some_var = undefined and it works for the purpose of testing typeof some_var == "undefined" but I really do not think it's the right way to go about it.

What do you think?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Guss
  • 30,470
  • 17
  • 104
  • 128

12 Answers12

540

The delete operator removes a property from an object. It cannot remove a variable. So the answer to the question depends on how the global variable or property is defined.

(1) If it is created with var, it cannot be deleted.

For example:

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) If it is created without var, it can be deleted.

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

Technical Explanation

1. Using var

In this case the reference g_a is created in what the ECMAScript spec calls "VariableEnvironment" that is attached to the current scope - this may be the a function execution context in the case of using var inside a function (though it may be get a little more complicated when you consider let) or in the case of "global" code the VariableEnvironment is attached to the global object (often window).

References in the VariableEnvironment are not normally deletable - the process detailed in ECMAScript 10.5 explains this in detail, but suffice it to say that unless your code is executed in an eval context (which most browser-based development consoles use), then variables declared with var cannot be deleted.

2. Without Using var

When trying to assign a value to a name without using the var keyword, JavaScript tries to locate the named reference in what the ECMAScript spec calls "LexicalEnvironment", and the main difference is that LexicalEnvironments are nested - that is a LexicalEnvironment has a parent (what the ECMAScript spec calls "outer environment reference") and when JavaScript fails to locate the reference in a LexicalEnvironment, it looks in the parent LexicalEnvironment (as detailed in 10.3.1 and 10.2.2.1). The top level LexicalEnvironment is the "global environment", and that is bound to the global object in that its references are the global object's properties. So if you try to access a name that was not declared using a var keyword in the current scope or any outer scopes, JavaScript will eventually fetch a property of the window object to serve as that reference. As we've learned before, properties on objects can be deleted.

Notes

  1. It is important to remember that var declarations are "hoisted" - i.e. they are always considered to have happened in the beginning of the scope that they are in - though not the value initialization that may be done in a var statement - that is left where it is. So in the following code, a is a reference from the VariableEnvironment and not the window property and its value will be 10 at the end of the code:

    function test() { a = 5; var a = 10; }
    
  2. The above discussion is when "strict mode" is not enabled. Lookup rules are a bit different when using "strict mode" and lexical references that would have resolved to window properties without "strict mode" will raise "undeclared variable" errors under "strict mode". I didn't really understand where this is specified, but its how browsers behave.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Dayong
  • 5,614
  • 1
  • 14
  • 6
  • 15
    What you said is a common misconception but is actually incorrect - in Javascript there are no "global variables". Variables defined without an explicit scope (such as using `var` outside a function) are properties of the "global object", which in web browsers is `window`. So - `var a = 1; delete window.a; console.log(a);` will successfully delete the variable and cause the last line to issue a reference error. – Guss Sep 20 '14 at 15:28
  • 11
    @Guss, your code `var a = 1; delete window.a; console.log(a);` displays 1. – Dayong Sep 22 '14 at 13:12
  • Which browser are you using? – Guss Sep 22 '14 at 14:07
  • 6
    I am using Google Chrome v36. I tested on other browsers. It looks like it isn't consistent cross browsers. Chrome and Opera displayed 1, while Firefox, Safari and IE 11 on my computer gave an error. – Dayong Sep 22 '14 at 15:47
  • ECMA-262 v5.1 has this to say: "Every execution context has an associated VariableEnvironment. Variables and functions declared in ECMAScript code evaluated in an execution context are added as bindings in that VariableEnvironment’s Environment Record"; and also: "The global environment’s Environment Record is an object environment record whose binding object is the global object." So if after running my code example above, `window.a == 1` then the browser is in violation of ECMA-262. – Guss Sep 23 '14 at 18:32
  • From what I can see, in Chrome `window` is magical and you cannot delete even its user defined properties, which would explain the behavior you described, but doesn't excuse this violation of the standard. – Guss Sep 23 '14 at 18:41
  • Maybe it is because browsers adapt to the standard gradually. I do not have old versions of Firefox, Safari or IE to verify. But I guess there were some old versions that would have window.a == 1. – Dayong Sep 25 '14 at 14:55
  • 5
    Ok, my mistake. See http://www.ecma-international.org/ecma-262/5.1/#sec-10.5 (sub-points 2 and 8.c.ii): When running my test in the developer console, it is generally considered "eval context" (though maybe not in Chrome), so it will raise an error. The same code in a real document's global context will output `1` correctly in all browsers. Running in real documents, your code examples are correct. I selected your answer as correct, but I'd appreciate it if you can edit it to include explaining `window.a = 1; delete window.a;` and possibly the mechanism. I can do so as well if you don't mind. – Guss Sep 29 '14 at 09:04
  • @Dayong What happens with scoped variables? Aren't they deleted automatically by the browser? –  Jun 07 '16 at 13:11
  • 2
    @KlaiderKlai yes. Function scoped variables are created and destroyed every time when the function is executed. Probably closure is an exception. – Dayong Jun 08 '16 at 20:40
  • No chance for `class`es – Green Dec 08 '16 at 00:51
  • Chrome: `var a = 1; delete window.a; console.log(a);` displays `1` and then `undefined` because 1 is for the first `var a=1` and then `undefined` is for `console.log` – serge Jan 22 '18 at 17:40
  • What's the harm in simply setting it back to `undefined`? – thdoan Jun 02 '18 at 19:24
  • The prescriptive answer is to use an object with a propery - e.g., obj.a=1 - then delete that property - delete obj.a - then reference it - `obj.a`. Not using use strict is just for legacy code. Writing code that behaves differently under strict and not strict is dangerous. Javascript is full of rabbit holes - avoid clever and stick to obvious clear practices so everyone can go home early. – Craig Hicks Jul 14 '19 at 17:59
  • Is there any difference between the behavior of `var` and `let` in this case? – Kyle Jan 15 '20 at 21:51
  • @Serge No, both, `1` and `undefined` are “for” `console.log()`. `1` is the console output generated and `undefined` is the return value of `console.log()`, which always returns `undefined`.—You can test this with `5; console.log('6');`. – Robert Siemer May 25 '20 at 07:31
  • @thdoan Setting the variable to undefined doesn't technically "undefine" the variable. Presumably the OP wanted the variable to be completely erased from existence, and simply setting it to undefined doesn't do this. –  Dec 30 '20 at 13:38
  • @Kyle this answer takes advantage of the "horror of implicit globals". Using only `=` to define a variable will implicitly define a property on `globalThis` (`window`). That way the `delete` keyword is applicable. –  Dec 30 '20 at 13:39
  • 1
    This answer is **outdated** and this use of `delete` is **deprecated**. When using *`strict mode`*, this will throw an [error](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Errors/Delete_in_strict_mode). The correct way its to simply set the the variable to ***`null`***. (More info [here](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Errors/Delete_in_strict_mode#freeing_the_contents_of_a_variable), and [this answer](https://stackoverflow.com/a/66001921/8112776).) – ashleedawg Feb 02 '21 at 00:22
294

scunliffe's answer will work, but technically it ought to be

delete window.some_var;

delete is supposed to be a no-op when the target isn't an object property. e.g.,

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

But since global variables are actually members of the window object, it works.

When prototype chains are involved, using delete gets more complex because it only removes the property from the target object, and not the prototype. e.g.,

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.

So be careful.

Note: My answer is somewhat inaccurate (see "Misconceptions" at the end). The link explains all the gory details, but the summary is that there can be big differences between browsers and depending on the object you are deleting from. delete object.someProp should generally be safe as long as object !== window. I still wouldn't use it to delete variables declared with var although you can under the right circumstances.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
noah
  • 21,289
  • 17
  • 64
  • 88
  • 14
    thanks @jedierikb for the link to that interesting article. more specifically to this part of that article where the author states that noah's statement "delete is supposed to be a no-op" is rather inaccurate along with an excellent exlpanation why it is inaccurate. (Don't shoot the messenger!) – Rob Wells Jul 09 '12 at 14:30
  • 2
    In regard to the last sentence of the revised answer, the only circumstance in which you can delete variables declared with `var` is when the variable was declared with `eval`. – Stephen Booher Oct 05 '12 at 15:40
  • 1
    In [this case](http://jsfiddle.net/W4KqQ/), the delete statement doesn't appear to do anything at all. What's going on here? – Anderson Green Jun 10 '13 at 01:19
  • @AndersonGreen—decalred global variables are created with *DontDelete* flag so not deletable. That code behaves exactly as expected. – RobG Apr 30 '14 at 05:46
  • 1
    This answer is **outdated** and this use of `delete` is **deprecated**. (See [here](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Errors/Delete_in_strict_mode#freeing_the_contents_of_a_variable), and [this answer](https://stackoverflow.com/a/66001921/8112776).) – ashleedawg Feb 02 '21 at 00:20
  • I think there's different answers here depending on what you are trying to do. Yes, in modern strict mode JS if you set an unqualified variable to null, it can be garbage collected. However if you want to remove the reference completely, delete is the way to go. As noah noted, you'd always want to use the fully qualified `delete window.propToRemove` if it was unqualified. – scunliffe Feb 02 '21 at 02:38
42

If you are implicitly declaring the variable without var, the proper way would be to use delete foo.

However after you delete it, if you try to use this in an operation such as addition a ReferenceError will be thrown because you can't add a string to an undeclared, undefined identifier. Example:

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

It may be safer in some situations to assign it to false, null, or undefined so it's declared and won't throw this type of error.

foo = false

Note that in ECMAScript null, false, undefined, 0, NaN, or '' would all evaluate to false. Just make sure you dont use the !== operator but instead != when type checking for booleans and you don't want identity checking (so null would == false and false == undefined).

Also note that delete doesn't "delete" references but just properties directly on the object, e.g.:

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)

If you have declared a variable with var you can't delete it:

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

In Rhino:

js> var x
js> delete x
false

Nor can you delete some predefined properties like Math.PI:

js> delete Math.PI
false

There are some odd exceptions to delete as with any language, if you care enough you should read:

pimvdb
  • 151,816
  • 78
  • 307
  • 352
meder omuraliev
  • 183,342
  • 71
  • 393
  • 434
  • Thanks for the complete answer with all the details. I marked it up for this, but I've accepted Noah's answer because I believe that for a simple question brevity is more important then completion. Again - thanks for the great work you did on this answer. – Guss Oct 22 '09 at 14:55
38

See noah's answer for full details

//Option A.) set to null
some_var = null;

//Option B.) set to undefined
some_var = undefined;

//Option C.) remove/delete the variable reference
delete obj.some_var
//if your variable was defined as a global, you'll need to
//qualify the reference with 'window'
delete window.some_var;

References:

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
scunliffe
  • 62,582
  • 25
  • 126
  • 161
  • 11
    This doesn't work if the scope of this code is a function. See @noah's answer for the correct solution. – Roatin Marth Oct 20 '09 at 20:35
  • 1
    Thanks for the answer, but I've accepted Noah's answer because it better explains the pitfalls of `delete`. – Guss Oct 22 '09 at 14:36
  • 3
    no worries... I gave a "quick n dirty" simple answer - @noah added all the details for the "other" cases thus he deserves credit too. ;-) – scunliffe Oct 22 '09 at 15:54
  • 7
    This is not correct. `delete` only works for a property. Setting it `null` the variable still exists. – Derek 朕會功夫 Jun 23 '12 at 18:39
  • 1
    This answer is good enough for the most likely case where you check with "if (some_var) { .. }" – BearCode Feb 19 '13 at 18:57
  • In java/js if you make `some_var = null` garbage collector will free memory for this var – fider Jun 26 '13 at 11:01
  • 1
    note that http://perfectionkills.com/understanding-delete/#misconceptions claims it's incorrect – test30 Sep 02 '14 at 12:47
  • This use of `delete` is **deprecated**. (See [here](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Errors/Delete_in_strict_mode#freeing_the_contents_of_a_variable) and also [this answer](https://stackoverflow.com/a/66001921/8112776).) – ashleedawg Feb 02 '21 at 00:18
19

TLDR: simple defined variables (without var, let, const) could be deleted with delete. If you use var, let, const - they could not be deleted neither with delete nor with Reflect.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"

Firefox Nightly 53.0a1 shows the same behaviour.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Serj.by
  • 534
  • 5
  • 17
  • Your answer is technically correct, so you get a point, but everything you wrote is covered by the selected answer with much more details and references to the ECMAScript specs - in the future it would be useful to review existing answer before posting. – Guss Jan 05 '17 at 17:08
  • 6
    Agreed. But there mentioned only `var` case. As for me it was interesting to test and share `let` and `const` cases as well. However, thanks for note. Will try to be more specific next time. – Serj.by Jan 06 '17 at 11:00
7

ECMAScript 2015 offers Reflect API. It is possible to delete an object property with Reflect.deleteProperty():

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

To delete a property of the global window object:

Reflect.deleteProperty(window, 'some_var');

In some cases properties cannot be deleted (when the property is not configurable) and then this function returns false (as well as the delete operator). In other cases it returns 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

There is a difference between deleteProperty function and delete operator when run in strict mode:

'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
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
madox2
  • 49,493
  • 17
  • 99
  • 99
7

⚠️ The accepted answer (and others) are outdated!

TL;DR

  • delete does not remove variables.
    (It's only for removing a property from an object.)

  • The correct way to "unset" is to simply set the variable to null. (source)
    (This enables JavaScript's automatic processes to remove the variable from memory.)

Example:

x = null;


More info:

Use of the delete operator on a variable is deprecated since 2012, when all browsers implemented (automatic) mark-and-sweep garbage-collection. The process works by automatically determining when objects/variables become "unreachable" (deciding whether or not the code still requires them).

With JavaScript, in all modern browsers:

  • Garbage collection is performed automatically. We cannot force or prevent it.
  • Objects are retained in memory while they are reachable.
  • Being referenced is not the same as being reachable: a pack of interlinked objects can become unreachable as a whole. (source)

The delete operator is only used to remove a property from an object; it does not remove variables.

Unlike what common belief suggests (perhaps due to other programming languages like delete in C++), the delete operator has nothing to do with directly freeing memory. Memory management is done indirectly via breaking references. (source)

When using strict mode ('use strict';, as opposed to regular/"sloppy mode") an attempt to delete a variable will throw an error and is not allowed. Normal variables in JavaScript can't be deleted using the delete operator (source) (or any other way, as of 2021).

 


 

...alas, the only solution:

Freeing the contents of a variable

To free the contents of a variable, you can simply set it to null:

var x;

// ...

x = null;    // (x can now be garbage collected)

(source)


Further Reading:

ashleedawg
  • 20,365
  • 9
  • 72
  • 105
  • Umm... Actually, `null` is a valid value - if setting a variable to `null` results in it disappearing (i.e. causing future references to throw `ReferenceError: x is not defined`), then a lot of software would break. If you would have said to set it to `undefined`, we might have had something to discuss. – Guss Feb 02 '21 at 07:18
  • 1
    Additionally, the original question was about a "global variable", which - as the the accepted answer correctly describes - is either a scoped variable, in which case it cannot be deleted (no, setting it to `null` does not delete it), or it is a property on the global object, in which case `delete` will remove it just fine. – Guss Feb 02 '21 at 07:21
  • After reading everything again, I think the issue I have with your answer is that it is mostly about how a Javascript VM manages memory and it deals (probably pretty well) with how one might want to free memory consumed by a value stored in Javascript. Unfortunately the OP isn't about memory management per-se, but is more about causing other Javascript code to not see that a variable (actually a global object property) has been defined. It is less about actual memory use and more about object metadata. – Guss Feb 02 '21 at 07:36
  • your answer would make sense if you would set it to undefined. If you set it to null it keeps value in memory in JS – strix25 May 25 '22 at 15:45
4

Note that delete returns true when it was successful.

Update 2021: tested on Chrome 88 and Firefox 84:

implicit_global = 1;
delete implicit_global; // true

window.explicit_global = 1;
delete explicit_global; // true

const _object = {property: 1};
delete _object.property; // true

function_set = function() {};
delete function_set; // true

function function_declaration() {};
delete function_declaration; // false

(function () {
    var _var = 1;
    console.log(delete _var); // false
    console.log(_var); // 1
})()

(function () {
    let _let = 1;
    console.log(delete _let); // false
    console.log(_let); // 1
})()

(function () {
    const _const = 1;
    console.log(delete _const); // false
    console.log(_const); // 1
})()

The previous edit of this answer is no longer relevant due to browser updates.

oriadam
  • 7,747
  • 2
  • 50
  • 48
  • It's not always correct. Especially in Chrome. Firefox returns everything correctly. Didn't test in any other browsers. As for `let` vars and `const` vars it is returning true what should means that variable deleted but it is not. You could check it in both in Chrome and FF. FF seems returning correct values while Chrome is not. So don't sure you could really rely on it. Let see: `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 Jan 09 '17 at 08:27
  • 1
    As jedierikb mentioned below there is perfect article by kangax http://perfectionkills.com/understanding-delete/ that mostly describes why and how `delete` operator works. But it is not describing why literally oposite situation with functions. As a pity. However, regarding variables things start appearing to be much more clear. – Serj.by Jan 09 '17 at 08:42
  • original answer was tested in 2015 on Chrome 52. the behavior is different now on Chrome 88 so i updated the answer. also tested on Firefox 84 - behavior is identical to Chrome 88. – oriadam Feb 02 '21 at 08:33
  • What is *"The original answer"*? Your own answer? [Dayong's answer](https://stackoverflow.com/questions/1596782/how-can-i-unset-a-javascript-variable/25919959#25919959)? Or some other answer? – Peter Mortensen Jul 24 '21 at 20:12
  • @PeterMortensen good point, i edited the question to be clearer. – oriadam Jul 26 '21 at 06:12
3

Variables, in contrast to simple properties, have the attribute [[Configurable]], meaning impossibility to remove a variable via the delete operator.

However, there is one execution context in which this rule does not affect. It is the eval context: there the [[Configurable]] attribute is not set for variables.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
1

The delete operator removes a property from an object.

delete object.property
delete object['property']

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/delete

According to the question you need one of followings

delete some_var;
delete window.some_var;
delete window['some_var'];
Scarecrow
  • 4,057
  • 3
  • 30
  • 56
  • 1
    The part about what `delete` *actually* does is correct but the proposed solution is not the correct method. (See [here](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Errors/Delete_in_strict_mode#freeing_the_contents_of_a_variable) and also [this answer](https://stackoverflow.com/a/66001921/8112776).) – ashleedawg Feb 02 '21 at 00:17
1

You cannot delete a variable if you declared it (with var x;) at the time of first use. However, if your variable x first appeared in the script without a declaration, then you can use the delete operator (delete x;) and your variable will be deleted, very similar to deleting an element of an array or deleting a property of an object.

Guss
  • 30,470
  • 17
  • 104
  • 128
  • 1
    I think the important distinction here (which I missed when I first asked the question) is that a non "declared" variable, isn't a variable - it is an object property. `delete` is used to make objects forget about properties being defined, it does not affect variables. – Guss Feb 02 '21 at 07:31
1

I am bit confused. If all you want is for a variables value to not pass to another script then there isn't any need to delete the variable from the scope.

Simply nullify the variable and then explicit check if it is or is not null. Why go through the trouble of deleting the variable from the scope? What purpose does this serve that nullifying can not?

foo = null;
if(foo === null) or if(foo !== null)
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
designdrumm
  • 64
  • 1
  • 4
  • The requirement is that the order script, that is not under my control, will not see that the variable exist - specifically for the OP case, the target script has a behavior for the `null` value that I don't want to trigger. – Guss Apr 13 '19 at 09:44
  • No "backend" was abused during the production of this question. These are just a couple of scripts on a website where I have no control of anything except this one script. – Guss Apr 15 '19 at 06:39
  • Are both scripts in the same document or in separate documents that one calls the other to load? You mentioned order script and target script. If it is a matter of a variable being passed to another script via a get/post variable, then I would delete it on the backend before any javascript get their hands on it. An example of this in php would be something like. `` – designdrumm Apr 15 '19 at 06:41
  • I see. Well if there is checks and balances for null then setting it to a value the target script will do nothing with seems more logical then deleting a variable from scope, but you look to have your answer, so I'll let the horse lay. Thanks for your responses. – designdrumm Apr 15 '19 at 06:51
  • One quick question. Will there ever be a script that gets called after yours that will not be in your control but will still need this variable? If so, then deleting the variable from scope is a bad idea. – designdrumm Apr 15 '19 at 06:54
  • This question is answered in the original post. – Guss Apr 15 '19 at 07:28
  • Doesn't "undefining" a variable speed up the program in a sense? Especially if the variable contains something like a data URI for a pdf. –  Dec 30 '20 at 13:41
  • @expressjs123 - except for memory pressure, not at all. If the value is very large, then setting it to any small value would ease memory pressure (`undefined` is good because it is the default resolution of any undefined property, but any small value would do, such as `0` or `""`). That being said, unless you are talking GBs sized value, it is unlikely to have any discernible effect. – Guss Feb 02 '21 at 07:29