0

Just for study purposes, I'd like to create a function with dot format (I don't know what is the name of this type of function). I'd like to understand how can I recreate this situation:

String('any string').toLowerCase()
// output ANY STRING

I have this example of mine:

class MyCustomString {
  constructor(value) {
    this.value = value
  }

  toUpperCase() {
    this.value = String(this.value).toUpperCase();
    return this;
  }

  replace(valueToReplaced, newValue) {
    this.value = String(this.value).replace(valueToReplaced, newValue);
    return this
  }
}

Then, I'd like to run:

MyCustomString('name').toUpperCase().replace('A', 'x');

and the expected output should be:

// output NXME

Can someone help me with how to build a function like this?

That's important to notice that my goal is not to create a new method String or to include prototypes to its structure (in this example, I've used the method String but could be Number, Array or whatever else method), but understanding how this chain of methods is built and mighty applying it to my projects. My intention is not to override any native method or create a new prototype.

Felipe Paz
  • 347
  • 1
  • 6
  • 19
  • The example you've provided does essentially what you describe if you rename `construction` to `constructor` and use `new` when creating the object. – JLRishe Jun 28 '21 at 04:15
  • 1
    you declare a class, so you have to use new : (new MyCustomString('name')).toUpperCase().replace('a', 'x'); – tuan nguyen Jun 28 '21 at 04:18
  • Sorry, I've fixed `constructor` declaration and I don't use `new` when calling my custom class. As expected result, I want to retrieve the string formatted and not the object `MyCustomString`. – Felipe Paz Jun 28 '21 at 04:19
  • you can wrap in function like this: function CustomString(s){ return new MyCustomString(s) } – tuan nguyen Jun 28 '21 at 04:20
  • btw. "dotted function" is called "chaining". – htho Jun 28 '21 at 05:14

2 Answers2

0

You've have instantiate the class using the new keyword to get it working. Like this

const customString = new MyCustomString('name').toUpperCase().replace('A', 'x'); // this in upper case as after toUpperCase() it will be in capital
console.info("customString", customString); // will give you desired output
sid
  • 1,779
  • 8
  • 10
  • In this case the output will be something like `{value: 'NxME'}` and but what I want is `NxME`. – Felipe Paz Jun 28 '21 at 04:23
  • then you need to return this.value from your methods. Add that and you're good to go – sid Jun 28 '21 at 04:25
  • I've tried to return `this.value` but the second function throws an error of undefined function. – Felipe Paz Jun 28 '21 at 04:30
  • @FelipePaz If your functions return strings, then you can't chain them like you are trying to do. If you want them to be chainable, then they need to return an instance of your class, not a string. You can't have it both ways. You have the option of doing `new MyCustomString('name').toUpperCase().replace('A', 'x').value`, though. – JLRishe Jun 28 '21 at 04:33
  • And how could be possible getting the same result without using `new`?. That's the question, I've tried what you said but using `new`. – Felipe Paz Jun 28 '21 at 04:37
  • @FelipePaz Have a look at this: https://stackoverflow.com/questions/30689817/es6-call-class-constructor-without-new-keyword – JLRishe Jun 28 '21 at 04:46
0

You've to wrap the instance creation inside a function, and return a new instance of the class from that wrapper. The "dot format" is called chaining, which provides the methods of the class to return the instance (this) instead of the value. When you need the actual value, you can shadow the native toString and valueOf methods with class methods, then you can get the assigned value just by referring to the object returned from the methods.

const MCS = (function() {
  class MCS {
    constructor(value) {
      this.value = value;
    }
    toUpperCase() {
      this.value = String(this.value).toUpperCase();
      return this;
    }
    replace(valueToReplaced, newValue) {
      this.value = String(this.value).replace(valueToReplaced, newValue);
      return this;
    }
    toString() {
      return this.value;
    }
    valueOf() {
      return +this.value; // Converts to number, omit +, if a string is strictly needed
    }
  }

  return function mcs(str) {
    return new MCS(str);
  }
}());

// Console doesn't use toString method, explicit conversion
console.log(MCS('abc').toUpperCase().replace('A', 'X').toString());

// Alternatively read value property of the returned object
console.log(MCS('alternative').toUpperCase().replace('A', 'x').value);

// DOM content is implicitly converted to string using toString method
document.querySelector('#div').textContent = MCS('name').toUpperCase().replace('A', 'x');
<div id="div"></div>
Teemu
  • 22,918
  • 7
  • 53
  • 106