JS由Number與new Number的區別引起的思考

在回答園子問題的時候發現了很多新東西,寫下來分享一下 ==javascript

 

下面的圖就是此篇的概覽,另外文章的解釋不包括ES6新增的Symbol,話說這貨有包裝類型,可是不能new...html

基於JS是面向對象的,因此咱們稱呼function爲「方法」,等同於「函數」。java

 

1.Number與Number Object ——原始類型與包裝類型(primitive VS wrapper object)git

  ECMAScript定義了7種數據類型:6種原始類型(ES6新增Symbol)以及Object。原始類型(primitive)不是對象,包裝類型對象(wrapper object)屬於Object的範疇,除了null和undefined原始類型都有對應的包裝類型,咱們不談Symbol(分明是不夠理解 ==):github

  ● String for the string primitive.web

  ● Number for the number primitive.算法

  ● Boolean for the boolean primitive.express

  ● Symbol for the symbol primitive.編程

  原始類型不是對象而且沒有方法,爲何"test".length怎麼可使用,不是原始類型沒有方法嗎?這裏有個機制:http://es5.github.io/#x10.4.3(step 3 的ToObject是核心),下文會提到,瞭解更多傳送門 -> Does javascript autobox?瀏覽器

 

2.類型檢測

  JS檢測數據類型的方式有不少,這裏主要說三種:typeof、instanceof、Object.prototype.toString。

  [1].typeof:主要用於原始類型的檢測,可檢測原始類型、方法/函數、對象,遵循(出於兼容考慮,直到ES6調用 typeof null 仍然返回object):

 

類型 結構
Undefined "undefined"
Null "object"
Boolean "boolean"
Number "number"
String "string"
Symbol (ECMAScript 6 新增) "symbol"
宿主對象(JS環境提供的,好比瀏覽器) Implementation-dependent
函數對象 (implements [[Call]] in ECMA-262 terms) "function"
任何其餘對象 "object"

 

   [2].instanceof:主要用於檢測自定義對象,用來判斷某個構造方法/構造函數(右側)的prototype屬性所指向的對象是否存在於另一個要檢測對象(左側)的原型鏈上。額外補點說明:1.跨Frame/多窗口間不要用instanceof檢測類型(能夠用[3].Object.prototype.toString);2.instanceof的結果不是一成不變的,它可能會受到徹底重寫prototype的影響:

 1 function Func(){}
 2 
 3 var f1 = new Func();
 4 console.log('---------- f1 ---------------');
 5 console.log(f1 instanceof Func);
 6 console.log(f1.__proto__);
 7 console.log(f1.constructor);
 8 
 9 console.log('----- Func原型被重寫後的f1 -----');
10 Func.prototype = {};
11 
12 console.log(f1 instanceof Func); 
13 console.log(f1.__proto__); // 重寫prototype不影響已建立對象,對象的[[prototype]]在函數對象構造時已經肯定 -> http://es5.github.io/#x13.2.2
14 console.log(f1.constructor);
15 
16 
17 console.log('---------- f2 ---------------');
18 
19 var f2 = new Func();
20 console.log(f2 instanceof Func);
21 console.log(f2.__proto__);
22 console.log(f2.constructor);
23 
24 console.warn('因此咱們應當避免徹底重寫,推薦在prototype上擴展');

 

  [3].Object.prototype.toString.apply() / Object.prototype.toString.call():

   ES5對Object.prototype.toString的描述對照中文以下:

15.2.4.2 Object.prototype.toString ( )

toString方法被調用時,會執行下面的操做步驟:

  1. 若是this的值爲undefined,則返回"[object Undefined]".
  2. 若是this的值爲null,則返回"[object Null]".
  3. O成爲調用ToObject(this)的結果.
  4. class成爲O的內部屬性[[Class]]的值.
  5. 返回三個字符串"[object ", class, 以及 "]"鏈接後的新字符串.

  注意上面step 3 ToObject,因此不要試圖用方法[3]判斷原始類型,no zuo no die ( why you try )。

  幾種類型判斷的使用場景,試試看:

 1 var primitive_number = 1;
 2 var numberObj = new Number(1);
 3 
 4 console.log(typeof primitive_number); // typeof適用於原始類型,primitive_number是原始類型number
 5 console.log(typeof numberObj); // numberObj是object類型
 6 
 7 
 8 console.log(primitive_number instanceof Object);
 9 console.log(numberObj instanceof Object); // instanceof適用於對象類型/自定義對象類型
10 
11 console.log(primitive_number instanceof Number);
12 console.log(numberObj instanceof Number); // instanceof適用於對象類型/自定義對象類型
13 
14 console.log(Object.prototype.toString.apply(primitive_number)); // 問題出現了,此方法不要對原始類型調用
15 console.log(Object.prototype.toString.apply(numberObj));

 

3.構造函數不僅用來建立對象

  對各類包裝類型對象(wrapper object)來講,不一樣的方式(是否使用new操做符)調用構造方法將會返回不一樣的結果。

  在ES5 Constructor Properties of the Global Object(構造器(類)屬性的全局對象,好比Object、Function、Array、String、Boolean、Number、Date、RegExp、Error... )的規範中,每一小節都有專門描述兩種方式調用構造函數的返回結果:

  ● The Xxx Constructor Called as a Function [未使用new操做符]

  (像調用普通函數同樣調用構造函數:會執行類型轉換並返回ToXxx(value)後的值)

  ● The Xxx Constructor [使用new操做符]

  (當使用new 構造函數方式調用:實例化新建立的對象)

  拿Number Objects來舉個栗子:

15.7 Number Objects

15.7.1 The Number Constructor Called as a Function

When Number is called as a function rather than as a constructor, it performs a type conversion.

15.7.1.1 Number ( [ value ] )

Returns a Number value (not a Number object) computed by ToNumber(value) if value was supplied, else returns +0.

15.7.2 The Number Constructor

When Number is called as part of a new expression it is a constructor: it initialises the newly created object.

  Boolean、String的描述也是相似的,對Number、Boolean、String來講:在調用他們的構造函數時,使用了new操做符將會返回對象(不出意外),不然執行相應的類型轉換後返回結果。類型轉換是什麼?就是規範中提到的ToBoolean(value)ToNumber(value)、ToString(value),值得注意的是,這些ToXxx方法的返回結果都是原始類型。

  對應規範,咱們看看Number(value) 與 new Number(value)分別作了什麼:

  Number(value):

  1. 若是參數value未提供(即Number()),返回+0;
  2. 返回ToNumber(value)的計算結果,此結果是一個Number類型(Number原始類型)的值(不是Number對象,即不是Number類型的包裝對象Number object)。

  new Number(value):

  1. 實例化新建立的對象;
  2. 新構造對象的[[Prototype]]內部屬性設置爲原生的Number prototype object —— 初始值爲Number.prototype(The Number prototype object is itself a Number object (its [[Class]] is "Number") whose value is +0.);
  3. 新構造對象的[[Class]]內部屬性設置爲Number
  4. 新構造對象的[[PrimitiveValue]]內部屬性被設置爲:若是參數value未提供(即new Number()),則爲+0;不然設置爲ToNumber(value)的計算結果;
  5. 新構造對象的[[Extensible]]內部屬性設置爲true。

  如今咱們能夠確定的說:Number(value)返回Number原始類型,new Number(value)返回對象。

 

4.偷樑換柱? →_→ 都是ToObject乾的!

  運行下面代碼,看看你在瀏覽器控制檯看到了什麼:

1 var obj = Object(3);
2 console.log(obj instanceof Number);
3 console.log(obj.__proto__);
4 console.log(Object.prototype.toString.apply(obj));

  欸,發生了什麼?看看Object(value)幹了些啥:

15.2 Object Objects #

15.2.1 The Object Constructor Called as a Function #

When Object is called as a function rather than as a constructor, it performs a type conversion.

當以方法形式而不是構造函數形式(沒用new操做符)調用Object時,會執行類型轉換。

15.2.1.1 Object ( [ value ] ) #

When the Object function is called with no arguments or with one argument value, the following steps are taken:

調用Object函數時當無參或者參數個數爲1時:

  1. If value is null, undefined or not supplied, create and return a new Object object exactly as if the standard built-in Object constructor had been called with the same arguments (15.2.2.1).  若是value是未提供或者是null、undefined,用相同參數建立一個標準的內建Object。

  2. Return ToObject(value).

15.2.2 The Object Constructor #

When Object is called as part of a new expression, it is a constructor that may create an object.

當以new Object形式調用時,它就是一個構造器,而且可能建立一個對象。

15.2.2.1 new Object ( [ value ] ) #

When the Object constructor is called with no arguments or with one argument value, the following steps are taken:

調用Object函數時當無參或者參數個數爲1時:

  1. If value is supplied, then  若是提供了value參數

    1. If Type(value) is Object, then  若是參數是object類型

      1. If the value is a native ECMAScript object, do not create a new object but simply return value. 若是參數是本地對象,返回原對象。

      2. If the value is a host object, then actions are taken and a result is returned in an implementation-dependent manner that may depend on the host object. 若是參數是宿主對象,返回結果依賴於宿主對象。

    2. If Type(value) is String, return ToObject(value).

    3. If Type(value) is Boolean, return ToObject(value).

    4. If Type(value) is Number, return ToObject(value).

  2. Asset: The argument value was not supplied or its type was Null or Undefined.

  3. Let obj be a newly created native ECMAScript object.

  4. Set the [[Prototype]] internal property of obj t to the standard built-in Object prototype object (15.2.4).

  5. Set the [[Class]] internal property of obj to "Object".

  6. Set the [[Extensible]] internal property of obj to true.

  7. Set the all the internal methods of obj as specified in 8.12

  8. Return obj.

   Object(3)和new Object(3)的返回結果是同樣的,來看new Object(3):咱們提供了參數3所以進入step 1,Type(3)返回Number類型,所以執行ToObject(3)。ToObject依照下表返回結果,因此咱們拿到了一個Number Object:

 

 

5.捆綁在相等性判斷上的類型比較

  ES2015/ES6定義了四種相等性判斷算法: Abstract Equality Comparison(==)、Strict Equality Comparison (===,嚴格相等)、SameValueZero(零值相等)、SameValue(同值相等)。兩等號判斷在比較時進行類型轉換,三等號判斷不會轉換類型,零值相等認爲+0和-0相等,同值相等用於JS引擎內部、相似三等號判斷、區別在於對部分數值類型(NaN、+0、-0)比較處理方式不一樣。

  JS提供了三種值(相等)比較運算符:loose equality(==)、strict equality(===,嚴格相等)、Object.is(ES6新增);

  關於JS的相等性判斷,MDN已經描述的很清楚了(仍是中文版)~ JavaScript 中的相等性判斷

  在各類比較方式中都大量用到了一個東西:Type(x),這貨是幹嗎的?其實就是typeof x(Within this specification, the notation 「Type(x)」 is used as shorthand for 「the type of x」 where 「type」 refers to the ECMAScript language and specification types defined in this clause.),也就是用來檢測數據類型的(7種數據類型=6種原始類型+Object)。

   來看看ES5規範是如何描述兩等號比較的算法部分的:

11.9.3 The Abstract Equality Comparison Algorithm #

The comparison x == y, where x and y are values, produces true or false. Such a comparison is performed as follows:

    1. If Type(x) is the same as Type(y), then

      1. If Type(x) is Undefined, return true.

      2. If Type(x) is Null, return true.

      3. If Type(x) is Number, then

        1. If x is NaN, return false.

        2. If y is NaN, return false.

        3. If x is the same Number value as y, return true.

        4. If x is +0 and y is 0, return true.

        5. If x is 0 and y is +0, return true.

        6. Return false.

      4. If Type(x) is String, then return true if x and y are exactly the same sequence of characters (same length and same characters in corresponding positions). Otherwise, return false.

      5. If Type(x) is Boolean, return true if x and y are both true or both false. Otherwise, return false.

      6. Return true if x and y refer to the same object. Otherwise, return false.

    2. If x is null and y is undefined, return true.

    3. If x is undefined and y is null, return true.

    4. If Type(x) is Number and Type(y) is String,
      return the result of the comparison x == ToNumber(y).

    5. If Type(x) is String and Type(y) is Number,
      return the result of the comparison ToNumber(x) == y.

    6. If Type(x) is Boolean, return the result of the comparison ToNumber(x) == y.

    7. If Type(y) is Boolean, return the result of the comparison x == ToNumber(y).

    8. If Type(x) is either String or Number and Type(y) is Object,
      return the result of the comparison x == ToPrimitive(y).

    9. If Type(x) is Object and Type(y) is either String or Number,
      return the result of the comparison ToPrimitive(x) == y.

    10. Return false.

  再來看看三等號比較的算法部分:

11.9.6 The Strict Equality Comparison Algorithm #

The comparison x === y, where x and y are values, produces true or false. Such a comparison is performed as follows:

  1. If Type(x) is different from Type(y), return false.

  2. If Type(x) is Undefined, return true.

  3. If Type(x) is Null, return true.

  4. If Type(x) is Number, then

    1. If x is NaN, return false.

    2. If y is NaN, return false.

    3. If x is the same Number value as y, return true.

    4. If x is +0 and y is 0, return true.

    5. If x is 0 and y is +0, return true.

    6. Return false.

  5. If Type(x) is String, then return true if x and y are exactly the same sequence of characters (same length and same characters in corresponding positions); otherwise, return false.

  6. If Type(x) is Boolean, return true if x and y are both true or both false; otherwise, return false.

  7. Return true if x and y refer to the same object. Otherwise, return false.

  尼瑪,這就是在不停的拿類型比較啊有木有。從這也能看出來爲何推薦使用嚴格相等(===),它沒有隱式轉換而且判斷步驟少得多。

  這裏也體現了非嚴格相等(==)用了兩種形式的類型轉換:

  1. 原始類型之間(ToNumber),因此"3" == 3會返回true。
  2. 對象轉原始類型(ToPrimitive),比較對象和number(原始)類型的時候會執行到step 9,因此new Number(3)==3也會返回true。

  有了規範的說明,咱們就能夠這麼解釋兩個object類型的變量用兩等號比較爲何返回false:類型相同,進入step 1 -> 若是是Undefined、Null,返回true -> 不是Number、String、Boolean(object怎麼多是原始類型),繼續 -> 是同一個對象的引用?返回true;不然,只能返回false了。若是使用三等號判斷,直接到了step 7...

 

6.回到問題

  ● Number(3) 與 new Number(3)

   1.依據上面兩等號比較的算法部分,上面剛解釋new Number(3)==3,而Number(3)將返回number原始類型的3,由於因此...new Number(3) == Number(3)

   2.依據上面三等號比較的算法部分,第一步就返回false了(一個是原始類型,另外一個是object).... 因此new Number(3) !== Number(3)

  ● Object(3) 與 Object(3)

    1.兩等號判斷,好吧,Object(3) => new Number(3),緣由見上文ToObject。兩個object類型的變量用兩等號比較爲何返回false。(見上問)

    2.若是使用三等號判斷,直接到了step 7。(見上)

  ● new Number(3) 與 Object(3)

    Object(3) => new Number(3),解釋同上。

 

----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

參考:

[1].ES5規範:http://es5.github.io/

[2].ES5規範官網:http://www.ecma-international.org/ecma-262/5.1/

[3].JS數據類型:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Grammar_and_types#Data_structures_and_types

[4].JavaScript的相等性判斷:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Equality_comparisons_and_sameness

[5].JavaScript:Object.prototype.toString方法的原理:http://www.cnblogs.com/ziyunfei/archive/2012/11/05/2754156.html

[6].JavaScript面向對象編程(5)重寫prototype形成的混亂:http://blog.csdn.net/zhengwei223/article/details/41785881

[7].重寫prototype原型後哪些東西改變了:http://www.cnblogs.com/web-coding/p/4723381.html

相關文章
相關標籤/搜索