Implementing Copy-on-Write Strings in JavaScript
Copy-on-write strings are a powerful optimization technique where string modifications only create a new string when a change is actually made. This avoids unnecessary memory allocation and copying when a string is intended to be immutable. This challenge asks you to implement a basic copy-on-write string class in JavaScript, demonstrating this principle.
Problem Description
You are to implement a CopyOnWriteString class in JavaScript. This class should behave like a standard string for read operations but should create a new, independent string object when a modification method (e.g., toUpperCase, toLowerCase, substring, replace) is called. The original CopyOnWriteString instance should remain unchanged. The class should internally store the string data and a flag indicating whether the string has been modified.
Key Requirements:
- Immutability (Original Instance): Calling any modification method on the original
CopyOnWriteStringinstance must not modify that instance. It should return a newCopyOnWriteStringinstance with the modification applied. - String-like Behavior: The class should support standard string operations like
length, accessing characters by index (e.g.,str[0]), and implicit conversion to a string (e.g., using it in aconsole.log). - Copy-on-Write: Modifications should only create a new string when necessary. If a method doesn't actually change the string (e.g.,
toUpperCaseon a string already in uppercase), it should return the original instance (or a new instance referencing the same internal string data). - Internal Data: The class should store the string data internally.
Expected Behavior:
- Creating a
CopyOnWriteStringwith a string should initialize the internal string data. - Read operations (e.g.,
length, character access) should work as expected on the original instance. - Modification methods should return a new
CopyOnWriteStringinstance with the modification applied. - The original
CopyOnWriteStringinstance should remain unchanged after a modification.
Edge Cases to Consider:
- Creating a
CopyOnWriteStringwith an empty string. - Calling modification methods on an empty string.
- Chaining modification methods (e.g.,
str.toUpperCase().toLowerCase()). - Methods that don't modify the string (e.g.,
toUpperCaseon an already uppercase string).
Examples
Example 1:
Input: const str = new CopyOnWriteString("hello");
const upperStr = str.toUpperCase();
const lowerStr = upperStr.toLowerCase();
Output:
str.valueOf() === "hello"
upperStr.valueOf() === "HELLO"
lowerStr.valueOf() === "hello"
str === upperStr // false
upperStr === lowerStr // false
Explanation: The original string "hello" remains unchanged. toUpperCase() creates a new string "HELLO", and toLowerCase() creates a new string "hello" from the "HELLO" instance. Each instance is distinct.
Example 2:
Input: const str = new CopyOnWriteString("WORLD");
const upperStr = str.toUpperCase();
Output:
str.valueOf() === "WORLD"
upperStr.valueOf() === "WORLD"
str === upperStr // true
Explanation: Since "WORLD" is already uppercase, toUpperCase() returns the original instance (or a new instance referencing the same string data), demonstrating the copy-on-write optimization.
Example 3:
Input: const str = new CopyOnWriteString("");
const upperStr = str.toUpperCase();
Output:
str.valueOf() === ""
upperStr.valueOf() === ""
str === upperStr // true
Explanation: Handles the edge case of an empty string correctly.
Constraints
- The internal string data should be stored efficiently.
- Modification methods should return a new
CopyOnWriteStringinstance or the original instance if no modification is needed. - The class should support the following methods:
constructor(str: string): Initializes theCopyOnWriteStringwith the given string.length(): number: Returns the length of the string.valueOf(): string: Returns the string value.toUpperCase(): CopyOnWriteString: Returns a newCopyOnWriteStringwith the string converted to uppercase.toLowerCase(): CopyOnWriteString: Returns a newCopyOnWriteStringwith the string converted to lowercase.substring(startIndex: number, endIndex: number): CopyOnWriteString: Returns a newCopyOnWriteStringcontaining a substring.replace(searchValue: string, replaceValue: string): CopyOnWriteString: Returns a newCopyOnWriteStringwith the specified search value replaced.
- Character access using bracket notation (e.g.,
str[0]) should be supported.
Notes
- Focus on the core copy-on-write principle. You don't need to implement all possible string methods. The provided methods are sufficient to demonstrate the concept.
- Consider how to efficiently share string data between instances when no modification is needed.
- Think about how to handle edge cases like empty strings and methods that don't modify the string.
- The
valueOf()method is crucial for allowing theCopyOnWriteStringto be used in contexts where a regular string is expected (e.g.,console.log). - You can use JavaScript's built-in string methods within your implementation, but be mindful of the copy-on-write requirement.