meyerweb.com

Skip to: site navigation/presentation
Skip to: Thoughts From Eric

Safari SyntaxError

In pursuit of better JavaScript skills, I’ve encountered a Safari problem that may be a limitation in the browser, or it may be my coding.  I have a temporary test file demonstrating what’s happening.  In Firefox, everything works as I’d hoped it would, but in Safari, all I get is an error on the JavaScript console stating:

SyntaxError – Parse error

Here’s the function in which the error occurs, with the offending line emphasized:

function Test(a,b) {
	this.a = a;
	this.b = b;
	this.c = new (function Inner() {
		this.x = 'woo';
	});
}

As I say, here’s the temporary test file in case you want to see the entire script.

My long experience with CSS and browser handling  of it teaches me that just because something works in one browser, that doesn’t mean it’s supposed to work at all.  Therefore, it could be that Firefox is letting me be sloppy, and Safari is telling me I’ve messed up; or it could be that Firefox is right, and Safari has a problem.

So which is it?  If it’s the former, how can I do the same thing I’m trying to do, except correctly?  If it’s the latter, are there any simple workarounds to get Safari to behave?

Thanks for any help.

Update: Lachlan Hunt rides in to the rescue by pointing out what probably should have been painfully obvious, but like I say, I’m new to this.  The solution here:

function Test(a,b) {
	this.a = a;
	this.b = b;
	this.c = new function() {
		this.x = 'woo';
	};
}

I’m still not 100% certain if this was a case of sloppy authoring or a bad browser, but I’m not so concerned with that right now.  I’ll leave up the post in case anyone else encounters a similar problem.

Update redux: Adrian (and, though I didn’t understand what he was saying at the time, Tim) points out a simpler way to do the same thing.

16 Responses»

    • #1
    • Comment
    • Mon 11 Jul 2005
    • 0755
    Dean Edwards wrote in to say...

    Your code works on Win32 IE/FF/Opera (I think the code is valid).

    Putting some brackets around the definition of “Inner” should fix this problem. I don’t have a MAC so I can’t test it.

    • #2
    • Comment
    • Mon 11 Jul 2005
    • 0759
    Dean Edwards wrote in to say...

    To be clear: new (function Inner() {this.x = "woo";});

    • #3
    • Comment
    • Mon 11 Jul 2005
    • 0814
    Eric Meyer wrote in to say...

    Thanks for the suggestion, Dean. Unfortunately, Safari is still choking, and in the same way. I’ve updated the entry to reflect the new test file’s contents.

    (By the way, it’s a “Mac”, not a “MAC”—unless of course one buys a Dell LATITUDE instead of a Dell Latitude.)

    • #4
    • Comment
    • Mon 11 Jul 2005
    • 0835
    Tim wrote in to say...

    Whats the new( ... ) for? If you’re defining a method then drop it.

    If you’re instantiating an object with an inline named constructor (eh?) then either just declare the object, or drop the brackets: would you write new (Object()) or new Object()?

    Whats wrong with: this.c = { x: "woo" }; ? The ‘serializer’ doesn’t seem to pick up the constructor name.

    So you want a named constructor, but within the local scope of the Test object constructor? Why not just declare it, then call this.c = new Inner();

    Never mind parsing errors, what are you trying to do?!?

    • #5
    • Comment
    • Mon 11 Jul 2005
    • 0844
    Lachlan Hunt wrote in to say...

    I’m not 100% sure if your code is syntactically correct or not, but try it like this without Inner(). It should work.

    this.c = new function() { this.x = "woo"; };

    • #6
    • Comment
    • Mon 11 Jul 2005
    • 0848
    Eric Meyer wrote in to say...

    Sorry, Tim, but I don’t completely understand your suggestions. This is because you used a number of words I understand only partially, or not at all: constructor, for one. Remember, I’m trying to learn JavaScript.

    What I’m trying to do is, as in the file, be able to set up a thingy (object? constructor? class? dunno!) that has a bunch of sub-parts that I can calculate, and then reference later. So I want to be able, later on, to just grab the values of obj.a, obj.b, and obj.c.x.

    This is just a simple test file, of course. For the actual purposes I have in mind, I need to have several properties of the object, and sub-properties of those properies. As an example, I’d eventually want to set up obj.c.x, obj.c.y, obj.c.z, and so on. They’re likely to involve simple math when the object is created, and need to be easily retrievable later.

    Apologies if that wasn’t clear from the contents of the test file.

    • #7
    • Comment
    • Mon 11 Jul 2005
    • 0850
    Eric Meyer wrote in to say...

    Lachlan: so it does. Thanks!

    It’s always the simple answers that I overlook in the most determined fashion…

    • #8
    • Comment
    • Mon 11 Jul 2005
    • 0851
    Adrian D. wrote in to say...

    Using “new” is a little wierd for this situation. As Tim said, it’s better to declare the object directly.

    function Test(a,b) {
    this.a = a;
    this.b = b;
    this.c = {
    x: ‘woo’,
    y: ‘wee’
    };
    }

    • #9
    • Comment
    • Mon 11 Jul 2005
    • 0856
    Eric Meyer wrote in to say...

    That works too, Adrian. Thanks!

    • #10
    • Comment
    • Mon 11 Jul 2005
    • 0917
    Tim wrote in to say...

    Sorry. I assumed your skillz were as good in JS as CSS!

    Arrays, Objects and Functions are all weirdly similar in JS.

    So using Adrian’s code (which does sound the best fit), you could actually call your properties using the array obj["a"] style.

    Or iterate over your properties:
    for p in obj {
    writeOut(obj[p]);
    }

    instead of explicitly calling obj.a and obj.b

    Then you can even create properties as you go along, just by assigning them. Array style, or . style.

    One thing that really pushed my JS skills was looking through the prototype library.

    Good luck, JS is a very intersting language.

    • #11
    • Comment
    • Mon 11 Jul 2005
    • 0957
    Jeff Walden wrote in to say...

    I may be outside my depth here, but my guess is that you’re probably encountering a part of JavaScript that Safari doesn’t support. Everything in the syntax you posted looks correct to me. The parentheses around function Inner(...) {...} are perfectly fine, even tho they’re unnecessary. Parentheses can and usually add explicit grouping so that you can reduce ambiguity, but here there’s none. Just because they’re not needed doesn’t make them invalid. They would be necessary if you wanted to call this constructor with any arguments, perhaps like so (I hope comments allow <pre/>):

    function Test(a,b) {
    this.a = a;
    this.b = b;
    this.c = new (function Inner(a) {
    this.x = ‘woo';
    this.y = a;
    })(17.5); /* 17.5 is argument a */
    }

    The above would create an object foo with foo.a == a, foo.b == b, foo.c.x == 'woo', and foo.c.y == 17.5. However, you don’t have any arguments like the above example, so you don’t need the parentheses.

    My guess is that Safari’s either bailing on the extraneous (but valid) parentheses or on the Inner part. Usually in a situation such as you propose the function is declared anonymously:

    function Test(a,b) {
    this.a = a;
    this.b = b;
    this.c = new (function () {
    this.x = ‘woo';
    })
    }

    You can name an “anonymous” function like this using the syntax you describe at the beginning, but it’s not common. Up until a week or so ago I’d never seen it used before, but it’s allowed.

    If you’re feeling really masochistic, you can look this up in the ECMA-262 specifications which are based upon JavaScript. Go to page 83 and look at the definition of FunctionExpression, noting that the Identifier part (the function name) has an “opt” subscript. Page 170 later defines a NewExpression as a MemberExpression, and a MemberExpression is defined as (among others) a FunctionExpression. Thus, the Inner part of your example is valid.

    • #12
    • Comment
    • Mon 11 Jul 2005
    • 1515
    Laurens Holst wrote in to say...

    Perhaps, if you put that original piece of code all on one line, it will work.

    Because Javascript is a language where the statement-ends do not have to be explicitly marked up with semicolons (why! why!?), it is very possible that the statement-end discovery rules considered e.g. the start of the { block as the end of the (then invalid) this.c = new (function Inner() code. Although I, of course, have not been able to try that out in that particular browser.

    ~Grauw

    • #13
    • Comment
    • Tue 12 Jul 2005
    • 0640
    Andy wrote in to say...

    Hmm. Lachlan’s suggestion is what I’d normally do – and it seems to be the usual way of doing this.

    The interesting thing about functions in Javascript is that they can be passed around like variables. What you’re doing with

    this.c = new function() { this.x = "woo"; };

    is saying ‘assign property c the value of a new variable’. It doesn’t matter that it’s a function.

    The other way you could do it is:

    function Inner() {
    this.x = "woo";
    }

    function Test(a,b) {
    this.a = a;
    this.b = b;
    this.c = Inner;
    }

    In this example, you define Inner, and then you set C be that function.

    The second way has the advantage that you can use the Inner function elsewhere – for calling independently, or for other properties.

    The first way has the advantage of being clearer (IMHO), and can be combined with a feature of Javascript called ‘closures’. If you’re just getting into JavaScript, it’s worth noting that they’re a).useful, b). something worth learning about, c). can leak memory, and d). that you’ll come back to later.

    • #14
    • Comment
    • Wed 13 Jul 2005
    • 0426
    Michaël Guitton wrote in to say...

    Eric, you can thank Dean, Adrian and Tim for their JavaScript expertise. Object literals or private/ privileged members are the way to go here. If you’d like to learn more of JavaScript, I recommend you to drop by The World’s Most Misunderstood Programming Language and Private Members in JavaScript pages. This should help you grasp the hidden power of JavaScript. ;-)

    • #15
    • Comment
    • Mon 12 Mar 2007
    • 1415
    Juan Mendes wrote in to say...

    This is a known parsing bug in Safari. Named functions must be global. Therefore, if you need to assign a function to a value, make it anonymous. There is no need to name if you are already assigning it to a variable or object property

    The following is the simplest code to prove this.


    var x = function y () {} // Doesn't work in Safari
    var x = function (){} // Works in safari

    I had been using named functions to help me while debugging code but stopped doing it once I found that Safari couln’t handle it.

    • #16
    • Pingback
    • Tue 13 Jan 2009
    • 1630
    Received from Justkez » Frustrating jQuery Safari error

    […] offending statement. The problem is, the statements aren’t offensive. It has been discussed before – a long time ago, I might […]

Leave a Comment

Line and paragraph breaks automatic, e-mail address required but never displayed, HTML allowed: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>



Remember to encode character entities if you're posting markup examples! Management reserves the right to edit or remove any comment—especially those that are abusive, irrelevant to the topic at hand, or made by anonymous posters—although honestly, most edits are a matter of fixing mangled markup. Thus the note about encoding your entities. If you're satisfied with what you've written, then go ahead...


July 2005
SMTWTFS
June August
 12
3456789
10111213141516
17181920212223
24252627282930
31  

Sidestep

Feeds

Extras