文同步本人掘金平臺的文章:https://juejin.cn/post/6844903858112577549
最近自己回歸基礎看了下javascript的相關知識點,想著看都看了,寫出來記錄下足跡也是一件好事,遂記錄~
在javascript中有兩種類型,一種是基本數據類型,一種是引用類型。
基本數據類型,也稱為簡單數據類型,在ES5中有以下五種:Undefined、Null、Boolean、Number和String,在ES6中新增了一種簡單的數據類型Symbol。
Undefined類型只有一個值undefined。在進行相關變量定義的時候,未賦值的情況下,默認是賦值為undefined了。但是也是有些特殊的情況下會報錯的。情況我大致羅列下:
# 情況1??:變量聲明了,但是沒有賦值
var message;
console.log(message); // undefined
# 情況2??:變量聲明并賦值了,但是在console運行之后
console.log(message); // undefined
var message='find a frontend job in Canton!';
# 情況3??:變量沒聲明,報引用報錯
// var message;
console.log(message); // Uncaught ReferenceError: message is not defined
# 情況4??:不通過var聲明,直接寫變量,報引用錯誤
message; // 不等價 var message;
console.log(message); // Uncaught ReferenceError: message is not defined
# 情況5??:不通過var聲明,直接寫變量賦值
message='find a frontend job in Canton!'; // 默認在message前添加了var
console.log(message); // find a frontend job in Canton!
# 情況6??:不通過var聲明,直接寫賦值,但是在console運行之后,報引用錯誤
console.log(message);
message='find a frontend job in Canton!'; // 相當于沒message變量
復制代碼
上面羅列的是ES5中通過var聲明的情況。也許你會對情況2??產生疑惑:我都給message賦值了啊,但是打印出undefined,這就有點尷尬了?
因為在js中執行上下文分為兩個階段,第一個階段是創建階段,第二個階段才是執行階段。
上面情況2??的執行情況如下:
1. 創建階段:
executionContextObj={
scopeChain: { ... },
variableObject: {
message: undefined
},
this: { ... }
}
復制代碼
2. 執行階段:
executionContextObj={
scopeChain: { ... },
variableObject: {
message: 'find a frontend job in Canton!'
},
this: { ... }
}
復制代碼
詳細的解析可以看下我之前翻譯的一篇文章JS的執行上下文和環境棧是什么?。
上面講到的是var,我們引入ES6的let 和 const來演示下:
# 情況7??:let聲明變量賦值
let message;
console.log(message); // undefined
# 情況8??:let聲明變量但是不賦值,在console運行之后
console.log(message); // Uncaught ReferenceError: Cannot access 'message' before initialization
let message='find a frontend job in Canton!';
# 情況9??:const聲明變量但是不賦值,報語法錯誤
const message;
console.log(message); // Uncaught SyntaxError: Missing initializer in const declaration
復制代碼
let和const改變了var命令會發生變量提升的現象,即變量可以在聲明之前使用,值為undefined。它們改變了這種奇怪的現象,聲明的變量一定要在聲明之后使用,否則報錯。
當然還有其他聲明變量的方法,比如function命令等,這里不一一列舉,只是探討下undefined的值而已~
Null類型的值是null。從邏輯角度來看,null值表示一個空對象指針。
如果定義的變量準備在將來用來保存對象,那么最好就是將變量初始化為null,而不是其他的數據值。這樣,只要直接檢查null值就可以知道相應的變量是否已經保存了一個對象的引用。如下面的例子:
if(car !=null) {
// 對car對象執行某些操作
}
復制代碼
undefined值是派生自null值的。雖然兩者在==比較時候是相等的,如下:
console.log(null==undefined); // true
復制代碼
當變量不確定類型的時候,可以不用為變量賦值,也就是默認賦值undefined了。但是如果你知道你的變量要保存對象但是還沒有真正保存對象的時候就要賦值null了。
Boolean類型在日常生活中使用頻繁了,其值是true和false,對應我們口頭的是和否。
將布爾值的true和false轉換為數值的話,可以用非0和0數字表示。
console.log( 1==true); // true
console.log( 0==false); // true
復制代碼
如果是恒等的比較方式===,那數字表示法是要涼涼的~
Number類型有二進制表示法,八進制表示法,十六進制表示法和十進制表示法。這里只討論十進制表示法,因為在平常的開發中,用到十進制的情況居多
這個類型用來表示整數值和浮點數值(即帶小數點的值)。
整數值的基本操作很是簡單,而且沒啥bug好說,除非不在Number.MIN_VALUE和Number.MAX_VALUE范圍內。帶小數點的還是要留意下的,比如:
let a=13.04;
let b=2.5;
console.log(a + b); // 15.54
console.log(a * b); // 32.599999999999994
console.log(a - b); // 10.54
復制代碼
咦咦,真是讓人尷尬,怎么上面代碼中兩個浮點數相乘會出現那么多位的數字啊,不是等于32.6嗎?
所以在進行浮點數的運算的時候還是得慎重點,先轉換成整數計算,之后再切換回去浮點數,比如上面的a * b可以考慮寫成(a * 100 * (b * 10))/1000。
當你要判斷一個值是否是數值,可以使用isNaN來表示,其返回一個布爾值,如下:
console.log(isNaN(NaN)); // true
console.log(isNaN(10)); // false
console.log(isNaN('10'); // false , '10'會被轉化為10
console.log('blue'); // true , 不能轉化為數值
console.log(true); // false, 可被轉化為數值1
復制代碼
還有將非數值轉化為數值的三個方法:Number()、parseInt()和parseFloat()。見名思義:
**Number()是將傳入的內容轉換為數字(整數)或NaN。但是在轉換字符串的時候比較復雜,一般用parseInt()**居多。**parseFloat()**就是轉化成浮點數的方法啦。
String類型也就是字符串類型啦。
字符串類型包含一些特殊的字符字面量,也叫轉義序列,用來表示非打印字符串。比如換行符\n啦。
在實際的開發中,我們需要將數字類型或對象類型轉換成字符串類型,那么我們可以直接使用toString()方法進行操作啦。好吧,這api的東西大家都會用,就不說了
Symbol類型是ES6引入的新類型,為了防止對象中屬性名沖突的問題。
Symbol值通過Symbol函數生成。這就是說,對象的屬性名現在可以有兩種類型,一種是原來就有的字符串,另一種就是新增的Symbol類型。凡是屬性名屬于Symbol類型,就都是獨一無二的,可以保證不會與其他屬性名產生沖突。
具體的看下阮一峰的es6入門中Symbol部分。
上面說到的是6種基本的數據類型,還有一種是引用類型。
引用類型:當復制保存對象的某個變量時,操作的是對象的引用,但是在為對象添加屬性時,操作的是實際的對象。引用類型值指那些可能有多個值構成的對象。
引用類型有這幾種:Object、Array、RegExp、Date、Function、特殊的基本包裝類型(String、Number、Boolean)以及單體內置對象(Global、Math)。
基本包裝類型這個有點好玩,咦?上面的基本數據類型都有String、Number和Boolean啦,怎么這里還有這些。是的,上面的基本類型是通過基本包裝類型來創建的。如下:
var s1='find a frontend job in Canton';
var s2=s1.substring(2);
復制代碼
上面的代碼實際進行了下面的步驟:
(1)創建String類型的一個實例;
(2)在實例中調用指定的方法;
(3)銷毀這個實例。
上面的三個步驟轉化為代碼如下:
var s1=new String('find a frontend job in Canton');
var s2=s1.substring(2);
s1=null;
復制代碼
(正規)的引用類型和基本包裝類型的主要區別就是對象的生存期。使用new操作符創建的引用類型的實例,在執行流離開當前作用域之前都一直保存在內存中。而自動創建的基本包裝類型的對象,則只存在于下一行代碼的執行瞬間,然后立即被銷毀。這意味著我們不能在運行時為基本類型值添加屬性和方法。來看下下面的例子:
var s1='find a frontend job in Canton';
s1.name='jia ming';
console.log(s1.name); // undefined
復制代碼
只能通過基本包裝類的原型來添加了,比如改寫toString方法:
var s1='find a frontend job in Canton';
String.prototype.toString=function() {
console.log('my name is jia ming');
}
console.log(s1.toString()); // my name is jia ming
復制代碼
嗯~苦口婆心介紹了javascript的數據類型,那么下面才是重頭戲。我們在實際的開發中,如何識別不同的數據類型呢?
數據類型有上面的7種類型,其中基本類型是Undefined、Null、Boolean、Number、String和Symbol,還有一種引用類型。引用類型又包含比較多種的對象,比如Object、Array等。
我們首先想到的是通過typeof來判斷,直接上代碼來試下吧:
let symbol=Symbol('jia ming');
let str='find a frontend job in Canton!';
let flag=true;
let height=99;
let job;
let obj=null;
console.log(typeof symbol); // symbol
console.log(typeof str); // string
console.log(typeof flag); // boolean
console.log(typeof height); // number
console.log(typeof job); // undefined
console.log(typeof obj); // object
復制代碼
嗯~很ok啦,對基本的數據類型都能判斷到啦,這個null得到的結果是object,你可以當成特殊情況來處理啦 -- 無中生有,一生萬物嘛。
我們再來看下引用類型打印出來的是什么東東
let person={
name: 'jia ming',
info: 'find a frontend job in Canton!',
};
let arr=['jia ming', 'find a frontend job in Canton!'];
let reg=new RegExp('jia ming', 'g');
let date=new Date();
let fn=()=> {
return 'find a frontend job in Canton!';
}
let math=Math.min(2, 4, 8);
console.log(typeof person); // object
console.log(typeof arr); // object
console.log(typeof reg); // object
console.log(typeof date); // object
console.log(typeof fn); // function
console.log(typeof math); // number
復制代碼
咦咦~著實讓人尷尬啊,這個為啥那么多object啊,我的小心臟。我們只是簡單通過typeof校驗比較尷尬啊,我們換個思路,我們來結合call改變下上下文對象,改寫一個方法進行判斷,如下:
let person={
name: 'jia ming',
info: 'find a frontend job in Canton!',
};
let arr=['jia ming', 'find a frontend job in Canton!'];
let reg=new RegExp('jia ming', 'g');
let date=new Date();
function handleType(obj, type) {
if(typeof obj==='object') {
return Object.prototype.toString.call(obj)===`[object ${type}]`;
}
return false;
}
console.log(handleType(person, 'Object')); // true
console.log(handleType(arr, 'Array')); // true
console.log(handleType(reg, 'RegExp')); // true
console.log(handleType(date, 'Date')); // true
復制代碼
美滋滋,可以實現區別判斷的哈。可是上面的基本類型中null也是object啊,然后是Math類型的typeof也是number啊,這個你可以自己做下處理啦。這里就不考慮了~
npm install type-yes
目地址:github.com/liutaigang/…
首先通過一個例子來認識下 Ty:
一個方法的參數類型判斷的例子,如:
function func(value) {
if( value 為 string 或 number 或 為空時 ) {
... do something
}
}
判斷方式:
// 方式一:常規版
typeof value==='string' || typeof value==='number' || value==null
// 方式二:Lodash 版
_.isString(value) || _.isNumber(value) || _.isNil(value)
// 方式三:Ty 版
Ty(value).str.num.nil.or
Ty 版的判斷是最簡潔的!!!,但是也會讓人有些疑惑——上述表達式:Ty(value).str.num.nil.or,它如何實現判斷的?下面分析下:
上述表達式可以簡單理解為:
// 當 value=123
[[value, 'str'], [value, 'num'], [value, 'nil']]==(判斷類型)==> [false, true, false]==(或運算)==> true
到了這里,你大概已經了解 Ty 的邏輯符 or 的使用,除了 or , Ty 還有 is,not,and,nor,nand
邏輯”是“判斷
// 常規
typeof value==='number'
// Ty
Ty(value).num.is
// Ty error, 當進行 is 判斷時,如果判斷參數(或判斷標識符)輸入多個值時,會報錯
Ty(value01, value02).num.is // error
Ty(value).num.str.is // error
邏輯”否“判斷, is 的取反
// 常規
typeof value !='number'
// Ty
Ty(value).num.not
// Ty error, 當進行 not 判斷時,如果判斷參數(或判斷標識符)輸入多個值時,會報錯。與 is 判斷相同
邏輯”或“判斷
// 常規
typeof value==='string' || typeof value==='number'
// Ty
Ty(value).str.num.or
// 等價于:
Ty(value, value).str.num.or // 參數會自動補全,所以這樣寫就“沒必要”了
邏輯”或非“判斷, or 的取反
// 常規
!(typeof value==='string' || typeof value==='number')
// Ty
Ty(value).str.num.nor
邏輯“與”判斷
示例一:
// 常規
typeof value01==='string' && typeof value02==='number'
// Ty
Ty(value01, value02).str.num.and
示例二:
// 常規
typeof value01==='string' && typeof value02==='string'
// Ty
Ty(value01, value02).str.and
// 等價于:
Ty(value01, value02).str.str.and // 標識符也會自動補全,所以這樣寫就“沒必要”了
邏輯“與非”判斷,and 的取反
// 常規
!(typeof value01==='string' && typeof value02==='number')
// Ty
Ty(value01, value02).arr.num.nand
上述的判斷中,除了所有的邏輯操作符的使用方法,我還認識了 num、str 、nil 等類型標識符。在 Ty 中,類型標識符共有 60+,其中包括:簡寫類型標識符、特殊類型標識符和常規類型標識符,下面我們將一一介紹:
簡寫標識符 | 對應的常規標識類 | 實際類型 |
obj | object | Object (這里的 object, 不包含 array 和 null ) |
arr | array | Array |
str | string | String |
num | number | Number |
bool | boolean | Boolean |
undef | undefined | undefined |
func | function | Function |
標識符 | 實際類型 |
nil | null 或 undefined |
empty | [] 或 {} |
emptyobject | {} —— 沒有任何屬性的空對象 |
emptyarray | [] —— 沒有任何元素的空數組 |
NaN | NaN |
infinity | Infinity 無窮大 |
primitive | 原始類型: null, undefined, boolean, number, bigint, string, symbol |
示例:
const isPrimitive=Ty(value).primitive.is // value=Symbol()
const isEmpty=Ty(value).empty.is // value=[]
標識符 | 實際類型 |
null | null (不包含 undefined) |
undefined | undefined |
boolean | Boolean |
number | Number |
string | String |
bigint | BigInt |
symbol | Symbol |
object | Object (這里的 object, 不包含 array 和 null ) |
array | Array |
function | Function |
promise | Promise |
date | Date |
regexp | RegExp |
map | Map |
set | Set |
......更多的請看附錄 |
示例:
const isIterator=Ty(value).array.map.set.or
cosnt isPrimitive=Ty(value).null.undefined.boolean.number.string.bigint.symbol.or
如果已有的類型標識符不滿足時, Ty 支持擴展,只要提供一個 TypeMatcher , 即類型匹配器:
type TypeMatcher<T extends string>=(parameter: any, typeFlag: T)=> boolean;
示例(ts):
import { Ty, TypeMatcher, TypeFlag, buildinTypeMatcher } from 'type-yes';
type MyType='element' | 'finite' | TypeFlag; // TypeFlag 是 Ty 的所有的類型標識符的一個聯合類型
const typeMather: TypeMatcher<MyType>=(parameter, typeFlag)=> { // parameter —— 判斷參數, typeFlag —— 類型標識符
switch (typeFlag) {
case 'element':
return parameter instanceof Element;
case 'finite':
return Number.isFinite(parameter);
default:
return buildinTypeMatcher(parameter, typeFlag); // buildinTypeMatcher —— Ty 內置的類型匹配器
}
};
const tty=new Ty(typeMather);
使用效果(element 和 finite 會出現在拼寫提示中):
Proxy 類型是難以判斷的——Proxy 代理的對象是什么類型,proxy 實例就判定為相應的類型,如:
const arr=['a', 'b', 'c'];
const arrProxy=new Proxy(arr, {});
typeof arrProxy; // array
Object.prototype.toString.call(arrProxy); // [object Array]
Ty 中,繼承 Proxy 實現了一個子類:IdentifiableProxy,這個子類的類型是可以判斷的,如:
const arr=['a', 'b', 'c'];
const arrProxy=new IdentifiableProxy(arr, {});
Object.prototype.toString.call(arrProxy); // [object Proxy-Array]
// 使用 Ty 判斷
Ty(arrProxy).proxy.is; // true —— 做 proxy 判斷時,arrProxy 判定為 proxy
Ty(arrProxy).array.is; // true —— 做 array 判斷時,arrProxy 判定為 array
Ty(arrProxy).array.proxy.and; // true
如何使用 Ty 實現下面這樣一個類型判斷:
typeof value01==='object' && typeof value02 !='number'
在 Ty 中,可以對單個類型標識符進行否運算:! + 類型標識符,如:
Ty(value01, value02).obj['!num'].and
標識符 | 對應類型 |
error | Error |
reflect | Reflect |
json | JSON |
math | Math |
int8array | Int8Array |
uint8array | Uint8Array |
uint8clampedarray | Uint8ClampedArray |
int16array | Int16Array |
uint16array | Uint16Array |
int32array | Int32Array |
uint32array | Uint32Array |
bigint64array | BigInt64Array |
biguint64array | BigUint64Array (en-US) |
float32array | Float32Array |
float64array | Float64Array |
weakmap | WeakMap |
weakset | WeakSet |
arraybuffer | ArrayBuffer |
atomics | Atomics |
dataview | DataView |
weakref | WeakRef |
finalizationregistry | FinalizationRegistry (en-US) |
iterator | Iterator |
proxy | Proxy |
intl | Intl |
intl.collator | Intl.Collator |
intl.datetimeformat | Intl.DateTimeFormat |
intl.displaynames | Intl.DisplayNames |
intl.listformat | Intl.ListFormat |
intl.locale | Intl.Locale |
intl.numberformat | Intl.NumberFormat |
intl.pluralrules | Intl.PluralRules |
intl.relativetimeformat | Intl.RelativeTimeFormat |
intl.segmenter | Intl.Segmenter |
global | node 環境下的 globalThis |
window | window 環境下的 globalThis 或 window |
作者:_code_bear_
鏈接:https://juejin.cn/post/7351321160809725990
JavaScript中,有多種方法可以判斷一個變量的數據類型。以下是一些常見的方法:
typeof 是JavaScript中的一元操作符,返回一個表示未計算變量類型或已計算對象類型的字符串。但是,需要注意的是 typeof 對于 null 和 array 的處理可能不是你所期望的:
console.log(typeof undefined); // "undefined"
console.log(typeof 123); // "number"
console.log(typeof 'hello'); // "string"
console.log(typeof true); // "boolean"
console.log(typeof {}); // "object"
console.log(typeof []); // "object" 而不是 "array"
console.log(typeof null); // "object" 而不是 "null"
console.log(typeof function(){}); // "function"
instanceof 操作符用于檢測構造函數的 prototype 屬性是否出現在對象的原型鏈中的任何位置。這主要用于檢測對象是否屬于某個類。
console.log([] instanceof Array); // true
console.log(null instanceof Object); // false,因為 null 不是一個對象
這是檢測一個值是否為數組的最佳方法。
console.log(Array.isArray([])); // true
console.log(Array.isArray({})); // false
這個方法返回表示該對象的字符串。對于檢測原始值類型,特別是當 typeof 給出不直觀的結果時(如 null 和 array),這是一個很有用的方法。
function getType(obj) {
return Object.prototype.toString.call(obj).slice(8, -1);
}
console.log(getType(null)); // "Null"
console.log(getType([])); // "Array"
console.log(getType({})); // "Object"
console.log(getType(123)); // "Number"
console.log(getType('hello')); // "String"
console.log(getType(true)); // "Boolean"
console.log(getType(undefined)); // "Undefined"
每個JavaScript對象都有一個 constructor 屬性,它指向創建該對象的構造函數。但請注意,如果 constructor 被手動修改,則可能不準確。
console.log(([]).constructor===Array); // true
console.log(({}).constructor===Object); // true
某些內置對象(如 Array、Date、RegExp 等)的 @@toStringTag 屬性值是一個字符串,該字符串用于定制 Object.prototype.toString.call(obj) 的默認行為。但通常你不需要直接使用這個屬性,除非你在實現自定義對象并希望改變 Object.prototype.toString.call(obj) 的默認行為。
*請認真填寫需求信息,我們會在24小時內與您取得聯系。