简体   繁体   中英

Why is window.print() accessible but not print() in Javascript?

Shouldn't all global objects (ie window.someObject) be available at all times? Why does console.log(print) return undefined?

Is window.print not part of the global execution context? Or is print() locked inside of the window object?

In the case of console.log and console.error , it makes sense that they return undefined since there is no console object to log to or error to.

The answer is a little more complicated than that. Essentially, the reason is because variables declared with var don't belong to any particular context, while those declared with let and const belong to a particular context (a block), and variables created with the "new" keyword are bound only to the object instance that they were created on, not the global execution context (like window).

The code in your question looks similar to this:

  function print(x) {...} //declares x as var (e.g., global variable)

  class SomeClass {
    constructor() { //constructor declares x as let (i.e., block variable)
      var x = 0;  
    }

    doSomething() {
      print(x); //prints 0 since variable x doesn't exist until after the constructor executes, and it's not part of the global execution context either (i.e., window) 

      var y = 1; //does not exist outside of this method; we can use y here because it's a block variable (since it's part of a class/method/function declaration), but not outside of that method/class/function

      print(y); //prints 1 since it exists here 

      this.y = 2; //creates a new variable y inside of the object that is unique to each instance of SomeClass

      print(this.y); //prints 2 since it's the same y we created on this instance
    }
  }

  var inst = new SomeClass();
  inst.doSomething(); //instances have their own unique context so no problem with printing 2 here

  print(y); //undefined because y isn't part of the global execution context or any object instance or any block (i.e., it doesn't exist)  
  print(this.y); //undefined because this.y doesn't exist outside of the object that was created from the SomeClass constructor (i.e., it doesn't exist)
  print(inst.y); //undefined because inst.y doesn't exist, since it wasn't created inside of this method nor was it passed into this method

  print(window.y); //works, because window is part of the global execution context  

In your question, you don't have a variable y in your global scope (that's what makes it undefined), but you do have a variable y in the constructor's local scope, so it's still "available" to use in your function (although undefined). You could also see that if we declare window as let or const instead of var, we would get an error because window is part of the global execution context and cannot be changed to be a block variable (or let or const). The reason that console.log() and console.error() return undefined is that they aren't part of the global execution context, so when we try to use them as normal variables, they become undefined since we can only access functions/objects defined within our script/global execution context (in this case just window) with them unless we explicitly add them to our environment with globalThis or self or something similar:

  function log(x) {
    console.log(x);
  }

  log(1); //prints 1 because console.log is part of the global execution context

  function error(x) {
    console.error(x);
  }

  error(2); //prints 2 since console.error is also part of the global execution context

  function print() {
    var log = console.log; //log is now a reference to the console's logging function; same as using `globalThis` or `self` here since `globalThis` and `self` are window in the browser
    log('3'); //logs 3 because our print() function now has access to the global execution context (i.e., window)  
  }

  print(); //prints 3 because our variable log was reassigned in this function to be a reference to console's logging function which means we have access to that context; note that if we try running this code with Chrome Dev Tools, it will give us an error as well because our variable log was only available inside of this scope and not globally available like it would be in Node where we can access stuff inside of functions from outside of them unlike in the browser where you have to add it as a parameter like with `globalThis` or `self`

The same issue exists in the case of new:

  class SomeClass {
    constructor() { //constructor declares x as let (i.e., block variable)
      var x = 0;  
    }

    doSomething() {
      this.x = 1; //creates a new variable x inside of the object that is unique to each instance of SomeClass, so it's not part of the global execution context or any object instance or any block (i.e., it doesn't exist)

      print(this.x); //undefined because this.x doesn't exist outside of the object that was created from the SomeClass constructor (i.e., it doesn't exist)

      window.x = 2; //works since window is part of the global execution context and we're explicitly adding window here to our environment with `globalThis` or `self`

      print(window.x); //prints 2 since now we have access to window's scope thanks to using `globalThis` or `self` on our print() function
    }
  }

  var inst = new SomeClass();
  inst.doSomething(); //instances have their own unique context so no problem with printing 2 here

  print(this.x); //undefined because this.x doesn't exist outside of the object that was created from the SomeClass constructor (i.e., it doesn't exist)
  print(window.x); //works, since window is part of the global execution context and we added it to our environment with `globalThis` or `self` in our print() function

In JavaScript, the variables declared with var are "hoisted", meaning that they are moved to the top of their scope (so they become available everywhere), and those declared with let and const aren't hoisted and thus must be initialized before use. If a variable isn't initialized before use, it becomes undefined. In order to access these variables from outside of their scope or initializer block (ie, from within a function), you need to explicitly add them to your environment using globalThis or self . If a variable is initialized without being explicitly added to your environment, it becomes undefined.

Just to be clear, let's look at this case from another angle:

  function foo() {
    let x = 0; //x becomes undefined because we haven't initialized it yet
    return x; //undefined because x isn't part of the global execution context or any object instance or any block (i.e., it doesn't exist)
  }

  print(foo()); //undefined because foo() doesn't exist outside of the function scope

  function bar() {
    let x = 0;

    if (true) { //this scope is hoisted so that it becomes available within the outer scope below
      return x; //works since this scope was hoisted and now we can access local variables in outer scopes
    }

    print(x); //works since this scope was hoisted and now we can access local variables in outer scopes  
  }

  bar(); //prints 0 because x is 0 after being initialized and then being returned by the if statement in bar(), which means we have access to its value via `globalThis` or `self` as well since it was hoisted

  function baz() {
    var x = 0;

    if (true) { //this scope isn't hoisted so that it doesn't become available within the outer scope below, so the only way to access variable is through `globalThis` or `self` for "this"
      return x; //works since this scope isn't hoisted and now we can access local variables in outer scopes
    }

    print(x); //undefined because the variable was created with var and was not hoisted, so it doesn't exist outside of its initializer block
  }

  baz(); //prints 0 because x is initialized in the initializer block, then returned by the if statement, which means we have access to its value via `globalThis` or `self` for "this" as well since it wasn't hoisted

As a general rule, you shouldn't use console.log or console.error when writing code that's supposed to be run from Node (because Node has no console), but if you have an existing application that uses them for debugging or some other purpose, you can use them when running your code in a browser (eg, with webpack dev server).

Generally speaking, loggers should only be used for logging purposes and not for any other purpose like recording things that happened during execution. There are lots of other ways to record information without using a logger. One example is using window.performance to record timing data that can be used later in performance analytics tools like Sentry or Google Analytics. If you really need a global object as part of your environment that is available everywhere instead of just inside of your script like window (ie, document) then there are a couple of options. The first option is just to use window, but that doesn't work in Node due to the fact that Node doesn't have a global object like the browser does (ie, window). So as an alternative, you can use globalThis or self :

  function foo() {
    console.log(globalThis); //works because we're explicitly adding window here with `globalThis` or `self` since it's not part of the global execution context
  }

  foo(); //prints {document: ...} since window is part of the global execution context and we explicitly added it to our environment with `globalThis` or `self`

Or if you wanted to add something like document and other globals to your environment, you could just create an object literal (ie, JavaScript object) and use that instead:

  function foo() {
    var obj = {}; //obj represents what we want to add to our environment; nothing else is needed since there are no local variables declared in foo()
    console.log(obj); //works because obj will be added to our environment with `globalThis` or `self`, so it's accessible throughout our application code, including any functions and methods defined within foo() or any functions/methods called from within those functions/methods  
  }

  foo(); //prints {} since there are no variables declared in foo()

Note that it's generally not a good idea to use the global execution context (ie, window) in Node. Instead, if you need to have a global object like window in your environment, you should just use globalThis or self .

Window.print does not return any value, it is a void function. And it is part of the global execution and you can access it in global scope.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM