How to set/update the value of a String object in JavaScript - javascript

I have an object with some properties which are String objects:
var obj = {
foo: new String("bar")
};
I am using String objects because I am needing to store additional sub-properties on the object while still being able to get the string value:
obj.foo.baz = "baz";
"" + obj.foo; //-> "bar";
I feel dumb for asking, but how can I update the value of a String object? Seems like some Array splice magic might need to be applied.
EDIT: Just to be clear, I understand string primitives in JS and immutability therein. This is an object I'm talking about. Here is the test that needs to pass:
assert.equal("" + obj.foo, "foo"); //-> true
assert.equal(obj.foo.baz, "baz"); //-> true
extend(obj, { foo: "foooooo" });
assert.equal("" + obj.foo, "foooooo"); //-> true
assert.equal(obj.foo.baz, "baz"); //-> true

You can't. Strings are immutable, regardless of how you "construct" them (literal or object).
What you should be doing is simply use an actual object to hold your values and your string.
At the most basic level this would be:
var obj = {
foo: "bar"
};
// later...
obj.baz = "baz";
"" + obj.foo; //-> "bar";
You can also consider using a monadic type as an "amplifier" / decorator, but that seems way overkill for this use case.
As a side note, adding properties and "random" functions to a string object is not a good OOP choice. These are strictly not relevant to the value that is the string, and only make sense to a higher level object, which is where they should reside.

You'd need to create a new String object and extend any new properties and values to that String object. I've provided a simple example below. That said, this example can be modified to suit your purposes (you'd create a custom extend or setter function).
Example of a property setter function
var createNewString = function (oldStringObj, string) {
var _new = new String(string);
var keys = Object.keys(oldStringObj); // returns only custom properties (not part of prototype)
for (var i=0,n=keys.length; i<n; i++){
var key = keys[i];
if (Number.isInteger(+key)) {
continue; // skip property if it's a numbered key
}
_new[key] = oldStringObj[key]; // simple assignment (not a deep copy) -- room for improvement
}
return _new;
};
Original object
var obj = {
foo: new String("bar")
};
obj.foo.baz = "baz"; // new property
Update the object
obj.foo = createNewString( obj.foo, 'foot' );
//obj.foo=='foot' and obj.foo.baz=='baz'

I suggest that you use a custom type for this, instead of the default String type. The ES6 spec defines that the underlying value for a String object is stored in its "[[StringData]] internal slot". The only place this slot is assigned is via the new String constructor, so it is implicitly immutable. You can create a new type which has the same string-like behaviours that you require, while being mutable.
class MutableString {
constructor(value) {
this.value = value;
}
toString() {
return this.value;
}
}
var obj = {
foo: new MutableString('bar')
};
obj.foo.baz = "baz";
console.assert("" + obj.foo == "bar");
console.assert(obj.foo + "" == "bar");
console.assert(obj.foo.baz == "baz");
console.assert(Object.keys({[obj.foo]: 1})[0] == "bar");
obj.foo.value = "foooooo";
console.assert("" + obj.foo == "foooooo");
console.assert(obj.foo + "" == "foooooo");
console.assert(obj.foo.baz == "baz");
console.assert(Object.keys({[obj.foo]: 1})[0] == "foooooo");
Because this isn't really a string it won't support any string methods, so it may not be suitable for your use. But it's more flexible and a little clearer, so I suggest considering it if possible.
It may be necessary to define a valueOf() method like toString() to handle some other cases, but I haven't verified.

Related

How can I create a object like a string, for example? A object with many properties, and a default console.log/evaluation value

I want to create a object with many properties, but, when I just console.log my object or insert it into a evaluation, it does have a default value to evaluate or log, a primitive value, like "test", for example.
I tried to use getters and setters but had no sucess.
const obj = { a: 'test1', b: 'test2' } // this is my object
console.log(obj.a); // this should return 'test1'
console.log(obj); // this should return a value of my choice, like 'testobj' or a number
'testobj' === obj; // should be true, since I want my obj to have a default value of 'testobj' in evaluations
// just like a primitive type, like strings or numbers. They have many functions and a default value
When an object is treated like a string, the JavaScript runtime will look to see if it has a toString() method and will return whatever that method indicates. If there isn't a toString(), then usually you'll see [object Object]. Also, to produce the underlying primitive value, give the object a valueOf value.
Additionally, when making objects that will be used in this way, use constructor functions, rather than object literals.
const objA = function(){ this.a= 'test1'; this.b= 'test2' }
let instA = new objA();
console.log(instA.toString());
const objB = function() {
this.a= 5;
this.b= 'test2';
this.toString= function(){ return this.a; };
this.valueOf = function() { return this.toString(); };
}
let instB = new objB();
console.log(instB.toString());
console.log(instB + 2); // Get the primitive and work with that
One year later I ran into this question again and found a solution. My objective was to create a string/number with a few methods that would use itself(this) to generate the output values and could be used in your code as a normal number/string without casting or anything like that, like a normal JS number/string.
Something like a number that has its own value but you can access the number.negative property and you will have the negative equivalent to your number. You can do it creating a class that extends the primitive type that you want to use, like, for a Number, I can use:
class Num extends Number {
negative = this * (-1);
// I can even add a few methods that receive props to calculate new output values
exponential(exp) {
let result = 1;
while(exp-- > 0) {
result *= this;
}
return result;
}
}
const myNum = new Num(3);
console.log('Simple sum: ', 1 + myNum); // outputs 4
console.log('Negative: ', myNum.negative); // outputs -3
console.log('Exponential: ', myNum.exponential(2)); // outputs 9
The code above works for strings as well. Hope I can help someone with this.

What is the Javascript equivalent of Python's get method for dictionaries

Python's get method for dictionaries lets me specify what should be returned if a key doesn't exist. For my current case I want a dictionary returned. How do I do this in Javascript?
There is no javascript equivalent of the python dictionary get method. If you would write it yourself, as a function, it would look like this:
function get(object, key, default_value) {
var result = object[key];
return (typeof result !== "undefined") ? result : default_value;
}
Use it like:
var obj = {"a": 1};
get(obj, "a", 2); // -> 1
get(obj, "b", 2); // -> 2
Note that the requested key will also be found in a prototype of obj.
If you really want a method rather than a function (obj.get("a", 2)), you need to extend the prototype of Object. This is generally considered a bad idea though, see Extending Object.prototype JavaScript
With modern javascript you can use the nullish coalescing operator ??
const result = obj[key] ?? default;
This will return the default value if key doesn't exist in obj. It will also return the default in cases like {myKey: undefined} or {myKey: null}, which may or may not be the desired behavior.
JavaScript has no helper feature to do that. You need to test explicitly.
if ("myProperty" in myObject) {
return { another: "object" };
} else {
return myObject.myProperty;
}
You can use a ternary operator to do the same thing with less code.
return ("myProperty" in myObject) ? myObject.myProperty : { another: "object" };
I prefer to use the logical OR like this:
foo.bar || 'default'
If checks is foo.bar is falsy, so it returns 'default' if bar is undefined.
You just need to care, that foo is an object. Otherwise a ReferenceError is thrown.
You could use a proxy for this (really new ):
var handler = {
get: function(target, name){
return name in target?
target[name] :
"Default";
}
};
var dictionary={"hi":true};
var dict = new Proxy(dictionary, handler);
dict.a = 1;
dict.b = undefined;
console.log(dict.a, dict.b,dict.hi); // 1, undefined,true
console.log(dict.new); //"Default"
//the proxied object gets changed:
console.log(dictionary.a, dictionary.b,dictionary.hi); // 1, undefined,true
console.log(dictionary.new); //undefined
A proxy is an object that reflects all changes and requests trough an handler. In this case we can write/access propertys of dictionary normally, but if we access values that do not exist it'll return "Default"
this works for me
let obj = {"a": 1};
let default = 100
obj["a"] || default; // -> 1
obj["b"] || default; // -> 100
But! there are some limitation, if !!obj["a"] === false we always get default value... so it's better to just check if key in obj, to be completely sure.

New JS Library, how to improve it?

I'm trying to improve on a small library I'm starting to write:
var syn=(function(){
var container = {};
return function(variable){
return{
val: container[variable],
is: function(value){
container[variable] = value;
}
};
};
})();
Currently it's capable of storing snippets of information using the syntax syn("a").is(42); and returning the value by using syn("a").val.
I'd like to be able to do two things with this library:
I'd like to be able to create new instances of the syn object, so I could create multiple "synapses". e.g., var SynapseA = new syn;
I'd like to reduce syn("a").val to just syn("a") so that if I refer to syn("a") without a property or method it returns the equivalent of syn("a").val, but I have no idea how this would be done?
Appreciate any help...
What you want is not possible, because it is in some way contradictory:
I'd like to reduce syn("a").val to just syn("a") so that if I refer to syn("a") without a property or method it returns the equivalent of syn("a").val
Assume this were possible, and the value stored for "a" is 42 as in your example, then both of the following expressions would need to return 42:
syn("a")
syn("a").val
But if the first returns 42, then the second can't work: 42 does not have a property val. If the second works, then the first will necessarily return an object with a val property, which obviously is not what 42 is. So this is a contradiction.
In my opinion, the closest you can get to what you want, is relying on the special valueOf method, which will kick in when you force coercion to a number (or boolean). However, for this to work, you must assume that the values you store, are indeed numbers.
Here is how it would work:
function Syn() { // constructor, so you can create more than 1 syn object
var container = {};
var f = function(variable){
return {
valueOf: function() { // special method
return container[variable];
},
val: container[variable],
assign: function(value){
container[variable] = value;
}
}
};
// Only include next line, if you REALLY need the returned
// object to be considered an instance of Syn:
Object.setPrototypeOf(f, Object.getPrototypeOf(this));
return f;
}
var syn = new Syn();
console.log(syn instanceof Syn); // true
syn("a").assign(42);
console.log(syn("a").val); // 42
console.log(+syn("a")); // 42, because `syn("a")` is coerced to number
NB: I renamed the method is to assign, as that seems more meaningful.
Similarly to valueOf, you could rely on toString, which does a similar thing when you coerce the return value of syn("a") to string:
function Syn() { // constructor, so you can create more than 1 syn object
var container = {};
var f = function(variable){
return {
toString: function() { // special method
return container[variable];
},
val: container[variable],
assign: function(value){
container[variable] = value;
}
}
};
// Only include next line, if you REALLY need the returned
// object to be considered an instance of Syn:
Object.setPrototypeOf(f, Object.getPrototypeOf(this));
return f;
}
var syn = new Syn();
console.log(syn instanceof Syn); // true
syn("a").assign("hello");
console.log(syn("a").val); // "hello"
console.log(''+syn("a")); // "hello", because `syn("a")` is coerced to string
This will do what you're asking I think:
var syn = function() {
this.container = {};
}
syn.prototype.is = function(index, value){
this.container[index] = value;
}
syn.prototype.val = function(index){
return this.container[index];
}
I can do the following:
var synapseA = new syn();
synapseA.is("a", 42);
console.log(synapseA.val("a"));
var synapseB = new syn();
synapseB.is("a", 30);
console.log(synapseB.val("a"));
synapseB.is("b", 20);
console.log(synapseB.val("b"));
And I get 42, 30 and 20 logged out. Is this what you're after?

What does the JavaScript "Object" function do?

What does the Object function in JavaScript do?
For example, what happens when we do Object(1)?
It forces something to be an object. I've not seen it being used in this way though.
var num = 1;
var obj = Object(num);
alert(typeof num); //displays "number"
alert(typeof obj): //displays "object"
alert(num + "," + obj); //displays "1,1"
The preferred, faster way to create an empty object on which you can put properties and methods on is by using {}. Three possible ways to create an object:
var emptyObj = {};
var emptyObj = new Object();
var emptyObj = new Object; // Object does not need an argument, so this is valid.
From the Mozilla developer site:
The Object constructor creates an object wrapper for the given value. If the value is null or undefined, it will create and return an empty object, otherwise, it will return an object of type that corresponds to the given value.
When called in a non-constructor context, Object behaves identically.
So Object(1) produces an object that behaves similarly to the primitive value 1, but with support for object features like assigning values to properties (Object(1).foo = 2 will work, (1).foo = 2 will not).
var obj = Object("test");
Creates a String "text", it's pretty similar to
var obj2 = "test";
Notice that the type of obj2 is "String" and of obj1 "Object"
Try this:
<script>
var obj = Object("test");
console.log(obj);
console.log(typeof(obj));
console.log(obj["0"]);
obj2 = "test";
console.log(obj2);
console.log(typeof(obj2));
console.log(obj2["0"]);
</script>
Creates an object http://www.w3schools.com/js/js_objects.asp
Object function is a constructor function, all other types(like Array, String, Number) inheritate it.

Can I create a method in an object?

I saw this code before, but I don't know what the meaning:
var person1 = {
toLocaleString : function(){
return "Nikolaos";
},
toString : function(){
return "Nicholas";
}
}
var person2 = {
toLocaleString : function(){
return "bum";
},
toString : function(){
return "Greg";
}
}
var people = [person1, person2];
alert(people.toString());
alert(people.toLocaleString());
does the function create an object with the method of toLocaleString and toString??or...??
That code is doing three things:
Using object literal syntax to create object instances
Using anonymous function expressions to create functions and bind them to properties on the objects. (Functions are first-class objects in JavaScript, so you can keep references to them, pass the references around, etc.)
Specifically, it's overriding two standard functions that all JavaScript objects inherit from the Object prototype.
Let's break it down a bit.
1) Object literal notation:
var obj = {propName: propValue};
The { and } in this case denote an object literal. Within an object literal, you can write propName: propValue to assign propValue to the property with the name propName on the object. This is the same as:
var obj = {}; // Get an empty object
obj.propName = propValue; // Add a property to it
You can do multiple properties separated with commas. So for instance:
var obj = {
author: "Douglas Adams",
title: "The Hitchhiker's Guide to the Galaxy",
answer: 42
};
That creates an object with three properties, two with string values and one with a number value.
Note that the right-hand side are processed just like an assignment, and so can be anything that can appear on the right-hand side of an assignment statement:
var x = "bar";
var obj = {
three: 1 + 2,
fubar: "foo " + x
};
The property names can be put in quotes if you like:
var x = "bar";
var obj = {
"three": 1 + 2,
"fubar": "foo " + x
};
...which is handy for specifying properties that have the names of reserved tokens (like "if", or "return") or formerly-reserved tokens (like "class") where it would be a syntax error if they weren't in quotes.
2) Now let's look at function expressions:
var f = function() { /* your code here */ };
That's a function expression. It creates a new function and assigns a reference to it to the variable f. You can call it by calling f().
var f = function(name) {
alert("Hi " + name);
};
f("Fred"); // alerts "Hi Fred"
1 + 2) So putting it together with object literal notation:
var obj = {
foo: function(name) {
alert("Hi " + name);
}
};
obj.foo("Fred"); // alerts "Hi Fred"
(I don't like anonymous functions, I prefer my functions to have names, but that's another topic.)
3) And finally: As maerics pointed out, the specific functions that are being used in that code are toString and toLocaleString, both of which are standard functions of JavaScript objects. That means that those will override the standard version and so return the given values whenever the standard function would have been called.
The toString() and toLocaleString() methods are implemented for all JavaScript objects by the specification of the language. So arrays (such as the one stored in the "people" variable) seem to implement those methods by returning each of their elements' string or "locale string" value, respectively (at least, in the web browsers we are testing).
That is, the Array class toString and toLocaleString methods must be implemented with something like:
Array.prototype.toString = function() {
var a = [];
for (var i=0; i<this.length; i++) {
a[i] = this[i].toString(); // Note "toString".
}
return a.join(",");
}
Array.prototype.toLocaleString = function() {
var a = [];
for (var i=0; i<this.length; i++) {
a[i] = this[i].toLocaleString(); // Note "toLocaleString".
}
return a.join(",");
}

Categories