前端学习手册

Undefined、Nulljavascript

一、为何有的编程规范要求用 void 0 代替 undefined?java

  Undefined 类型表示未定义,它的类型只有一个值,就是 undefined。任何变量在赋值前是 Undefined 类型、值为 undefined,通常咱们能够用全局变量 undefined(就是名为 undefined 的这个变量)来表达这个值,或者 void 运算来把任意一个表达式变成 undefined 值。由于 JavaScript 的代码 undefined 是一个变量,而并不是是一个关键字,这是 JavaScript 语言公认的设计失误之一,因此,咱们为了不无心中被篡改,我建议使用 void 0 来获取 undefined 值。Undefined 跟 Null 有必定的表意差异,Null 表示的是:“定义了可是为空”。因此,在实际编程时,咱们通常不会把变量赋值为 undefined,这样能够保证全部值为 undefined 的变量,都是从未赋值的天然状态。Null 类型也只有一个值,就是 null,它的语义表示空值,与 undefined 不一样,null 是 JavaScript 关键字,因此在任何代码中,你均可以放心用 null 关键字来获取 null 值。算法

String编程

一、string最大长度浏览器

String 用于表示文本数据。String 有最大长度是 2^53 - 1,这在通常开发中都是够用的,可是有趣的是,这个所谓最大长度,并不彻底是你理解中的字符数。由于 String 的意义并不是“字符串”,而是字符串的 UTF16 编码,咱们字符串的操做 charAt、charCodeAt、length 等方法针对的都是 UTF16 编码。因此,字符串的最大长度,其实是受字符串的编码长度影响的。函数

Note:现行的字符集国际标准,字符是以 Unicode 的方式表示的,每个 Unicode 的码点表示一个字符,理论上,Unicode 的范围是无限的。
UTF 是 Unicode 的编码方式,规定了码点在计算机中的表示方法,常见的有 UTF16 和 UTF8。
Unicode 的码点一般用 U+??? 来表示,其中 ??? 是十六进制的码点值。
0-65536(U+0000 - U+FFFF)的码点被称为基本字符区域(BMP)。

Number性能

值得注意的是,JavaScript 中有 +0 和 -0,在加法类运算中它们没有区别,可是除法的场合则须要特别留意区分,“忘记检测除以 -0,而获得负无穷大”的状况常常会致使错误,而区分 +0 和 -0 的方式,正是检测 1/x 是 Infinity 仍是 -Infinity。this

根据双精度浮点数的定义,Number 类型中有效的整数范围是 -0x1fffffffffffff 至 0x1fffffffffffff,因此 Number 没法精确表示此范围外的整数。一样根据浮点数的定义,非整数的 Number 类型没法用 ==(=== 也不行) 来比较,一段著名的代码,为何在 JavaScript 中,0.1+0.2 不能 =0.3:这是浮点运算的特色,也是不少同窗疑惑的来源,浮点数运算的精度问题致使等式左右的结果并非严格相等,而是相差了个微小的值。因此实际上,这里错误的不是结论,而是比较的方法,正确的比较方法是使用 JavaScript 提供的最小精度值:编码

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

  检查等式左右两边差的绝对值是否小于最小精度,才是正确的比较浮点数的方法。这段代码结果就是 true 了。spa

Symbol

Symbol 是 ES6 中引入的新类型,它是一切非字符串的对象 key 的集合,在 ES6 规范中,整个对象系统被用 Symbol 重塑。Symbol 能够具备字符串类型的描述,可是即便描述相同,Symbol 也不相等。咱们建立 Symbol 的方式是使用全局的 Symbol 函数。例如:

var mySymbol = Symbol("my symbol");

  一些标准中提到的 Symbol,能够在全局的 Symbol 函数的属性中找到。例如,咱们可使用 Symbol.iterator 来自定义 for…of 在对象上的行为:

var o = new Object;
o[Symbol.iterator] = function() {
   var v = 0
   return {
    next: function() {
       return { value: v++, done: v > 10 }
    }
  }
};
for(var v of o)
   console.log(v); // 0 1 2 3 ... 9

  代码中咱们定义了 iterator 以后,用 for(var v of o) 就能够调用这个函数,而后咱们能够根据函数的行为,产生一个 for…of 的行为。这里咱们给对象 o 添加了 Symbol.iterator 属性,而且按照迭代器的要求定义了一个 0 到 10 的迭代器,以后咱们就能够在 for of 中愉快地使用这个 o 对象啦。这些标准中被称为“众所周知”的 Symbol,也构成了语言的一类接口形式。它们容许编写与语言结合更紧密的 API。

Object

为何给对象添加的方法能用在基本类型上?

在 JavaScript 中,对象的定义是“属性的集合”。属性分为数据属性和访问器属性,两者都是 key-value 结构,key 能够是字符串或者 Symbol 类型。由于 C++ 和 Java 的成功,在这两门语言中,每一个类都是一个类型,两者几乎等同,以致于不少人经常会把 JavaScript 的“类”与类型混淆。事实上,JavaScript 中的“类”仅仅是运行时对象的一个私有属性,而 JavaScript 中是没法自定义类型的。

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.hello = () => console.log("hello");

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

  点运算符提供了装箱操做,它会根据基础类型构造一个临时对象,使得咱们能在基础类型上调用对应对象的方法

类型转换

 

   在不传入第二个参数的状况下,parseInt 只支持 16 进制前缀“0x”,并且会忽略非数字字符,也不支持科学计数法。在一些古老的浏览器环境中,parseInt 还支持 0 开头的数字做为 8 进制前缀,这是不少错误的来源。因此在任何环境下,都建议传入 parseInt 的第二个参数,而 parseFloat 则直接把原字符串做为十进制来解析,它不会引入任何的其余进制。多数状况下,Number 是比 parseInt 和 parseFloat 更好的选择。

在较小的范围内,数字到字符串的转换是彻底符合你直觉的十进制表示。当 Number 绝对值较大或者较小时,字符串表示则是使用科学计数法表示的。这个算法细节繁多,咱们从感性的角度认识,它其实就是保证了产生的字符串不会过长。

装箱转换

每一种基本类型 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 来区分基本类型仍是对象类型。

拆箱转换

在 JavaScript 标准中,规定了 ToPrimitive 函数,它是对象类型到基本类型的转换(即,拆箱转换)。对象到 String 和 Number 的转换都遵循“先拆箱再转换”的规则。经过拆箱转换,把对象变成基本类型,再从基本类型转换为对应的 String 或者 Number。拆箱转换会尝试调用 valueOf 和 toString 来得到拆箱后的基本类型。若是 valueOf 和 toString 都不存在,或者没有返回基本类型,则会产生类型错误 TypeError。

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

    o * 2
    // valueOf
    // toString
    // TypeError

  咱们进行 o*2 这个运算的时候,你会看见先执行了 valueOf,接下来是 toString,最后抛出了一个 TypeError,这就说明了这个拆箱转换失败了。到 String 的拆箱转换会优先调用 toString。咱们把刚才的运算从 o*2 换成 String(o),那么你会看到调用顺序就变了。

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

 String(o) 
// toString
// valueOf 
// TypeError        

  在 ES6 以后,还容许对象经过显式指定 @@toPrimitive Symbol 来覆盖原有的行为。

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

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


    console.log(o + "")
    // toPrimitive
    // hello

类型”在 JavaScript 中是一个有争议的概念。一方面,标准中规定了运行时数据类型; 另外一方面,JavaScript 语言中提供了 typeof 这样的运算,用来返回操做数的类型,但 typeof 的运算结果,与运行时类型的规定有不少不一致的地方。咱们能够看下表来对照一下。

 

 在表格中,多数项是对应的,可是请注意 object——Null 和 function——Object 是特例,咱们理解类型的时候须要特别注意这个区别。

相关文章
相关标签/搜索