美味值:🌟🌟🌟🌟🌟javascript
口味:芥末蝦仁球前端
爲了和易老師對線,咱們先來簡單複習下。java
JavaScript 的數據類型包括原始類型和對象類型:git
咱們習慣把對象稱爲引用類型,固然還有不少特殊的引用類型,好比 Function、Array、RegExp、Math、Date、Error、Set、Map、各類定型數組 TypedArray 等。github
原始類型值保存在棧中,對象類型值保存在堆中,在棧中保留了對象的引用地址,當 JavaScript 訪問數據的時候,經過棧中的引用訪問。面試
在 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 屬性。學習
可是 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 嗎?