String.prototype.fullTrim()

I create repository from template:

This function can remove unnessesary blankspaces, let's read README.md :)

The name "trim" strictly implies it's only removing things from the ends.

Why is this needed in the language when str.replace(/ +/g, ' ').trim() works just as well?

Yeah, of course but on every project we must copy and paste it and extend prototypes or create function like fullTrim.

Else we must type this function manualy every time, we can also type "trim" function manualy, but we have it on language.

We can extend this function with additional arguments, for trimming every sign in whole text, e.g. "I have a-word !wtih_some-strange_signs " and i can trim i to "I have a word with some strange signs"

I don't have manually create regex and replace, this function can be something like helper for "replace", put signs which who you want to remove from text to arguments and then you have good looking text, without any regex, anymore replace for situation like this.

Hopefully never extend prototypes, ever - http://www.nczonline.net/blog/2010/03/02/maintainable-javascript-dont-modify-objects-you-down-own/

Generally things aren't added to the language merely to avoid the cost of defining it yourself; it's when doing it yourself is easy to do incorrectly, or when having it in the language can afford performance or interoperability or coordination benefits, none of which apply here.

2 Likes

If I create a class which extends String class, and add method "fullTrim" or something, this is similar to .prototypes because classes in ES6 working on prototypes, if my knowledges is good.

And then it's still bad habits?

No, that's perfectly fine, but then you'd have to convert all your string primitives into a DestroxString object before using the method - just making a function that takes a string as the first argument seems much simpler.

Ok, but this not perfect solution, in every place is DestroxString, then when somebody start to coding with my, don't know what i do :)

I think about situation you have "Something on string" and i need to have use fullTrim like a method, you know "...".fullTrim()

What i must to do for the wolf is full and the sheep are whole? :)

Correct, while it's technically possible to subclass String and add stuff to it, it's just much simpler to create a utility module that has functions like your fullTrim function:

export const fullTrim = str => str.replace(/ +/g, ' ').trim()

And, when you want to use it, you'd just do fullTrim(yourString) instead of yourString.fullTrim(), and that's it.

If you don't like the way it looks to do fullTrim(yourString), then you can wait for the pipeline proposal to get through, which would let you write this sort of logic in a more linear fashion (instead of nested function calls).

Sorry if I wasn't clear. Here's the two approaches we've been discussing absent your proposal:

  1. class DestroxString extends String { fullTrim() {…} } - this means everywhere you want to use it, you have to do DestroxString(str).fullTrim(). not ideal.
  2. function fullTrim(s) { return s.replace(/ +/g, ' ').trim(); } - then you do fullTrim(str) whenever you want to use it.

I understand that str.fullTrim() seems easier, but fullTrim(str) is one less character, and doesn't require any language changes, and is pretty trivial to get right.

Here you go, two more alternative approaches:

String.prototype.fullTrim = function() {
	return this.replace(/ +/g, ' ').trim();
}

function fullTrim(strs) {
	return strs.join("").replace(/ +/g, ' ').trim();
}

let fullTrimmed = fullTrim`    my   string    `

fullTrimmed = "    my   string    ".fullTrim()

The second is generally discouraged; but hey they're never going to add it to the String.prototype, so you can do so without worrying about conflicts. But as a rule of thumb, don't ever add anything to the prototype of standard objects.

This got me thinking, is there any ideas/proposals regarding safely extending object prototypes. I have been using extension methods in C#. They are pretty cool. Are there any similar kind of ideas floating around?

It's not just about conflicts with the future standard, it's also about conflicts with other authors. Mostly because fullTrim isn't exactly an unambiguous name - when reading it I wouldn't understand what the method does, so it might do anything, probably some trimming with a fuller whitespace character set than that of normal .trim()?

And of course, in your own code you can do whatever you want, and extend overwrite prototype methods. You just can't expect your code to work together with that of others, which might expect a different value in the prototype property. So don't use it in libraries.

You can use symbols to safely extend arbitrary object prototypes (that aren't frozen, at least).

As for "extension method" syntax, see GitHub - tc39/proposal-extensions: Extensions proposal for ECMAScript and GitHub - tc39/proposal-bind-this: A resurrected proposal for a bind-this operator in JavaScript..

1 Like

I will shock some people, but extending built-in prototypes with custom methods is something I do without shame. Of course, with elementary precautions, in particular (1) using some improbable sigil, and (2) only for private code (not shared libraries). E.g.

String.prototype.$indexOfOrLength = function (x) {
    var result = this.indexOf(x)
    return result === -1 ? this.length : result
}

Please do that sort of thing in secret, then, because for decades, the community has said NEVER EVER do it, because when people receive a message that is any less strong than that, they do it, and not as carefully as I'm sure you are doing it - thus holding back the web for everyone.

Please never modify objects you don't own.

1 Like

Wasn't Symbol added to make extending objects you don't own safe?

const FULL_TRIM = Symbol();
String.prototype[FULL_TRIM] = function fullTrim() { /* magic happens here */ };

And then, later, you pass your Symbol value to use it as:

'I  have   many      spaces     in  between'[FULL_TRIM]();

// though, more likely like below

someString[FULL_TRIM]();

That's look good, this is 100% safe?

This can't overwrite method on basic class, here is a "String"|

I ran it in the console, and it worked:

image

But maybe I don't understand your question.

Yeah, but i have a question about security, if you working with team, and you do this, this is 100%.

You know, when i extend prototypes, this can overwrite a function built-in JS, e.p. .trim(), you can create your own .trim, String.prototype.trim() this can overwrite a built-in function

Yes, technically others would be able to override your function if you use protocols. But, the end-user would have to be explicitly trying to do so, and they would know that they're messing with things they shouldn't. (Similar to how the end user can choose to delete or override the existing String.prototype.toUpperCase function, but if they do so, they know they're messing with something they shouldn't).

So, it all depends on the level of security you need.

You can use the Object.getOwnPropertySymbols() function to get a list of all of the symbols found on an object. From there, you can use those symbol keys to get your hands on whatever it is you want to touch.

Like this:

// Your code
export let toUpper
{
  const upperCase = Symbol('Upper Case')
  String.prototype[upperCase] = function() { return this.toUpperCase() }
  toUpper = x => x[upperCase]()
}

// Someone else's code
> console.log(toUpper('hi there'))
HI THERE
> const symbols = Object.getOwnPropertySymbols(String.prototype)
> symbols
[ Symbol(Symbol.iterator), Symbol(Upper Case) ]
> String.prototype[symbols[1]] = () => 'Whoops!'
> console.log(toUpper('hi there'))
Whoops!