Bug 20141

Summary: Web Inspector: cannot call unbound reference to console.log
Product: WebKit Reporter: Will Moffat <wdm>
Component: Web InspectorAssignee: Nobody <webkit-unassigned>
Status: RESOLVED DUPLICATE    
Severity: Normal CC: aandrey, ap, aroben, arv, burg, chrisjshull, commit-queue, costan, darin, eflews.bot, esprehn+autocc, getify, glenn, graouts, gyuyoung.kim, joepeck, kangax, mathias, ojan.autocc, oliver, sindresorhus, timothy, webkit-bug-importer
Priority: P2 Keywords: InRadar
Version: 528+ (Nightly build)   
Hardware: All   
OS: All   
Attachments:
Description Flags
test case
none
Patch
none
Patch
none
Patch eflews.bot: commit-queue-

Description Will Moffat 2008-07-23 06:20:48 PDT
I'm trying to do cross-browser logging, so I set the variable 'log' to point to the browser-specific log function, in WebKit's case, console.log:

var log = console.log;
log('ff');
--> TypeError: Type error 

console.log() works fine, but log() is broken.
Comment 1 Alexey Proskuryakov 2008-07-23 09:01:34 PDT
Created attachment 22453 [details]
test case
Comment 2 Alexey Proskuryakov 2008-07-23 09:06:38 PDT
That's because logging as currently implemented in WebKit requires a page pointer, which is stored in the console object. So, the console.log function cannot be called on any other object.
Comment 3 Sam Weinig 2008-07-23 10:04:14 PDT
I do not think this is a bug.  One cannot expect a generic function to work without a this object.  You would not expect, for instance, var g = document.getElementsById; g("foo"); to work.
Comment 4 Adam Roben (:aroben) 2008-07-23 20:35:50 PDT
A possible solution would be:

var log = function() { console.log(arguments) }

I agree that this doesn't really seem like a bug, though it is perhaps a little surprising.
Comment 5 Will Moffat 2008-07-23 23:17:57 PDT
Thanks guys, my approach worked in both FF+Firebug and Opera so I assumed it was a WebKit bug. The error message was also confusing. But your explanation makes complete sense. 

@Adam: your example always outputs the string 'Arguments' - works fine in FF though ;-)

This works:
var log = function() { console.log.apply(console,arguments) };
Comment 6 Alexey Proskuryakov 2008-07-23 23:46:37 PDT
(In reply to comment #3)
> I do not think this is a bug.  One cannot expect a generic function to work
> without a this object. 

Well, you can expect that from a function that is documented as being generic, or one that is not formally specified, but is generic in other implementations.
Comment 7 Glenn Maynard 2010-12-03 12:49:08 PST
Please reevaluate this.

Creating a wrapper causes an annoying problem: every stack trace from console logs ends at the wrapper you created, instead of the actual point of the log.

For example,

  console.log("test 1");
  var x = function() { console.log.apply(console, arguments); }
  x("test 2");
  x("test 3");
  x("test 4");

usually shows:

test 1 logging.js:1
test 2 logging.js:2
test 3 logging.js:2
test 4 logging.js:2

It renders the stack frame useless in console summaries--every log using that function will always say "logging.js:2".  Making the logging functions not depend on the this avoids this problem.  Console should just be a namespace, and the functions in it should be closures on any objects they need access to.

(The more general fix for this is to add a parameter to the logging functions, giving a hint of how many stack frames should be ignored.  That's particularly useful when you really do need to wrap logging, eg. to intercept and tee the log somewhere else.)
Comment 8 kangax 2011-01-27 10:21:49 PST
Would love to see this behavior changed as well. It is indeed quite inconvenient to have to `function(){console.log.apply(console, arguments)}` or `console.log.bind(console)` all the time.
Comment 9 Victor Costan 2013-04-10 23:37:09 PDT
*** Bug 114406 has been marked as a duplicate of this bug. ***
Comment 10 Victor Costan 2013-04-10 23:56:51 PDT
Created attachment 197513 [details]
Patch
Comment 11 WebKit Commit Bot 2013-04-10 23:59:21 PDT
Attachment 197513 [details] did not pass style-queue:

Failed to run "['Tools/Scripts/check-webkit-style', '--diff-files', u'LayoutTests/ChangeLog', u'LayoutTests/fast/dom/Window/console-unbound-functions-expected.txt', u'LayoutTests/fast/dom/Window/console-unbound-functions.html', u'Source/WebCore/ChangeLog', u'Source/WebCore/bindings/scripts/CodeGeneratorJS.pm', u'Source/WebCore/bindings/scripts/IDLAttributes.txt', u'Source/WebCore/bindings/scripts/test/CPP/WebDOMTestObj.cpp', u'Source/WebCore/bindings/scripts/test/CPP/WebDOMTestObj.h', u'Source/WebCore/bindings/scripts/test/GObject/WebKitDOMTestObj.cpp', u'Source/WebCore/bindings/scripts/test/GObject/WebKitDOMTestObj.h', u'Source/WebCore/bindings/scripts/test/JS/JSTestObj.cpp', u'Source/WebCore/bindings/scripts/test/JS/JSTestObj.h', u'Source/WebCore/bindings/scripts/test/ObjC/DOMTestObj.h', u'Source/WebCore/bindings/scripts/test/ObjC/DOMTestObj.mm', u'Source/WebCore/bindings/scripts/test/TestObj.idl', u'Source/WebCore/page/Console.cpp', u'Source/WebCore/page/Console.h', u'Source/WebCore/page/Console.idl']" exit_code: 1
Source/WebCore/bindings/scripts/test/CPP/WebDOMTestObj.h:188:  The parameter name "str" adds no information, so it should be removed.  [readability/parameter_name] [5]
Total errors found: 1 in 18 files


If any of these errors are false positives, please file a bug against check-webkit-style.
Comment 12 Victor Costan 2013-04-10 23:59:37 PDT
(copied from the description of #114406)

In order to achieve this, the patch introduces the [LenientThis] WebKitIDL attribute, which is heavily inspired from the LenientThis WebIDL.
http://www.w3.org/TR/WebIDL/#LenientThis

I implemented LenientThis for methods.  I figured the only way to not require a specific type for "this" is to completely ignore the value, so a LenientThis method calls a static method in WebCore. A LenientMethod is similar to a static method, but it can be accessed as a property of the interface instances, instead of being a property of the interface object. I hope this makes sense, and I look forward to feedback.
Comment 13 Darin Adler 2013-04-11 11:08:40 PDT
Comment on attachment 197513 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=197513&action=review

I’m going to say review+ but I think we can improve this further.

> Source/WebCore/bindings/scripts/CodeGeneratorJS.pm:191
> -    
> +

It’s strange to do all this whitespace stripping. I’m not sure we normally encourage that sort of thing.

>> Source/WebCore/bindings/scripts/test/CPP/WebDOMTestObj.h:188
>> +    void lenientThisMethod(const WebDOMString& str);
> 
> The parameter name "str" adds no information, so it should be removed.  [readability/parameter_name] [5]

The style queue complaint is not new to the patch, and not important. The best way to fix it would be to omit argument names entirely in these automatically generated declarations. I don’t think we need the names in these files. But this is specific to the C++ binding. I don’t even know the status of that binding. Who is using it? Can we remove it? Worth discussing.

> Source/WebCore/page/Console.cpp:272
> +Page* Console::pageFromScriptState(ScriptState* state)

This should be a free function at the top of the file, not a Console member function.

> Source/WebCore/page/Console.cpp:280
> +    Console* console = window->console();
> +    if (!console)
> +        return 0;
> +    return console->page();

This seems strange. To get from DOMWindow to page, I would not suggest going through the console. It will work, but it’s not the right way to do things, even inside the Console class. The right way is to walk from DOMWindow to Page, staying with the core classes such as Document, Frame, and Page.

> Source/WebCore/page/Console.h:88
> +    inline static Page* pageFromScriptState(ScriptState*);

I believe the inline here is non-helpful and should be omitted. I suggest having this be a free function, not a Console member function.
Comment 14 Victor Costan 2013-04-11 14:46:25 PDT
Created attachment 197675 [details]
Patch
Comment 15 WebKit Commit Bot 2013-04-11 14:47:59 PDT
Attachment 197675 [details] did not pass style-queue:

Failed to run "['Tools/Scripts/check-webkit-style', '--diff-files', u'LayoutTests/ChangeLog', u'LayoutTests/fast/dom/Window/console-unbound-functions-expected.txt', u'LayoutTests/fast/dom/Window/console-unbound-functions.html', u'Source/WebCore/ChangeLog', u'Source/WebCore/bindings/scripts/CodeGeneratorJS.pm', u'Source/WebCore/bindings/scripts/IDLAttributes.txt', u'Source/WebCore/bindings/scripts/test/CPP/WebDOMTestObj.cpp', u'Source/WebCore/bindings/scripts/test/CPP/WebDOMTestObj.h', u'Source/WebCore/bindings/scripts/test/GObject/WebKitDOMTestObj.cpp', u'Source/WebCore/bindings/scripts/test/GObject/WebKitDOMTestObj.h', u'Source/WebCore/bindings/scripts/test/JS/JSTestObj.cpp', u'Source/WebCore/bindings/scripts/test/JS/JSTestObj.h', u'Source/WebCore/bindings/scripts/test/ObjC/DOMTestObj.h', u'Source/WebCore/bindings/scripts/test/ObjC/DOMTestObj.mm', u'Source/WebCore/bindings/scripts/test/TestObj.idl', u'Source/WebCore/page/Console.cpp', u'Source/WebCore/page/Console.h', u'Source/WebCore/page/Console.idl']" exit_code: 1
Source/WebCore/bindings/scripts/test/CPP/WebDOMTestObj.h:188:  The parameter name "str" adds no information, so it should be removed.  [readability/parameter_name] [5]
Total errors found: 1 in 18 files


If any of these errors are false positives, please file a bug against check-webkit-style.
Comment 16 Victor Costan 2013-04-11 14:52:20 PDT
Created attachment 197676 [details]
Patch
Comment 17 WebKit Commit Bot 2013-04-11 14:55:14 PDT
Attachment 197676 [details] did not pass style-queue:

Failed to run "['Tools/Scripts/check-webkit-style', '--diff-files', u'LayoutTests/ChangeLog', u'LayoutTests/fast/dom/Window/console-unbound-functions-expected.txt', u'LayoutTests/fast/dom/Window/console-unbound-functions.html', u'Source/WebCore/ChangeLog', u'Source/WebCore/bindings/scripts/CodeGeneratorJS.pm', u'Source/WebCore/bindings/scripts/IDLAttributes.txt', u'Source/WebCore/bindings/scripts/test/CPP/WebDOMTestObj.cpp', u'Source/WebCore/bindings/scripts/test/CPP/WebDOMTestObj.h', u'Source/WebCore/bindings/scripts/test/GObject/WebKitDOMTestObj.cpp', u'Source/WebCore/bindings/scripts/test/GObject/WebKitDOMTestObj.h', u'Source/WebCore/bindings/scripts/test/JS/JSTestObj.cpp', u'Source/WebCore/bindings/scripts/test/JS/JSTestObj.h', u'Source/WebCore/bindings/scripts/test/ObjC/DOMTestObj.h', u'Source/WebCore/bindings/scripts/test/ObjC/DOMTestObj.mm', u'Source/WebCore/bindings/scripts/test/TestObj.idl', u'Source/WebCore/page/Console.cpp', u'Source/WebCore/page/Console.h', u'Source/WebCore/page/Console.idl']" exit_code: 1
Source/WebCore/bindings/scripts/test/CPP/WebDOMTestObj.h:188:  The parameter name "str" adds no information, so it should be removed.  [readability/parameter_name] [5]
Total errors found: 1 in 18 files


If any of these errors are false positives, please file a bug against check-webkit-style.
Comment 18 Victor Costan 2013-04-11 14:56:06 PDT
Darin, thank you very much for your feedback!

I uploaded a new patch that addresses most of it.

Regarding the style error in the bindings, I'm new to WebKit, so I wouldn't know who uses which bindings :)  Still, I can look into changing the code generator. I think that the change will cause a big diff in the test outputs, so I'd prefer to do it in a separate patch. Do you think this makes sense?
Comment 19 Glenn Maynard 2013-04-11 19:18:01 PDT
This shouldn't work.  Comment #7 is solved simply by using bind(); calling a bound console function does work as expected (it may not have back in 2010, but it does today).

You can't pull random member functions off of objects and have it work.  You can't say "g = document.getElementById; g('foo')".  That's what bind() is for.

This might make a little sense if console.log acted like a static function (eg. if it didn't actually care about the console object), but it doesn't; which console.log you call determines which console it goes to.  Please don't add new weird things to the web.

(In reply to comment #12)
> In order to achieve this, the patch introduces the [LenientThis] WebKitIDL attribute, which is heavily inspired from the LenientThis WebIDL.
> http://www.w3.org/TR/WebIDL/#LenientThis

Notice that WebIDL has a big angry red box explicitly warning against this.
Comment 20 Victor Costan 2013-04-11 20:13:17 PDT
Glenn, thank you vey much for your feedback! I hope you will reconsider your opinion, based on the following arguments.

(1) I think there is great value in having developers be able to use "console.log" in a bound form while debugging any Web page / application. Changing the source code of the page I'm debugging might be a hassle. Also, if I'm debugging a library using its unit tests page, I wouldn't want to mess with the browser environment. bind works, but having to use it breaks the "I'll just type this one-liner in the Web Inspector" flow.

(1') All the developers that I talked to were happy to hear about this possible change. I think this is worth a bit of extra complexity in the code. Being able to pass console.{log, warn, error....} as an async is a nice little useful debugging tool, and having to do some setup from it detracts from this.

(2) "console" is a singleton, so we don't really need "this" to decide what to do. To a Web developer, the TypeError seems like being fussy for no reason.  To the best of my knowledge, getElementById works for any Document and DocumentFragment instance, so calling it without a value for "this" would be ambiguous.

(3) The WebIDL warning mentions specifications, and WebKit is an implementation.

(4) The WebIDL warning also mentions "unless for compatibility reasons". Using console.log unbound works in Firefox, so one could argue that this helps compatibility. The complaints in this Chromium bug, though unverified, seem to indicate that some developers assume console.log will work.

http://crbug.com/167911

Sorry for the long response, and thank you very much for reading it!
Comment 21 Glenn Maynard 2013-04-11 20:44:17 PDT
(In reply to comment #20)
> Glenn, thank you vey much for your feedback! I hope you will reconsider your opinion, based on the following arguments.
> 
> (1) I think there is great value in having developers be able to use "console.log" in a bound form while debugging any Web page / application. Changing the source code of the page I'm debugging might be a hassle. Also, if I'm debugging a library using its unit tests page, I wouldn't want to mess with the browser environment. bind works, but having to use it breaks the "I'll just type this one-liner in the Web Inspector" flow.

Sorry, I don't know what you're referring to.  You can just call console.log in web consoles, and anywhere you might say "x = console.log" you can say "x = console.log.bind(console)".

> (1') All the developers that I talked to were happy to hear about this possible change. 

I thought this was a good idea a couple years ago, too (comment #7).  I was mistaken.  Unless those people join the conversation and make technical arguments one way or the other, popularity contests aren't useful.

> I think this is worth a bit of extra complexity in the code. Being able to pass console.{log, warn, error....} as an async is a nice little useful debugging tool, and having to do some setup from it detracts from this.

Having to say ".bind(something)" if you want to take a reference to a function and use it as a standalone function is universal to the platform.  Having to do the same thing here that you have to do everywhere else enhances, not detracts from, the platform.

> (2) "console" is a singleton, so we don't really need "this" to decide what to do. To a Web developer, the TypeError seems like being fussy for no reason.  To the best of my knowledge, getElementById works for any Document and DocumentFragment instance, so calling it without a value for "this" would be ambiguous.

No, console is definitely not a singleton.

https://zewt.org/~glenn/test-window-console.html

Here, we have two console objects, one for each window, and logs to each go to the corresponding window's console.

> (3) The WebIDL warning mentions specifications, and WebKit is an implementation.

Do you think that APIs that lack a specification should do things which are considered bad practice for those that do?

> (4) The WebIDL warning also mentions "unless for compatibility reasons". Using console.log unbound works in Firefox, so one could argue that this helps compatibility.

This change wasn't proposed for web-compatibility reasons.  I think you're hunting for arguments.

> The complaints in this Chromium bug, though unverified, seem to indicate that some developers assume console.log will work.

This incorrect assumption can be made for every single API on the platform.  You can just as easily try "create = document.createElement; create('div')", or any of the hundreds of other functions on objects.  Making it work for console isn't going to reduce confusion; it'll only make the platform more inconsistent, and make people even more confused when they find that this works with console, but pretty much nothing else.
Comment 22 Victor Costan 2013-04-12 05:56:59 PDT
Glenn, I'm obviously going to defer to you on this, but here are a couple more thoughts.

First, the main goal of console.log and its cousins is to speed up development. It's already very different from the other Web APIs, because it "talks" to another world (the debugging console), and it has "superpowers" (e.g., it can enumerate all the properties of an object when dumping it). I think that making console.log as useful as possible should trump consistency with DOM APIs, because it's not really a DOM API. (for example, it's in node.js as well). At the same time, I recognize I'm really biased, because I think that speeding up Web development would give us cool new toys to play with :)

Also, since you brought up the issue of a specification, I think console should have been Console, and Console.log should have behaved like IDBKeyRange.upperBound, which is a static method. I also think that being able to log to another window's console is a bit confusing, but at least that's self-inflicted pain. Last, having a Console singleton would justify exposing it to Web Workers. (it'd work in an obvious way for dedicated workers, shared workers are a bit more difficult) But that's water under the bridge now, since console.log is a de-facto standard.

Second, in my experience teaching Web development to MIT students, people learning JavaScript are thoroughly confused by "this" anyway, because of the way it's bound in DOM events. I conjecture that by the time they figure out "this", people would be able to handle the console inconsistency, and be grateful that the browser developers tried to make console.log more useful for async APIs. In summary, I think Firefox got this right.

Last, if you're really against special-casing console.{log, info, error, ...}, would you be willing to entertain the idea of supplying bound console functions, in a manner similar to "dir", in the Inspector?

Once again, sorry for the long answer, and thank you very much for reading it!
Comment 23 Glenn Maynard 2013-04-12 06:18:08 PDT
(In reply to comment #22)
> Glenn, I'm obviously going to defer to you on this, but here are a couple more thoughts.

I'm just another web developer offering his input--the decision is up to WebKit folks.

> First, the main goal of console.log and its cousins is to speed up development. It's already very different from the other Web APIs, because it "talks" to another world (the debugging console), and it has "superpowers" (e.g., it can enumerate all the properties of an object when dumping it). I think that making console.log as useful as possible should trump consistency with DOM APIs, because it's not really a DOM API. (for example, it's in node.js as well). At the same time, I recognize I'm really biased, because I think that speeding up Web development would give us cool new toys to play with :)

If console.log was actually inconvenient, I might agree, but I don't think it is.

> Also, since you brought up the issue of a specification, I think console should have been Console, and Console.log should have behaved like IDBKeyRange.upperBound, which is a static method. I also think that being able to log to another window's console is a bit confusing, but at least that's self-inflicted pain. Last, having a Console singleton would justify exposing it to Web Workers. (it'd work in an obvious way for dedicated workers, shared workers are a bit more difficult) But that's water under the bridge now, since console.log is a de-facto standard.

Web Workers can still support console (and really, really needs to), but it'll take some thought to figure out which window's console logs should go to.  That's simple for dedicated workers, but shared workers are trickier.  (This is a more general problem, see http://lists.w3.org/Archives/Public/public-whatwg-archive/2013Mar/0283.html, so maybe a general solution will be found.)

> Second, in my experience teaching Web development to MIT students, people learning JavaScript are thoroughly confused by "this" anyway, because of the way it's bound in DOM events. I conjecture that by the time they figure out "this", people would be able to handle the console inconsistency, and be grateful that the browser developers tried to make console.log more useful for async APIs. In summary, I think Firefox got this right.

I think this is a case of "everyone agrees consistency is good, and everyone thinks their case is the exception".  Adding inconsistencies to the platform is bad, since it's a big, complex system, and I don't see the justification for this one.

Where is console.log not useful for async APIs?

> Last, if you're really against special-casing console.{log, info, error, ...}, would you be willing to entertain the idea of supplying bound console functions, in a manner similar to "dir", in the Inspector?

I won't be too bothered by things WebKit does in web consoles, since it doesn't affect the Web--it's an implementation detail.

But, I still don't understand the use case.  I've never had to say "console.log.bind(console)" in a console.
Comment 24 kangax 2013-04-12 06:28:58 PDT
(In reply to comment #19)
> This shouldn't work.  Comment #7 is solved simply by using bind(); calling a bound console function does work as expected (it may not have back in 2010, but it does today).
> 
> You can't pull random member functions off of objects and have it work.  You can't say "g = document.getElementById; g('foo')".  That's what bind() is for.
> 
> This might make a little sense if console.log acted like a static function (eg. if it didn't actually care about the console object), but it doesn't; which console.log you call determines which console it goes to.  Please don't add new weird things to the web.

I don't see anything new and weird about `log` being bound to its console. Host objects returning "unknown" typeof in IE and blowing up on simple property access — that was weird. And even that was allowed by the spec.

The binding is not a deal breaker but it's certainly a slight inconvenience to bind it manually in cases when it's not called as a method of console (e.g. `someArray.forEach(console.log)`).

And if it is bound, which problems does it cause? Maybe I'm missing something?

FWIW, both Firefox and Opera make it bound. IE10 doesn't. So that makes it 2-vs-2, and — once Opera switches to webkit — 3-vs-1.
Comment 25 Erik Arvidsson 2013-04-12 08:49:42 PDT
It is not necessarily bad to make it bound by default but it is an inconsistency that I don't think pays the price.

Lets base this on real world compat issues instead. For example if x% of the Alexa top 500 breaks due to this we should add this wart.
Comment 26 Victor Costan 2013-04-12 10:43:57 PDT
@Glenn: The use case is exploring or debugging APIs. For example, if I want to see how DOM level 3 IME input events work, I can enter something along the lines of "document.querySelector('#id').oncompositionupdate = console.log" in the Web Inspector and start interacting with the field.

An example of using asynchronous APIs is reading a file from someone's Dropbox using my dropbox.js library. The easiest way to see how that works is to type the line below in the Web Inspector.

client.readFile("/path/to/file", console.log)

Another example is learning / debugging jQuery's XHR behavior.

jQuery.ajax({url: "/path/to/json", dataType: "json", success: console.info, error: console.warn)


@Erik: I would hope that the sites with the most traffic have a release process that ensures they wouldn't break in WebKit browsers, and I would assume that at least some have their own logging mechanism built on top of console.log. At most, some small sites will have short outages until people complain and the logging statements get fixed / removed.

The biggest share of the price of the current console.log isn't site breakage. It is slowing down the people who build sites, by not being as versatile as it could be. My story below (which you can skip) illustrates this.

<blahblah>
The first time I tried to use console.log as a callback, I was happy for realizing that it should work as a callback, since JavaScript is a functional language. Then I saw the Type error and realized what was going on. I was immediately curious if Firefox works the same way, so I opened it up and tried it there. Afterwards I figured I'm not going to bother writing "function(x) { console.log(x); }" (didn't think of bind() on the spot, it's not that much easier to type though). So I moved back to my editor, added a logging statement, and refreshed the page. By the end of this, my flow was interrupted.

Next time (a few months later) I bumped into another case where I'd have liked to pass console.log to a callback, I checked quickly to see if it still throws a TypeError, grumbled and moved on. Later, I stumbled upon the Chromium bug, and a few days ago I stumbled upon the issue again, and was in a good position to address it.
</blahblah>

The main takeaway here is that most developers running into this won't consider it serious enough to complain about. It's just a tiny hurdle, but removing tiny hurdles can add up to quite a bit in the long run.
Comment 27 Kyle Simpson 2013-04-12 13:30:34 PDT
would the browsers allow `console.log = console.log.bind(console)`, basically just overwriting the built-in one with a hard-bound version, so that it can be passed around at will? you could even feature-detect which browsers this was necessary in via a try/catch.
Comment 28 Glenn Maynard 2013-04-13 17:38:10 PDT
Note that if we want console.log, etc., to act like they're pre-bound (as if you did "console.log = console.log.bind(this)", then it doesn't look like this is the right patch.  This patch is described as making console.log a static function, which is quite different and means the logging functions can no longer go to the console for the window they're invoked on.

I don't know of any WebIDL feature that describes "prebinding", but I don't think this is LenientThis.

(In reply to comment #26)
> "document.querySelector('#id').oncompositionupdate = console.log"

Saving people 14 characters of typing--".bind(console)"--isn't a good reason.  There are lots of things that slow down development, but this isn't one of them.

> Host objects returning "unknown" typeof in IE and blowing up on simple property access — that was weird. And even that was allowed by the spec.

The inconsistencies and weirdnesses already in the platform should never be used as an argument to add more of them.

(In reply to comment #24)
> FWIW, both Firefox and Opera make it bound. IE10 doesn't. So that makes it 2-vs-2, and — once Opera switches to webkit — 3-vs-1.

(2 vs. 1, actually--Opera won't count as a separate browser any more than Chrome does.)

IE9 did what Firefox does, and if that hadn't changed I'd probably agree that WebKit should just follow suit for the sake of interop, but IE10 matches WebKit.  With the current versions of IE and WebKit agreeing on behavior here, hopefully Firefox will change to match.
Comment 29 Kyle Simpson 2013-04-14 12:41:21 PDT
I really can't think of any reason for the `log()` function (and all the other console.*) to be "unbound", or rather, rebindable, to other objects. Why wouldn't you just want all of them to act as if they're hard bound to the console and/or window object? What use case would it serve to leave them re-bindable (whereas several use-cases in this thread have already pointed out shorter typing and nicer abstractability as reasons why the defaulted hard-binding would be nice)?

I understand the general argument of "so it works like all other methods", but in JS in general, there's lots of usefulness to be able to borrow functions and manually set their `this` object... except in the specific case of the console.* functions, I'm just not seeing that usefulness. Am I missing something we'd want to preserve?

Seems to me like the lesser evil would be to remove some surprises from the use cases stated here, at the almost zero cost/risk of surprising someone who's intentionally doing something very strange like trying to rebind `log()` to some other object.
Comment 30 Glenn Maynard 2013-04-14 13:30:31 PDT
(In reply to comment #29)
> I really can't think of any reason for the `log()` function (and all the other console.*) to be "unbound", or rather, rebindable, to other objects. Why wouldn't you just want all of them to act as if they're hard bound to the console and/or window object? What use case would it serve to leave them re-bindable (whereas several use-cases in this thread have already pointed out shorter typing and nicer abstractability as reasons why the defaulted hard-binding would be nice)?

The burden of showing use cases is on people arguing to do something unusual, not on the ones saying we shouldn't, just as you don't get to say "what are the use cases for *not* implementing my feature proposal?".

("Shorter typing" to save 14 characters in uncommon scenarios isn't something that I can take seriously, and I don't know what you mean by "nicer abstractability".)

> I understand the general argument of "so it works like all other methods", but in JS in general, there's lots of usefulness to be able to borrow functions and manually set their `this` object... except in the specific case of the console.* functions, I'm just not seeing that usefulness. Am I missing something we'd want to preserve?

Am I missing something that makes the console functions special and different from everything else?  It's just another API like any other, and a particularly simple and mundane one at that.

> Seems to me like the lesser evil would be to remove some surprises from the use cases stated here, at the almost zero cost/risk of surprising someone who's intentionally doing something very strange like trying to rebind `log()` to some other object.

This is a non-argument, since you could make it for pretty much every API on the platform.
Comment 31 Victor Costan 2013-04-14 13:59:51 PDT
Kyle, yes, the statement that you suggested would work. I'm not sure how to do the feature detection without risking spamming the log, though. If this patch doesn't work out, I might have to write some extension that effectively does that in the developer tools.

Glenn, I'm sorry, I'm having a really hard time following your argument.

"There are lots of things that slow down development, but this isn't one of them." seems to imply that this change won't make developers' life better. The fact that I took the time to figure out how console works in WebKit and put together this patch means that it would make _my_ life better. Also, my experience (and the stars on the Chromium bug) indicate that there is a class of people whose lives will be better, which is why I'm trying to get WebKit fixed instead of implementing a personal solution.

Given that this change would make some people's lives better, I would like to understand how it would make your life worse, so we can see if there's a way to make things work for everyone. You can still use "console" the way you used to.

I think that consistency is a good goal to aim for, because it helps us learn the platform faster, which makes us more productive. At the same time, it's important to remember that consistency is a means to achieve efficiency, not a goal on its own.

Also, sorry for repeating myself, but this change doesn't cripple console.log, so it still works in the way that you'd expect, based on DOM APIs. It only makes console.log more powerful than the DOM APIs. I think it's reasonable to expect that a debugger is more powerful than the platform APIs. For example, I will expect the Web Inspector to show me an object's ES6 private name properties and their values, even though the (draft) specification clearly says that they shouldn't be visible without the names.

Last point on consistency: I don't think IE's behavior is very relevant. During my Web development classes at MIT, the Microsoft folks admitted that they develop on Chrome and test on IE, so I don't think IE sets a good example for developer friendliness. FWIW, node.js's console.log is a function, but again I think this patch should be considered based on usefulness.

I think the real cost of this patch is the extra complexity in the WebKit code. I also think it's hard to objectively assess whether the complexity is worth the efficiency boost I mentioned above. For whatever it's worth, I plan to help with any necessary follow-up work (like I did in #111255), and to help revert this change if ends up being a major pain down the line.
Comment 32 Victor Costan 2013-04-14 14:01:23 PDT
Erik, I'm a bit confused by your position. The equivalent bug in Chromium (http://crbug.com/167911) is tagged M28, so I thought Chromium is planning to adopt this change.
Comment 33 Timothy Hatcher 2013-04-14 14:02:05 PDT
There is no reason to feature detect. You can unconditionally do `console.log = console.log.bind(console)`.
Comment 34 Kyle Simpson 2013-04-14 14:15:09 PDT
(In reply to comment #30)
>> What use case would it serve to leave them re-bindable...?
> 
> The burden of showing use cases is on people arguing to do something unusual, not on the ones saying we shouldn't, just as you don't get to say "what are the use cases for *not* implementing my feature proposal?".

Fortunately, several use cases have already been presented in this thread, so I don't really feel the burden to repeat them. That you don't like or agree with them is not sufficient grounds to imply that none have been presented.

I was merely pointing out that some use-cases do exist where this hard-binding would be nice (as listed), and asking if there are any contrary use-cases that I was missing. Sounds like there are none, or you don't care to spend the time to illustrate any.


> ("Shorter typing" to save 14 characters in uncommon scenarios isn't something that I can take seriously

It's a little deeper than "shorter typing" because what we're actually suggesting is that there cases where people are using `console.log()` without understanding the caveats that `console` is not just a namespace for the generic `log()` name, but a critical contextual component to identifying which place the log output should be directed.

I get that, and I wish everyone did, but I think many (maybe even most) don't understand that subtlety, so the loss of context breaking `log()` is confusing and surprising.

I myself even worked at Firefox for 9 months, on the developer tools team, and it hadn't occurred to me until reading this thread that there could quite possibly be multiple targets for console output. You can't seriously think that this is a detail that all JS developers just readily and automatically understand.

That's *especially* true if you don't do any `this` style programming at all in JS, where you prefer for instance module patterns and closures, and you almost never deal with loss of `this` context. You can be quite an experienced and prolific JS coder and never run into those issues, and then you merrily come along and try to deal with `console.log()` for some debugging, and you may get surprised.

Point being, it's not just about the characters, and I think you're being a bit too glib in suggesting that is the main point of this thread.

-------
That having been said, I cannot deny that saving the characters in this specific sort of usage (which I do *all the time*) would also be really nice:

`Object.keys(myobj).forEach(console.log)`

as opposed to:

`Object.keys(myobj).forEach(console.log.bind(console))`


> I don't know what you mean by "nicer abstractability".)

I thought there was already some implication of "logging abstractions" earlier in the thread, but I'll give one example to put slightly more concrete on it.

Imagine I have a multi-purpose logger utility in JS... in some circumstances, such as production, I don't want logs going to the JS console, but instead want to capture and transmit them to the server. In other cases, like dev, I do want them to go to the console (and maybe also get logged offsite).

Now, imagine, this API passes around a `log` function through my various contexts, that all my devs use to write out. It would be nicer, in terms of abstraction (and even narrowly, for performance), if I could pass around an actual reference to the console.log() function, for dev purposes, and only do a wrapped form of it when I need to hijack in production. But importantly, it'd be nice if the devs writing these `log(..)` statements didn't know those details, and didn't have to care whether the real or fake `log()` was in play.

We can go down the rabbit hole further if you still don't think there's any cases where log abstractions exist or are useful, but this is just one of many ways I could see a defaulted hard-binding being slightly nicer.


> > I understand the general argument of "so it works like all other methods", but in JS in general, there's lots of usefulness to be able to borrow functions and manually set their `this` object... except in the specific case of the console.* functions, I'm just not seeing that usefulness. Am I missing something we'd want to preserve?
> 
> Am I missing something that makes the console functions special and different from everything else?  It's just another API like any other, and a particularly simple and mundane one at that.

Yes, I guess you are missing what makes them special. I thought that was self-obvious so I didn't get specific at first.

In normal `this` style JS programming, if there's a function like:

```
var obj = {
   me: "obj",
   identify: function() { alert(this.me); }
};
```

There's a variety of ways that I can borrow that `obj.identify()` and use him against my own objects (overriding his `this`), so it's nice that, by default, `obj.identify()` has this re-bindability, through `call()` & `apply()`, as well as `new`. Of course, you could come along and hard-bind the reference with `obj.identify = obj.identify.bind(obj)`, but by default, his unbound nature can be quite useful and flexible.

However, by contrast, the core implementation of the `log()` function, as far as I'm aware, could not possibly be borrowed and reused against any random JS object in any useful way, as certainly rebinding `log()`'s `this` to something other than the legitimate `console` object in a specific window context would NOT result in any successful logging. At best it would silently fail, but it seems pretty likely it would error in an ugly way.

Point being, in normal JS, methods of objects default to being non-hard-bound so that they can more flexibly be reused and borrowed and such, but the console.* methods really have no value in being borrowed, because their very nature constricts them to use against a well defined window context.

Hence, my core argument that since these methods are not like all other JS methods, then having them, by default, hard-bound to the context they need to be bound to, **might** be justifiable.


> > Seems to me like the lesser evil would be to remove some surprises from the use cases stated here, at the almost zero cost/risk of surprising someone who's intentionally doing something very strange like trying to rebind `log()` to some other object.
> 
> This is a non-argument, since you could make it for pretty much every API on the platform.

No, not any API. Most APIs exposed to JS benefit from re-bindability. These console ones don't seem to have any re-bindability benefit, so I think they're at least potential candidates for special handling.
Comment 35 Glenn Maynard 2013-04-14 14:59:31 PDT
> However, by contrast, the core implementation of the `log()` function, as far as I'm aware, could not possibly be borrowed and reused against any random JS object in any useful way, 

Two problems:

- This isn't something special about console.  Calling Element.prototype.appendChild on something that isn't an Element, or Worker.prototype.terminate on something that isn't a Worker, doesn't make any more sense than calling console.log on something that isn't a console.  If we take "not useful to call on random objects" as a reason to do this, then we'll be doing it with tons of APIs--which is why we shouldn't.
- This isn't a reason why methods on other APIs aren't bound to their object.  After all, if you do want to call a method--say, appendChild--on an interface on something other than an instance of the object, you wouldn't say "document.appendChild.call(otherObject, node)", you get the method from the interface: "HTMLElement.prototype.appendChild.call(otherObject, node)".  Binding the functions wouldn't prevent this, but we still don't do it.

I'm running low on time and energy for this, and if the discussion so far hasn't convinced whoever in WebKit's process needs to be convinced, I don't think another five pages of discussion will help.  So, unless new information arises, I'll be winding down in this discussion for now.
Comment 36 Victor Costan 2013-04-14 16:17:18 PDT
Darin, I prepared a patch for getting rid of the style errors in the C++ bindings.
https://bugs.webkit.org/show_bug.cgi?id=114596
Comment 37 EFL EWS Bot 2013-04-23 14:33:43 PDT
Comment on attachment 197676 [details]
Patch

Attachment 197676 [details] did not pass efl-wk2-ews (efl-wk2):
Output: http://webkit-queues.appspot.com/results/168145
Comment 38 Radar WebKit Bug Importer 2014-01-12 15:21:49 PST
<rdar://problem/15801400>
Comment 39 Andreas Kling 2014-02-05 11:22:24 PST
Comment on attachment 197676 [details]
Patch

Clearing review flag on patches from before 2014. If this patch is still relevant, please reset the r? flag.
Comment 40 Andrey Adaikin 2014-04-22 03:53:22 PDT
IMO "console.log" is no more special than, for example, "performance.now", which also does not work being called unbound.

Actually, "performance.now" does seem even more confusing to me, since there is a similar static "Date.now" that works if called unbound. For example, the following code, that did happen in my practice, will not work as expected in WebKit:

var tick = (window.webkitPerformance || window.performance || Date).now;
var start = tick();
//...
var time = tick() - start;


I don't have a strong position whether to allow unbound console.log or not, but at least we'd better be consistent with similar APIs, like performance.now
Comment 41 Brian Burg 2014-12-17 17:19:53 PST
I recommend resolving this as WONTFIX. It adds complexity and solves a tiny use case.

If adding console.log = console.log.bind(console) is seriously slowing down or complicating someone's development, then I'm really curious why that is so. And besides, what browsers don't support console.log in 2014?
Comment 42 Sindre Sorhus 2016-01-12 15:16:19 PST
Relevant whatwg/console issue: https://github.com/whatwg/console/issues/3
Comment 43 Timothy Hatcher 2016-05-02 13:15:54 PDT
Chrome/V8 will soon do this. https://codereview.chromium.org/1859293002
Comment 44 Joseph Pecoraro 2016-05-02 16:45:09 PDT
I agree. I'm going to address this in:
<https://webkit.org/b/157286> Make console a namespace object (like Math/JSON), allowing functions to be called unbound
Comment 45 Darin Adler 2016-05-02 18:07:48 PDT

*** This bug has been marked as a duplicate of bug 157286 ***