The reason for the difference is that the top-level scope in a browser script is the global scope, but the top-level scope in a Node.js file is module scope. (By default it's an old-style Node.js module which is in loose mode, but in modern versions of Node.js you can opt into JavaScript standard module scope, which is in strict mode.)
At global scope, var creates a property on the global object, which you can access via the window global, via this at global scope, or via this in a loose-mode function you call without doing anything specific to set this. Your call to fn above is that last category: a loose-mode function called without setting this.
But at Node.js module scope, var just creates a var in the module, it doesn't create a global or a property on the global object. It's like doing this:
(function() { // The wrapper Node.js provides node-style modules
// This is "module" scope
var foo = 10;
function fn () {
console.log (this.foo);
}
fn();
})(); // End of the wrapper
(Why did I change length to foo? Because by default, window has a length property [the number of frames the window contains], which confused matters. :-) )
So it's just a difference in scope.
You can also use module scope on modern browsers, including Chrome, via type="module" on the script tag. But since JavaScript standard modules are always in strict mode, the this inside the call to fn would be undefined, causing an error:
<script type="module">
var length = 10;
function fn () {
console.log(this); // undefined
console.log(this.length); // TypeError
}
fn();
</script>