JavaScript scope question

My question is based on the following code snippet:
<!DOCTYPE html>
<html>
<head>
<title></title>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
<script>
    $(function() {
        function Test() {

            function innerTest() {
                sayHello("Saying hello from innerTest!");
            }
            
            // I put a 'this' on sayHello() to make it a public method outside of this constructor
            // function, without it, I won't be able to access it outside. But by using 'this', 
            // it no longer becomes accessible to innerTest() above. If take out the 'this', 
            // it will be accessible.
            this.sayHello = function(s) {
                console.log(s);
            }
            
            innerTest();
        }
        
        var t = new Test();
        
        t.sayHello("I'm the sayHello() function.");
        
    });

</script>
<style>
</style>
</head>
<body>    
</body>
</html>

Open in new window


I guess what is confusing me is that I always thought that the sayHello() and this.sayHello() would still be identical in scope, i.e, function scope. Apparently not. So can someone differentiate to me the scope differences between this.sayHello() and simply sayHello()?
elepilAsked:
Who is Participating?

[Product update] Infrastructure Analysis Tool is now available with Business Accounts.Learn More

x
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

Bob LearnedCommented:
I find "this" in JavaScript, to be confusing.

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

A function's this keyword behaves a little differently in JavaScript compared to other languages. It also has some differences between strict mode and non-strict mode.

I would think that you would need "this" in the call:

this.sayHello("Saying hello from innerTest!");

Open in new window

0
elepilAuthor Commented:
Bob Learned, thanks for responding.

If you tried to run the code, you will know using this.sayHello() inside InnerTest() would generate an error "Uncaught TypeError: this.sayHello is not a function".

Besides, what I was really after was an explanation why this is happening the way it is.
0
Slick812Commented:
greetings  elepil, , OK, the way that javascript organizes  the levels of "Scope", are based on the containers, that are "around" the "variables" , which can be several layers ( containers ) deep. Unlike most any other code language, in javascript all functions ARE VARIABLES, (changeable, not fixed-permanent),  and use variable names to access that function, so if you have a function, to can access it by variable name, and completely change it -
<script>
function showString(str) {
alert("string to show is - "+str);
}
// you can just change the old function to new operations
showString = function(s, n) {
n *= 2;
alert("string and number to show is - "+s+", number is - "+n);
}
</script>

 So, if you do a function, as you do, with a function inside of it, and use the "this" reference, then you seem to think that inner functions are the same as a "this" reference, , But in javascript, this is not true, as by nessesity, JS needs to limit the scope and has any thing on the "this" reference, be scope-wise separate, from any internal function variables, like -

function junk(init) {
var limit = init;
this.limit = init+5;

this showLimit = function() {
  alert("two different Limits, inter L = "+limit+", this L = "+this.limit);
  }
}

although there are Two separate and not related Limit, in this function, the Scope of each is different. You need to explicitly declare any "this" variable, with that operator, unlike many other languages.
0
Big Business Goals? Which KPIs Will Help You

The most successful MSPs rely on metrics – known as key performance indicators (KPIs) – for making informed decisions that help their businesses thrive, rather than just survive. This eBook provides an overview of the most important KPIs used by top MSPs.

elepilAuthor Commented:
Slick812, thank you for responding.

I'm not sure I made my question clear, so let me restate, and I hope you will have the patience to copy/paste my very brief code snippet and try out what I'm going to say so you will know exactly what I'm talking about. Look at this excerpt of code:

function innerTest() {
                // This will work, innerTest() will be able to see the sayHello function definition
                sayHello("Saying hello from innerTest!");
            }
            
            sayHello = function(s) {
                console.log(s);
            }

Open in new window


Now look at this:

function innerTest() {
                // We put a 'this' in front of sayHello() definition, now this WON'T WORK anymore.
                sayHello("Saying hello from innerTest!");
            }
            
            // Because I added 'this', innerTest() above can't find it anymore
            this.sayHello = function(s) {
                console.log(s);
            }

Open in new window


The way I see it, there are only THREE scopes we are talking about here:

Global Scope (the scope of the constructor function Test())
        Function Scope (the scope of sayHello()=function.... )
            Inner function scope (the scope of innerTest()) 

Open in new window


It puzzles me why innerTest() could see sayHello() when it didn't have 'this', and then would totally fail to see it when it has a 'this'. There are only 3 levels of scopes that I can see in my example, but it is behaving as if there is a fourth scope that I'm not seeing.

So Slick812, do you see what I'm asking now?
0
Steve BinkCommented:
You are conflating two different scope concepts.  Here's some reading for you:

http://javascript.crockford.com/private.html
http://philipwalton.com/articles/implementing-private-and-protected-members-in-javascript/

The basics are that you are defining a "private" function inside your prototype.  In OOP terms, you are creating a static method - a function without the first-glance-intuition of "this".  Your code follows this pattern:
function a() {
  function b() {}
  this.c = function() {}
}

Open in new window


In that example, a() is a function, which creates a scope, and limits the available scope to the "big global", and anything in a()'s top-level scope - the "small global".  Anything created inside a()'s scope will be placed in the "small global", and also have access to everything in the "small global".  Creating a() also creates a reference to "this" pointing at the definition of a().  "this", in the context of a(), is the function object a().  

When you create b(), you are creating ANOTHER scope, and another reference to "this".  Inside b(), "this" points to the function object b().  

When you create this.c(), you are explicitly setting property c of "this".  In the context in which it is being set, "this" points to the function object a(), so it becomes a().c().  An additional point here is that c(), when called, will be given a binding telling it that "this" points to its owning a() object.  That extra step does not happen when b() is called, since b() is just one more standard function definition...its "this" will point to itself.

There are a couple ways to work around this problem.  

First and foremost, if b() needs access to the a() object, then perhaps it should not be created inside a() as a standard function (pronounced: private member).  Just as in OOP languages, where static methods are designed to be utility methods without access to the *instanced object*, so should private members be in Javascript.  The first solution is to refactor your code until b() either no longer needs access to a(), or it is created as a privileged/public member (i.e., this.b = function(){} ).  I believe this is the correct answer.

Another alternative is to leverage b()'s ability to see the inner scope of a() by creating a reference for it to use.  This strategy is commonly used in loop closures:
function MyObject() {
  // right now, this points to where you think it does...the called MyObject instance
  // let's save it into the private scope
  var $this = this;
  // now some variables.  first, a private one
  var a = [1, 2, 3, 4, 5];
  // then a public one
  this.b = array();
  a.forEach(function(v,k) {
    // right here, "this" does *not* point to MyObject, but rather the closure implementing the forEach
    // but, since it was created inside MyObject's scope, it should have access to everything within it:
    $this.b[v] = true;
  });
}

Open in new window


I'm not convinced the second option is correct in the context of your basic example, but I'd have to know more about it in order to come up with an argument.  Technically, it will work, though it seems messy organizationally.

Finally, you could call b() by using the apply, bind, or call functions.  Those functions can create new function, or call existing ones, with a "this" reference pointing to an arbitrary object.  This is very handy for manipulating scopes.
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
elepilAuthor Commented:
Steve Bink, your explanation and the links you provided gave me an epiphany on how JavaScript handles scoping.

I now realize private functions can only access private properties and private methods. They CANNOT access public properties and public methods by default. In order for private functions to access public properties, a closure has to be created to bridge the gap between private and public, as in your example when you created var $this = this.

Thanks for your help.
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
JavaScript

From novice to tech pro — start learning today.