美味值:🌟🌟🌟🌟🌟javascript
口味:芥末蝦仁球前端
爲了和易老師對線,咱們先來簡單複習下。java
JavaScript 的數據類型包括原始類型和對象類型:git
咱們習慣把對象稱爲引用類型,固然還有不少特殊的引用類型,好比 Function、Array、RegExp、Math、Date、Error、Set、Map、各類定型數組 TypedArray 等。github
原始類型值保存在棧中,對象類型值保存在堆中,在棧中保留了對象的引用地址,當 JavaScript 訪問數據的時候,經過棧中的引用訪問。web
在 JavaScript 中,原始類型的賦值會完整複製變量值,而對象(引用)類型的賦值是複製引用地址。面試
let a = {
name: '前端食堂',
age: 2
}
let b = a
console.log(a.name)
b.name = '童歐巴'
console.log(a.name)
console.log(b.name)
// 前端食堂
// 童歐巴
// 童歐巴
複製代碼
第一題 So Easy,閉着眼睛也能答對。數組
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}
複製代碼
這道題可能有些同窗會答錯,咱們來一塊兒分析一下:安全
expand 函數傳進來的參數 b,其實傳遞的是對象在堆中的內存地址值,經過調用 b.age = 18 能夠改變 a 對象的 age 屬性。markdown
可是 return 又把 b 變成了另外一個內存地址,將 {name: "童歐巴", age: 25}
存入,致使最後返回 a 的值就變成了 {name: "童歐巴", age: 25}
我會問你一些問題,你隨時能夠喝水。
JavaScript 中檢測數據類型的方法有哪些你知道嗎?
那 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'
複製代碼
兩條結論:
爲何 typeof null 的值是 object?
typeof 檢測 null 時返回 object,是最初 JavaScript 語言的一個 Bug,爲了兼容老代碼一直保留至今。
若是想了解更多,請戳下面連接。
這裏不得不提一下 NaN,畢竟咱們都知道它戲比較多。
typeof NaN // number
複製代碼
F**k NaN!
instanceof 能判斷出哪些類型你知道嗎?
檢測構造函數的 prototype 屬性是否出如今某個實例對象的原型鏈上。
也就是使用 a instanceof B
判斷的是:a 是否爲 B 的實例,即 a 的原型鏈上是否存在 B 的構造函數。
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
複製代碼
兩條結論:
instanceof
能夠準確判斷對象(引用)類型,可是不能準確檢測原始類型。若是我就想用 instanceof 檢測原始類型,你能知足個人需求嗎?
好,知足。
雖然 instanceof
不能檢測原始類型,可是有一種方法可讓其用於檢測原始類型。
Symbol.hasInstance
容許咱們自定義 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
複製代碼
代碼來源下面連接。
既然你對 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
複製代碼
要理解 instanceof 的工做原理,就必須理解原型鏈,對 JavaScript 原型鏈掌握的不夠深入的同窗能夠戳下面連接學習。
constructor 怎麼樣,好用嗎?
對於數值直接量,直接使用 constructor 是會報錯的,這個錯誤來自於浮點數的字面量解析過程,而不是 "." 做爲存取運算符的處理過程。
在 JS 中,浮點數的小數位是能夠爲空的,所以 1. 和 1.0 會解析成相同的浮點數。
// 因此須要加上一個小括號,小括號運算符可以把數值轉換爲對象
(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
複製代碼
兩條結論:
constructor
能夠正確檢測出原始類型和對象(引用)類型。constructor
致使檢測結果不許確,因此這種方法是不安全的。還剩下 Object.prototype.toString 了,它就無懈可擊了嗎?
toString() 方法返回一個表示該對象的字符串,咱們能夠改變它的 this 指向,將 this 指向要檢測的值,便可返回當前檢測值的信息。
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]'
複製代碼
你能封裝一個檢測數據類型的通用方法嗎?
封裝方法的時候注意大小寫。
方案有不少種,這裏簡單提供兩個思路。
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
複製代碼
固然,換個姿式,這樣也能夠實現。
Object.prototype.toString.call('1').slice(8, -1).toLowerCase()
// 'string'
複製代碼
聊到這,基本上就是滿分答案了。
若是你以爲哪裏有遺漏,歡迎在評論區補充。
最後一個易老師的問題留給你們:
你,喜歡 JavaScript 嗎?
1.若是你以爲食堂酒菜還合胃口,就點個贊支持下吧,你的贊是我最大的動力。
2.關注公衆號前端食堂,吃好每一頓飯!
3.點贊、評論、轉發 === 催更!