1

So I'm new to 'proper' Javascript programming as a whole. I've only ever really used it for simple DOM manipulations with a bit of dabbling in jQuery. Now I'm trying to learn the full concepts of programming in Javascript including OOP and the like. I liked to think I understood a fair bit about scope in JS, but I can't understand this:

function Person(name, age){
    this.name = name;
    this.age = age;

    this.getName = function(){
        return name;
    }

    this.setName = function(name){
        this.name = name;
    }

    this.getAge = function(){
        return age;
    }

    this.setAge = function(age){
        this.age = age;
    }
}

p = new Person("Bob", 12);
console.log(p.getName());
console.log(p.getAge());

console.log(name);  //logs "an empty string" - What?
console.log(age);   //logs "ReferenceError: age is not defined" - Expected

After the first two console.logs came out fine, I wanted to check the values of the properties within the Person function to make sure nothing had fallen out of scope. name had to some extent (only gave an empty value - but still existed), but age hadn't.

I can't see any typos, which is the only thing I would've understood causing this issue. So why does console.log(name); not give a ReferenceError?

As much as I'd like to just know the answer, I'd really appreciate a full explanation of the root issue, including any key terminology such as prototypes, lexical scope, hoisting etc.

Jazcash
  • 3,145
  • 2
  • 31
  • 46

2 Answers2

2

The name identifier exists in browsers' global scope, it's a property of the window.

See window.name.

Fabrício Matté
  • 69,329
  • 26
  • 129
  • 166
  • Thank you too! It makes me curious as to why `window` variables are made completely global instead of only being able to access it through `window.name`. I'd understand if there was something like `import window` at the top but without it feels uncomfortable knowing I might accidentally use one of these variables at any point. – Jazcash Dec 12 '13 at 09:00
  • 2
    @Jazcash "window" is actually a property of the global object which points to itself (see [`window.window`](https://developer.mozilla.org/en-US/docs/Web/API/Window.window)), and all properties of the global object are part of the global scope. JavaScript has lexical scoping so inner scopes can reference identifiers from outer scopes (hence how the `window` identifier resolves to the global object). You can put your code inside an [IIFE](http://benalman.com/news/2010/11/immediately-invoked-function-expression/) and all variable/function definitions won't conflict with the global ones. `=]` – Fabrício Matté Dec 12 '13 at 09:05
  • 1
    My personal setup is `(function(){ 'use strict;' /*code here*/ }());`, this way var/function declarations do not conflict with the global scope and undeclared variables do not leak to the global scope (throws a ReferenceError instead, see [Strict mode](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions_and_function_scope/Strict_mode)). Hopefully ES6 Modules will be standardized soon enough to get us rid of this boilerplate. – Fabrício Matté Dec 12 '13 at 09:26
1

You're not using prototype at all, it's better to put functions on the constructors prototype. For more info about constructor functions and prototype you can check out this answer.

As for why age is not logged; it's because you're trying to log window.age instead of p.age

p = new Person("Bob", 12);
console.log(p.name);
console.log(p.age);
console.log("this is:",this);
console.log("does window have a name property?",typeof this.name);
console.log("does window have an age property?",typeof this.age);
Community
  • 1
  • 1
HMR
  • 37,593
  • 24
  • 91
  • 160
  • You're absolutely right, I'm just learning this stuff now. The code in the question was just one of my test examples. Thank you though. – Jazcash Dec 12 '13 at 09:06