Safari SyntaxError
Published 19 years, 3 months pastIn 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.
Comments (16)
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.
To be clear:
new (function Inner() {this.x = "woo";});
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.)
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())
ornew 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?!?
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"; };
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.
Lachlan: so it does. Thanks!
It’s always the simple answers that I overlook in the most determined fashion…
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’
};
}
That works too, Adrian. Thanks!
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.
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
withfoo.a == a
,foo.b == b
,foo.c.x == 'woo'
, andfoo.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.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
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.
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. ;-)
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.
Pingback ::
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 […]