Function That Can Return A Value And Also Act As An Object
Solution 1:
It’s impossible to return a plain value (without a wrapper or extra properties) that’s chainable in that way. This is especially true for strings, as they’re primitives and can’t have their own properties. jQuery accomplishes this by making everything a wrapper, along the lines of:
function test(word) {
this.word = word;
}
test.prototype.next = function (word) {
this.word = word;
return this;
};
// or, if next() shouldn’t modify its context
test.prototype.next = function (word) {
return new test(word);
};
and then
test.prototype.get = function () {
return this.word;
};
to unwrap the wrapper. You’d use it as:
new test('Hello').next('World').get();
To drop the new
like jQuery,
function test(word) {
if (!(this instanceof test)) {
return new test(word);
}
this.word = word;
}
Solution 2:
Some ideas:
Add the methods to the object the returned value inherits from.
function test(word) { return word; } String.prototype.replaceWord = function(word) { return word; } test('Hello'); // 'Hello' test('Hello').replaceWord('World'); // 'World'
However, modifying objects you didn't create (
String.prototype
) is not recommended.Also, it won't work if
test
returnsundefined
ornull
.Subclass the object the returned value inherits from.
function myString(str) { this._value = str; } myString.prototype = Object.create(String.prototype); myString.prototype.constructor = myString; myString.prototype.toString = myString.prototype.valueOf = function() { return this._value; }; function test(word) { return new myString(word); } myString.prototype.replaceWord = function(word) { return word; } test('Hello') + ''; // 'Hello' test('Hello').replaceWord('World'); // 'World'
Here
test
can't return a literal string, but returns an object which behaves close to a string object.Note this may not work on some browsers.
Use a wrapper, and retrieve the value at the end
function Chain(value) { this.value = value; } Chain.prototype.replaceWord = function(word) { this.value = word; return this; } function test(word) { return new Chain(word); } test('Hello').value; // "Hello" test('Hello').replaceWord('World').value; // "World"
In this case, the functions don't directly return the value (you must retreve it) but it's the most flexible.
Solution 3:
Not exactly sure what you are ultimately trying to accomplish, but perhaps this might help:
Object.prototype.next = function(word) { return word; }
test('Hello') // "Hello"
test('Hello').next('World') // "World"
Solution 4:
The trick with a system like jQuery is that the first function builds an object of some particular type and returns that. The prototype of that object is where the subsequent "chain" methods are located. It's important that all of those methods cooperate with the scheme and return the same object (or a transformed version of that object). That's why the basic pattern for a jQuery method is (roughly):
$.fn.someMethod = function() {
return this.each(function() {
// do something
});
};
The .each()
method inside is itself a cooperative jQuery method, so it returns this
(which, unlike in jQuery callbacks, is a reference to the jQuery object and not a DOM element). Thus, by dutifully returning the jQuery object, the chain can be continued.
Note that some jQuery methods, by their very nature, can't follow that rule. A familiar example is .val()
called with no parameters. That function returns the "value" property of the first DOM element in the jQuery object list, and since that's a string (or undefined
) you can't chain after that.
Exactly how you'd construct a similar system largely depends on your own needs and your own design preferences, but that's the idea: a class of objects with methods that cooperate to make method chaining work.
As to your concern about how you can have the chainable methods work for string values directly, the answer is you can't if you're unwilling to modify the String prototype. It's a similar situation faced by jQuery, one that everybody using the library has to get used to: a jQuery object is not a DOM element; it's a wrapper. To explicitly get to a wrapped DOM element, it has to be unwrapped.
Solution 5:
Return an object with a .toString method defined instead of returning a string directly.
function test(word) {
return {
word,
replaceWord(word) {
this.word = word;
return this;
},
toString() {
return this.word;
}
}
}
The resulting value is an object, but can be used as a string in most places, or can be cast explicitly in other cases:
// cases where string must be explicit
var str = String(test("Hello").replace("World"));
// other cases in which string cast is implicit
var str = `message: ${test("Hello").replace("World")}`;
var str = "message: " + test("Hello").replace("World");
If you are working with numeric data, valueOf
might be more appropriate, rather than toString
(or maybe both in odd cases). With valueOf
implemented, you can do something like:
var num = 3 + test(32).replaceNumber(16);
Post a Comment for "Function That Can Return A Value And Also Act As An Object"