OK, first, i'm not going to seek a method to convert the Object to String.
but i'm facing a problem like this:
String.prototype.foo = function() {
return this;
};
var rawString = "abcde";
var fooString = "abcde".foo();
console.log(typeof(rawString) + ': ', rawString);
console.log(typeof(fooString) + ': ', fooString);
or jsfiddle you preferred.
also, a screenshot is attached:
as you can see, i did almost nothing in the prototype method foo, i just return this.
but the result of typeof are totally different
Why is this? how can i just return abcde rather thant {0: "a"...} ?
Thanks!
The this references the String Object, so you need to return it like
return this.toString();
which in turn, creates the primitive string version and returns it.
Related
This question is based on previous question:
Difference in these 2 Strings (JavaScript)
The people replied that there is a difference by creating string via 2 separate ways:
String Literal creates string (var a = "abc")
new keyword creates string-object, I assume it is a kind of object (var b = new String("def"))
Now, typeof in 1st case will return string. In 2nd case it will return Object. I am okay, there might be certain way of working, of JS engine, by the people who developed it.
But, then, in the following example why the hell I am getting the answer in last line as abcdef. Now, that too should have consoled [object][object], as the line above. Now I am really confused, new String is either creating a string, or object or what? It is just NOT consistent!
Can someone help me explain?
var obj1 = new Object({name: "Peter"});
var obj2 = new Object({age: 29});
var str1 = new String("abc");
var str2 = new String("def");
console.log(obj1);
console.log(obj2);
console.log(str1);
console.log(str2);
console.log(obj1 + obj2);
console.log(str1 + str2);
new String creates a String object, which inherits from Object, but has its own .toString and .valueOf methods, so it will print the string content.
var o = new Object();
var s = new String("foo");
console.log(o.toString === s.toString); // false
console.log(o.valueOf === s.valueOf); // false
You can override them with your own to see that they're invoked. Just be sure to invoke the originals too.
var s = new String("foo");
s.valueOf = function() {
console.log("custom valueOf");
return String.prototype.valueOf.call(this);
}
s.toString = function() {
console.log("custom toString");
return String.prototype.toString.call(this);
}
console.log(s + s);
[""].join(s);
But, then, in the following example why the hell I am getting the answer in last line as abcdef. Now, that too should have consoled [object][object], as the line above.
The difference is a String object has a default valueOf method that returns the string value contained within the object.
The Object object has a more generic valueOf method which simply returns a string of [object Object]
See -
const one =
{ valueOf: _ => 1 }
const two =
{ valueOf: _ => 2 }
console.log(one + two)
// 3
You can define valueOf to be anything -
const foo =
{ valueOf: _ => "foo" }
const bar =
{ valueOf: _ => "bar" }
console.log(foo + bar)
// "foobar"
I know it is possible to convert a JavaScript data object to JSON using JSON.stringify. e.g:
var cat = {
soundString: 'Meow!'
};
Then call JSON.stringify(cat) to get "{"soundString":"Meow!"}"
I am interested to know if it is possible to have a parallel to this, except instead of turning the JavaScript object into JSON, turn it into a JavaScript string that can be evaluated back to the object.
var cat = {
meow: function() {
console.log('Meow!');
}
};
I want something that would take the cat object and produce a string "{meow: function(){console.log('Meow!');}}", which can be parsed back using eval.
Can this be accomplished?
Write a recursive deep copy method and check for Array.isArray(item)
Use .hasOwnProperty to avoid the prototype chain
Put additional quotations wherever you want
var str = "{"
for (var key in cat) {
str += key + ":" + cat[key] + ","
}
str = str.substr(0, str.length-1) + "}"
I've seen instances of a variable being both an object and a string. Example:
alert(x + ""); // Hello
alert(x()); // World
This kind of thing is what jQuery does with the $ variable.
How might you get this effect?
No, a variable can't be both an object and a (primitive) string.
However, if you attempt to convert an object to a string (e.g. using obj + ''), the string returned by its toString method is used instead.
function x() {
return "World";
}
x.toString = function() {
return "Hello";
};
I have the following situation:
There is a certain string that contains some vars, such as:
var string = '/page/{id}/{title}';
Now, I want to be able to replace {id} and {title} with the vars from the following array:
var arr = {'id':10, 'title':'test-page'};
I came up with this little regex:
string.replace ( /{([a-zA-Z0-9]+)}/g , '$1' );
Which, as expected, just returns this:
/page/id/title
So I tried this:
string.replace ( /{([a-zA-Z0-9]+)}/g , arr [ '$1' ] );
But that returns a
/page/undefined/undefined
Now, I understand that something like this would be possible with a loop et cetera, but it would be nice to have a one-liner for this. I am not very used to JS, so I hope that there is some function or option that I am unaware of that helps me out with this :).
Best regards!
Try something like this:
var arr = {'id':10, 'title':'test-page'};
'/page/{id}/{title}'.replace(/\{([\w\d]+?)\}/g, function(a, b) {
return arr[b] || '';
});
If you use this replace thing often I would create a String helper prototype method. For example:
String.prototype.template = function(data) {
return this.replace(/\{([\w\d]+?)\}/g, function(a, b) {
return data[b] || '';
});
};
and use it like this:
'/page/{id}/{title}'.template(arr);
According to MDN article,
Because we want to further
transform the result of the match before the final substitution is
made, we must use a function. This forces the evaluation of the match
prior to the toLowerCase() method. If we had tried to do this using
the match without a function, the toLowerCase() would have no effect.
(In the text above, replace toLowerCase() with "accessing property in object")
Then, you can use
function replacer(match, p1, p2, p3/*, ...*/, offset, string){
return arr[p1];
}
var arr = {'id':10, 'title':'test-page'};
'/page/{id}/{title}'.replace(/\{([\w\d]+?)\}/g, replacer);
So, I was interested to find that JSON.stringify reduces a RegExp to an empty object-literal (fiddle):
JSON.stringify(/^[0-9]+$/) // "{}"
Is this behavior expected? I realize that a RegExp is an object with no properties to serialize. That said, dates are objects too; yet JSON.stringify() manages to produce a meaningful string:
JSON.stringify(new Date) // "2014-07-03T13:42:47.905Z"
I would have hoped that JSON would give RegExp the same consideration by using RegExp.prototype.toString().
If somebody would be interested, there is a nice workaround. I don't think, that current behaviour is correct. For example, Date instance is not serialized to empty object like RegExp, though it is an object and also has no JSON representation.
RegExp.prototype.toJSON = RegExp.prototype.toString;
// sample
var foo = { rgx: /qux$/ig, date: new Date }
JSON.stringify(foo);
//> {"rgx":"/qux$/gi","date":"2014-03-21T23:11:33.749Z"}"
Both JSON.stringify and JSON.parse can be customized to do custom serialization and deserialization by using the replacer and reviver arguments.
var o = {
foo: "bar",
re: /foo/gi
};
function replacer(key, value) {
if (value instanceof RegExp)
return ("__REGEXP " + value.toString());
else
return value;
}
function reviver(key, value) {
if (value.toString().indexOf("__REGEXP ") == 0) {
var m = value.split("__REGEXP ")[1].match(/\/(.*)\/(.*)?/);
return new RegExp(m[1], m[2] || "");
} else
return value;
}
console.log(JSON.parse(JSON.stringify(o, replacer, 2), reviver));
You just have to come up with your own serialization format.
Yes, because there's no canonical representation for a RegExp object in JSON. Thus, it's just an empty object.
edit — well it's 2018 now; the answers suggesting solutions using .toJSON() etc are probably fine, though I'd add the method to the prototype with
Object.defineProperty(RegExp.prototype, "toJSON", {
value: RegExp.prototype.toString
});
and so on. That ensures that the function name isn't enumerable, which makes the monkey-patch somewhat more hygienic.
Here's how I solved this issue:
Serialize it as a string:
var pattern = /foobar/i;
var serialized = JSON.stringify(pattern.toString());
Then rehydrate it using another regex:
var fragments = serialized.match(/\/(.*?)\/([a-z]*)?$/i);
var rehydrated = new RegExp(fragments[1], fragments[2] || '');
Preserves the pattern and flags - hope this helps someone!
RegExp.prototype.toJSON = RegExp.prototype.toString;
var regexp = /^[0-9]+$/;
var foo = { rgx: regexp.source, date: new Date };
var stringified = JSON.stringify(foo);
new RegExp(JSON.parse(stringified).rgx)
I think a good approach would be something like this:
function stringifyFilter(key,value) {
if (value instanceof RegExp) {
return value.toString();
}
return value;
}
var myObj = {
text : 'Howdy ho!',
pattern : /[a-z]+/i
}
JSON.stringify(myObj,stringifyFilter); // output: {"text":"Howdy ho!","pattern":"/[a-z]+/i"}