refactor objects, add optional chaining

This commit is contained in:
Ilya Kantor 2020-05-03 16:56:16 +03:00
parent 09a964e969
commit b057341f6c
35 changed files with 579 additions and 435 deletions

View file

@ -1,19 +0,0 @@
Sure, it works, no problem.
The `const` only protects the variable itself from changing.
In other words, `user` stores a reference to the object. And it can't be changed. But the content of the object can.
```js run
const user = {
name: "John"
};
*!*
// works
user.name = "Pete";
*/!*
// error
user = 123;
```

View file

@ -1,18 +0,0 @@
importance: 5
---
# Constant objects?
Is it possible to change an object declared with `const`? What do you think?
```js
const user = {
name: "John"
};
*!*
// does it work?
user.name = "Pete";
*/!*
```

View file

@ -92,6 +92,30 @@ let user = {
```
That is called a "trailing" or "hanging" comma. Makes it easier to add/remove/move around properties, because all lines become alike.
````smart header="Object with const can be changed"
Please note: an object declared as `const` *can* be modified.
For instance:
```js run
const user = {
name: "John"
};
*!*
user.name = "Pete"; // (*)
*/!*
alert(user.name); // Pete
```
It might seem that the line `(*)` would cause an error, but no. The `const` fixes the value of `user`, but not its contents.
The `const` would give an error only if we try to set `user=...` as a whole.
There's another way to make constant object properties, we'll cover it later in the chapter <info:property-descriptors>.
````
## Square brackets
For multiword properties, the dot access doesn't work:
@ -161,7 +185,7 @@ alert( user.key ) // undefined
### Computed properties
We can use square brackets in an object literal. That's called *computed properties*.
We can use square brackets in an object literal, when creating an object. That's called *computed properties*.
For instance:
@ -249,9 +273,25 @@ let user = {
};
```
## Property names limitations
Property names (keys) must be either strings or symbols (a special type for identifiers, to be covered later).
As we already know, a variable cannot have a name equal to one of language-reserved words like "for", "let", "return" etc.
But for an object property, there's no such restriction:
```js run
// these properties are all right
let obj = {
for: 1,
let: 2,
return: 3
};
alert( obj.for + obj.let + obj.return ); // 6
```
In short, there are no limitations on property names. They can be any strings or symbols (a special type for identifiers, to be covered later).
Other types are automatically converted to strings.
@ -267,25 +307,7 @@ alert( obj["0"] ); // test
alert( obj[0] ); // test (same property)
```
**Reserved words are allowed as property names.**
As we already know, a variable cannot have a name equal to one of language-reserved words like "for", "let", "return" etc.
But for an object property, there's no such restriction. Any name is fine:
```js run
let obj = {
for: 1,
let: 2,
return: 3
};
alert( obj.for + obj.let + obj.return ); // 6
```
We can use any string as a key, but there's a special property named `__proto__` that gets special treatment for historical reasons.
For instance, we can't set it to a non-object value:
There's a minor gotcha with a special property named `__proto__`. We can't set it to a non-object value:
```js run
let obj = {};
@ -295,19 +317,13 @@ alert(obj.__proto__); // [object Object] - the value is an object, didn't work a
As we see from the code, the assignment to a primitive `5` is ignored.
The nature of `__proto__` will be revealed in detail later in the chapter [](info:prototype-inheritance).
As for now, it's important to know that such behavior of `__proto__` can become a source of bugs and even vulnerabilities if we intend to store user-provided keys in an object.
The problem is that a visitor may choose `__proto__` as the key, and the assignment logic will be ruined (as shown above).
There are two workarounds for the problem:
1. Modify the object's behavior to treat `__proto__` as a regular property. We'll learn how to do it in the chapter [](info:prototype-methods).
2. Using [Map](info:map-set) data structure which supports arbitrary keys. We'll learn it in the chapter <info:map-set>.
We'll cover the special nature of `__proto__` in [subsequent chapters](info:prototype-inheritance), and suggest the [ways to fix](info:prototype-methods) such behavior.
## Property existence test, "in" operator
A notable objects feature is that it's possible to access any property. There will be no error if the property doesn't exist! Accessing a non-existing property just returns `undefined`. It provides a very common way to test whether the property exists -- to get it and compare vs undefined:
A notable feature of objects in JavaScript, compared to many other languages, is that it's possible to access any property. There will be no error if the property doesn't exist!
Reading a non-existing property just returns `undefined`. So we can easily test whether the property exists:
```js run
let user = {};
@ -315,7 +331,7 @@ let user = {};
alert( user.noSuchProperty === undefined ); // true means "no such property"
```
There also exists a special operator `"in"` to check for the existence of a property.
There's also a special operator `"in"` for that.
The syntax is:
```js
@ -333,17 +349,18 @@ alert( "blabla" in user ); // false, user.blabla doesn't exist
Please note that on the left side of `in` there must be a *property name*. That's usually a quoted string.
If we omit quotes, that would mean a variable containing the actual name will be tested. For instance:
If we omit quotes, that means a variable, it should contain the actual name to be tested. For instance:
```js run
let user = { age: 30 };
let key = "age";
alert( *!*key*/!* in user ); // true, takes the name from key and checks for such property
alert( *!*key*/!* in user ); // true, property "age" exists
```
````smart header="Using \"in\" for properties that store `undefined`"
Usually, the strict comparison `"=== undefined"` check the property existence just fine. But there's a special case when it fails, but `"in"` works correctly.
Why does the `in` operator exist? Isn't it enough to compare against `undefined`?
Well, most of the time the comparison with `undefined` works fine. But there's But there's a special case when it fails, but `"in"` works correctly.
It's when an object property exists, but stores `undefined`:
@ -357,11 +374,10 @@ alert( obj.test ); // it's undefined, so - no such property?
alert( "test" in obj ); // true, the property does exist!
```
In the code above, the property `obj.test` technically exists. So the `in` operator works right.
Situations like this happen very rarely, because `undefined` is usually not assigned. We mostly use `null` for "unknown" or "empty" values. So the `in` operator is an exotic guest in the code.
````
Situations like this happen very rarely, because `undefined` should not be explicitly assigned. We mostly use `null` for "unknown" or "empty" values. So the `in` operator is an exotic guest in the code.
## The "for..in" loop
@ -396,7 +412,6 @@ Note that all "for" constructs allow us to declare the looping variable inside t
Also, we could use another variable name here instead of `key`. For instance, `"for (let prop in obj)"` is also widely used.
### Ordered like an object
Are objects ordered? In other words, if we loop over an object, do we get all properties in the same order they were added? Can we rely on this?
@ -480,262 +495,6 @@ for (let code in codes) {
Now it works as intended.
## Copying by reference
One of the fundamental differences of objects vs primitives is that they are stored and copied "by reference".
Primitive values: strings, numbers, booleans -- are assigned/copied "as a whole value".
For instance:
```js
let message = "Hello!";
let phrase = message;
```
As a result we have two independent variables, each one is storing the string `"Hello!"`.
![](variable-copy-value.svg)
Objects are not like that.
**A variable stores not the object itself, but its "address in memory", in other words "a reference" to it.**
Here's the picture for the object:
```js
let user = {
name: "John"
};
```
![](variable-contains-reference.svg)
Here, the object is stored somewhere in memory. And the variable `user` has a "reference" to it.
**When an object variable is copied -- the reference is copied, the object is not duplicated.**
If we imagine an object as a cabinet, then a variable is a key to it. Copying a variable duplicates the key, but not the cabinet itself.
For instance:
```js no-beautify
let user = { name: "John" };
let admin = user; // copy the reference
```
Now we have two variables, each one with the reference to the same object:
![](variable-copy-reference.svg)
We can use any variable to access the cabinet and modify its contents:
```js run
let user = { name: 'John' };
let admin = user;
*!*
admin.name = 'Pete'; // changed by the "admin" reference
*/!*
alert(*!*user.name*/!*); // 'Pete', changes are seen from the "user" reference
```
The example above demonstrates that there is only one object. As if we had a cabinet with two keys and used one of them (`admin`) to get into it. Then, if we later use the other key (`user`) we would see changes.
### Comparison by reference
The equality `==` and strict equality `===` operators for objects work exactly the same.
**Two objects are equal only if they are the same object.**
For instance, if two variables reference the same object, they are equal:
```js run
let a = {};
let b = a; // copy the reference
alert( a == b ); // true, both variables reference the same object
alert( a === b ); // true
```
And here two independent objects are not equal, even though both are empty:
```js run
let a = {};
let b = {}; // two independent objects
alert( a == b ); // false
```
For comparisons like `obj1 > obj2` or for a comparison against a primitive `obj == 5`, objects are converted to primitives. We'll study how object conversions work very soon, but to tell the truth, such comparisons are necessary very rarely and usually are a result of a coding mistake.
### Const object
An object declared as `const` *can* be changed.
For instance:
```js run
const user = {
name: "John"
};
*!*
user.age = 25; // (*)
*/!*
alert(user.age); // 25
```
It might seem that the line `(*)` would cause an error, but no, there's totally no problem. That's because `const` fixes only value of `user` itself. And here `user` stores the reference to the same object all the time. The line `(*)` goes *inside* the object, it doesn't reassign `user`.
The `const` would give an error if we try to set `user` to something else, for instance:
```js run
const user = {
name: "John"
};
*!*
// Error (can't reassign user)
*/!*
user = {
name: "Pete"
};
```
...But what if we want to make constant object properties? So that `user.age = 25` would give an error. That's possible too. We'll cover it in the chapter <info:property-descriptors>.
## Cloning and merging, Object.assign
So, copying an object variable creates one more reference to the same object.
But what if we need to duplicate an object? Create an independent copy, a clone?
That's also doable, but a little bit more difficult, because there's no built-in method for that in JavaScript. Actually, that's rarely needed. Copying by reference is good most of the time.
But if we really want that, then we need to create a new object and replicate the structure of the existing one by iterating over its properties and copying them on the primitive level.
Like this:
```js run
let user = {
name: "John",
age: 30
};
*!*
let clone = {}; // the new empty object
// let's copy all user properties into it
for (let key in user) {
clone[key] = user[key];
}
*/!*
// now clone is a fully independent clone
clone.name = "Pete"; // changed the data in it
alert( user.name ); // still John in the original object
```
Also we can use the method [Object.assign](mdn:js/Object/assign) for that.
The syntax is:
```js
Object.assign(dest, [src1, src2, src3...])
```
- Arguments `dest`, and `src1, ..., srcN` (can be as many as needed) are objects.
- It copies the properties of all objects `src1, ..., srcN` into `dest`. In other words, properties of all arguments starting from the 2nd are copied into the 1st. Then it returns `dest`.
For instance, we can use it to merge several objects into one:
```js
let user = { name: "John" };
let permissions1 = { canView: true };
let permissions2 = { canEdit: true };
*!*
// copies all properties from permissions1 and permissions2 into user
Object.assign(user, permissions1, permissions2);
*/!*
// now user = { name: "John", canView: true, canEdit: true }
```
If the receiving object (`user`) already has the same named property, it will be overwritten:
```js
let user = { name: "John" };
// overwrite name, add isAdmin
Object.assign(user, { name: "Pete", isAdmin: true });
// now user = { name: "Pete", isAdmin: true }
```
We also can use `Object.assign` to replace the loop for simple cloning:
```js
let user = {
name: "John",
age: 30
};
*!*
let clone = Object.assign({}, user);
*/!*
```
It copies all properties of `user` into the empty object and returns it. Actually, the same as the loop, but shorter.
Until now we assumed that all properties of `user` are primitive. But properties can be references to other objects. What to do with them?
Like this:
```js run
let user = {
name: "John",
sizes: {
height: 182,
width: 50
}
};
alert( user.sizes.height ); // 182
```
Now it's not enough to copy `clone.sizes = user.sizes`, because the `user.sizes` is an object, it will be copied by reference. So `clone` and `user` will share the same sizes:
Like this:
```js run
let user = {
name: "John",
sizes: {
height: 182,
width: 50
}
};
let clone = Object.assign({}, user);
alert( user.sizes === clone.sizes ); // true, same object
// user and clone share sizes
user.sizes.width++; // change a property from one place
alert(clone.sizes.width); // 51, see the result from the other one
```
To fix that, we should use the cloning loop that examines each value of `user[key]` and, if it's an object, then replicate its structure as well. That is called a "deep cloning".
There's a standard algorithm for deep cloning that handles the case above and more complex cases, called the [Structured cloning algorithm](https://html.spec.whatwg.org/multipage/structured-data.html#safe-passing-of-structured-data). In order not to reinvent the wheel, we can use a working implementation of it from the JavaScript library [lodash](https://lodash.com), the method is called [_.cloneDeep(obj)](https://lodash.com/docs#cloneDeep).
## Summary
Objects are associative arrays with several special features.
@ -753,10 +512,6 @@ Additional operators:
- To check if a property with the given key exists: `"key" in obj`.
- To iterate over an object: `for (let key in obj)` loop.
Objects are assigned and copied by reference. In other words, a variable stores not the "object value", but a "reference" (address in memory) for the value. So copying such a variable or passing it as a function argument copies that reference, not the object. All operations via copied references (like adding/removing properties) are performed on the same single object.
To make a "real copy" (a clone) we can use `Object.assign` or [_.cloneDeep(obj)](https://lodash.com/docs#cloneDeep).
What we've studied in this chapter is called a "plain object", or just `Object`.
There are many other kinds of objects in JavaScript:

View file

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="370" height="249" viewBox="0 0 370 249"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:&apos;PT Mono&apos;;font-weight:700;font-style:normal;src:local(&apos;PT MonoBold&apos;),url(/font/PTMonoBold.woff2) format(&apos;woff2&apos;),url(/font/PTMonoBold.woff) format(&apos;woff&apos;),url(/font/PTMonoBold.ttf) format(&apos;truetype&apos;)}</style></defs><g id="combined" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="variable-contains-reference.svg"><g id="noun_1211_cc" fill="#E8C48F" transform="translate(12 119)"><path id="Shape" d="M17 28.196h112.558v42.95c0 .373-.079.862-.279 1.294-.2.433-16.574 35.56-16.574 35.56V53.64l16.854-25.444L148 0H35.44L17 28.196zM17 57V29L2 57"/><path id="Shape" d="M0 59v54.73c0 3.42 1.484 5.27 4.387 5.27h100.086c3.122 0 5.527-2.548 5.527-3.476V59H0z"/></g><text id="user" fill="#FFF" font-family="OpenSans-Bold, Open Sans" font-size="18" font-weight="bold"><tspan x="47" y="215">user</tspan></text><path id="Rectangle-4" fill="#FFF" d="M116 153h44v6h-44z"/><path id="Shape-3" fill="#EE6B47" d="M123.854 154.322l13.779-13.476c1.613-1.274 5.185-4.427 3.572-5.702-1.613-1.275-2.664-.472-4.275.803l-18.499 18.375c-1.613 1.275-1.613 2.845 0 4.12 0 0 21.483 20.067 22.774 20.067 3.005 0 2.677-1.821 1.064-3.096l-18.415-17.826H201.028c2.281 0 3.75.279 3.75-1.524 0-1.804-1.469-1.741-3.75-1.741h-77.174z" transform="matrix(-1 0 0 1 322 0)"/><g id="Rectangle-3-+-Shape" transform="translate(58 135)"><path id="Rectangle-3" fill="#FFF" d="M0 0h47v37H0z"/><path id="Shape" fill="#EE6B47" d="M44 33.772H4V4h40v29.772zM8.733 31.018h30.533L29.22 20.868l-1.902 1.889c-.824.812-2.003 1.277-3.242 1.277l-.021-.001c-1.244-.005-2.427-.48-3.246-1.304l-1.934-1.933-10.14 10.222zM6.791 8.726V29.04l10.122-10.202L6.79 8.726zm24.395 10.187L41.209 29.04V8.954l-10.023 9.96zM8.74 6.755l14.057 14.042c.304.305.766.48 1.27.482h.008c.496 0 .968-.173 1.264-.467L39.49 6.755H8.739z"/></g><path id="Rectangle-4-Copy" fill="#D1C4B1" stroke="#D1C4B1" stroke-width="4" d="M241.937 117l-16.667 20h130.46l-16.667-20h-97.126z" opacity=".5"/><g id="Group-2" transform="translate(271 10)"><g id="Group"><path id="Rectangle-7" fill="#FFF" d="M0 0h32v124H0z"/><path id="Combined-Shape" fill="#E8C48F" d="M32 0v124H0V0h32zM16.5 100a7.5 7.5 0 100 15 7.5 7.5 0 000-15zM28 5H4v87h24V5z"/></g><text id="name" fill="#EE6B47" font-family="PTMono-Bold, PT Mono" font-size="16" font-weight="bold" transform="rotate(-90 15.5 47.5)"><tspan x="-3.7" y="52.5">name</tspan></text></g><path id="Rectangle-4" fill="#FFF9EB" stroke="#BCA68E" stroke-width="4" d="M223 131h135v50H223z"/><path id="Rectangle-8" stroke="#BCA68E" stroke-width="3" d="M278.5 151.5h25v10h-25z"/></g></g></svg>

Before

Width:  |  Height:  |  Size: 2.8 KiB

View file

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="599" height="260" viewBox="0 0 599 260"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:&apos;PT Mono&apos;;font-weight:700;font-style:normal;src:local(&apos;PT MonoBold&apos;),url(/font/PTMonoBold.woff2) format(&apos;woff2&apos;),url(/font/PTMonoBold.woff) format(&apos;woff&apos;),url(/font/PTMonoBold.ttf) format(&apos;truetype&apos;)}</style></defs><g id="combined" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="variable-copy-reference.svg"><g id="Group" transform="translate(11 125)"><g id="noun_1211_cc" fill="#E8C48F"><path id="Shape" d="M17 28.196h112.558v42.95c0 .373-.079.862-.279 1.294-.2.433-16.574 35.56-16.574 35.56V53.64l16.854-25.444L148 0H35.44L17 28.196zM17 57V29L2 57"/><path id="Shape" d="M0 59v54.73c0 3.42 1.484 5.27 4.387 5.27h100.086c3.122 0 5.527-2.548 5.527-3.476V59H0z"/></g><text id="user" fill="#FFF" font-family="OpenSans-Bold, Open Sans" font-size="18" font-weight="bold"><tspan x="35" y="96">user</tspan></text><g id="Rectangle-4-+-Shape-3" transform="translate(104 15)"><path id="Rectangle-4" fill="#FFF" d="M0 19h44v6H0z"/><path id="Shape-3" fill="#EE6B47" d="M7.854 20.322L21.633 6.846c1.613-1.274 5.185-4.427 3.572-5.702-1.613-1.275-2.664-.472-4.275.803L2.43 20.322c-1.613 1.275-1.613 2.845 0 4.12 0 0 21.483 20.067 22.774 20.067 3.005 0 2.677-1.821 1.064-3.096L7.854 23.587H85.028c2.281 0 3.75.279 3.75-1.524 0-1.804-1.469-1.741-3.75-1.741H7.854z" transform="matrix(-1 0 0 1 90 0)"/></g><g id="Rectangle-3-+-Shape" transform="translate(46 16)"><path id="Rectangle-3" fill="#FFF" d="M0 0h47v37H0z"/><path id="Shape" fill="#EE6B47" d="M44 33.772H4V4h40v29.772zM8.733 31.018h30.533L29.22 20.868l-1.902 1.889c-.824.812-2.003 1.277-3.242 1.277l-.021-.001c-1.244-.005-2.427-.48-3.246-1.304l-1.934-1.933-10.14 10.222zM6.791 8.726V29.04l10.122-10.202L6.79 8.726zm24.395 10.187L41.209 29.04V8.954l-10.023 9.96zM8.74 6.755l14.057 14.042c.304.305.766.48 1.27.482h.008c.496 0 .968-.173 1.264-.467L39.49 6.755H8.739z"/></g></g><g id="noun_1211_cc" fill="#E8C48F" transform="translate(418 125)"><path id="Shape" d="M17 28.196h112.558v42.95c0 .373-.079.862-.279 1.294-.2.433-16.574 35.56-16.574 35.56V53.64l16.854-25.444L148 0H35.44L17 28.196zM17 57V29L2 57"/><path id="Shape" d="M0 59v54.73c0 3.42 1.484 5.27 4.387 5.27h100.086c3.122 0 5.527-2.548 5.527-3.476V59H0z"/></g><text id="admin" fill="#FFF" font-family="OpenSans-Bold, Open Sans" font-size="18" font-weight="bold"><tspan x="446" y="221">admin</tspan></text><g id="Rectangle-4-+-Shape-3" transform="matrix(-1 0 0 1 451 140)"><path id="Rectangle-4" fill="#FFF" d="M0 19h44v6H0z"/><path id="Shape-3" fill="#EE6B47" d="M7.854 20.322L21.633 6.846c1.613-1.274 5.185-4.427 3.572-5.702-1.613-1.275-2.664-.472-4.275.803L2.43 20.322c-1.613 1.275-1.613 2.845 0 4.12 0 0 21.483 20.067 22.774 20.067 3.005 0 2.677-1.821 1.064-3.096L7.854 23.587H85.028c2.281 0 3.75.279 3.75-1.524 0-1.804-1.469-1.741-3.75-1.741H7.854z" transform="matrix(-1 0 0 1 90 0)"/></g><g id="Rectangle-3-+-Shape" transform="translate(464 141)"><path id="Rectangle-3" fill="#FFF" d="M0 0h47v37H0z"/><path id="Shape" fill="#EE6B47" d="M44 33.772H4V4h40v29.772zM8.733 31.018h30.533L29.22 20.868l-1.902 1.889c-.824.812-2.003 1.277-3.242 1.277l-.021-.001c-1.244-.005-2.427-.48-3.246-1.304l-1.934-1.933-10.14 10.222zM6.791 8.726V29.04l10.122-10.202L6.79 8.726zm24.395 10.187L41.209 29.04V8.954l-10.023 9.96zM8.74 6.755l14.057 14.042c.304.305.766.48 1.27.482h.008c.496 0 .968-.173 1.264-.467L39.49 6.755H8.739z"/></g><path id="Rectangle-4-Copy" fill="#D1C4B1" stroke="#D1C4B1" stroke-width="4" d="M235.937 125l-16.667 20h130.46l-16.667-20h-97.126z" opacity=".5"/><g id="Group-2" transform="translate(265 18)"><g id="Group"><path id="Rectangle-7" fill="#FFF" d="M0 0h32v124H0z"/><path id="Combined-Shape" fill="#E8C48F" d="M32 0v124H0V0h32zM16.5 100a7.5 7.5 0 100 15 7.5 7.5 0 000-15zM28 5H4v87h24V5z"/></g><text id="name" fill="#EE6B47" font-family="PTMono-Bold, PT Mono" font-size="16" font-weight="bold" transform="rotate(-90 15.5 47.5)"><tspan x="-3.7" y="52.5">name</tspan></text></g><path id="Rectangle-4" fill="#FFF9EB" stroke="#BCA68E" stroke-width="4" d="M217 139h135v50H217z"/><path id="Rectangle-8" stroke="#BCA68E" stroke-width="3" d="M272.5 159.5h25v10h-25z"/></g></g></svg>

Before

Width:  |  Height:  |  Size: 4.3 KiB

View file

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="359" height="143" viewBox="0 0 359 143"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:&apos;PT Mono&apos;;font-weight:700;font-style:normal;src:local(&apos;PT MonoBold&apos;),url(/font/PTMonoBold.woff2) format(&apos;woff2&apos;),url(/font/PTMonoBold.woff) format(&apos;woff&apos;),url(/font/PTMonoBold.ttf) format(&apos;truetype&apos;)}</style></defs><g id="combined" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="variable-copy-value.svg"><g id="noun_1211_cc-+-Message" transform="translate(11 6)"><g id="noun_1211_cc"><path id="Shape" fill="#E8C48F" d="M17 37.196h112.558v42.95c0 .373-.079.862-.279 1.294-.2.433-16.574 35.56-16.574 35.56V62.64l16.854-25.444L148 9H35.44L17 37.196zM17 66V38L2 66"/><g id="Rectangle-5-+-&quot;World!&quot;" transform="translate(15)"><path id="Rectangle-5" fill="#FFF9EB" stroke="#8A704D" stroke-width="2" d="M18.861 1.809L2 17.533l53.14 56.986L72 58.794 18.861 1.81z"/><text id="&quot;Hello!&quot;" fill="#8A704D" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold" transform="rotate(47 38.202 38.946)"><tspan x="2.822" y="43.482">&quot;Hello!&quot;</tspan></text></g><path id="Shape" fill="#E8C48F" d="M0 68v54.73c0 3.42 1.484 5.27 4.387 5.27h100.086c3.122 0 5.527-2.548 5.527-3.476V68H0z"/></g><text id="message" fill="#FFF" font-family="OpenSans-Bold, Open Sans" font-size="18" font-weight="bold"><tspan x="17" y="105">message</tspan></text></g><g id="Group" transform="translate(210 6)"><path id="Shape" fill="#E8C48F" d="M17 37.196h113.417v42.95c0 .373-.08.862-.28 1.294-.202.433-16.702 35.56-16.702 35.56V62.64l16.983-25.444L149 9H35.582L17 37.196z"/><path id="Shape" fill="#E8C48F" d="M18 66V38L2 66"/><g id="Rectangle-5-+-&quot;World!&quot;" transform="translate(15)"><path id="Rectangle-5" fill="#FFF9EB" stroke="#8A704D" stroke-width="2" d="M19.117 1.8l-17.1 15.734 53.866 56.994 17.1-15.734L19.118 1.799z"/><text id="&quot;Hello!&quot;" fill="#8A704D" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold" transform="rotate(47 38.162 37.693)"><tspan x="2.782" y="42.23">&quot;Hello!&quot;</tspan></text></g><path id="Shape" fill="#E8C48F" d="M0 68v54.73c0 3.42 1.497 5.27 4.427 5.27h100.996c3.15 0 5.577-2.548 5.577-3.476V68H0z"/><text id="phrase" fill="#FFF" font-family="OpenSans-Bold, Open Sans" font-size="18" font-weight="bold"><tspan x="25" y="105">phrase</tspan></text></g></g></g></svg>

Before

Width:  |  Height:  |  Size: 2.5 KiB