1. 类型

    1. JavaScript的每一个值都属于某一种数据类型。JavaScript规定了7种语言类型。语言类型广泛用于变量、函数参数、表达式、函数返回值等场合。这7种语言类型是:
      • Undefined;
      • Null;
      • Boolean;
      • String;
      • Number;
      • Symbol;
      • Object.

    2. Undefined、Null

    1. Undefined表时未定义,这种类型只有一个值:undefined。
    2. 任何变量在赋值前是Undefined类型、值为undefined。
    3. 一般可以用全局变量undefined来表达这个值,或者void运算来吧任意表达式变成undefined值。注:void 0 === undefined true
    4. undefined是一个变量,但不是关键字,所以一般建议用void 0 来获取undefined值
    5. null表示“定义了但是为空”。Null类型只有一个值,null。null是关键字,所以在任何代码中,可以放心用null关键字获取null值。

    3. Boolean

    truefalse

    4. String

    1. String并非“字符串”,而是字符串的UTF16编码,字符串的操作charAt,charCodeAt、length等都是针对UTF16编码。所以字符串的最大长度受字符串的编码长度影响(2^53-1)

    Note: 现行的字符集国际标准,字符用Unicode方式表示,每个Unicode的码点表示一个字符,理论上其范围是无限的。UTF是Unicode的编码方式,规定了码点在计算机中的表示方法,常见的有 UTF16 和 UTF8.

    1. JavaScript中的字符串永远无法变更,一旦构造完成,无法用任何方式改变内容,具有值类型的特征。

    5. Number

    1. JavaScript 中的 Number 类型有
      18437736874454810627(即253+3)个值。
    2. 基本符合双精度浮点数规则,但为了表达额外的语言场景(比如不让除以0报错,而引入无穷大的概念),规定了几个例外情况:
      • NaN,占用了9007100254740990,这是符合IEEE规则的数字
      • Infinity,无穷大
      • -Infinity,负无穷大
        ps: JavaScript中有+0和-0,在加法类运算中没有区别,但是除法中需要特别留意:“忘记检测除以-0,而得到负无穷大”经常会导致错误,而区分+0和-0的方式,就是检测1/x是infinity还是-infinity。0 === +0 === -0,
    3. Number有效的证书范围:-0x1fffffffffffff到0x1fffffffffffff,无法精确表示此范围外的整数。
    4. 非整数的Number类型无法用==、=== 来比较,比如 0.1 + 0.2 === 0.3。浮点数运算的精度问题导致等式左右的结果并不是严格相等,而是相差了微小的值。错在比较的方法上,正确的比较方法是JavaScript提供的最小精度值:
    1. console.log( Math.abs(0.1 + 0.2 - 0.3) <= Number.EPSILON);

    检查等式左右两边差的绝对值是否小于最小精度,才是比较浮点数的方法。

    6. Symbol

    1. Symbol是ES6引入的新类型:它是一切非字符串的对象key的集合,在ES6中,整个对象系统被用Symbol重塑。
    2. Symbol可以具有字符串类型的描述,但即使描述相同,Symbol也不想等。
    3. 创建Symbol的方式是使用全局的Symbol函数。例如:

      1. var mySymbol = Symbol("my symbol");
    4. 我们可以用Symbol.iterator来自定义for…of在对象上的行为: ```javascript var o = new Object o[Symbol.iterator] = function() { var v = 0 return {

      1. next: function() {
      2. return { value: v++, done: v > 10}
      3. }

      } };

    for(var v of o) console.log(v); // 0 1 2 3 … 9 // 这里面涉及到的:Symbol.iterator、迭代器

    1. **7. Object**
    2. 1. Object是最复杂的类型,也是JavaScript的核心机制之一,是一切有形和无形物体的总称。
    3. 2. 对象(Object):属性的集合。
    4. 3. 属性:数据属性和访问器属性,二者都是key-value结构,key可以是字符串或者Symbol类型。
    5. 4. 事实上,JavaScript的”类“仅仅是运行时对象的一个私有属性,而JavaScript中是无法自定义类型的。
    6. 5. JavaScript中的几个基本类型,都在对象类型中有一个”亲戚“。他们是:
    7. - Number;
    8. - String;
    9. - Boolean;
    10. - Symbol;
    11. <br />6. NumberStringBoolean,三个构造器是两用的,当和new搭配时,产生对象,当直接调用时,表示强制类型转换。
    12. 7. Symbol比较特殊,直接用new调用会抛出错误,但它仍然是Symbol对象的构造器。
    13. 8. JavaScript语言设计上试图模糊对象和基本类型之间的关系,我们日常代码可以把对象的方法在基本类型上使用,比如:
    14. ```javascript
    15. console.log("abc".charAt(0)); // a
    1. 甚至我们在原型上添加方法,都可以应用于基本类型,比如一下代码,在Symbol原型上添加了hello方法,在任何Symbol类型变量都可以调用。 ```javascript Symbol.prototype.hello = () => console.log(“hello”);

    var a = Symbol(“a”); console.log(typeof a);// symbol, a 并不是对象 a.hello(); // hello, 有效

    1. 10. 问题“为什么给对象添加的方法能用在基本类型上”:“."运算符提供了装箱操作,他会根据基础类型构造一个临时对象,使得我们能在基础类型上调用对应对象的方法。
    2. **8. 类型转换**
    3. 1. 因为JavaScript是弱类型语言,所以类型转换非常频繁,大部分熟悉的运算都会先进性类型转换。
    4. 2. JS中" == "因为试图实现跨类型的比较,所以规则复杂到基本没人可以记住。因此很多实践中推荐禁止使用" == ",而要求程序员进行显式的类型转换后,用“ === ”比较。
    5. 3. 大部分的类型转换规则简单,如下表所示:
    6. <br />![](https://static001.geekbang.org/resource/image/71/20/71bafbd2404dc3ffa5ccf5d0ba077720.jpg#align=left&display=inline&height=282&originHeight=447&originWidth=1127&status=done&width=711)
    7. <br />接下来看一下几种转换规则。
    8. **[转换规则]9. StringToNumber**
    9. 1. 字符串到数字的类型转换,存在一个语法结构,类型转换支持十进制、二进制、八进制和十六进制,比如:
    10. - 30;
    11. - 0b111;
    12. - 0o13;
    13. - 0xFF。
    14. 2. 此外,JavaScript支持的字符串语法还包括正负号科学记数法,可以使用大写或者小写e来表时:
    15. - 1e3;
    16. - -1e-2.
    17. 3. 需要注意的是:parseInt和parseFloat并不使用这个转换,所以支持的语法跟这里不尽相同(有相同的地方也有不同的地方)
    18. - 再不传入第二个参数的情况下,parseInt只支持16进制前缀“0x”,而且会忽略非数字字符,也不支持科学记数法。
    19. 4. 在一些古老浏览器中,parseInt还支持0开头数字作为八进制前缀,这是很多错误的源头。所以在任何环境下,都建议传入parseInt的第二个参数,而parseFloat则直接把源字符串作为十进制来解析,它不会引入任何的其他进制。
    20. 5. 多数情况下,Number 是比parseInt和parseFloat更好的选择(这是什么意思。。)
    21. **[转换规则]10. NumberToString**
    22. 1. 具体开发中很少用到,可以去JavaScript的语言标准查阅。
    23. **[转换规则]11. 装箱转换**
    24. 1. 每一种基本类型Number、String、Boolean、Symbol在对象中都有对应的类,所谓装箱转换,正是把基本类型转换为对应的对象,他是类型转换中一种相当重要的种类。
    25. 2. 我们可以用一个函数的call方法强迫产生装箱,定义一个函数,里面只有return this,然后call到一个Symbol类型的值上,就会产生一个symbolObject。
    26. ```javascript
    27. var symbolObject = (function(){ return this; }).call(Symbol("a"));
    28. console.log(typeof symbolObject);// object
    29. console.log(symbolObject instanceof Symbol); // true
    30. console.log(symbolObject.constructor ==Symbol); // true
    1. 装箱机制会频繁产生临时对象,在对性能要求较高的场景下,我们应该尽量避免对基本类型做装箱转换。
      使用内置的Object函数,可以在JavaScript代码中显式调用装箱能力: ```javascript var symbolObject = Object(Symbol(“a”));

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

    1. <br />每一类装箱对象皆有私有的Class属性,这些属性可以用`Object.prototype.toString`获取:
    2. ```javascript
    3. var symbolObject = Object(Symbol("a"));
    4. console.log(Object.prototype.toString.call(symbolObject));//[object Symbol]


    在JavaScript中,没有任何方法可以更改私有的Class属性,因此Object.prototype.toString是可以准确识别对象对应的基本类型的方法,它比instanceof更加准确。
    但需要注意的是,call本身会产生装箱操作,所以需要配合typeof来区分基本类型还是对象类型。

    [转换规则]12. 拆箱转换

    1. 在JavaScript标准中,规定了ToPrimitive函数,他是对象类型到基本类型的转换(即拆箱转换)。
    2. 对象到String和Number的转换都遵循“先拆箱再转换”的规则。通过拆箱转换,把对象编程基本类型,再从基本类型转换为对应的String或者Number。
    3. 拆箱转换会尝试调用valueOftoString来获得拆箱后的基本类型。如果都不存在或者没有返回基本类型,则会产生类型错误TypeError
      1. var o = {
      2. valueof: () => { console.log("vauleof"); return {}},
      3. toString: () => { console.log("toString"); return {}}
      4. }
      5. o * 2
      6. // valueof
      7. // toString
      8. // TypeError


    我们定义了一个对象o,有valueOftoString两个方法,都返回了一个对象,然后进行o2运算时候,限制性了valueOf,接下来是toString,最后抛出了一个TypeError,说明这个拆箱转换失败了。
    到String的拆箱转换会优先调用toString。把刚才的运算从o * 2 变成o + “”,调用顺序变为:

    1. // toString
    2. // valueOf
    3. // TypeError
    1. ES6之后,还允许对象显式指定@toPrimitive Symbol来覆盖原有的行为。 ```javascript var o = { valurOf: () => {console.log(“valueOf”); return {}}, toString: () => {console.log(“toString”); return {}} }

    o[Symbol.toPrimitive] = () => {console.log(“toPrimitive”); return “hello”}

    console.log(o + “”) // toPrimitive // hello ```

    13. 结语

    1. 除了七种语言类型,还有一些语言的实现者更关心的规范类型。
      • List 和 Recort:用于描述函数传参过程。
      • Set:主要用于姐是字符集等。
      • Completion Record:用于描述异常、跳出等语句执行过程。
      • Reference: 用于描述对象属性访问、delete等。
      • Property Descriptor: 用于描述对象的属性。
      • Lexical Environment 和 Environment Record:用于描述变量和作用域。
      • Data Block:用于描述二进制数据。
        2. 补充:
      1. typeof和运行时类型的规定不一致的地方:

        多数是对的,但是object——Null和function——Object是特例。需要特别注意