你可能還不知道的關於JavaScript類型的細節?

来源:https://www.cnblogs.com/moqiutao/archive/2019/05/23/10865022.html
-Advertisement-
Play Games

類型 七種類型: Undefined Null Boolean String Number Symbol Object Undefined、Null 閱讀問題:為什麼有的編程規範要求使用void 0 代替undefined? Undefined類型表示未定義,它的類型只有一個值即undefined, ...


類型

七種類型:

  • Undefined
  • Null
  •  Boolean
  •  String
  • Number
  • Symbol
  • Object

Undefined、Null

閱讀問題:為什麼有的編程規範要求使用void 0 代替undefined?

Undefined類型表示未定義,它的類型只有一個值即undefined,任何變數在賦值前都是Undefined類型,它的值是undefined。由於JS中的undefined是一個變數,而並非是一個關鍵字,這是JavaScript語言公認的設計失誤之一。因此,為了避免無意中被篡改,一般用void 0來獲取undefined的值。

Null類型也只有一個值為null,null是JavaScript的關鍵字,可以使用null關鍵字來獲取null值。

關於void運算符,有兩種寫法:

  • 1.void expression
  • 2.void (expression)

void運算符的操作數可以是任意類型。該運算符指定要計算一個表達式但是不論該表達式原來是否有自己的返回值,其返回值都為undefined。其作用如下:

作用一:返回undefined,(對於為什麼不直接使用undefined,是因為undefined不是關鍵字,意味著它隨時可能被篡改成其他值。。。)。

作用二:防止不必要的行為,如下代碼:

<a href='javascript:void(0)'>阻止預設行為跳轉</a>

例子:

function func(){
    return this;
}
console.log(void func()); //undefined

從上所述就能夠說明為什麼有的編程規範要求用void 0代替undefined。

 String

問題:字元串是否有最大長度?

首先,String有最大長度為2^53-1,但這個所謂的最大長度並不完全是你所理解中的字元數。因為String的意義,並非“字元串“,而是字元串的UTF16編碼。我們字元串的方法charAt、charCodeAt、length等方法都是針對的是UTF16編碼。所以,字元串的最大長度實際上是受字元串的編碼長度影響的。

現行的字元集國際標準,字元是以 Unicode 的方式表示的,每一個 Unicode 的碼點表示一個字元,理論上,Unicode 的範圍是無限的。UTF 是 Unicode 的編碼方式,規定了碼點在電腦中的表示方法,常見的有 UTF16 和 UTF8。 Unicode 的碼點通常用 U+??? 來表示,其中 ??? 是十六進位的碼點值。 0-65536(U+0000 - U+FFFF)的碼點被稱為基本字元區域(BMP)。剩下的字元都放在輔助平面(縮寫SMP),碼點範圍從U+010000一直到U+10FFFF。如下圖所示:

碼點解釋:它從0開始,為每個符號指定一個編號,這叫做"碼點"(code point)。比如,碼點0的符號就是null(表示所有二進位位都是0)。

U+0000 = null

上式中,U+表示緊跟在後面的十六進位數是Unicode的碼點。

 詳細的編碼可參考:

 Number

JavaScript的Number類型有2^64-2^53+3個值。JS的Number類型基本符合IEEE 754-2008規定的雙精度浮點數規則。但是JS為了表達幾個額外的語言場景(比如不讓除以0出錯,引入了無窮大的概念),規定了幾個例外情況:

  • NaN:占用了 9007199254740990,這原本是符合IEEE 規則的數字;
  • infinity:正無窮大;
  • -infinity:負無窮大。

JS中+0和-0,在除法場合需要特別留意,“忘記檢測除以-0,而得到負無窮大”的情況經常會導致錯誤,而區分+0和-0的方式,正是檢測1/x是infinity和-infinity。

根據雙精度浮點數的定義,Number類型的有效的整數範圍是 -0x1fffffffffffff 至 0x1fffffffffffff,所以Number無法精確表示此範圍外的整數。

為什麼在JavaScript中,0.1+0.2不能=0.3?

console.log( 0.1 + 0.2 == 0.3) //false

左右兩邊不相等的原因就是浮點數運算的結果。浮點數運算的精度問題導致等式左右的結果並不是嚴格相等,而是相差了個微小的值。

所以實際上,這裡錯誤的不是結論,而是比較的方法,正確的比較方法是使用 JavaScript 提供的最小精度值:

console.log(Math.abs(0.1 + 0.2 - 0.3) <= Number.EPSILON) //true

檢查等式左右兩邊差的絕對值是否小於最小精度,才是正確的比較浮點數的方法。這段代碼結果就是 true 了。

Symbol

創建symbol的方式如下:

var mysymbol = Symbol('my symbol');  //Symbol(my symbol)

可以使用Symblo.interator定義for...of在對象上的行為:

var obj = {
    a:1,
    b:2,
    [Symbol.iterator]:function(){
        var v = 0;
        return {
            next:function(){
                return {value:v++,done:v>10}
            }
        }
    }
};
for(var v of obj){
    console.log(v); //0 1 2 3 4 5 6 7 8 9
}

 Object

問題描述:為什麼給對象添加的方法能用在基本類型上?

Object 是 JavaScript 中最複雜的類型,也是 JavaScript 的核心機制之一。

在 JavaScript 中,對象的定義是“屬性的集合”。屬性分為數據屬性和訪問器屬性,二者都是 key-value 結構,key 可以是字元串或者 Symbol 類型。

JavaScript 中的幾個基本類型,都在對象類型中有一個“親戚”。它們是:

  • Number
  • String
  • Boolean
  • Symbol

我們必須認識到 3 與 new Number(3) 是完全不同的值,它們一個是 Number 類型, 一個是對象類型。

Number、String 和 Boolean,三個構造器是兩用的,當跟 new 搭配時,它們產生對象,當直接調用時,它們表示強制類型轉換。Symbol 函數比較特殊,直接用 new 調用它會拋出錯誤,但它仍然是 Symbol 對象的構造器。

JavaScript 語言設計上試圖模糊對象和基本類型之間的關係,我們日常代碼可以把對象的方法在基本類型上使用,比如:

console.log("abc".charAt(0)); //a

甚至我們在原型上添加方法,都可以應用於基本類型,比如以下代碼,在 Symbol 原型上添加了 hello 方法,在任何 Symbol 類型變數都可以調用。

Symbol.prototype.helloWorld = () => console.log("hello world");

var a = Symbol("a");
console.log(typeof a); //symbol,a 並非對象
a.helloWorld(); //hello world,有效

 針對上面的問題,答案就是: .運算符提供了裝箱操作,它會根據基礎類型構造一個臨時對象,使得我們能在基礎類型上調用對應對象的方法。

裝箱轉換

每一種基本類型 Number、String、Boolean、Symbol 在對象中都有對應的類,所謂裝箱轉換,正是把基本類型轉換為對應的對象,它是類型轉換中一種相當重要的種類。

前文提到,全局的 Symbol 函數無法使用 new 來調用,但我們仍可以利用裝箱機制來得到一個 Symbol 對象,我們可以利用一個函數的 call 方法來強迫產生裝箱。

我們定義一個函數,函數裡面只有 return this,然後我們調用函數的 call 方法到一個 Symbol 類型的值上,這樣就會產生一個 symbolObject。

我們可以用 console.log 看一下這個東西的 type of,它的值是 object,我們使用 symbolObject instanceof 可以看到,它是 Symbol 這個類的實例,我們找它的 constructor 也是等於 Symbol 的,所以我們無論從哪個角度看,它都是 Symbol 裝箱過的對象:

var symbolObject = (function(){ return this; }).call(Symbol("a"));

console.log(typeof symbolObject); //object
console.log(symbolObject instanceof Symbol); //true
console.log(symbolObject.constructor == Symbol); //true

裝箱機制會頻繁產生臨時對象,在一些對性能要求較高的場景下,我們應該儘量避免對基本類型做裝箱轉換。

使用內置的 Object 函數,我們可以在 JavaScript 代碼中顯式調用裝箱能力。

var symbolObject = Object(Symbol("a"));

console.log(typeof symbolObject); //object
console.log(symbolObject instanceof Symbol); //true
console.log(symbolObject.constructor == Symbol); //true

每一類裝箱對象皆有私有的 Class 屬性,這些屬性可以用 Object.prototype.toString 獲取:

var symbolObject = Object(Symbol("a"));

console.log(Object.prototype.toString.call(symbolObject)); //[object Symbol]

在 JavaScript 中,沒有任何方法可以更改私有的 Class 屬性,因此 Object.prototype.toString 是可以準確識別對象對應的基本類型的方法,它比 instanceof 更加準確。

但需要註意的是,call 本身會產生裝箱操作,所以需要配合 typeof 來區分基本類型還是對象類型。

在 ES5 開始,[[class]] 私有屬性被 Symbol.toStringTag 代替,Object.prototype.toString 的意義從命名上不再跟 class 相關。我們甚至可以自定義 Object.prototype.toString 的行為,以下代碼展示了使用 Symbol.toStringTag 來自定義 Object.prototype.toString 的行為:

var obj  = {
    [Symbol.toStringTag]:'myobject'
}
console.log(obj + ''); //[object myobject]

這裡創建了一個新對象,並且給它唯一的一個屬性 Symbol.toStringTag,我們用字元串加法觸發了 Object.prototype.toString 的調用,發現這個屬性最終對 Object.prototype.toString 的結果產生了影響。

註:以下代碼展示了所有具有內置 class 屬性的對象:

var o = new Object;
var n = new Number;
var s = new String;
var b = new Boolean;
var d = new Date;
var arg = function(){ return arguments }();
var r = new RegExp;
var f = new Function;
var arr = new Array;
var e = new Error;
console.log([o, n, s, b, d, arg, r, f, arr, e].map(v => Object.prototype.toString.call(v))); 
//["[object Object]", "[object Number]", "[object String]", "[object Boolean]", "[object Date]", "[object Arguments]", "[object RegExp]", "[object Function]", "[object Array]", "[object Error]"]

拆箱轉換

在 JavaScript 標準中,規定了 ToPrimitive 函數,它是對象類型到基本類型的轉換(即,拆箱轉換)。

對象到 String 和 Number 的轉換都遵循“先拆箱再轉換”的規則。通過拆箱轉換,把對象變成基本類型,再從基本類型轉換為對應的 String 或者 Number。

拆箱轉換會嘗試調用 valueOf 和 toString 來獲得拆箱後的基本類型。如果 valueOf 和 toString 都不存在,或者沒有返回基本類型,則會產生類型錯誤 TypeError。

var obj = {
    valueOf:() => {
        console.log('valueOf');
        return {};
    },
    toString:() => {
        console.log('toString');
        return {};
    }
}

obj * 2;
//valueOf
//toString
//Uncaught TypeError: Cannot convert object to primitive value

從上面結果可以看出,拆箱操作失敗了。

到 String 的拆箱轉換會優先調用 toString。那麼你會看到調用順序就變了,如下代碼:

var obj = {
    valueOf:() => {
        console.log('valueOf');
        return {};
    },
    toString:() => {
        console.log('toString');
        return {};
    }
}

String(obj);
//toString
//valueOf
//Uncaught TypeError: Cannot convert object to primitive value

在 ES6 之後,還允許對象通過顯式指定 @@toPrimitive Symbol 來覆蓋原有的行為。

var obj = {
    valueOf:() => {
        console.log('valueOf');
        return {};
    },
    toString:() => {
        console.log('toString');
        return {};
    },
    [Symbol.toPrimitive]: () => {
        console.log('toPrimitive');
        return 'result msg';
    }
}

console.log( obj + '');
//toPrimitive
//result msg

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 下麵這篇中,已經有準備一些數據: 《MS SQL server對象類型type》https://www.cnblogs.com/insus/p/10903916.html 為前端服務,直接在SQL把查詢結果轉換為JSON格式數據: 實現這樣的功能,可以使用SQL的FOR JSON PATH: SEL ...
  • 執行下麵代碼,將獲取ms sql server對象類型以及其說明 IF OBJECT_ID('tempdb.dbo.#type') IS NOT NULL DROP TABLE #type CREATE TABLE #type ( [type] NVARCHAR(2), [desc] NVARCHA ...
  • 本文以一個簡單的小例子,簡述在Android開發中,動畫的簡單應用,僅供學習分享使用。 ...
  • 手機端配置tcp方式連接 su setprop service.adb.tcp.port 5555 stop adbd start adbd 首先使用管理員許可權,然後打開監聽5555埠 電腦端使用adb遠程連接 adb connect ip_address 此時手機端彈出認證請求點擊確認即可連接成 ...
  • [TOC] 0、概述websocket (1) 個人總結:後臺設置了websocket地址,伺服器開啟後等待有人去連接它。 一個客戶端一打開就去連接websocket地址,同時傳遞某些識別參數。這樣一來後臺和客戶端連接成功了,然後後臺就可以發消息給客戶端了,(客戶端也可以再回話給後臺)。 (2) s ...
  • anticipation n. 預期,期望 appreciation n. 感謝,感激 array n. 陳列,一系列 assurance n. 保證 emergency n. 緊急情況 encouragement n. 鼓勵 compulsory a. 強迫的,強制的,義務的 confidenti ...
  • ambient a.周圍的,包圍著的 ambiguous a.模棱兩可的;分歧的 ambitious a.有雄心的;熱望的 ample a.足夠的;寬敞的 amplitude n.廣大;充足;振幅 amusement n.娛樂,消遣,樂趣 analogue n.類似物;同源語 analogy n.相 ...
  • 本文公眾號「AndroidTraveler」首發。 背景 在實際的應用場景中,Android 手機的截屏其實是很普遍的。 比如說 PPT 演示,比如說技術博客圖文並茂講解。 因此懂得 Android 手機截屏的各種操作就顯得尤為重要。 使用 AndroidStudio 自帶的截屏工具 在 Logca ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...