delicious value: 🌟🌟🌟🌟🌟
Flavor: Wasabi Shrimp Ball
In order to match up with Mr. Yi, let's review briefly.
The data types of JavaScript include primitive types and object types:
- Primitive types: Null, Undefined, Number, String, Boolean, Symbol, BigInt
- Object type: Object
We are used to referring to objects as reference types. Of course, there are many special reference types, such as Function, Array, RegExp, Math, Date, Error, Set, Map, various stereotyped arrays, TypedArray, and so on.
The primitive type value is stored in the stack, the object type value is stored in the heap, and the reference address of the object is retained in the stack. When JavaScript accesses the data, it is accessed through the reference in the stack.
In JavaScript, the assignment of the primitive type will copy the variable value completely, and the assignment of the object (reference) type will copy the reference address.
Here are two more frequently asked interview questions to practice your hands
let a = {
name: '前端食堂',
age: 2
}
let b = a
console.log(a.name)
b.name = '童欧巴'
console.log(a.name)
console.log(b.name)
// 前端食堂
// 童欧巴
// 童欧巴
The first question, So Easy, can be answered correctly with your eyes closed.
let a = {
name: '前端食堂',
age: 2
}
const expand = function(b) {
b.age = 18
b = {
name: '童欧巴',
age: 25
}
return b
}
let c = expand(a)
console.log(c.age)
console.log(a.age)
console.log(a)
// 25
// 18
// {name: "前端食堂", age: 18}
This question may be answered incorrectly by some students, let’s analyze it together:
The parameter b passed in by the expand function is actually the memory address value of the object in the heap. The age attribute of the a object can be changed by calling b.age = 18.
But return turns b into another memory address, and stores {name: "Tong Ouba", age: 25}, resulting in the value of a finally returned as
{name: "Tong Ouba" , age: 25}
Next, let us welcome Mr. Yi to come on stage with warm applause!
I will ask you some questions. You can drink water at any time.
Do you know how to detect data types in JavaScript?
- typeof
- instanceof
- constructor
- Object.prototype.toString.call()
How does typeof work?
1.typeof
typeof 'a' // 'string'
typeof 1 // 'number'
typeof true // 'boolean'
typeof undefined // 'undefined'
typeof Symbol('a') // 'symbol'
typeof 1n // 'bigint'
typeof null // 'object'
typeof function() {} // 'function'
typeof [] // 'object'
typeof {} // 'object'
typeof /a/ // 'object'
typeof new Date() // 'object'
typeof new Error() // 'object'
typeof new Map() // 'object'
typeof new Set() // 'object'
two conclusions:
- typeof can determine primitive types other than null.
- typeof can only judge the Function in the object type. Others cannot be judged, and they are all objects.
Why is the value of typeof null object?
Typeof returns object when it detects null. This is a bug in the original JavaScript language, and it has been retained for compatibility with the old code.
If you want to know more, please click the link below.
Tips
I have to mention NaN here, after all, we all know that it plays more.
typeof NaN // number
F**k NaN!
Can instanceof tell which types you know?
2.instanceof
Check whether the prototype property of the constructor appears in the prototype chain of an instance object.
That is to say, using a instanceof B
determine whether a is an instance of B, that is, whether there is a constructor of B on the prototype chain of a.
console.log(1 instanceof Number) // false
console.log(new Number(1) instanceof Number) // true
const arr = []
console.log(arr instanceof Array) // true
console.log(arr instanceof Object) // true
const Fn = function() {
this.name = '构造函数'
}
Fn.prototype = Object.create(Array.prototype)
let a = new Fn()
console.log(a instanceof Array) // true
two conclusions:
instanceof
can accurately determine the object (reference) type, but cannot accurately detect the original type.- Since we can modify the orientation of the prototype at will, the detection result is inaccurate, so this method is not safe.
If I want to use instanceof to detect primitive types, can you meet my needs?
Good, satisfied.
Although instanceof
cannot detect the original type, there is a method that can be used to detect the original type.
Symbol.hasInstance
allows us to customize the behavior of instanceof
class PrimitiveNumber {
static [Symbol.hasInstance] = x => typeof x === 'number';
}
123 instanceof PrimitiveNumber; // true
class PrimitiveString {
static [Symbol.hasInstance] = x => typeof x === 'string';
}
'abc' instanceof PrimitiveString; // true
class PrimitiveBoolean {
static [Symbol.hasInstance] = x => typeof x === 'boolean';
}
false instanceof PrimitiveBoolean; // true
class PrimitiveSymbol {
static [Symbol.hasInstance] = x => typeof x === 'symbol';
}
Symbol.iterator instanceof PrimitiveSymbol; // true
class PrimitiveNull {
static [Symbol.hasInstance] = x => x === null;
}
null instanceof PrimitiveNull; // true
class PrimitiveUndefined {
static [Symbol.hasInstance] = x => x === undefined;
}
undefined instanceof PrimitiveUndefined; // true
The source of the code is linked below.
Now that you know so much about instanceof, can you write one on the spot?
Handwritten instanceof
const myInstanceof = function(left, right) {
if (typeof left !== 'object' || left === null) return false
let proto = Reflect.getPrototypeOf(left)
while (true) {
if (proto === null) return false
if (proto === right.prototype) return true
proto = Reflect.getPrototypeOf(proto)
}
}
const arr = []
console.log(myInstanceof(arr, Array)) // true
console.log(myInstanceof(arr, Object)) // true
console.log(myInstanceof(arr, RegExp)) // false
To understand the working principle of instanceof, you must understand the prototype chain. Students who don't have a deep understanding of the JavaScript prototype chain can click the link below to learn.
- JavaScript in-depth from prototype to prototype chain
- How to answer the JavaScript prototype chain question in the interview
How about constructor, is it easy to use?
3.constructor
For numerical literals, using the constructor directly will cause an error. This error comes from the literal parsing process of floating-point numbers, not the processing of "." as the access operator.
In JS, the decimal places of floating-point numbers can be empty, so 1. and 1.0 will parse into the same floating-point number.
// 所以需要加上一个小括号,小括号运算符能够把数值转换为对象
(1).constructor // ƒ Number() { [native code] }
// 或者
1..constructor // ƒ Number() { [native code] }
const a = '前端食堂'
console.log(a.constructor) // ƒ String() { [native code] }
console.log(a.constructor === String) // true
const b = 5
console.log(b.constructor) // ƒ Number() { [native code] }
console.log(b.constructor === Number) // true
const c = true
console.log(c.constructor) // ƒ Boolean() { [native code] }
console.log(c.constructor === Boolean) // true
const d = []
console.log(d.constructor) // ƒ Array() { [native code] }
console.log(d.constructor === Array) // true
const e = {}
console.log(e.constructor) // ƒ Object() { [native code] }
console.log(e.constructor === Object) // true
const f = () => 1
console.log(f.constructor) // ƒ Function() { [native code] }
console.log(f.constructor === Function) // true
const g = Symbol('1')
console.log(g.constructor) // ƒ Symbol() { [native code] }
console.log(g.constructor === Symbol) // true
const h = new Date()
console.log(h.constructor) // ƒ Date() { [native code] }
console.log(h.constructor === Date) // true
const i = 11n
console.log(i.constructor) // ƒ BigInt() { [native code] }
console.log(i.constructor === BigInt) // true
const j = /a/
console.log(j.constructor) // ƒ RegExp() { [native code] }
console.log(j.constructor === RegExp) // true
String.prototype.constructor = 'aaa'
console.log(a.constructor === String) // false
const k = null
console.log(k.constructor) // Cannot read property 'constructor' of null
const l = undefined
console.log(l.constructor) // Cannot read property 'constructor' of undefined
Two conclusions:
- Except for null and undefined,
constructor
can correctly detect primitive types and object (reference) types. - Since we can modify
constructor
at will, the detection result is inaccurate, so this method is not safe.
Object.prototype.toString is left, is it invulnerable?
4.Object.prototype.toString
The toString() method returns a string representing the object, we can change its this point, point this to the value to be tested, and then return the information of the current test value.
Object.prototype.toString({}) // '[object Object]'
Object.prototype.toString.call({}) // '[object Object]'
Object.prototype.toString.call('a') // '[object String]'
Object.prototype.toString.call(1) // '[object Number]'
Object.prototype.toString.call(true) // '[object Boolean]'
Object.prototype.toString.call(null) // '[object Null]'
Object.prototype.toString.call(undefined) // '[object Undefined]'
Object.prototype.toString.call(Symbol('a')) // '[object Symbol]'
Object.prototype.toString.call(11n) // '[object BigInt]'
Object.prototype.toString.call(/a/) // '[object RegExp]'
Object.prototype.toString.call(new Date()) // '[object Date]'
Object.prototype.toString.call([0, 1, 2]) // '[object Array]'
Object.prototype.toString.call(function() {}) // '[object Function]'
Object.prototype.toString.call(new Error()) // '[object Error]'
Object.prototype.toString.call(new Set()) // '[object Set]'
Object.prototype.toString.call(new Map()) // '[object Map]'
Can you encapsulate a general method for detecting data types?
General method of package inspection data type
Pay attention to case when encapsulating methods.
There are many kinds of schemes, here are two ideas.
const getType = function(obj) {
let type = typeof obj
if (type !== 'object') {
return type
}
return Object.prototype.toString.call(obj).replace(/^\[object (\S+)\]$/, '$1').toLowerCase()
}
getType({}) // object
getType('a') // string
getType(1) // number
getType(true) // boolean
getType(null) // null
getType(undefined) // undefined
getType(Symbol('a')) // symbol
getType(11n) // bigint
getType(/a/) // regexp
getType(new Date()) // date
getType([0, 1, 2]) // array
getType(function() {}) // function
getType(new Error()) // error
getType(new Map()) // map
getType(new Set()) // set
Of course, this can also be achieved by changing the posture.
Object.prototype.toString.call('1').slice(8, -1).toLowerCase()
// 'string'
Talking about this, it is basically the answer with full marks.
If you think something is missing, please add it in the comment area.
The last question from Mr. Yi is left for everyone:
Do you like JavaScript?
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。