how do i get “this = this” in prototype working

2019-02-20 10:37发布

问题:

Ok peep's so I know it's bad practice to mess with prototypes but here it is anyway...


Array.prototype.rev=
    function(){
        this.reverse();
    }

Works fine! Updates the source array variable, ary, as expected eg:

ary = [123, 456];
ary.rev();

// result:  ary == [456, 123]

My problem comes when writing a similar property for String.

What I would like to do is something like this...

String.prototype.rev=
    function(){
        this.split('');
        this.reverse();
        this.join('');
    }

Seems simple enough right! Split the string, reverse it, and join it back together so the original string variable, str, is a reverse of it's former self, as was with the ary above!

Thing is: Although this.split() has been called it needs to be stored as a variable, ie:

split = this.split('');

And there-in lies the this = this problem...

Now split is defined, it takes the focus away from editing the source variable and it's not like I can just say at the end of the function:

this = split;

As this is 'immutable' or what ever it is when they mean it is static and unchangeable?


Getting to the point! My Question is this...

str = 'abc'

I want to be able to say str.rev() not str = str.rev() and get the result of str = 'cba' where str === 'cba', catch my drift?!

All work-around's and tuition welcome peep's, I just ask that u know what ur talkin' 'bout. thx

回答1:

My problem comes when writing a similar property for String...

The main issue here is that strings are immutable in JavaScript; you cannot change a string in place. Because of that, it's impossible to define a rev method that would behave like this:

var a = 'abc';
a.rev();        // <== This can't work this way
console.log(a); // cba

Instead, your rev should do what all other String methods do: Return a new string with the updates.

A secondary issue is that your code in your rev method doesn't work, because you're not saving the results of things like this.split(''); anywhere, but the way split works, it returns an array with the entries.

Here's a version of rev that addresses both issues:

String.prototype.rev=
    function(){
        return this.split('').reverse().join('');
    };

Then:

var a = 'abc'.rev();
console.log(a);  // cba

Example:

String.prototype.rev = function(){
  return this.split('').reverse().join('');
};

var a = 'abc'.rev();
snippet.log(a);
<!-- Script provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>

This is how all of the standard string methods (toLowerCase, replace, substring, ...) work, by returning the result.


The all-on-one-line version might not be hyper clear (and is hard to debug), here's the split-apart version for clarity:

String.prototype.rev=
    function(){
        var characters = this.split('');
        characters.reverse();
        return characters.join('');
    };

(Note that Array#reverse reverses the array in place and returns the array reference; the fact it also returns the array reference is what makes the all-in-one-line version possible.)


Side note: If you're going to play around with prototypes, consider using Object.defineProperty rather than just assigning:

Object.defineProperty(String.prototype, "rev", {
    value: function() { ... }
});

...so that the new properties are non-enumerable (don't show up in for-in loops). Doesn't matter that much with String, but a lot of people still incorrectly use for-in for looping through arrays, so...

Example:

Object.defineProperty(String.prototype, "rev", {
  value: function(){
    return this.split('').reverse().join('');
  }
});

var a = 'abc'.rev();
snippet.log(a);
<!-- Script provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>