Dmitry said:
Dmitry said:
Dmitry A. Soshnikov wrote:
[snip]
A public library that modifies
the built-ins can't really be trusted to work with other code.
I understand, and told myself, that's this is the only (again - the
only.) reason. And from this point of view, you should mention this
just as a warning for authors of libraries, but not as a rule for
programming on ECMAScript.
[...]
What I have as draft includes most of what I wrote in the last message.
The advice to create a top-level function advise creating a method as a
member of the global object, having the same problems.
Instead, a separate interface should be created. The interface should be
clearly defined, cohesive, and unlikely to conflict with other objects
that use the system.
Formally, there's no full protected "interface" from this point of
view. Will you name it `MyVeryOwnNamespace', and tomorrow, the same
name will be in all `libraries' and in ES itself. So, naming
collisions should not be treated as the main reason.
So, again, it's absolutely normal to augmenting objects in ES,
providing good documentation of what have you augmented (and for whole
code in general).
If you still wanna to write this as a rule, please mentioned, that
it's not the rule, but *just your own suggestion and own meaning*,
meanwhile other people can choose different (good) way augmenting
object and write in OOP-style such as `string.capitalize()' instead of
long ugly `Ext.util.Format.capitalize(string)'. Especially in own
project.
You can do that, but it doesn't fit what I think of as OOP. It is an
abstraction using inheritance, but it lacks encapsulation and modularity.
What exactly do you mean? Please explain.
Modifying the public interface of String changes Strings to be something
different. Any code that uses String now has that change.
Nope, it hasn't. Sorry, bug that's just a demagogy. The code will have
that changes when it will *use* that changes. And please, show me, how
many changes you'll need to do for modifying method name from e.g.
`capitalize' to `capitalizeMethod', if that method uses in e.g. 10
files and doesn't matter where it is described - in
`String.prototype.capitalize' or in
`VeryOwnStringNamespace.capitalize' How many? Please count and tell
me. So, please, do not use demagogy as the logical arguments.
Given a piece of code M that has:-
String.prototype.capitalize = 1;
Another piece of code O that uses M now has capitalize method available.
<script src="m.js"></script>
<script src="o.js"></script>
So m.js creates a dependency from everything, to modification to
String.prototype.
If m.js were to define something else:-
M.capitalize= 1;
- then the exact same problem occurs. I think this is what you were
getting at.
Does. Absolutely the same. Take the example above - please count the
changes you need - in *real* dependency - when some code *uses* that
name: 'string'.capitalize() or VeryOwnStringNamespace.capitalize
('string').
A separate *file* would be depended on where it is included.
<script src="p.js"></script>
<script src="o.js"></script>
If p.js does not contain m.js (this could happen in a build), there
would be no dependency on M.capitalize.
If m.js is an author file (not using a build tool), is not about
built-in String/String.prototype, and is about formatting strings, then
m is defining two things:-
1) user defined string formatting
2) built in String
The user-defined String formatting routines could becomes properties of
one object, maybe stringFormat. That is clearly about one thing.
Nope, how can't you see - that the dependency is equal in case you're
describing.
A module that adds a property to something it doesn't own introduces
something globally accessible that unrelated to the module itself. It is
doing more than it should. It is not really modular.
It doesn't minimizes as dependency is equal. But, yep it's true that
"The less dependency you have, the easier it is to change" - but it
doesn't related to the case.
A way to reduce change dependency would be to do less in each module.
Modifying built-ins prototypes maximizes the dependency.
I've already described my meaning why augmenting of built-ins could be
the issue - only if:
(a) augmenting Object.ptototype till we haven't control of {DontEnum}/
[[Enumerable]]. This point goes without saying. But when we'll have
control of [[Enumerable]], I thinks it could be very useful.
(b) Using 3rd-party libs. But from this point of view - there's no
difference, where to describe your objects.
On this point, there is a huge difference where the object is defined.
If the client of the API defines String.prototype.capitalize, and the
library defines the same, there will be a conflict.
The library new version could define `YourCompanyNameSpace`, but why
would it?
(c) If some, using some augmented built-ins, will have a habit to use
'string'.capitalize(), he can be confused not founding this method in
other project. But again - there's no difference which habit he has:
'string'.capitalize() or VeryOwnStringNamespace.capitalize() - there
will be no such method in other project. Or, maybe just psychological
difference - in case of 'string'.capitalize() user can think that it's
really own method - but, that's problem of the user - he first should
learn language on which he writes.
On point (c), I see an issue with shared codebase; not necessarily
another project, per se, but elsewhere in the same application that is
using your module.
So - the dependency is equal.
With either VeryOwnStringNamespace.capitalize, or
String.prototype.capitalize, yes, they both introduce globally
accessible method where they are included.
I want to look at Ext.util.Format.capitalize, then consider an
alterantive String.prototype.capitalize and compare alternatives. First
look at the Ext version.
First off, the package `Ext.util.Format` is not specific. Is `Format` a
verb or a noun? `Format` as a noun would seem to be a constructor, so
it shouldn't be that. If Format is a verb, then is it a method? What
does `Format` format? Does it format HTML code? Dates? Numbers?
Templates? As lengthy as that namespace is, it does not describe string
formatting. Indeed, format seems in the verb sense here, but it is not a
method; it is an object. The object has methods that perform many types
of formatting. None of the methods have much to do with each other;
they're categorized to work with strings and return strings. Instead, I
would prefer to shorten that to have just:-
Ext.formatString
Ext.formatHTML
Ext.formatDate
I've reformatted `Ext.util.Format.capitalize` function to wrap:
capitalize : function(value){
return !value ? value : value.charAt(0).toUpperCase() +
value.substr(1).toLowerCase();
}
There are a few problems with that function. When passed a falsish
value, the value itself is returned.
The non-standard substr method should be replaced with a call to the
standard String.prototype.substring.
The method does not consider strings that begin with non-characters,
such as leading whitespace or tic marks. For example, If input is "
higiri" and it is desired to have the method return the first
non-whitespace to upper-case, so " Higiri".
Here's a prototype'd version of Ext-js's "Ext.util.Format.capitalize":
if(!String.prototype.capitalize) { // Should we add a safety check?
return this.charAt(0).toUpperCase() + this.substr(1).toLowerCase();
}
Should there be a safety check? What if the safety check fails?
What about a piece of code that has access to the change to
String.prototype.capitalize, and wants to change it? Is that okay to
redefine String.prototype.capitalize? Is capitalization in a certain way
something that all strings should have, and should the method name be
`capitalize`?
Or what about:
String.prototype.startsWith?
What should that do?
If startsWith does something that is purely related to a *String*, and
not how *your application* is *using* strings, then it may be a good
candidate for a Standard in ES Harmony. For example,
String.prototype.trim that was added in ES5. Perhaps
String.prototype.startsWith could be a candidate for ES6.
If something is a good candidate for a Standard, then it is best to not
create a potential conflict. If ES6 defines String.prototype.startsWith,
but slightly differently, then the code that is defining
String.prototype.trim would want to avoid the safety check. It would
have to hide the built-in startsWith method and would want to avoid any
safety checks like:-
// No good if it is defined (in ES6, etc)
if(!String.prototype.startsWith) {
}
An alternative is the code could instead redefine
String.prototype.startsWith to String.prototype.myVeryOwnStartsWith.
But then we are getting to the point where the method name is trying
hard to have an identifier that identifies it as being something added
not built in.
Absolutely right.
Of cause not. "Everything" will have that modification when that
"everything" will *use* that modification. And there's no difference
between 'string'.capitalize() vs. VeryOwnStringNamespace.capitalize()
- dependency is equal.
By "use", I believe you mean to describe code that is accessing the
`capitalize` method, and not code that exists where the script that
defines `capitalize` exists.
Either way, I don't think it matters much. Once the API is published,
you don't get to decide who calls what. You can say: "I defined this
property, but don't use it," and if there is no value in using it, then
probably nobody will use it. If there is some value in using that
feature, then it has a better chance of being used. How would you know
who is using `capitalize`?
Either way, the method is globally accessible.
The main idea (and understand that) is to show that you don't exactly
know what will some library provide "tomorrow". So your own "APE.dom"
may be overridden by all of that libs you'll use.
I think we are arguing the same point.
That is, you don't know what may be defined tomorrow.
That public interface, is a property of global object, and so it could
be replaced by a simple assignment.
Anyone using that namespace could be expected to not redeclare that
namespace and to not use another piece of code that does.
YUI's YAHOO.namespace method fails on this account.
YAHOO.namespace fails because it adds user-defined properties to the
YAHOO object. Calling YAHOO.namespace("example") creates YAHOO.example,
if it does not exist alreay. YUI's namespace strategy makes it easy for
conflits to occur. Consider a client using YUI that wants to define
YAHOO.touchevent namespace. Will a future release of YUI have its own
YAHOO.touchevent object? YAHOO has no way to be certain that a client of
YAHOO had not followed YUI's advice and used
YAHOO.namespace("touchevent"). If YUI decides to use YAHOO.touchevent =
{}, then the namespace will be obliterated.
I know what modules are, be sure. `String.prototype' - is the kind of
a module itself. And if you are sure what you are doing, it's
absolutely not a bad practice to put `capitalize' into it.
String.prototype is a built-in. If it is to be called a "module", then
it is a built-in module. If you've modified it, then it is a built-in
module with a mix of user-defined properties.
If user-defined properties are defined, then where should these
definitions occur?
If the definition of user-defined additions to String.prototype is
defined in a author file that is defining another module, then the
author is defining two things in that file instead of one. So those
modifications should not occur in same author file as another module's code.
By "author file" I mean the file the author edits, not necessarily the
same file the http client receives.
The author file should be defining one thing only.
That's good, but you understand that if you'll using dozen of 3rd-
party libs combined, your "APE.dom" theoretically can just gone in one
moment - when all the libs will provide the same names and structure.
Do you see the difference in this case from augmenting built-ins for
own goals (when you know and understand what are you doing)?
The only user-defined identifier that is ReadOnly is the Identfier for
FunctionExpression. And that doesn't work in JScript, as you probably know.
By that fact, you are are right; any one library can replace APE.dom.
A conflict can occur when *one* external module or library also modifies
the property String.prototype.capitalize.
The point is to not do that; to not modify objects you don't own.
Instead, I am suggesting that methods exist as part of units or modules
and with an easily identifiable role.
This can be achieved by defining one global namespace and hanging
properties off that.
If it is OK to modify String.prototype, is it okay for everyone to do
that, or just an internal organization?
Is it ok to modify objects you don't own in general? If so, are there
exceptions to that rule?
[...]
That just means - that ideologically it's normal to augment built-ins
if language is constructed so and if this fact is in it's ideology.
The language allowing something does not make that thing ideal. The
language makes assignment to identifier end up with globals. How many
times have you seen a missing - var - statement.
for(i - 0; i < 10; i++)
?
One useful way to modify built-ins is to add the standard feature for
implementations that have not yet implemented it, or have implemented it
wrong, but preceeding that with a feature test, e.g.
if(!String.prototype.trim) ... .
If the rule "don't modify objects you don't own", is followed, and the
only exception to that rule is to add a global property, then conflicts
should not occur.
So, I don't propagate everyone to augment built-ins (I think you think
so about my position, so I'm telling you - nope). My position is to be
fair for augmenting built-ins (which means, it against the categorical
statement "Augmenting built-ins - is a bad practice"). To augment or
not in this case - everyone choose - understanding all the issues.
I got that. I don't see a good reason for modifying objects you don't
own. I see a few downsides:-
1) increases potential conflict with
* new standard
* new version of 3rd party library
* code defined somewhere else in the organization
2) Method presence doesn't provide indication where that method is
likely to be defined.