JavaScript raw value and wrapper object

preface

As JavaScript becomes more and more popular, more and more developers begin to contact and use JavaScript.

At the same time, I also found that many developers do not have a clear understanding of the most basic original values and wrapper objects of JavaScript.

In this article, I'll introduce them in detail.

🧐 Let's go!

text

Primitive types

Primitive types are also called "basic types".

Currently, there are several primitive types in JavaScript:

  • String (string)
  • number
  • boolean (boolean)
  • null (empty)
  • undefined
  • bigint (large integer, ES6)
  • symbol (sign? ES6)

📝 As follows:

typeof 'chenpipi';  // "string"
typeof 12345;       // "number"
typeof true;        // "boolean"
typeof null;        // "object"
typeof undefined;   // "undefined"
typeof 12345n;      // "bigint"
typeof Symbol();    // "symbol"

💡 Particular attention

Although typeof null returns "object", it does not mean that null is an object. In fact, it is a Bug of JavaScript, and it has been so since the birth of JavaScript.

In the initial implementation of JavaScript, the value in JavaScript is represented by a label representing the type and the actual data value. The label type of the object is 0. Since null represents a null pointer (0x00 in most platforms), the type label of null is 0, and typeof null returns "object".

The history of "typeof null": https://2ality.com/2013/10/typeof-null.html

Primitive values

The original value is the value (data) of the original type.

A primitive value is data that is not an object and has no methods.

The original value is a kind of non object data without any method.

In other words, the values of primitive types such as string, number and boolean do not have any properties and methods.

😨 At this time, does the little friend with keen sense of smell have noticed something wrong?

It's cumin! I added cumin! (manual dog head and cross off)

🤓 There is a very interesting point here, but before discussing this issue, let's know the packaging object.

Wrapper objects

All primitive types except null and undefined have their corresponding wrapper objects:

  • String (string)
  • Number
  • Boolean (Boolean)
  • BigInt (large integer, ES6)
  • Symbol (logo? ES6)

Object

Object is a reference type.

First, the wrapper object itself is an object and a function.

String instanceof Object;   // true
String instanceof Function; // true

Constructor

Instance

String, Number and Boolean all support the use of the new operator to create corresponding wrapper object instances.

📝 For example, the declaration of String (excerpt):

interface StringConstructor {
  new(value?: any): String;
  (value?: any): string;
  readonly prototype: String;
}
declare var String: StringConstructor;

📝 The data obtained by using the new operator is an Object:

// character string
typeof 'pp';                      // "string"
typeof new String('pp');          // "object"
new String() instanceof Object;   // true
// number
typeof 123;                       // "number"
typeof new Number(123);           // "object"
new Number() instanceof Object;   // true
// Boolean
typeof true;                      // "boolean"
typeof new Boolean(true);         // "object"
new Boolean() instanceof Object;  // true

📝 We can call the valueOf() function of the wrapper object instance to get its original value:

// character string
let s = new String('pp');
s.valueOf();                // "pp"
typeof s.valueOf();         // "string"
// number
let n = new Number(123);
n.valueOf();                // 123
typeof n.valueOf();         // "number"
// Boolean
let b = new Boolean(true);
b.valueOf();                // true
typeof b.valueOf();         // "boolean"

"Attention"

BigInt and Symbol belong to "incomplete classes" and do not support the new operator.

📝 For example, BigInt's statement (excerpt):

interface BigIntConstructor {
  (value?: any): bigint;
  readonly prototype: BigInt;
}
declare var BigInt: BigIntConstructor;

You can see that there are no new operator related functions in the declaration of BigInt.

Ordinary function

Wrapper objects can also be used as normal functions.

The String(), Number() and Boolean() functions can be used to explicitly type convert any type of data.

In addition, the Object() function can also be used for explicit type conversion, but it will not be expanded in this article.

String

📝 Example code:

typeof String();    // "string"
String();           // ""
String('pp');       // "pp"
String(123);        // "123"
String(true);       // "true"
String(false);      // "false"
String(null);       // "null"
String(undefined);  // "undefined"
String([]);         // ""
String({});         // "[object Object]"

💡 Tip 1

When we use the String() function to convert an object, JavaScript will first access the toString() function on the object. If it is not implemented, it will look up along the prototype chain.

🌰 For example: execute string ({tostring() {return 'pp';}}) The returned result is "pp", not "[object]".

Therefore, the String() function cannot be used to judge whether a value is an object (rollover).

💡 Tips 2

The common way to judge an object is object prototype. toString({}) === '[object Object]'.

🌰 Take a chestnut: execute object prototype. toString({ toString() { return 'pp'; } }) Returned is "[object]".

Number

📝 Example code:

typeof Number();    // "number"
Number();           // 0
Number('');         // 0
Number('pp');       // NaN
Number(123);        // 123
Number(true);       // 1
Number(false);      // 0
Number(null);       // 0
Number(undefined);  // NaN
Number([]);         // 0
Number({});         // NaN

💡 Tips

For the Number() function, perhaps the most practical conversion is to convert true and false to 1 and 0.

Boolean

📝 Example code:

typeof Boolean();   // "boolean"
Boolean();          // false
Boolean('');        // false
Boolean('pp');      // true
Boolean(0);         // false
Boolean(1);         // true
Boolean(null);      // false
Boolean(undefined); // false
Boolean([]);        // true
Boolean({});        // true

💡 Tips

In some cases, we will use 0 and 1 in the data to represent the true and false state. At this time, we can use Boolean() to judge the state.

BigInt

The BigInt() function converts an integer to a large integer.

This function accepts an integer as a parameter. If the incoming parameter is a floating point number or any non numeric data, an error will be reported.

📝 Example code:

BigInt(123);        // 123n
BigInt(123n);       // 123n
typeof 123n;        // "bigint"
typeof BigInt(123); // "bigint"
BigInt & Number

It should be noted that BigInt and Number are not strictly equal (loosely equal).

📝 Example code:

123n === 123; // false
123n == 123;  // true

Symbol

The Symbol() function is used to create a value of type symbol.

This function accepts a string as a descriptor (parameter) and will be converted to a string (except undefined) if other types of values are passed in.

Note that each symbol value is unique, even if their descriptors are the same.

And the data of symbol type can only be created through the Symbol() function.

📝 Example code:

// The following return value is simulated by Devtools, not the actual value
Symbol('pp');                   // Symbol(pp)
Symbol(123);                    // Symbol(123)
Symbol(null);                   // Symbol(null)
Symbol({});                     // Symbol([object Object])

// type
typeof Symbol('pp');            // "symbol"
Symbol('pp') === Symbol('pp');  // false

// descriptor 
Symbol('pp').description;       // "pp"
Symbol(123).description;        // "123"
Symbol({}).description;         // "[object Object]"
Symbol().description;           // undefined
Symbol(undefined).description;  // undefined

The original value is not an object

🎃 Here comes the interesting one~

No properties, no functions

As mentioned earlier in this article, "raw value is a kind of non object data without any method."

We all know that objects can have properties and methods.

But the string is not an object, so you can't add properties to the string.

📝 Do a small experiment:

let a = 'chenpipi';
console.log(a.length);  // 8
// Try adding a new attribute
a.name = 'Wu Yanzu';
console.log(a.name);    // undefined
// Attempt to modify an existing property
typeof a.slice;         // "function"
a.slice = null;
typeof a.slice;         // "function"

🎬 Slag skin small theater

At this time, a small partner of toutie uses the refutation skill.

Don't fool around here, scum. I usually write bugs. When I don't write code, I can call methods on strings, numbers and Boolean values!

📝 For example, the following code can execute normally and get the expected results:

// character string
let s = 'chenpipi';
s.toUpperCase();      // "CHENPIPI"
'ChenPiPi'.slice(4);  // "PiPi"
// number
let n = 123;
n.toString();         // "123"
(123.45).toFixed(2);  // "123.5"
// Boolean value
let b = true;
b.toString();         // "true"
false.toString();     // "false"

💡 Useless little knowledge

Have you found that the function cannot be called directly after the literal value of the number? For example, execute 123 Tostring() will report syntax error.

This is because the number (floating point number) itself uses the decimal point, The calling function also needs to use the decimal point, so there is ambiguity (string and Boolean value do not have this trouble).

In this case, we can wrap the numbers in parentheses (e.g. 123) toString(); Or use two consecutive decimal points To call functions, such as 123 toString().

🤔 It's strange

Since strings are not objects, why do strings have properties and methods?

On second thought, numbers are numbers. How can there be methods in numbers?

This is indeed illogical, but it contradicts reality.

What's going on???

I can't translate this

The answer is revealed~

😎 Operate secretly

Take the string as an example. When we read the properties or methods of the string in the code, JavaScript will silently perform the following operations:

  1. Create a temporary wrapper object instance by new String();
  2. Execute our code logic through the created object (read properties or execute functions);
  3. Temporary objects are no longer used and can be destroyed.

📝 Such as the following chestnuts:

let a = 'chenpipi';
console.log(a);   // "chenpipi"
// ------------------------------
let b1 = a.length;
console.log(b1);  // 8
// The above code is equivalent to:
let b2 = (new String(a)).length;
console.log(b2);  // 8
// ------------------------------
let c1 = a.toUpperCase();
console.log(c1);  // "CHENPIPI"
// The above code is equivalent to:
let c2 = (new String(a)).toUpperCase();
console.log(c2);  // "CHENPIPI"

Numbers are the same as Booleans, but numbers are created by new Number() to create temporary objects, while Booleans are created by new Boolean().

📝 In addition to the above example, the most powerful proof is their constructor:

'chenpipi'.constructor === String;  // true
(12345).constructor === Number;     // true
true.constructor === Boolean;       // true

All this is done secretly by JavaScript, and the temporary objects generated in the process are one-time (throw them away when they are used up).

😮 i see

Wuhu, that makes sense!

This also explains why we can access properties and methods on strings, but can't add or modify properties.

That's because the goal of our actual operation is actually the temporary object created by JavaScript, not the string itself!

Therefore, our add or modify operation actually takes effect, but it takes effect on the temporary object!

📝 Like this:

// In the code:
let a = 'chenpipi';
a.name = 'Wu Yanzu';
console.log(a.name);  // undefined

// amount to:
let a = 'chenpipi';
(new String(a)).name = 'Wu Yanzu';
console.log(a.name);  // undefined

// amount to:
let a = 'chenpipi';
let temp = new String(a);
temp.name = 'Wu Yanzu';
console.log(a.name);  // undefined

Summary

🎉 The above is the whole content of this article.

Finally, let's summarize:

  1. Most primitive types have corresponding packaging objects;
  2. Some packaging objects can be new, others can't;
  3. Wrapper objects are generally used for explicit type conversion;
  4. There are properties and methods on the object;
  5. There are no attributes and methods on the original value;
  6. The original value cannot have attributes and methods;
  7. But we can manipulate the original value as we manipulate the object;
  8. This is because JavaScript secretly makes small actions when executing code;
  9. JavaScript uses temporary wrapper objects to perform operations on behalf of the original values.

We usually don't pay much attention to this when we write code. In fact, these won't affect us when we write code.

So, isn't this article for nothing?

🙉 Yes, not all~

Know yourself and the enemy and win every battle.

Learning these useless little knowledge can be regarded as a deeper understanding of JavaScript. At least it can be used to boast (manual dog head ~).

Relevant information

JavaScript advanced programming (4th Edition)

Authoritative guide to JavaScript (6th Edition)

Primitive - MDN: https://developer.mozilla.org/en-US/docs/Glossary/Primitive

The history of "typeof null": https://2ality.com/2013/10/typeof-null.html

Portal

Wechat tweet version

Personal blog: rookie Inn

Open source homepage: tangerine peel

Eazax Cocos game development kit

More sharing

Cocos Creator Performance Optimization: DrawCall

"Draw a cool radar picture in Cocos Creator"

"Write a perfect wave with Shader"

Managing pop ups gracefully and efficiently in Cocos Creator

Interpretation of Cocos Creator source code: engine startup and main cycle

JavaScript memory explanation & Analysis Guide

Cocos Creator Editor Extension: Quick Finder

official account

Rookie Inn

😺 I am Chen pipi, a game developer who is still learning and a Cocos Star Writer who loves sharing.

🎨 This is my personal official account, but it is not limited to game development and front-end technology sharing.

💖 Every original is very attentive. Your attention is the driving force of my original!

Input and output.

Keywords: Javascript Programming

Added by maxime on Thu, 10 Feb 2022 11:06:34 +0200