整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          JavaScript 代碼邏輯判斷的優化

          們日常使用到的邏輯判斷語句有 if...else...switch...case...do...while...等。

          在簡單場景下,我們可能對這些語法的性能沒有什么感覺,但當遇到復雜的業務場景時,如果處理不善,就會出現大量的邏輯嵌套,可讀性差并且難以擴展。

          千里之行始于足下,編寫高可維護性和高質量的代碼,我們就需要從細節處入手,我們今天主要討論 JavaScript 中如何優化邏輯判斷代碼。

          嵌套層級優化

          function supply(fruit, quantity) {
              const redFruits = ['apple', 'strawberry', 'cherry', 'cranberries'];
              // 條件 1: 水果存在
              if (fruit) {
                  // 條件 2: 屬于紅色水果
                  if (redFruits.includes(fruit)) {
                      console.log('紅色水果');
                      // 條件 3: 水果數量大于 10 個
                      if (quantity > 10) {
                          console.log('數量大于 10 個');
                      }
                  }
              } else {
                  throw new Error('沒有水果啦!');
              }
          }

          通過上面這個例子,我們可以看到:判斷流程中規中矩,符合現實世界的映射。但是,因代碼層層嵌套,導致閱讀和維護都存在困難。

          如果傳入了 fruit 參數,則每次執行都至少需要經過兩步 if 判斷,在性能上也存在問題。

          我們來對上面的代碼進行一下優化處理:

          function supply(fruit, quantity) {
              const redFruits = ['apple', 'strawberry', 'cherry', 'cranberries'];
              if (!fruit) throw new Error('沒有水果啦');     // 條件 1: 當 fruit 無效時,提前處理錯誤
              if (!redFruits.includes(fruit)) return; // 條件 2: 當不是紅色水果時,提前 return
          
              console.log('紅色水果');
          
              // 條件 3: 水果數量大于 10 個
              if (quantity > 10) {
                  console.log('數量大于 10 個');
              }
          }

          這里主要對嵌套層級做了優化,提前終止掉了不符合的條件,將三層嵌套減少到了一層,簡化了代碼結果結構,增強了可閱讀性。

          多條件分支的優化

          相信我們很多人對下面這種代碼不陌生吧?(想想剛開始寫代碼那會啊)

          function pick(color) {
              // 根據顏色選擇水果
              if (color === 'red') {
                  return ['apple', 'strawberry'];
              } else if (color === 'yellow') {
                  return ['banana', 'pineapple'];
              } else if (color === 'purple') {
                  return ['grape', 'plum'];
              } else {
                  return [];
              }
          }

          我們需要知道一點原則:if else 更適合于條件區間判斷,而 switch case 更適合于具體枚舉值的分支判斷。

          我們使用 switch...case...進行一下改寫:

          function pick(color) {
              // 根據顏色選擇水果
              switch (color) {
                  case 'red':
                      return ['apple', 'strawberry'];
                  case 'yellow':
                      return ['banana', 'pineapple'];
                  case 'purple':
                      return ['grape', 'plum'];
                  default:
                      return [];
              }
          }

          switch...case... 優化之后的代碼看上去格式整齊,思路很清晰,但還是很冗長。繼續優化:

          • 借助 Object 的 {key: value} 結構,我們可以在 Object 中枚舉所有的情況,然后將 key 作為索引,直接通過 Object.key 或者 Object[key] 來獲取內容:
          const fruitColor = {
              red: ['apple', 'strawberry'],
              yellow: ['banana', 'pineapple'],
              purple: ['grape', 'plum'],
          }
          function pick(color) {
              return fruitColor[color] || [];
          }
          • 使用 Map 數據結構,真正的(key, value) 鍵值對結構:
          const fruitColor = new Map()
              .set('red', ['apple', 'strawberry'])
              .set('yellow', ['banana', 'pineapple'])
              .set('purple', ['grape', 'plum']);
          
          function pick(color) {
              return fruitColor.get(color) || [];
          }

          優化之后,代碼更簡潔、更容易擴展。

          為了更好的可讀性,還可以通過更加語義化的方式定義對象,然后使用 Array.filter 達到同樣的效果:

          const fruits = [
              {name: 'apple', color: 'red'},
              {name: 'strawberry', color: 'red'},
              {name: 'banana', color: 'yellow'},
              {name: 'pineapple', color: 'yellow'},
              {name: 'grape', color: 'purple'},
              {name: 'plum', color: 'purple'}
          ];
          
          function pick(color) {
              return fruits.filter(f => f.color == color);
          }

          總結

          上面使用的例子和手段都比較初級,但是其中的思想卻值得我們細品,希望大家能夠有所收獲!

          學習有趣的知識,結識有趣的朋友,塑造有趣的靈魂!

          大家好!我是〖編程三昧〗的作者 隱逸王,我的公眾號是『編程三昧』,歡迎關注,希望大家多多指教!

          知識與技能并重,內力和外功兼修,理論和實踐兩手都要抓、兩手都要硬!

          TML究竟算不算是一門編程語言,這是爭執已久的話題。其實,從本質來講,HTML確實算不上是一門編程語言。

          HTML全稱,HyperText Markup Language。字面理解,HTML就是一種超文本語言,何謂超文本,就是其用途和意義已經超越了純文本。因為,HTML重新定義了文本的格式,而且HTML不僅僅只有文本還包括音視頻。

          相比其他語言,HTML的字面意義更容易理解,就是為了方便人類和機器閱讀。例如,我們常見的頭標簽,輸入標簽,換行符等等,都是HTML中常見和常用的。此類標簽不僅讓人類能夠輕松理解,而且對于電腦這種機器來說,也是很容理解的。

          HTML語言不具備很強的邏輯性。基于此,很多程序員都不承認HTML是一門編程語言。不像其他語言,例如Java,C++,Python等流行語言,這些語言都帶有很強的邏輯和流程控制功能。

          不僅僅是HTML無邏輯性和流程控制的問題,同時HTML還是缺乏靈活性的,因為HTML都是按照W3C的標準限定死的語言,主要用于規范HTML文檔的書寫格式。不像其他語言,用戶可以自定義的地方有很多,千變萬化。

          HTML不被承認是一門編程語言,最重要的一點是因為,HTML不能按照人類的設計對一件工作進行重復的循環,直至得到讓人類滿意的答案。這一點最重要,其他語言都可以輕松做到。

          在編程語言方面的話,小編還是更為推崇c/c++的!雖然Java、Python熱度不斷上漲,但是作為編程界元老的C++依然具有其無可比擬的優勢,小編是一個有著7年工作經驗的架構師,對于c++,自己有做資料的整合,一個完整學習C語言c++的路線,學習資料和工具。可以進我的群7418,18652領取,免費送給大家。希望你也能憑自己的努力,成為下一個優秀的程序員!

          當然,我們也不能否認HTML的重要性,作為web領域的重要元老,在當前時候,前端工程師還是有相當大的發展前途的!

          在程序員的眼中,HTML是算不上一門編程語言的。雖然如此,但是HTML在WEB領域的重要作用遠遠超越其他任何編程語言,瀏覽器打開i一個網頁第一步就要解析一個HTML的DOM樹,越簡單越重要。

          元芳,你怎么看呢?

          ypeof

          typeof 操作符返回一個字符串,表示未經計算的操作數的類型。


          語法

          typeof 運算符后接操作數:

          typeof operand

          typeof(operand)


          參數

          operand 一個表示對象或原始值的表達式,其類型將被返回。


          描述

          下面總結了 typeof 可能的返回值。有關類型和原始值的更多信息,可查看 JavaScript 數據結構 頁面。

          類型

          結果

          Undefined

          "undefined"

          Null

          "object" (見下文)

          Boolean

          "boolean"

          Number

          "number"

          BigInt (ECMAScript 2020 新增)

          "bigint"

          String

          "string"

          Symbol (ECMAScript 2015 新增)

          "symbol"

          宿主對象(由 JS 環境提供)

          取決于具體實現

          Function 對象 (按照 ECMA-262 規范實現 [[Call]])

          "function"

          其他任何對象

          "object"


          原始值

          除對象類型(object)以外的其它任何類型定義的不可變的值(值本身無法被改變)。例如(與 C 語言不同),JavaScript 中字符串是不可變的(譯注:如,JavaScript 中對字符串的操作一定返回了一個新字符串,原始字符串并沒有被改變)。我們稱這些類型的值為“原始值”。


          特性

          1、typeof 總是返回一個字符串。

          2、typeof 能正確判斷原始值的類型,null 除外;引用類型數據能正確判斷 Function、函數的類型,其他的都會返回 'object'。


          示例

          // 數值
          console.log(typeof 37 === 'number') // true
          console.log(typeof 3.14 === 'number') // true
          console.log(typeof (42) === 'number') // true
          console.log(typeof Math.LN2 === 'number') // true
          console.log(typeof Infinity === 'number') // true
          console.log(typeof NaN === 'number') // true 盡管它是 "Not-A-Number" (非數值) 的縮寫
          console.log(typeof Number(1) === 'number') // true Number 會嘗試把參數解析成數值
          
          console.log(typeof 42n === 'bigint') // true
          
          
          // 字符串
          console.log(typeof '' === 'string') // true
          console.log(typeof 'bla' === 'string') // true
          console.log(typeof `template literal` === 'string') // true
          console.log(typeof '1' === 'string') // true 注意內容為數字的字符串仍是字符串
          console.log(typeof (typeof 1) === 'string') // true typeof 總是返回一個字符串
          console.log(typeof String(1) === 'string') // true String 將任意值轉換為字符串,比 toString 更安全
          
          
          // 布爾值
          console.log(typeof true === 'boolean') // true
          console.log(typeof false === 'boolean') // true
          console.log(typeof Boolean(1) === 'boolean') // Boolean() 會基于參數是真值還是虛值進行轉換
          console.log(typeof !!(1) === 'boolean') // true 兩次調用 ! (邏輯非) 操作符相當于 Boolean()
          
          
          // Symbols
          console.log(typeof Symbol() === 'symbol') // true
          console.log(typeof Symbol('foo') === 'symbol') // true
          console.log(typeof Symbol.iterator === 'symbol') // true
          
          
          // Undefined
          console.log(typeof undefined === 'undefined') // true
          console.log(typeof declaredButUndefinedVariable === 'undefined') // true
          console.log(typeof undeclaredVariable === 'undefined') // true
          
          
          // 對象
          console.log(typeof { a: 1 } === 'object') // true
          
          // 使用 Array.isArray 或者 Object.prototype.toString.call
          // 區分數組和普通對象
          console.log(typeof [1, 2, 4] === 'object') // true
          
          console.log(typeof new Date() === 'object') // true
          console.log(typeof /regex/ === 'object') // true 歷史結果請參閱正則表達式部分
          
          
          // 使用 new 操作符
          // 除 Function 外的所有構造函數的類型都是 'object'
          var func = new Function()
          console.log(typeof func) // 返回 'function'
          
          var A = function() {}
          var b = new A()
          console.log(typeof b) // 返回 'object'
          
          // 下面的例子令人迷惑,非常危險,沒有用處。避免使用它們。
          console.log(typeof new Boolean(true) === 'object') // true
          console.log(typeof new Number(1) === 'object') // true
          console.log(typeof new String('abc') === 'object') // true
          
          
          // 函數
          console.log(typeof function () { } === 'function') // true
          console.log(typeof class C { } === 'function') // true
          console.log(typeof Math.sin === 'function') // true
          
          
          // Null
          // JavaScript 誕生以來便如此
          console.log(typeof null === 'object') // true

          在 JavaScript 最初的實現中,JavaScript 中的值是由一個表示類型的標簽和實際數據值表示的。對象的類型標簽是 0。由于 null 代表的是空指針(大多數平臺下值為 0x00),因此,null 的類型標簽是 0,typeof null 也因此返回 "object"。(參考來源


          console.log(typeof 0); // number
          console.log(typeof BigInt(Number.MAX_SAFE_INTEGER)); // bigint
          console.log(typeof '0'); // string
          console.log(typeof true); // boolean
          console.log(typeof undefined); // undefined
          console.log(typeof function () { }); // function
          console.log(typeof Symbol); // function
          console.log(typeof Symbol()); // symbol
          
          console.log(typeof Date); // function
          console.log(typeof Date()); // string
          console.log(typeof new Date); // object
          console.log(typeof new Date()); // object
          console.log(typeof RegExp); // function
          console.log(typeof RegExp()); // object
          console.log(typeof new RegExp); // object
          console.log(typeof new RegExp()); // object
          console.log(typeof []); // object
          console.log(typeof {}); // object
          console.log(typeof null); // object

          如果我們想判斷一個對象的正確類型,可以考慮使用 instanceof,因為內部機制是通過 判斷實例對象的 __proto__ 和生成該實例的構造函數的 prototype 是不是引用的同一個地址(也就是原型鏈的方式)來判斷的。

          錯誤

          在 ECMAScript 2015 之前,typeof 總能保證對任何所給的操作數返回一個字符串。即便是沒有聲明的標識符,typeof 也能返回 'undefined'。使用 typeof 永遠不會拋出錯誤。


          但在加入了塊級作用域的 letconst 之后,在其被聲明之前對塊中的 let 和 const 變量使用 typeof 會拋出一個 ReferenceError。塊作用域變量在塊的頭部處于“暫存死區”,直至其被初始化,在這期間,訪問變量將會引發錯誤。

          typeof undeclaredVariable === 'undefined';
          
          typeof newLetVariable; // ReferenceError
          typeof newConstVariable; // ReferenceError
          typeof newClass; // ReferenceError
          
          let newLetVariable;
          const newConstVariable = 'hello';
          class newClass{};


          instanceof

          instanceof 運算符用于檢測構造函數 prototype 屬性是否出現在某個實例對象的原型鏈上。可以用來判斷都屬于 Object 類型和一些特殊情況的對象,如:數組和對象,但不能用于基礎數據類型。


          語法

          object instanceof constructor


          參數

          object 某個實例對象

          constructor 某個構造函數


          描述

          instanceof 運算符用來檢測 constructor.prototype 是否存在于參數 object 的原型鏈上。


          示例

          B instanceof A:判斷 B 是否為 A 的實例,可以用于繼承關系中

          function Car(make, model, year) {
            this.make = make;
            this.model = model;
            this.year = year;
          }
          const auto = new Car('Honda', 'Accord', 1998);
          
          console.log(auto instanceof Car);
          // expected output: true
          
          console.log(auto instanceof Object);
          // expected output: true
          // 定義構造函數
          function C(){}
          function D(){}
          
          var o = new C();
          
          o instanceof C; // true,因為 Object.getPrototypeOf(o) === C.prototype
          
          o instanceof D; // false,因為 D.prototype 不在 o 的原型鏈上
          
          o instanceof Object; // true,因為 Object.prototype.isPrototypeOf(o) 返回 true
          C.prototype instanceof Object // true,同上
          
          C.prototype = {};
          var o2 = new C();
          
          o2 instanceof C; // true
          
          o instanceof C; // false,C.prototype 指向了一個空對象,這個空對象不在 o 的原型鏈上.
          
          D.prototype = new C(); // 繼承
          var o3 = new D();
          o3 instanceof D; // true
          o3 instanceof C; // true 因為 C.prototype 現在在 o3 的原型鏈上

          需要注意的是,如果表達式 obj instanceof Foo 返回 true,則并不意味著該表達式會永遠返回 true,因為 Foo.prototype 屬性的值有可能會改變,改變之后的值很有可能不存在于 obj 的原型鏈上,這時原表達式的值就會成為 false。另外一種情況下,原表達式的值也會改變,就是改變對象 obj 的原型鏈的情況,雖然在目前的ES規范中,我們只能讀取對象的原型而不能改變它,但借助于非標準的 __proto__ 偽屬性,是可以實現的。比如執行 obj.__proto__ = {} 之后,obj instanceof Foo 就會返回 false 了。


          A 是 B 的父對象,c 是 B 的實例,c instanceof A 與 c instanceof B 結果均為 true。

          function A() { }
          function B() { }
          B.prototype = new A()
          const c = new B()
          
          console.log(c instanceof B); // true
          console.log(c instanceof A); // true
          console.log(A instanceof B); // false
          console.log(B instanceof A); // false
          console.log(B.__proto__ === A.prototype); // false
          console.log(B.prototype.__proto__ === A.prototype); // true
          console.log(c instanceof Object); // true c 屬于
          console.log(B instanceof Object); // true
          console.log(A instanceof Object); // true


          演示 String 對象和 Date 對象都屬于 Object 類型和一些特殊情況

          下面的代碼使用了 instanceof 來證明:String 和 Date 對象同時也屬于Object 類型(他們是由 Object 類派生出來的)。

          但是,使用對象文字符號創建的對象在這里是一個例外:雖然原型未定義,但 instanceof Object 返回 true。

          var simpleStr = "This is a simple string";
          var myString  = new String();
          var newStr    = new String("String created with constructor");
          var myDate    = new Date();
          var myObj     = {};
          var myNonObj  = Object.create(null);
          
          console.log(simpleStr instanceof String) // 返回 false, 非對象實例,因此返回 false
          console.log(myString  instanceof String) // 返回 true
          console.log(newStr    instanceof String) // 返回 true
          console.log(myString  instanceof Object) // 返回 true
          
          console.log(myObj instanceof Object)     // 返回 true, 盡管原型沒有定義
          console.log(({})  instanceof Object)     // 返回 true, 同上
          console.log(myNonObj instanceof Object)  // 返回 false, 一種創建非 Object 實例的對象的方法
          
          console.log(myString instanceof Date)    // 返回 false
          
          console.log(myDate instanceof Date)      // 返回 true
          console.log(myDate instanceof Object)    // 返回 true
          console.log(myDate instanceof String)    // 返回 false



          Object.prototype.toString.call(object)/Object.prototype.toString.apply(object)

          toString.call() 或 toString.apply() 方法幾乎可以精準判斷各類數據的類型。

          console.log(Object.prototype.toString.call("kevin"))   // [object String]
          console.log(Object.prototype.toString.call(18))        // [object Number]
          console.log(Object.prototype.toString.call(true))      // [object Boolean]
          console.log(Object.prototype.toString.call(undefined)) // [object Undefined]
          console.log(Object.prototype.toString.call(null))      // [object Null]
          console.log(Object.prototype.toString.call(NaN))       // [object Number]
          console.log(Object.prototype.toString.call({ name: "kevin" })) // [object Object]
          console.log(Object.prototype.toString.call(function () { }))   // [object Function]
          console.log(Object.prototype.toString.call([]))                // [object Array]
          console.log(Object.prototype.toString.call(new Date))          // [object Date]
          console.log(Object.prototype.toString.call(/\d/))              // [object RegExp]
          console.log(Object.prototype.toString.call(Math))              // [object Math]
          function Person() { }
          console.log(Object.prototype.toString.call(new Person))        // [object Object]
          var o = { [Symbol.toStringTag]: "A" }
          console.log(Object.prototype.toString.call(o))                 // [object A]
          console.log(window.toString())                                 // "[object Window]"
          console.log(Object.prototype.toString.call(window))            // "[object Window]"
          console.log(Object.prototype.toString.call(Symbol()));         // "[object Symbol]"
          
          // 封裝
          function getTypeof(data) {
            let dataType = Object.prototype.toString.call(data);
            return dataType.slice(8, -1)
          }
          console.log(getTypeof(18)) // Number
          console.log(getTypeof("kevin")) // String
          console.log(getTypeof(new Date)) // Date
          console.log(getTypeof([])) // Array
          console.log(getTypeof({})) // Object
          function Person() { }
          console.log(getTypeof(new Person)) // Object
          console.log(getTypeof(new Person())) // Object


          語法

          obj.toString()


          返回值

          一個表示該對象的字符串


          描述

          每個對象都有一個 toString() 方法,當該對象被表示為一個文本值時,或者一個對象以預期的字符串方式引用時自動調用。默認情況下,toString() 方法被每個 Object 對象繼承。如果此方法在自定義對象中未被覆蓋,toString() 返回 "[object type]",其中 type 是對象的類型。以下代碼說明了這一點:

          var o = new Object();
          console.log(o.toString()); // [object Object]

          備注:如 ECMAScript 5 和隨后的 Errata 中所定義,從 JavaScript 1.8.5 開始,toString() 調用 null 返回[object Null]undefined 返回 [object Undefined]。請參閱下面的使用toString()檢測對象類型


          示例

          覆蓋默認的 toString 方法

          可以自定義一個方法,來取代默認的 toString() 方法。該 toString() 方法不能傳入參數,并且必須返回一個字符串。自定義的 toString() 方法可以是任何我們需要的值,但如果它附帶有關對象的信息,它將變得非常有用。


          以下代碼定義了 Dog 對象類型,并創建了一個 Dog 類型的 theDog 對象:

          function Dog(name,breed,color,sex) {
            this.name = name;
            this.breed = breed;
            this.color = color;
            this.sex = sex;
          }
          var theDog = new Dog("Gabby", "Lab", "chocolate", "female");
          
          // 如果當前的對象調用了 toString() 方法,它將會返回從 Object繼承而來的 toString() 方法的返回默認值:
          console.log(theDog.toString()); // 返回 [object Object]
          
          // 下面的代碼中定義了一個叫做 dogToString() 的方法來覆蓋默認的 toString() 方法。
          // 這個方法生成一個 "property = value;" 形式的字符串,該字符串包含了當前對象的 name、breed、color 和 sex 的值。
          Dog.prototype.toString = function dogToString() {
           var ret = "Dog " + this.name + " is a " + this.sex + " " + this.color + " " + this.breed;
           return ret;
          }
          
          // 也可以這樣寫
          Dog.prototype.toString = function dogToString() {
            return `Dog ${this.name} is a ${this.sex} ${this.color} ${this.breed}`;
          }
          // 使用上述代碼,任何時候在字符串上下文中使用 theDog.toString() 時,
          // JavaScript 都會自動調用 dogToString() 方法(dogToString() 可以是一個匿名函數),并且返回以下字符串:
          // "Dog Gabby is a female chocolate Lab"
          console.log(theDog.toString()); // 返回 "Dog Gabby is a female chocolate Lab"
          
          // 也可以這樣寫
          Dog.prototype.toString = function dogToString() {
            return '[object Dog]';
          }
          Dog.prototype[Symbol.toStringTag] = 'Dog'
          
          // 也可以這樣寫
          theDog[Symbol.toStringTag] = 'Dog'
          
          console.log(theDog.toString()); // 返回 [object Dog]
          console.log(Dog.prototype.toString()); // 返回 [object Dog]
          console.log(Dog.prototype.toString.call(theDog)); // 返回 [object Dog]
          console.log(Object.prototype.toString.call(theDog)); // 返回 [object Object]


          使用 toString() 檢測對象

          可以通過 toString() 來獲取每個對象的類型。為了每個對象都能通過 Object.prototype.toString() 來檢測,需要以 Function.prototype.call() 或者 Function.prototype.apply() 的形式來調用,傳遞要檢查的對象作為第一個參數,稱為 thisArg。

          var toString = Object.prototype.toString;
          
          toString.call(new Date); // [object Date]
          toString.call(new String); // [object String]
          toString.call(Math); // [object Math]
          
          //Since JavaScript 1.8.5
          toString.call(undefined); // [object Undefined]
          toString.call(null); // [object Null]


          檢測原理

          Object.prototype.toString.call(obj) 類型檢測的原理是什么?首先我們來看一下 toString() 方法:

          var num = 1
          console.log(num.toString())       // '1'
          
          var str = 'kevin'
          console.log(str.toString())       // 'kevin'
          
          var bool = false
          console.log(bool.toString())      // 'false'
          
          var arr = [1, 2, 3]
          console.log(arr.toString())       // '1,2,3'
          
          var obj = { name: 'kevin' }
          console.log(obj.toString())       // '[object Object]'
          
          var fn = function(){}
          console.log(fn.toString())        // 'function(){}'
          
          console.log(JSON.toString())      // '[object JSON]'
          
          console.log(Atomics.toString())   // '[object Atomics]'
          
          console.log(null.toString())      // Cannot read property 'toString' of null
          
          console.log(undefined.toString()  // Cannot read property 'toString' of undefined
          
          console.log(window.toString())    // '[object Window]'

          從以上示例可以知道 toString 是將數據轉換為字符串(null 和 undefined 除外),并且各種類型的數據轉換為字符串的方式又不一樣。即若參數不為 nullundefined,則將參數轉為對象,再作判斷。對于原始類型,轉為對象的方法即裝箱。

          轉為對象后,取得該對象的 [Symbol.toStringTag] 屬性值(可能會遍歷原型鏈)作為 tag,如無該屬性,或該屬性值不為字符串類型,則依下表取得 tag,然后返回 "[object " + tag + "]" 形式的字符串。

          新標準引入了 [Symbol.toStringTag] 屬性,是為了把此方法接口化,用于規范新引入的對象對此方法的調用。但對于“老舊”的對象,就只能直接輸出值,以保證兼容性。

          // 1. 三個容器對象。這類對象用作命名空間,用于存儲同一類方法。
          JSON[Symbol.toStringTag];         // => "JSON"
          Math[Symbol.toStringTag];         // => "Math"
          Atomics[Symbol.toStringTag];      // => "Atomic"
          // 這三個對象的 toString() 都沒有重寫,直接調用 toString() 方法也可以得到相同的結果。
          JSON.toString();                  // => "[object JSON]"
          Math.toString();                  // => "[object Math]"
          Atomics.toString();               // => "[object Atomics]"
          
          // 2. 兩個新引入的類型 BigInt 和 Symbol。
          BigInt.prototype[Symbol.toStringTag];      // => "BigInt"
          Symbol.prototype[Symbol.toStringTag];      // => "Symbol"
          
          // 3. 四個集合(Collection)對象。
          Set.prototype[Symbol.toStringTag];         // => "Set"
          Map.prototype[Symbol.toStringTag];         // => "Map"
          WeakSet.prototype[Symbol.toStringTag];     // => "WeakSet"
          WeakMap.prototype[Symbol.toStringTag];     // => "WeakMap"
          
          // 4. 在不同的實現中,有些第三方對象也部署了此屬性。
          // 比如在瀏覽器中:
          Window.prototype[Symbol.toStringTag];       // => "Window"
          HTMLElement.prototype[Symbol.toStringTag];  // => "HTMLElement"
          Blob.prototype[Symbol.toStringTag];         // => "Blob"
          
          // 5. 模塊命名空間對象(Module Namespace Object)。
          // 新引入的模塊命名空間對象(Module Namespace Object)也是部署了此屬性的。
          import * as module from "./export.js";
          module[Symbol.toStringTag];                 // => "Moduel"
          
          // 6. 在 Node.js 中
          global[Symbol.toStringTag];                 // => "global"

          我們再來看一下 Object 以及其原型上的 toString 方法:

          Object.toString();           // "function Object() { [native code] }"
          Object.prototype.toString(); // "[object Object]"
          
          var o = new Object();
          console.log(o.toString());            // 返回 [object Object]
          console.log(o.__proto__.toString());  // 返回 [object Object]
          console.log(o.__proto__.toString === Object.prototype.toString); // true

          我們可以看出 Object 和它的原型鏈上各自有一個 toString 方法,Object 輸出的是其函數體 "function Object() { [native code] }",而 Object 原型上輸出的是其類型 "[object Object]"。


          數據類型

          例子

          輸出

          字符串

          "foo".toString()

          "foo"

          數字

          1.toString()

          Uncaught SyntaxError: Invalid or unexpected token

          布爾值

          true.toString()

          "true"

          undefined

          undefined.toString()

          Uncaught TypeError: Cannot read property 'toString' of undefined

          null

          null.toString()

          Uncaught TypeError: Cannot read property 'toString' of null

          String

          String.toString()

          "function String() {[native code]}"

          Number

          Number.toString()

          "function Number() {[native code]}"

          Boolean

          Boolean.toString()

          "function Boolean() {[native code]}"

          Array

          Array.toString()

          "function Array() {[native code]}"

          Function

          Function.toString()

          "function Function() {[native code]}"

          Date

          Date.toString()

          "function Date() {[native code]}"

          RegExp

          RegExp.toString()

          "function RegExp() {[native code]}"

          Error

          Error.toString()

          "function Error() {[native code]}"

          Promise

          Promise.toString()

          "function Promise() {[native code]}"

          Object

          Object.toString()

          "function Object() {[native code]}"

          Math

          Math.toString()

          "[object Math]"

          Window

          Window.toString()

          "function Window() { [native code] }"

          window

          window.toString()

          "[object Window]"

          數據類型調用 toString() 方法的返回值,由此我們看出不同的數據類型都有其自身toString()方法


          // Boolean 類型,tag 為 "Boolean"
          console.log(Object.prototype.toString.call(true));            // => "[object Boolean]"
          
          // Number 類型,tag 為 "Number"
          console.log(Object.prototype.toString.call(1));               // => "[object Boolean]"
          
          // String 類型,tag 為 "String"
          console.log(Object.prototype.toString.call(""));              // => "[object String]"
          
          // Array 類型,tag 為 "String"
          console.log(Object.prototype.toString.call([]));              // => "[object Array]"
          
          // Arguments 類型,tag 為 "Arguments"
          console.log(Object.prototype.toString.call((function() {
            return arguments;
          })()));                                                       // => "[object Arguments]"
          
          // Function 類型, tag 為 "Function"
          console.log(Object.prototype.toString.call(function(){}));    // => "[object Function]"
          
          // Error 類型(包含子類型),tag 為 "Error"
          console.log(Object.prototype.toString.call(new Error()));     // => "[object Error]"
          
          // RegExp 類型,tag 為 "RegExp"
          console.log(Object.prototype.toString.call(/\d+/));           // => "[object RegExp]"
          
          // Date 類型,tag 為 "Date"
          console.log(Object.prototype.toString.call(new Date()));      // => "[object Date]"
          
          // 其他類型,tag 為 "Object"
          console.log(Object.prototype.toString.call(new class {}));    // => "[object Object]"
          
          // window 全局對象
          console.log(Object.prototype.toString.call(window);           // => "[object Window]")

          在 JavaScript 中,所有類都繼承于 Object,因此 toString 方法應該也被繼承了,但由上述可見事實并不像我們想的那樣,其實各數據類型使用 toString() 后的結果表現不一的原因在于:所有類在基礎 Object 的時候,改寫了 toString 方法。盡管如此,但 Object 原型上的方法是可以輸出數據類型的,因此我們想判斷數據類型時,也只能使用原型上的 toString 方法:Object.prototype.toString.call(object)


          直接調用

          toString(); // "[object Undefined]"
          
          (function(){
              console.log(toString()); // [object Undefined]
          })();
          
          也就是說直接調用toString()方法,等價于
          
          Object.prototype.toString.call(); // "[object Undefined]"
          Object.prototype.toString.call(undefined); // "[object Undefined]"
          
          即:直接調用 toString() 方法這里不可以理解成為全局作用域調用 toString() 方法,即 window.toString()
          
          所以直接調用 toString() 應該就是變相的 undefined.toString() 方法(這里說的是相當于,實際 undefined 并沒有方法,調用會報錯)。


          驗證

          // 定義一個數組
          var arr = [1, 2, 3]
          
          // 數組原型上是否具有 toString() 方法
          console.log(Array.prototype.hasOwnProperty('toString')) //true
          
          // 數組直接使用自身的 toString() 方法
          console.log(arr.toString()) // '1,2,3'
          
          
          // delete操作符刪除數組原型上的 toString()
          delete Array.prototype.toString
          
          // 刪除后,數組原型上是否還具有 toString() 方法
          console.log(Array.prototype.hasOwnProperty('toString')) //false
          
          // 刪除后的數組再次使用 toString() 時,會向上層訪問這個方法,即 Object 的 toString()
          console.log(arr.toString()) // '[object Array]'

          當我們把 Array 自身的 toString() 方法刪除之后,再次使用它時,由原型鏈它會向上查找這個方法,即 Object 的 toString(),也便將 Object 上的 toString() 方法作用在數組上,得出其數據類型 [object Array]


          為什么需要call/apply

          經常有人用 toString.call/apply(類型) 去代替 Object.prototype.toString.call/apply(類型) 使用,其實這樣是不嚴謹的,容易導致一些問題,如下所示

          function toString(){
              console.log("1")
          }
          toString();        // 1
          toString.call({}); // 1
          toString.call([]); // 1

          我們可以發現,當我們自定義了 toString() 方法時,直接調用 toString() 方法,就不會再默認調用 Object 類的 toString() 方法,而是會使用我們自定義的方法,這樣可能得不到我們想要的結果,所以我們還是應當盡量使用 Object.prototype.toString.call/apply(類型)。


          正因為 Object.prototype.toString() 本身允許被重寫,像 Array、Boolean、Number 的 toString 就被重寫過,所以需要調用 Object.prototype.toString.call(arg) 或 Object.prototype.toString.apply(arg) 或 Reflect.apply() 來判斷 arg 的類型,call 將 arg 的上下文指向 Object,所以 arg 執行了 Object 的 toString() 方法。

          至于 call,就是改變對象的 this 指向,當一個對象想調用另一個對象的方法,可以通過 call 或者 apply 改變其 this 指向,將其 this 指向擁有此方法的對象,就可以調用該方法了。

          var x = {
            toString() {
              return "X";
            },
          };
          
          x.toString();                                     // => "X"
          
          Object.prototype.toString.call(x);                // => "[object Object]"
          
          Object.prototype.toString.apply(x);               // => "[object Object]"
          
          Reflect.apply(Object.prototype.toString, x, []);  // => "[object Object]"


          判斷原生JSON對象


          主站蜘蛛池模板: 亚洲AV日韩精品一区二区三区| 精品国产一区二区三区无码| 韩国一区二区三区视频| 国产成人精品一区二三区| 久久精品国产一区二区三区肥胖| A国产一区二区免费入口| 国产一区二区三区无码免费| A国产一区二区免费入口| 国产免费一区二区三区| 一区二区中文字幕在线观看| 人妻激情偷乱视频一区二区三区| 一区二区三区免费视频网站| 一区二区三区日韩| 一区二区三区国模大胆| 99精品国产高清一区二区麻豆| 天码av无码一区二区三区四区| 中文字幕日韩一区| 国产suv精品一区二区6| 亚洲成AV人片一区二区密柚 | 无码日韩人妻AV一区免费l| 无码国产精品一区二区免费vr | 无码一区二区三区AV免费| 精品一区二区三区四区在线| 亚洲熟妇无码一区二区三区导航| 国产精品被窝福利一区 | 波多野结衣久久一区二区| 狠狠色婷婷久久一区二区三区| 亚洲av乱码一区二区三区按摩| 日韩人妻无码一区二区三区| 日韩人妻无码免费视频一区二区三区| 亚洲日韩精品一区二区三区无码| 无码人妻aⅴ一区二区三区有奶水 人妻夜夜爽天天爽一区 | 无码AV动漫精品一区二区免费| 在线视频一区二区| 最美女人体内射精一区二区| 国产一区二区三区日韩精品| 国产大秀视频一区二区三区| 日本精品高清一区二区2021| 国产综合视频在线观看一区| 国产乱子伦一区二区三区| 精品一区二区三区水蜜桃|