I'm working on a simple browser mud-client, and i need to provide some basic functions to string processing. So, when some user casts a mass spell, it should be collapsed into a one string, i.e. CAST: User1 -> [target1, target2]. I wrote the code:
function CastGroup(caster, cast, targets, text) {
this.cast = cast || '';
this.targets = targets || [];
this.caster = caster || '';
this.text = text || '';
}
CastGroup.prototype = new String;
CastGroup.prototype.render = function(){
var targets = this.targets ? '[' + this.targets.join(', ') + ']' : '';
var text = '<b>CAST</b>: ' + this.caster + ' ' + this.cast + ' -> ' + targets + '\n';
this.text = text;
return new CastGroup(this.caster, this.cast, this.targets, this.text);
};
CastGroup.prototype.valueOf = function(){
return this.text;
};
CastGroup.prototype.toString = function(){
return this.render();
};
var c = new CastGroup('name', 'supercast', ['1', '2']);
console.log(typeof c); // object
var s = c.replace('name', 'nomnom');
console.log(typeof s); // string
Any string function, i.e. String.replace() replaces the original object. How can i avoid it?
EDIT1
I have a post-process highlighting "engine", that calls user's callbacks. User should think, that bundle has only strings. bundle is an array with raw text, plain text, and colorized text. User defines callbacks in user-space, that should do all the highlighting work.
function process_highlights(bundle){
if (!bundle || !bundle.length){
return bundle;
}
var highlight_result = bundle;
for (var i=0; i<HIGHLIGHTS.length; i++){
highlight_result = HIGHLIGHTS[i](highlight_result);
}
return highlight_result;
}
So, text process chain looks like: original_bundle -> subst_processor -> trigger_processor -> highlight_processor -> output_window. All of these processors takes and return a bundle, that should contain strings. I cannot change the design now.
If I understand your question correctly, you need to remove this: CastGroup.prototype = new String;
and do this: CastGroup.prototype = String.prototype;
This will give you the String methods without returning a new String object. To learn more about this (and about advanced Javascript in general), check out these slides.
Update:
I think I understand your question a little better now. The replace string method returns a new string, which is why it's overwriting your object.
You don't need to inherit from the String object at all. String methods won't even work on an object (so delete CastGroup.prototype = new String). What you want to do is just modify the object's values directly.
If you need to modify the 'text' value of your CastGroup, then declare another method:
CastGroup.prototype.modifyText = function (findValue, replaceValue) {
var text = this.text;
this.text = text.replace(findValue, replaceValue);
return this;
};
This worked for me.
CastGroup.prototype.replace = function() {
this.text = this.text.replace.apply(this.text, arguments);
return this;
};
Overwrite the prototype in your object, update the field that needs updating, then return the object.
Related
So I want to get the Object which is essentialy a string. The issue is I cant transfer it into the string format since the resulting string is just anything but the thing I want. Bringing the object into a json doesnt bring a proper string either so my only way of achieving that is the concat method.
I have a Popup-Love which returns the string as follows foo, foo1 ,foo2 while I need it as
'foo1','foo2',...,'foo999' .
My method manages to do that for the first element while all the other elements remove the apostrophe resulting in something like 'foo,foo1,foo2'. How do i fix that?
var i = 0;
if(i == 0){
var t ="'";
var t = t.concat(apex.item("P29_STANDORT").getValue());
var t = t.concat("'");
apex.item("P29_TEST").setValue(t);
i = i +1;
} else {
var t = t.concat("'");
var t = t.concat(apex.item("P29_STANDORT").getValue());
var t = t.concat("'");
apex.item("P29_TEST").setValue(t);
}
You can "overwrite" the native toString() function of the Object and replace it with a function that does what you want. Something like below
function MyObj(){
this.creationTime = new Date().toLocaleString();
}
MyObj.prototype.toString = function something(){
return 'I was created on ' + this.creationTime;
}
var myObj = new MyObj();
console.log('String version of my custom object: ' + myObj);
Let's say for some delicate reason I have the following react state:
{ superGreeting: 'Hello!!!' }
Now, assume I have this complicated operation that basically takes the superGreeting string and works on it, in the end replacing a character at a specific position. New state then should be:
{ superGreeting: 'Hullo!!!' }
So, there would be my action:
action = (index) => {
var { superGreeting: newGreeting } = this.state;
newGreeting[index] = 'u';
this.setState({superGreeting: newGreeting});
}
Unfortunatelly, such approach does not work and ends with:
TypeError: Cannot assign to read only property '1' of string 'Hello!!!', indicating this line as the offending one: newGreeting[index] = 'u'
I use react.js, ES6, no redux, no mobx, no immutable.js. Thought that the issue is caused by the string still being related/used by the soon-to-be-previous state, so I thought that creating a copy would work (I tried newGreeting = newGreeting.toString(), '' + newGreeting, `${newGreeting}`, ''.concat(newGreeting), without any success). Any ideas?
Strings in JavaScript are immutable. Your example can be trimmed down to
(function(){
"use strict";
var str = 'Hullo!!!';
str[1] = 'e';
})();
If you want to mutate a string, you'll need to create a new string, e.g.
(function(){
"use strict";
var str = 'Hullo!!!';
str = str.slice(0, 1) + 'e' + str.slice(2);
})();
Strings in JS are immutable, but you can turn it into an array, work on it, then join it back together.. also there are string functions like substr and replace which return a new string if those are applicable.
var split = superGreeting.split('')
split[index] = 'u'
var newGreeting = split.join('')
Your problem here does not have anything to do with react. Strings in javascript are immutable.
You could create the following helper function:
var replaceCharAt = function(str, index, c) {
return str.slice(0, index) + c + str.slice(index+1)
}
so that
replaceCharAt('012', 1, 'x') === '0x2'
I have a URL that I want to replace parts of, depending on the call I'm making.
For example, my URL is of the form
var url = "http://www.example.com/server/32/users/255667753/images/233.jpg"
I want to make calls to different URLs of the same form but with only the numbers changed. Currently, I am doing it like this
var urlPart1 = "http://www.example.com/server/"
var urlPart2 = "/users/";
var urlPart3 = "/images/";
var urlPart4 = ".jpg";
var sendRequestTo = urlPart1 + 32 + urlPart2 + 255667753 + urlPart3 + 233 + urlPart4.
However, is there a better way, like there is in Java?
You can use regexp
String.prototype.format = function() {
var regexp = /\{(\d+)\}/g, values = arguments;
return this.replace(regexp, function(match, index) {
return values[index];
});
};
var server = "http://www.example.com";
console.log("{0} is {1}".format("a", "b"));
console.log("{3}/server/{0}/users/{1}/images/{2}.jpg".format(32, 255667753, 233, server));
This approach is more like c# though which allows you to put the arguments in any order.
Since TypeScript is more on the side of object oriented programming, we need to extend interface. We need to extend the String interface and then we need to supply an implementation:
interface String {
format(...replacements: string[]): string;
}
if (!String.prototype.format) {
String.prototype.format = function() {
var args = arguments;
return this.replace(/{(\d+)}/g, function(match, number) {
return typeof args[number] != 'undefined'
? args[number]
: match
;
});
};
}
And you can user anywhere this way:
var myStr = 'This is an {0} for {0} purposes: {1}';
alert(myStr.format('example', 'end'));
Cheers!
Declare this function globally somewhere
String.prototype.format = function (args) {
var newStr = this;
for (var key in args) {
newStr = newStr.replace('{' + key + '}', args[key]);
}
return newStr;
}
// This is generic url that will be formatted on the fly with dynamic numbers
var url = "http://www.example.com/server/{serverNo}/users/{userNo}/images/{imgNo}.jpg"
// call it with your dynamic numbers
url = url.format({serverNo:230948,userNo:123897,imgNo:1239378});
And finally your url will become
"http://www.example.com/server/230948/users/123897/images/1239378.jpg"
Happy coding.. :)
I have a function to help me create ad hoc objects and save me time typing.
Additional EDIT: to clarify this function will only ever sit inside an anon function.
(function(){ // clarification of the functions location
var objectPump = function (props, defaults){
var str;
if(typeof defaults === "string"){
defaults = defaults.split(",");
}else
if(typeof defaults === "undefined" || !defaults.isArray){
defaults =[];
}
if(props !== undefined){
if(typeof props === "string"){
props = props.split(",");
}
}else{
throw new TypeError("No properties defined for objectPump.");
}
// create function body
str = "var obj={};";
props.each( function(p,i) {
str += "obj." + p + "=";
if (typeof defaults[i] === "string") {
str += p + "===undefined?" + '"' + defaults[i] + '":';
} else
if (typeof defaults[i] === "number") {
str += p + "===undefined?" + defaults[i] + ":";
}
str += p + ";";
});
str += "return obj;";
str = "return new Function('" + props.join("','") + "','" + str + "')";
// Uses new Function to create the new function
return (new Function(str))(); // Is this dangerous???
}
})(); // wrapped in an anon function
Which lets me create objects without having to name all the properties and code in defaults.
Edit: Use of the above function.
var car = objectPump("colour,year,type", // objects property names
"white,2015,All Wheel Drive"); // object defaults
// or as arrays
var car = objectPump(["colour","year","type"], // objects property names
["white",2015,"All Wheel Drive"]); // object defaults
var cars = [
car("red",2011), // missing property defaults to All Wheel Drive
car("blue",2015,"bike"),
];
var aCar = car("blue",2015,"bike");
// same as
var aCar = {
colour:"blue",
year:2015,
type:"bike"
}; // but saves me having to type out the property names for each new object
To me it looks very similar to using eval and a place a third party harker could get some malicious code in. So far it has been very handy and I am tempted to use new Function for other tasks.
Should I use new Function() to generate code or is it considered bad and/or dangerous for public code.
var car = objectPump("colour,script", // objects property names
"white,\" + alert(\"test\") + \""); // object defaults
console.log(new car('blue, but the nice one')); // throws alert
Do you mean like this dangerous?
To be honest, I don't really like objectPump function. There are other viable options you have:
Use TypeScript and its default values for functions (http://www.typescriptlang.org/Handbook#functions-optional-and-default-parameters)
Use typeof for defining default values even though it's more typing:
function foo(a, b)
{
a = typeof a !== 'undefined' ? a : 42;
b = typeof b !== 'undefined' ? b : 'default_b';
...
}
(https://stackoverflow.com/a/894877/99256)
EDIT: The function objectPump does not give your attacker any advantage. 1) If your attacker can modify your JS file, then she will use eval straight away and she does not need any objectPump. 2) If you sanitize all input from your users, there is no problem here.
My primary concern here is that you will eventually shoot yourself in the foot, rather than an attacker will.
I'm currently using javascript eval() to check and create a multidimensional object that I have no idea of the depth.
Basically, I want to know if there's any way to create this multi-depth object. The object can be as deep as result['one']['two']['three']['four']['five']['six']['seven']. I know there are cases where using eval() is perfectly fine, but I'm also worried about performance. I thought about referencing each depth to a new variable, but I don't know how to do pointers in Javascript
create = function(fields, create_array){
var field;
for (j = 0; j < len; j++){
field = fields.slice(0, j).join('');
if (field){
// is there any way to do this without eval?
eval('if (typeof result' + field + ' == "undefined" || !result' + field + ') result' + field + ' = ' + (create_array?'[]':'{}') + ';');
}
}
}
How about
var deep = { one: { two: { three: { four: { five: { six: { seven: 'peek-a-boo!' }}}}}}};
I don't see what "eval()" has to do with this at all; there's no reason to "initialize" such an object. Just create them.
If you wanted to write a function with an API like you've got (for reasons I don't understand), you could do this:
function create(fields, create_array) {
var rv = create_array ? [] : {}, o = rv;
for (var i = 0; i < fields.length; ++i) {
o = o[fields[i]] = create_array ? [] : {};
}
return rv;
}
There doesn't seem to be any point to the "create_array" flag, since you're presumably always using strings for keys.
Never mind, found my way in. I used a recursive function to ensure that the object was created properly.
create = function(create_array, res, path){
var field = fields.shift();
if (field){
if (typeof res[field] == "undefined" || !res[field]) res[field] = (create_array?[]:{});
path.push('["' + field + '"]');
create(create_array, res[field], path);
}
}
var result = {}, strpath = [], fields[];
create(true, result, strpath);
eval('result' + strpath.join('') + ' = value;');
being variable "field" a variable outside the function, that contained the levels of the object. doing result["field"]["name"]["first"] = value without the ["field"] or ["name"] field existing or defined as an object, would throw an error and stop execution, that's why I'm pre-creating the object variable, either as an array or object.
I couldn't find another option for the second eval() though. There's no way to provide a way to access multiple properties on an object without knowing the depth.