8
头图

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:

  1. typeof can determine primitive types other than null.
  2. 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:

  1. instanceof can accurately determine the object (reference) type, but cannot accurately detect the original type.
  2. 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.

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:

  1. Except for null and undefined, constructor can correctly detect primitive types and object (reference) types.
  2. 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?

童欧巴
2.6k 声望4.2k 粉丝