JS
數(shù)據(jù)類(lèi)型
JavaScript中什么是基本數(shù)據(jù)類(lèi)型什么是引用數(shù)據(jù)類(lèi)型?以及各個(gè)數(shù)據(jù)類(lèi)型是如何存儲(chǔ)的?
基本數(shù)據(jù)類(lèi)型有
- Number
- String
- Boolean
- Null
- Undefined
- Symbol(ES6新增數(shù)據(jù)類(lèi)型)
- bigInt
引用數(shù)據(jù)類(lèi)型統(tǒng)稱(chēng)為Object類(lèi)型,細(xì)分的話(huà)有 - Object
- Array
- Date
- Function
- RegExp
基本數(shù)據(jù)類(lèi)型的數(shù)據(jù)直接存儲(chǔ)在棧中;而引用數(shù)據(jù)類(lèi)型的數(shù)據(jù)存儲(chǔ)在堆中,每個(gè)對(duì)象在堆中有一個(gè)引用地址。引用類(lèi)型在棧中會(huì)保存他的引用地址,以便快速查找到堆內(nèi)存中的對(duì)象。
提示:棧內(nèi)存是自動(dòng)分配內(nèi)存的。而堆內(nèi)存是動(dòng)態(tài)分配內(nèi)存的,不會(huì)自動(dòng)釋放。所以每次使用完對(duì)象的時(shí)候都要把它設(shè)置為null,從而減少無(wú)用內(nèi)存的消耗
類(lèi)型轉(zhuǎn)換
在JS中為什么0.2+0.1>0.3?
因?yàn)樵贘S中,浮點(diǎn)數(shù)是使用64位固定長(zhǎng)度來(lái)表示的,其中的1位表示符號(hào)位,11位用來(lái)表示指數(shù)位,剩下的52位尾數(shù)位,由于只有52位表示尾數(shù)位。
而0.1轉(zhuǎn)為二進(jìn)制是一個(gè)無(wú)限循環(huán)數(shù)0.0001100110011001100......(1100循環(huán))
由于只能存儲(chǔ)52位尾數(shù)位,所以會(huì)出現(xiàn)精度缺失,把它存到內(nèi)存中再取出來(lái)轉(zhuǎn)換成十進(jìn)制就不是原來(lái)的0.1了,就變成了0.100000000000000005551115123126,而為什么02+0.1是因?yàn)?/p>
// 0.1 和 0.2 都轉(zhuǎn)化成二進(jìn)制后再進(jìn)行運(yùn)算
0.00011001100110011001100110011001100110011001100110011010 +
0.0011001100110011001100110011001100110011001100110011010 =
0.0100110011001100110011001100110011001100110011001100111
// 轉(zhuǎn)成十進(jìn)制正好是 0.30000000000000004
那為什么0.2+0.3=0.5呢?
// 0.2 和 0.3 都轉(zhuǎn)化為二進(jìn)制后再進(jìn)行計(jì)算
0.001100110011001100110011001100110011001100110011001101 +
0.0100110011001100110011001100110011001100110011001101 =
0.10000000000000000000000000000000000000000000000000001 //尾數(shù)為大于52位
// 而實(shí)際取值只取52位尾數(shù)位,就變成了
0.1000000000000000000000000000000000000000000000000000 //0.5
答:0.2 和0.3分別轉(zhuǎn)換為二進(jìn)制進(jìn)行計(jì)算:在內(nèi)存中,它們的尾數(shù)位都是等于52位的,而他們相加必定大于52位,而他們相加又恰巧前52位尾數(shù)都是0,截取后恰好是0.1000000000000000000000000000000000000000000000000000也就是0.5
判斷數(shù)據(jù)類(lèi)型有幾種方法
typeof
缺點(diǎn):typeof null的值為Object,無(wú)法分辨是null還是Object
instanceof
缺點(diǎn):只能判斷對(duì)象是否存在于目標(biāo)對(duì)象的原型鏈上
constructor
Object.prototype.toString.call()
一種最好的基本類(lèi)型檢測(cè)方式 Object.prototype.toString.call() ;它可以區(qū)分 null 、 string 、
boolean 、 number 、 undefined 、 array 、 function 、 object 、 date 、 math 數(shù)據(jù)類(lèi)型。
缺點(diǎn):不能細(xì)分為誰(shuí)誰(shuí)的實(shí)例
// -----------------------------------------typeof
typeof undefined // 'undefined'
typeof '10' // 'String'
typeof 10 // 'Number'
typeof false // 'Boolean'
typeof Symbol() // 'Symbol'
typeof Function // ‘function'
typeof null // ‘Object’
typeof [] // 'Object'
typeof {} // 'Object'
// -----------------------------------------instanceof
function Foo() { }
var f1 = new Foo();
var d = new Number(1)
console.log(f1 instanceof Foo);// true
console.log(d instanceof Number); //true
console.log(123 instanceof Number); //false -->不能判斷字面量的基本數(shù)據(jù)類(lèi)型
// -----------------------------------------constructor
var d = new Number(1)
var e = 1
function fn() {
console.log("ming");
}
var date = new Date();
var arr = [1, 2, 3];
var reg = /[hbc]at/gi;
console.log(e.constructor);//? Number() { [native code] }
console.log(e.constructor.name);//Number
console.log(fn.constructor.name) // Function
console.log(date.constructor.name)// Date
console.log(arr.constructor.name) // Array
console.log(reg.constructor.name) // RegExp
//-----------------------------------------Object.prototype.toString.call()
console.log(Object.prototype.toString.call(undefined)); // "[object Undefined]"
console.log(Object.prototype.toString.call(null)); // "[object Null]"
console.log(Object.prototype.toString.call(123)); // "[object Number]"
console.log(Object.prototype.toString.call("abc")); // "[object String]"
console.log(Object.prototype.toString.call(true)); // "[object Boolean]"
function fn() {
console.log("ming");
}
var date = new Date();
var arr = [1, 2, 3];
var reg = /[hbc]at/gi;
console.log(Object.prototype.toString.call(fn));// "[object Function]"
console.log(Object.prototype.toString.call(date));// "[object Date]"
console.log(Object.prototype.toString.call(arr)); // "[object Array]"
console.log(Object.prototype.toString.call(reg));// "[object RegExp]"
instanceof原理
instanceof原理實(shí)際上就是查找目標(biāo)對(duì)象的原型鏈
function myInstance(L, R) {//L代表instanceof左邊,R代表右邊
var RP = R.prototype
var LP = L.__proto__
while (true) {
if(LP == null) {
return false
}
if(LP == RP) {
return true
}
LP = LP.__proto__
}
}
console.log(myInstance({},Object));
為什么typeof null是Object
因?yàn)樵贘avaScript中,不同的對(duì)象都是使用二進(jìn)制存儲(chǔ)的,如果二進(jìn)制前三位都是0的話(huà),系統(tǒng)會(huì)判斷為是Object類(lèi)型,而null的二進(jìn)制全是0,自然也就判斷為Object
==和===有什么區(qū)別
答:
===是嚴(yán)格意義上的相等,會(huì)比較兩邊的數(shù)據(jù)類(lèi)型和值大小
數(shù)據(jù)類(lèi)型不同返回false
數(shù)據(jù)類(lèi)型相同,但值大小不同,返回false
==是非嚴(yán)格意義上的相等,
兩邊類(lèi)型相同,比較大小
兩邊類(lèi)型不同,根據(jù)下方表格,再進(jìn)一步進(jìn)行比較。
Null == Undefined ->true
String == Number ->先將String轉(zhuǎn)為Number,在比較大小
Boolean == Number ->現(xiàn)將Boolean轉(zhuǎn)為Number,在進(jìn)行比較
Object == String,Number,Symbol -> Object 轉(zhuǎn)化為原始類(lèi)型
手寫(xiě)call、apply、bind
答:
call和apply實(shí)現(xiàn)思路主要是:
判斷是否是函數(shù)調(diào)用,若非函數(shù)調(diào)用拋異常
通過(guò)新對(duì)象(context)來(lái)調(diào)用函數(shù)
給context創(chuàng)建一個(gè)fn設(shè)置為需要調(diào)用的函數(shù)
結(jié)束調(diào)用完之后刪除fn
bind實(shí)現(xiàn)思路
判斷是否是函數(shù)調(diào)用,若非函數(shù)調(diào)用拋異常
返回函數(shù)
判斷函數(shù)的調(diào)用方式,是否是被new出來(lái)的
new出來(lái)的話(huà)返回空對(duì)象,但是實(shí)例的proto指向_this的prototype
完成函數(shù)柯里化
Array.prototype.slice.call()
call:
Function.prototype.myCall = function (context) {
// 先判斷調(diào)用myCall是不是一個(gè)函數(shù)
// 這里的this就是調(diào)用myCall的
if (typeof this !== 'function') {
throw new TypeError("Not a Function")
}
// 不傳參數(shù)默認(rèn)為window
context = context || window
// 保存this
context.fn = this
// 保存參數(shù)
let args = Array.from(arguments).slice(1) //Array.from 把偽數(shù)組對(duì)象轉(zhuǎn)為數(shù)組
// 調(diào)用函數(shù)
let result = context.fn(...args)
delete context.fn
return result
}
apply
Function.prototype.myApply = function (context) {
// 判斷this是不是函數(shù)
if (typeof this !== "function") {
throw new TypeError("Not a Function")
}
let result
// 默認(rèn)是window
context = context || window
// 保存this
context.fn = this
// 是否傳參
if (arguments[1]) {
result = context.fn(...arguments[1])
} else {
result = context.fn()
}
delete context.fn
return result
}
bind
Function.prototype.myBind = function(context){
// 判斷是否是一個(gè)函數(shù)
if(typeof this !== "function") {
throw new TypeError("Not a Function")
}
// 保存調(diào)用bind的函數(shù)
const _this = this
// 保存參數(shù)
const args = Array.prototype.slice.call(arguments,1)
// 返回一個(gè)函數(shù)
return function F () {
// 判斷是不是new出來(lái)的
if(this instanceof F) {
// 如果是new出來(lái)的
// 返回一個(gè)空對(duì)象,且使創(chuàng)建出來(lái)的實(shí)例的__proto__指向_this的prototype,且完成函數(shù)柯里化
return new _this(...args,...arguments)
}else{
// 如果不是new出來(lái)的改變this指向,且完成函數(shù)柯里化
return _this.apply(context,args.concat(...arguments))
}
}
}