Jump to content

Recommended Posts

BACKGROUND  I have created two functions both of which have been tested and function as expected -- so well, in fact, that one of them can be used to enhance the output of the other.  The problem arises when I seek to call these functions with separate button alerts provided below in the order given.  Both functions are defined in a jQuery ready statement.

BUTTON ALERTS

<script>
	var num = 5;
</script>
<button onclick="alert(num.annotate('sec'));">Alert!</button>

<script>
	var sec_num = 59039;
</script>
<button onclick="alert(secTranslate(sec_num));">Alert!</button>

DILEMMA:  Although the first button-alert works, the second fails with the following error message:  "ReferenceError: secTranslate is not defined." with a reference to the onclick attribute.

QUESTION:  How does one explain this phenomenon?

Roddy

Share this post


Link to post
Share on other sites

Is the second a plain simple function OR object constructor function? if latter, you have to use 'new' to create the object using constructor  function its pointing to.

Share this post


Link to post
Share on other sites

It is a plain simple function that makes use of the function that precedes it in the ready statement.

Number.prototype.annotate = function(unit) {
    var s = String(this);
    var unit = String(unit);
    s = s + " " + unit;
    return s;
}
function secTranslate(sec_duration) {
    var hrCount = Math.floor(sec_duration / 3600);
    sec_duration %= 3600;
    var minCount = Math.floor(sec_duration / 60);
    var secCount = sec_duration % 60;
    return hrCount.annotate('hr') + " " + minCount.annotate('min') + " " + secCount.annotate('sec'); 
}

Roddy

Share this post


Link to post
Share on other sites

Depending upon how one has defined the function, it may be limited in scope.  There's no way to modify a prototype which limits the scope, but there are definitely ways to define a function only inside a specific scope.  If one has put everything inside an event handler then that's one possibility, it may only be defined inside that event handler.

Share this post


Link to post
Share on other sites

So, I placed the declaration(definition) of the  .annotate() function inside the secTranslate() function and commented out the original declaration.  Obviously the first button-alert was no longer possible, but it did nothing to remove the error response for the second button-alert.

Roddy

Share this post


Link to post
Share on other sites

Right, if secTranslate is already undefined, moving another function definition inside that function is not going to cause the function to get defined.  In fact, now you have to run secTranslate for the other thing to get defined, and you're going to keep re-defining that function every time you run secTranslate.

Share this post


Link to post
Share on other sites

With no argument about the failure of the modification to produce the desired result, why would having placed the .annotate( ) declaration inside the secTranslate( ) not have been a potential source of solution?  After all, if the return value of the .annotate( ) function were not achieved, then the secTranslate( ) function would necessarily fail.

Roddy

Edited by iwato

Share this post


Link to post
Share on other sites

With no argument about the failure of the modification to produce the desired result, why would having placed the .annotate( ) declaration inside the secTranslate( ) not have been a potential source of solution?

If the secTranslate function is not getting defined in the first place, then why would the body of the function affect whether or not it gets defined?  The body of the function doesn't even get executed until you run the function, it does not get executed when the function is defined.  So changing anything at all about the body of the function (with the exclusion of introducing or fixing syntax errors) will never affect whether or not the function gets defined in any particular scope.

In other words, the contents of the function do not affect whether or not it gets defined, and are not relevant to this problem.

Share this post


Link to post
Share on other sites

That we not become distracted.  It is only in the following context that the secTranslate( ) function fails.

<script>
	var num = 5;
</script>
<button onclick="alert(num.annotate('sec'));">Alert!</button>

<script>
	var sec_num = 59039;
</script>
<button onclick="alert(secTranslate(sec_num));">Alert!</button>

When run at load time within the jQuery ready() function the following two lines of code produce the desired result

var sec_num = 59039;
alert(secTranslate(sec_num));

-- namely,

Quote

16 hr 23 min 59 sec

Roddy

Share this post


Link to post
Share on other sites

Right, like I've mentioned this is a problem of scope, at least that's what I gather with the limited information I've been given.  You'll notice, for example, that if you copy the code in your second post into a blank HTML document without embedding it inside any event handlers or anything else, it will run fine.  The code you are using is not the problem.  The problem is where you are using it, the scope.  You haven't shown that context, so this is a lot of assuming from me but it's all I can do with the limited information.

Share this post


Link to post
Share on other sites

Do the terms undefined and not defined mean the same thing?

I have seen the term undefined returned when a value for a variable is not present.   So, would it not apply here as well?

Roddy

Share this post


Link to post
Share on other sites

Actually, you don't even need to put that in a new file to prove that.  Just open your developer tools, go to the console, and run this code directly on the console:

Number.prototype.annotate = function(unit) {
    var s = String(this);
    var unit = String(unit);
    s = s + " " + unit;
    return s;
}
function secTranslate(sec_duration) {
    var hrCount = Math.floor(sec_duration / 3600);
    sec_duration %= 3600;
    var minCount = Math.floor(sec_duration / 60);
    var secCount = sec_duration % 60;
    return hrCount.annotate('hr') + " " + minCount.annotate('min') + " " + secCount.annotate('sec'); 
}
console.log(secTranslate(1234));

You will notice that it prints the expected output to the console.  So, none of the code that you've shown so far is responsible for the actual problem.  It is how you are using the code, not the code itself.

Share this post


Link to post
Share on other sites
Quote

Do the terms undefined and not defined mean the same thing?

I have seen the term undefined returned when a value for a variable is not present.   So, would it not apply here as well?

In general language, yes.  In Javascript, undefined is also a special value.  You can even set it:

var a = undefined;

So, yes, undefined means it is not defined, but in Javascript it is also a special value, like null or NaN.

Share this post


Link to post
Share on other sites

Here's something else to consider.  You can do what you're trying to do without a helper function, you can do it just by adding methods to Number.  e.g.:

Number.prototype.annotate = function(unit) {
    var s = String(this);
    var unit = String(unit);
    s = s + " " + unit;
    return s;
}
Number.prototype.secondsToTimespan = function() {
    var sec_duration = this.valueOf();
    var hrCount = Math.floor(sec_duration / 3600);
    sec_duration %= 3600;
    var minCount = Math.floor(sec_duration / 60);
    var secCount = sec_duration % 60;
    return hrCount.annotate('hr') + " " + minCount.annotate('min') + " " + secCount.annotate('sec');
}

var num = 1234;
console.log(num.secondsToTimespan());

Now, consider why that will work in your situation, and the other way you tried will not.  The difference is scope.

Share this post


Link to post
Share on other sites
The problem is where you are using it, the scope.  You haven't shown that context, so this is a lot of assuming from me but it's all I can do with the limited information. 

But, I have shown the context.  All that is left is HTML, and I have checked this as well. Nothing bad is reported.

Share this post


Link to post
Share on other sites

But, I have shown the context.

If that's true, then why does it work to copy your code into the console and run it?  There's no error there.

Share this post


Link to post
Share on other sites

Let me spell it out: you said you defined it inside jQuery's ready handler, but you did not show that code.  You extracted out only the function definitions, but those work.  Those aren't the problem.  The problem is the scope and the context where you defined it, which is inside the ready handler.  You assumed that was not the problem, so you didn't show it.  Assumptions like that waste a lot of time when tracking down bugs.

Share this post


Link to post
Share on other sites
Quote

If that's true, then why does it work to copy your code into the console and run it?  There's no error there.

And, I would not expect there to be.  For, I have already stated that I can achieve the same on load in the ready() function without the alert-button.

Roddy

Share this post


Link to post
Share on other sites

For, I have already stated that I can achieve the same on load in the ready() function without the alert-button.

Absolutely, because when you do that, and the example in post 1, you are running the code in 2 different scopes.  It works in one, and not the other.

Share this post


Link to post
Share on other sites

I'm trying my hardest to impress upon you that the problem is the scope where you have defined it.  At this point I guarantee that's the issue, I don't have to guess now.

Share this post


Link to post
Share on other sites

Great! This means that we are making progress.  Now, if you are able, why would the first button-alert work, but not the second.  For, this is, indeed, the issue, is it not?

Roddy

Share this post


Link to post
Share on other sites

Again, the second one does not work because you have defined the function inside a limited scope.  It is not available in the scope where the click event on that button executes.  Everything in Javascript executes inside a specific scope.  By default, the scope is the window object.  That's why you can refer to window.alert, window.prompt, window.location etc without needing to preface it with the window object, because that's the default scope.  You have defined the function inside a non-global scope, so it is not defined globally.  Conversely, when you modify the prototype of any object, there's only one prototype.  Prototypes are not defined in some scope but not others, they are always global, so no matter where you make changes to a prototype it will always affect every instance of that object.

Let me be super explicit: if you want to show your complete code, without removing anything that you're assuming is not the problem, then I will just tell you what changes to make and why that fixes it.

  • Thanks 1

Share this post


Link to post
Share on other sites

Please, I can make the second button work by simply declaring the secTranslate( ) function in the body of the document -- thus, placing it within the scope of the document -- this, I have known for a very long time.

At least, now I have a good explanation for why this works.  It is something that has always worked, but I have never clearly understood.

The problem, then, has to do with the ready( ) function, and why it sometimes works and sometimes does not in similar, but obviously different contexts.  Even now I believe to have the answer to this.  According to StackOverflow functions declared within the ready() function are not within the global scope, and it is within the global scope that the onclick() function is looking.

Surely you must agree with this interpretation.  It has been a great lesson for me!  Hooray! Hooray!

Roddy

Share this post


Link to post
Share on other sites

That's what I've been explaining, yes.  Scope is a major topic in Javascript, it's important to understand how it works.  It's easy to get a shallow understanding of this stuff, but that is often not enough.  Consider Newton vs. Einstein.  Newton's laws were set in stone for over 2 centuries before Einstein came along, and he noticed that the Newtonian laws worked for the vast majority of situations, but not all of them.  He spent years proving this to people, who finally accepted that he was correct (consider what it meant for the "best" physicists to have to admit that their centuries-old laws were wrong, but that's a completely unrelated but very interesting story).

It's easy to look at things like this and make assumptions that are correct 90 or 95% of the time, but that causes a problem when you find the situation where your assumptions are wrong and you need to consider that you don't understand what you thought you did.  This is why it is important to not make assumptions and seek an understanding of this stuff beyond the surface.  Don't be content with observing how it works, seek out discussions of why it works from some of the more experienced people.

There are a lot of articles online which discuss Javascript scope, and I would be surprised if any reputable Javascript book did not have a chapter entirely on the subject.  I don't think that Brendan Eich himself has written any books, but a lot of people have written a lot of books about Javascript.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×