翼度科技»论坛 编程开发 JavaScript 查看内容

【读书笔记】你不知道的JavaScript(中)

6

主题

6

帖子

18

积分

新手上路

Rank: 1

积分
18
第一部分 类型和语法

第一章 类型

JavaScript 有七种内置类型:
• 空值(null)
• 未定义(undefined)
• 布尔值( boolean)
• 数字(number)
• 字符串(string)
• 对象(object)
• 符号(symbol,ES6 中新增)
  1. typeof undefined === "undefined"; // true
  2. typeof true === "boolean"; // true
  3. typeof 42 === "number"; // true
  4. typeof "42" === "string"; // true
  5. typeof { life: 42 } === "object"; // true
  6. // ES6中新加入的类型
  7. typeof Symbol() === "symbol"; // true
  8. typeof null === "object"; // true
  9. // 使用复合条件来检测 null 值的类型
  10. var a = null;
  11. (!a && typeof a === "object"); // true
  12. typeof function a(){ /* .. */ } === "function"; // true 是 object 的一个“子类型”
  13. typeof [1,2,3] === "object"; // true 也是 object 的一个“子类型”
复制代码
JavaScript 中的变量是没有类型的,只有值才有。变量可以随时持有任何类型的值。
已在作用域中声明但还没有赋值的变量,是 undefined 的。相反,还没有在作用域中声明过的变量,是 undeclared 的。
直接调用 undefined 的变量不会报错,但是直接调用 undeclared 的会报错,所以判断变量的 typeof 比直接判断变量更安全;如下;
  1. var a;
  2. if (a) {} //这里会报错;
  3. if (typeof a === undefined) {} // 这里不会报错
  4. if (window.a) {} // 这种方式也可以
复制代码
第二章 值

数组

字符串键值能够被强制类型转换为十进制数字的话,它就会被当作数字索引来处理。如
  1. var a = [];
  2. a["123"] = 23;
  3. a.length;// 124
复制代码
类数组转换成数组:

  • Array.prototype.slice.call(arguments);
  • Array.from(arguements);
字符串

JavaScript 中字符串是不可变的,而数组是可变的。
字符串不可变是指字符串的成员函数不会改变其原始值,而是创建并返回一个新的字符串。而数组的成员函数都是在其原始值上进行操作。
  1. c = a.toUpperCase();
  2. a === c; // false
  3. a; // "foo"
  4. c; // "FOO"
  5. // 字符串反转
  6. var c = a
  7. // 将a的值转换为字符数组
  8. .split( "" )
  9. // 将数组中的字符进行倒转
  10. .reverse()
  11. // 将数组中的字符拼接回字符串
  12. .join( "" );
复制代码
数字

JavaScript 只有一种数值类型:number(数字),包括“整数”和带小数的十进制数。
  1. // tofixed指定小数位数
  2. var a = 1.234
  3. a.toFixed(0);// 1;
  4. a.toFixed(4);// 1.2340
  5. // toPrecision(..) 方法用来指定有效数位的显示位数
  6. var a = 42.59;
  7. a.toPrecision( 1 ); // "4e+1"
  8. a.toPrecision( 2 ); // "43"
  9. a.toPrecision( 3 ); // "42.6"
  10. a.toPrecision( 4 ); // "42.59"
  11. a.toPrecision( 5 ); // "42.590"
  12. a.toPrecision( 6 ); // "42.5900"
  13. 42.toFixed( 3 ); // SyntaxError
  14. // 下面的语法都有效:
  15. (42).toFixed( 3 ); // "42.000"
  16. 0.42.toFixed( 3 ); // "0.420"
  17. 42..toFixed( 3 ); // "42.000"
  18. 42 .toFixed(3); // "42.000" 注意其中的空格
  19. var onethousand = 1E3; // 即 1 * 10^3
  20. var onemilliononehundredthousand = 1.1E6; // 即 1.1 * 10^6
  21. 0xf3; // 243的十六进制
  22. 0Xf3; // 同上
  23. 0363; // 243的八进制
  24. // 从 ES6 开始,严格模式(strict mode)不再支持 0363 八进制格式(新格式如
  25. //下)。0363 格式在非严格模式(non-strict mode)中仍然受支持,但是考虑到
  26. //将来的兼容性,最好不要再使用(我们现在使用的应该是严格模式)。
  27. 0o363; // 243的八进制
  28. 0O363; // 同上
  29. 0b11110011; // 243的二进制
  30. 0B11110011; // 同上
  31. 0.1 + 0.2 === 0.3 // false 合适因为在js中,二进制浮点数不是十分精确的
  32. console.log(.1 + .2); // 0.30000000000000004
复制代码
如何解决浮点数不精确的问题:最常见的方法是设置一个误差范围值,通常称为“机器精度”(machine epsilon),对 JavaScript 的数字来说,这个值通常是 2^-52 (2.220446049250313e-16)。从 ES6 开始,该值定义在 Number.EPSILON 中;
  1. // polyfill
  2. if (!Number.EPSILON) {
  3. Number.EPSILON = Math.pow(2,-52);
  4. }
  5. function numbersCloseEnoughToEqual(n1,n2) {
  6. return Math.abs( n1 - n2 ) < Number.EPSILON;
  7. }
  8. var a = 0.1 + 0.2;
  9. var b = 0.3;
  10. numbersCloseEnoughToEqual( a, b ); // true
  11. numbersCloseEnoughToEqual( 0.0000001, 0.0000002 ); // false
复制代码
PS:会有误差的原因是:计算机是通过二进制的方式存储数据的,在相加的时候,是拿二进制去相加,0.1 的二进制是 0.0001100110011001100...(1100 循环),0.2 的二进制是:0.00110011001100...(1100 循环);在 JavaScript 的 Number 实现遵循 IEEE 754 标准,使用 64 位固定长度来表示,也就是标准的 double 双精度浮点数。在二进制科学表示法中,双精度浮点数的小数部分最多只能保留 52 位,再加上前面的 1,其实就是保留 53 位有效数字,剩余的需要舍去,遵从“0 舍 1 入”的原则。
JS 中能够被最大呈现的整数为:2^53 - 1,即 9007199254740991,在 ES6 中被定义为 Number.MAX_SAFE_INTEGER。最小整数是 -9007199254740991,在 ES6 中被定义为 Number.MIN_SAFE_INTEGER。如果需要精确呈现,需要将其转成 string;
整数检测:
  1. // es6 是否是整数
  2. Number.isInteger( 42 ); // true
  3. Number.isInteger( 42.000 ); // true
  4. Number.isInteger( 42.3 ); // false
  5. // polyfill
  6. if (!Number.isInteger) {
  7. Number.isInteger = function(num) {
  8. return typeof num == "number" && num % 1 == 0;
  9. };
  10. }
  11. // es6 是否是安全的整数
  12. Number.isSafeInteger( Number.MAX_SAFE_INTEGER ); // true
  13. Number.isSafeInteger( Math.pow( 2, 53 ) ); // false
  14. Number.isSafeInteger( Math.pow( 2, 53 ) - 1 ); // true
  15. // 最大安全数为什么不安全?
  16. 2**53 //9007199254740992
  17. 2**53 + 1 //9007199254740992
  18. 2**53 + 2 //9007199254740994
  19. 2**53 + 3 //9007199254740996
  20. 2**53 + 4 //9007199254740996
  21. // polyfill
  22. if (!Number.isSafeInteger) {
  23. Number.isSafeInteger = function(num) {
  24. return Number.isInteger( num ) &&
  25. Math.abs( num ) <= Number.MAX_SAFE_INTEGER;
  26. };
  27. }
复制代码
特殊等式 Object.is
  1. var a = 42;
  2. console.log( void a, a ); // undefined 42
复制代码
简单值通过值复制的方式来赋值 / 传递,包括 null、undefined、字符串、数字、布尔和 ES6 中的 symbol。
复合值(compound value)——对象(包括数组和封装对象)和函数,则总是通过引用复制的方式来赋值 / 传递。
第三章 原生函数

内部属性 [[Class]]
  1. var a = 2 / "foo";
  2. a == NaN; // false
  3. a === NaN; // false
  4. var a = 2 / "foo";
  5. var b = "foo";
  6. a; // NaN
  7. b; "foo"
  8. window.isNaN( a ); // true
  9. window.isNaN( b ); // true——晕! 只要用数值除以某个变量,这个变量就是NaN
  10. 2/{};
  11. isNaN({})// true;
  12. // es6有Number.isNaN
  13. // polyfill
  14. if (!Number.isNaN) {
  15. Number.isNaN = function(n) {
  16. return (
  17. typeof n === "number" &&
  18. window.isNaN( n )
  19. );
  20. };
  21. }
  22. var a = 2 / "foo";
  23. var b = "foo";
  24. Number.isNaN( a ); // true
  25. Number.isNaN( b ); // false——好!
  26. // 还有个更为简单的方法 即利用 NaN 不等于自身这个特点
  27. if (!Number.isNaN) {
  28. Number.isNaN = function(n) {
  29. return n !== n;
  30. };
  31. }
复制代码
封装对象包装:  由于基本类型值没有 .length 和 .toString() 这样的属性和方法,需要通过封装对象才能访问,此时 JavaScript 会自动为基本类型值包装(box 或者 wrap)一个封装对象;
拆封:得到封装对象中的基本类型值,可以使用 valueOf() 函数;
构造函数 Array(..) 不要求必须带 new 关键字。不带时,它会被自动补上。因此 Array(1,2,3) 和 new Array(1,2,3) 的效果是一样的。
稀疏数组:将包含至少一个“空单元”的数组;
  1. console.log(1 / 0); // Infinity
  2. console.log(-1 / 0); // -Infinity
  3. a = Number.MAX_VALUE;
  4. // 就近取整模式
  5. console.log(a); // 1.7976931348623157e+308
  6. console.log(a * 2); // Infinity
  7. console.log(a + Math.pow( 2, 969 )); // 1.7976931348623157e+308
  8. console.log(Infinity / Infinity); // NaN
  9. 1/Infinity // 0
  10. 1/-Infinity //-0
  11. Infinity / 1 //Infinity
  12. -Infinity / 1 //-Infinity
复制代码
永远不要创建和使用空单元数组
除非万不得已,否则尽量不要使用 Object(..)/Function(..)/RegExp(..)
RegExp(..) 有时还是很有用的,比如动态定义正则表达式时
Date(..) 和 Error(..)
  1. // 加法和减法运算不会得到负零(negative zero)
  2. var a = 0 / -3;
  3. // 至少在某些浏览器的控制台中显示是正确的
  4. console.log(a); // -0
  5. // 但是规范定义的返回结果是这样!
  6. console.log(a.toString()); // "0"
  7. console.log(a + ""); // "0"
  8. console.log(String( a )); // "0"
  9. // JSON也如此,很奇怪
  10. console.log(JSON.stringify( a )); // "0"
  11. console.log(+"-0"); // -0
  12. console.log(Number( "-0") ); // -0
  13. console.log(JSON.parse( "-0" )); // -0 JSON.stringify(-0) 返回 "0",而 JSON.parse("-0") 返回 -0。
  14. var a = 0;
  15. var b = 0 / -3;
  16. console.log(a == b); // true
  17. console.log(-0 == 0); // true
  18. console.log(a === b); // true
  19. console.log(-0 === 0); // true
  20. console.log(0 > -0); // false
  21. console.log(a > b); // false
  22. // 是否是-0
  23. function isNegZero(n) {
  24.     n = Number( n );
  25.     return (n === 0) && (1 / n === -Infinity);
  26. }
  27. isNegZero( -0 ); // true
  28. isNegZero( 0 / -3 ); // true
  29. isNegZero( 0 ); // false
复制代码
如果调用 Date() 时不带 new 关键字,则会得到当前日期的字符串值。其具体格式规范没有规定,浏览器使用 "Fri Jul 18 2014 00:31:02 GMT-0500 (CDT)"这样的格式来显示。
String#indexOf(..):在字符串中找到指定子字符串的位置。
String#charAt(..):获得字符串指定位置上的字符。
String#substr(..)、String#substring(..) 和 String#slice(..) :获得字符串的指定部分。
String#toUpperCase() 和 String#toLowerCase():将字符串转换为大写或小写。
String#trim():去掉字符串前后的空格,返回新的字符串。
以上方法并不改变原字符串的值,而是返回一个新字符串。
  1. var a = 2 / "foo";
  2. var b = -3 * 0;
  3. Object.is( a, NaN ); // true
  4. Object.is( b, -0 ); // true
  5. Object.is( b, 0 ); // false
  6. // polyfill
  7. if (!Object.is) {
  8.     Object.is = function(v1, v2) {
  9.         // 判断是否是-0
  10.         if (v1 === 0 && v2 === 0) {
  11.             return 1 / v1 === 1 / v2;
  12.         }
  13.         // 判断是否是NaN
  14.         if (v1 !== v1) {
  15.             return v2 !== v2;
  16.         }
  17.         // 其他情况
  18.         return v1 === v2;
  19.     };
  20. }
复制代码
第四章 强制类型转换

类型转换发生在静态类型语言的编译阶段,而强制类型转换则发生在动态类型语言的运行时(runtime)。
抽象值操作

toString


  • 该方法可重新定义;
  • JSON.stringfy 在将 JSON 对象序列化为字符串时也用到了 ToString 在对象中遇到 undefined、function 和 symbol 时会自动将其忽略,在数组中则会返回 null(以保证单元位置不变)。
JSON.stringify(value[, replacer[, space]])



    • value: 必需, 要转换的 JavaScript 值(通常为对象或数组)。
    • replacer: 可选。用于转换结果的函数或数组。
      如果 replacer 为函数,则 JSON.stringify 将调用该函数,并传入每个成员的键和值。使用返回值而不是原始值。如果此函数返回 undefined,则排除成员。根对象的键是一个空字符串:""。
      如果 replacer 是一个数组,则仅转换该数组中具有键值的成员。成员的转换顺序与键在数组中的顺序一样。
    • space: 可选,文本添加缩进、空格和换行符,如果 space 是一个数字,则返回值文本在每个级别缩进指定数目的空格,如果 space 大于 10,则文本缩进 10 个空格。space 也可以使用非数字,如:\t。

(1) 字符串、数字、布尔值和 null 的 JSON.stringify(..) 规则与 ToString 基本相同。
(2) 如果传递给 JSON.stringify(..) 的对象中定义了 toJSON() 方法,那么该方法会在字符串化前调用,以便将对象转换为安全的 JSON 值。
JSON.stringify(..) 并不是强制类型转换。在这里介绍是因为它涉及 ToString 强制类型转换
  1. Object.prototype.toString.call( [1,2,3] );
  2. // "[object Array]"
  3. Object.prototype.toString.call( /regex-literal/i );
  4. // "[object RegExp]"
  5. Object.prototype.toString.call( null );
  6. // "[object Null]"
  7. Object.prototype.toString.call( undefined );
  8. // "[object Undefined]"
  9. Object.prototype.toString.call( "abc" );
  10. // "[object String]"
  11. Object.prototype.toString.call( 42 );
  12. // "[object Number]"
  13. Object.prototype.toString.call( true );
  14. // "[object Boolean]"
复制代码
ToNumber

true 转换为 1,false 转换为 0。undefined 转换为 NaN,null 转换为 0。
为了将值转换为相应的基本类型值,抽象操作 ToPrimitive(参见 ES5 规范 9.1 节)会首先检查该值是否有 valueOf() 方法。如果有并且返回基本类型值,就使用该值进行强制类型转换。如果没有就使用 toString()的返回值(如果存在)来进行强制类型转换。如果 valueOf() 和 toString() 均不返回基本类型值,会产生 TypeError 错误。
  1. var a = new Array( 3 );
  2. var b = [ undefined, undefined, undefined ];
  3. var c = [];
  4. c.length = 3;
  5. a.join( "-" ); // "--"
  6. b.join( "-" ); // "--"
  7. a.map(function(v,i){ return i; }); // [ undefined x 3 ]
  8. b.map(function(v,i){ return i; }); // [ 0, 1, 2 ]
  9. var a = Array.apply( null, { length: 3 } );// 等同于Array(undefined, undefined, undefined)
  10. a; // [ undefined, undefined, undefined ]
复制代码
ToBoolean

假值:
• undefined
• null
• false
• +0、-0 和 NaN
• ""
假值列表以外的值都是真值。
假值对象(falsy object)
document.all 浏览器自带的来判断浏览器是否是老版本的 IE。
:if(document.all) { /_ it’s IE _/ }
  1. // Date.now的polyfill
  2. if (!Date.now) {
  3. Date.now = function(){
  4. return (new Date()).getTime();
  5. };
  6. }
复制代码
显式强制类型转换
  1. var a = 'abcde';
  2. a.charAt(1) // 'b'
  3. a.charCodeAt(1) //98
  4. a.concat(1) //'abcde1'
  5. a.endsWith('e') // true
  6. a.indexOf('b') //1
  7. a.includes('b') //true
  8. a.lastIndexOf('d') //3
  9. a.match(/e/g) //['e']
  10. a.repeat()//''
  11. a.repeat(2)//'abcdeabcde'
  12. a.replace('d', 4)//'abc4e'
  13. a.replaceAll('e', '5')//'abcd5'
  14. a.search(/b/)//1
  15. a.slice(0, 1)//'a'
  16. a.split('')//(5) ['a', 'b', 'c', 'd', 'e']
  17. a.startsWith(1)//false
  18. a.substr(0,2) // 'ab' 第一个参数 起始下标 第二参数 长度
  19. a.substring(1,2)//'b' 第一个参数 起始下标 第二个参数 结束下标
  20. 'B'.toLowerCase()//'b'
  21. a.toUpperCase()//'ABCDE'
  22. ' fd '.trim() //'fd'
复制代码
一元运算符 - 和 + 一样,并且它还会反转数字的符号位。由于 -- 会被当作递减运算符来处理,所以我们不能使用 -- 来撤销反转,而应该像 - -"3.14" 这样,在中间加一个空格,才能得到正确结果 3.14。
  1. JSON.stringify( undefined ); // undefined
  2. JSON.stringify( function(){} ); // undefined
  3. JSON.stringify(
  4. [1,undefined,function(){},4]
  5. ); // "[1,null,null,4]"
  6. JSON.stringify(
  7. { a:2, b:function(){} }
  8. ); // "{"a":2}"
  9. // 对包含循环引用的对象执行 JSON.stringify(..) 会出错。
  10. // Uncaught TypeError: Converting circular structure to JSON
  11. //     --> starting at object with constructor 'Object'
  12. //     |     property 'c' -> object with constructor 'Object'
  13. var o = { };
  14. var a = {
  15. b: 42,
  16. c: o,
  17. d: function(){}
  18. };
  19. // 在a中创建一个循环引用
  20. o.e = a;
  21. // 循环引用在这里会产生错误
  22. // JSON.stringify( a );
  23. // 自定义的JSON序列化 toJSON() 应该“返回一个能够被字符串化的安全的 JSON 值”,而不是“返回一个 JSON 字符串”。
  24. a.toJSON = function() {
  25. // 序列化仅包含b
  26. return { b: this.b };
  27. };
  28. JSON.stringify( a ); // "{"b":42}"
  29. var a = {
  30. b: 42,
  31. c: "42",
  32. d: [1,2,3]
  33. };
  34. JSON.stringify( a, ["b","c"] ); // "{"b":42,"c":"42"}"
  35. JSON.stringify( a, function(k,v){
  36. if (k !== "c") return v;
  37. } );
  38. // "{"b":42,"d":[1,2,3]}"
  39. var a = {
  40. b: 42,
  41. c: "42",
  42. d: [1,2,3]
  43. };
  44. JSON.stringify( a, null, 3 );
  45. // "{
  46. //                 "b": 42,
  47. //                 "c": "42",
  48. //                 "d": [
  49. //                                 1,
  50. //                                 2,
  51. //                                 3
  52. //                 ]
  53. // }"
  54. JSON.stringify( a, null, "-----" );
  55. // "{
  56. // -----"b": 42,
  57. // -----"c": "42",
  58. // -----"d": [
  59. // ----------1,
  60. // ----------2,
  61. // ----------3
  62. // -----]
  63. // }"
复制代码
JavaScript 有一处奇特的语法,即构造函数没有参数时可以不用带 ()。于是我们可能会碰到 var timestamp = +new Date; 这样的写法。这样能否提高代码可读性还存在争议,因为这仅用于 new fn(),对一般的函数调用 fn() 并不适用。
奇特的 ~ 运算符
~x 大致等同于 -(x+1)。
用法
  1. var a = {
  2. valueOf: function(){
  3. return "42";
  4. }
  5. };
  6. var b = {
  7. toString: function(){
  8. return "42";
  9. }
  10. };
  11. var c = [4,2];
  12. c.toString = function(){
  13. return this.join( "" ); // "42"
  14. };
  15. Number( a ); // 42
  16. Number( b ); // 42
  17. Number( c ); // 42
  18. Number( "" ); // 0
  19. Number( [] ); // 0
  20. Number( [ "abc" ] ); // NaN
复制代码
字位截除
~~x 能将值截除为一个 32 位整数,x | 0 也可以,而且看起来还更简洁。
  1. var a = "false";
  2. var b = "0";
  3. var c = "''";
  4. var d = Boolean( a && b && c );// "''"是真值
复制代码
显式解析数字字符串
解析允许字符串中含有非数字字符,解析按从左到右的顺序,如果遇到非数字字符就停止。而转换不允许出现非数字字符,否则会失败并返回 NaN。解析字符串中的浮点数可以使用 parseFloat(..) 函数;
  1. var a = 42;
  2. var b = String( a );// String(..) 遵循前面讲过的 ToString 规则
  3. // var b = a.toString(); 同上
  4. var c = "3.14";
  5. var d = Number( c ); // Number(..) 遵循前面讲过的 ToNumber 规则
  6. // var d = +c; 同上   +是运算符的一元(unary)形式
  7. b; // "42"
  8. d; // 3.14
  9. var c = "3.14";
  10. var d = 5+ +c;
  11. d; // 8.14
复制代码
ES5 之前的 parseInt(..) 有一个坑导致了很多 bug。即如果没有第二个参数来指定转换的基数(又称为 radix),parseInt(..) 会根据字符串的第一个字符来自行决定基数。从 ES5 开始 parseInt(..) 默认转换为十进制数,除非另外指定。如果你的代码需要在 ES5 之前的环境运行,请记得将第二个参数设置为 10。
解析非字符串
  1. 1 + - + + + - + 1; // 2 负负得正
  2. +new Date();// 日期显式转换为数字
  3. Date.now();// 比较好的写法
  4. // Date.now的polyfill 不建议对日期类型使用强制类型转换,应该使用 Date.now() 来获得当前的时间戳,使用 new Date(..).getTime() 来获得指定时间的时间戳。
  5. if (!Date.now) {
  6. Date.now = function() {
  7. return +new Date();
  8. };
  9. }
复制代码
parseInt(1/0, 19) 实际上是 parseInt("Infinity", 19)。第一个字符是 "I",以 19 为基数时值为 18。第二个字符 "n" 不是一个有效的数字字符,解析到此为止,和 "42px" 中的 "p"一样
显式转换为布尔值: 使用 Boolean(a) 和 !!a 来进行显式强制类型转换
隐式强制类型转换: 代码可读性不好,但是也是可以减少冗余,让代码更简洁 抽象和隐藏那些细枝末节,有助于提高代码的可读性
字符串和数字之间的隐式强制类型转换
  1. if (!~a.indexOf( "ol" )) { // true 这里~ 比 >= 0 和 == -1 更简洁。
  2. // 没有找到匹配!
  3. }
  4. ~12.1 // -13
  5. -(12.1+1) // -13.1
  6. ~~12.1 // 12
复制代码
a + "" 会对 a 调用 valueOf() 方法,然后通过 ToString 抽象操作将返回值转换为字符串。
  1. Math.floor( -49.6 ); // -50
  2. ~~-49.6; // -49
  3. ~~1E20 / 10; // 166199296
  4. 1E20 | 0 / 10; // 1661992960
  5. (1E20 | 0) / 10; // 166199296
复制代码
布尔值到数字的隐式强制类型转换
如果其中有且仅有一个参数为 true,则 onlyOne(..) 返回 true。
  1. var a = "42";
  2. var b = "42px";
  3. Number( a ); // 42
  4. parseInt( a ); // 42
  5. Number( b ); // NaN
  6. parseInt( b ); // 42
复制代码
|| 和 &&:选择器运算符”(selector operators)或者“操作数选择器运算符”(operand selector operators)
在 a ? a : b 中,如果 a 是一个复杂一些的表达式(比如有副作用的函数调用等),它有可能被执行两次(如果第一次结果为真)。而在 a || b 中 a 只执行一次,其结果用于条件判断和返回结果(如果适用的话)。
Symbol 符号的强制类型转换
  1. parseInt( 1/0, 19 ); // 18
  2. parseInt( 0.000008 ); // 0 ("0" 来自于 "0.000008")
  3. parseInt( 0.0000008 ); // 8 ("8" 来自于 "8e-7")
  4. parseInt( false, 16 ); // 250 ("fa" 来自于 "false")
  5. parseInt( parseInt, 16 ); // 15 ("f" 来自于 "function..")
  6. parseInt( "0x10" ); // 16
  7. parseInt( "103", 2 ); // 2 3在二进制中不存在,所以取10 转10进制 为2
复制代码
== 允许在相等比较中进行强制类型转换,而 === 不允许
== 和 === 都会检查操作数的类型。区别在于操作数类型不同时它们的处理方式不同。
抽象相等:==
字符串和数字之间的相等比较:
(1) 如果 Type(x) 是数字,Type(y) 是字符串,则返回 x == ToNumber(y) 的结果。
(2) 如果 Type(x) 是字符串,Type(y) 是数字,则返回 ToNumber(x) == y 的结果。
其他类型和布尔类型之间的相等比较:
(1) 如果 Type(x) 是布尔类型,则返回 ToNumber(x) == y 的结果;
(2) 如果 Type(y) 是布尔类型,则返回 x == ToNumber(y) 的结果。
null 和 undefined 之间的相等比较:
(1) 如果 x 为 null,y 为 undefined,则结果为 true。
(2) 如果 x 为 undefined,y 为 null,则结果为 true。
  1. var a = [1,2];
  2. var b = [3,4];
  3. a + b; // "1,23,4" 数组的valueOf() 操作无法得到简单基本类型值,于是它转而调用 toString()
复制代码
a == null 等价为 a=null&& a=undefined
对象和非对象之间的相等比较:
(1) 如果 Type(x) 是字符串或数字,Type(y) 是对象,则返回 x == ToPrimitive(y) 的结果;
(2) 如果 Type(x) 是对象,Type(y) 是字符串或数字,则返回 ToPromitive(x) == y 的结果。
  1. var a = {
  2. valueOf: function() { return 42; },
  3. toString: function() { return 4; }
  4. };
  5. a + ""; // "42"
  6. String( a ); // "4"
复制代码
因为没有对应的封装对象,所以 null 和 undefined 不能够被封装(boxed),Object(null)和 Object() 均返回一个常规对象。NaN 能够被封装为数字封装对象,但拆封之后 NaN == NaN 返回 false,因为 NaN 不等于 NaN
  1. function onlyOne() {
  2.    var sum = 0;
  3.    for (var i=0; i < arguments.length; i++) {
  4.      // 跳过假值,和处理0一样,但是避免了NaN
  5.      if (arguments[i]) {
  6.                      sum += arguments[i];
  7.      }
  8.    }
  9.    return sum == 1;
  10. }
  11. var a = true;
  12. var b = false;
  13. onlyOne( b, a ); // true
  14. onlyOne( b, a, b, b, b ); // true
复制代码
安全运用隐式强制类型转换
如果两边的值中有 true 或者 false,千万不要使用 ==。
如果两边的值中有 []、"" 或者 0,尽量不要使用 ==。
抽象关系比较

比较双方首先调用 ToPrimitive,如果结果出现非字符串,就根据 ToNumber 规则将双方强制类型转换为数字来进行比较。
  1. var s1 = Symbol( "cool" );
  2. String( s1 ); // "Symbol(cool)"
  3. var s2 = Symbol( "not cool" );
  4. s2 + ""; // TypeError
复制代码
4.6 生成器并发

通信顺序进程(Communicating Sequential Processes,CSP): 讲道理 这里没看懂 跳过吧
4.7 形式转换程序

形实转换程序(thunk):JavaScript 中的 thunk 是指一个用于调用另外一个函数的函数,没有任何参数
  1. var a = 42;
  2. var b = "42";
  3. a === b; // false
  4. a == b; // true
  5. var x = true;
  6. var y = "42";
  7. x == y; // false
  8. var a = null;
  9. var b;
  10. a == b; // true
  11. a == null; // true
  12. b == null; // true
  13. a == false; // false
  14. b == false; // false
  15. a == ""; // false
  16. b == ""; // false
  17. a == 0; // false
  18. b == 0; // false
复制代码
第 5 章 程序性能

5.1 Web Worker

Worker 之间以及它们和主程序之间,不会共享任何作用域或资源,那会把所有多线程编程 的噩梦带到前端领域,而是通过一个基本的事件消息机制相互联系。
Web Worker 通常应用于哪些方面呢?

  • 处理密集型数学计算
  • 大数据集排序
  • 数据处理(压缩、音频分析、图像处理等)
高流量网络通信
使用 worker 的应用需要在线程之间通过事件机制传递大量的信息,可能是双向的。
在早期的 Worker 中,唯一的选择就是把所有数据序列化到一个字符串值中。除了双向序 列化导致的速度损失之外,另一个主要的负面因素是数据需要被复制,这意味着两倍的内 存使用(及其引起的垃圾收集方面的波动)。
如果要传递一个对象,可以使用结构化克隆算法,这样就不用付出 to-string 和 from-string 的性能损失了,但是这种方案还是要使用双倍的内存。IE10 及更高版本以及所有其他主流浏览器都支持这种方案。
还有一个更好的选择,特别是对于大数据集而言,就是使用 Transferable 对象
这时发生的是对象所 有权的转移,数据本身并没有移动。一旦你把对象传递到一个 Worker 中,在原来的位置 上,它就变为空的或者是不可访问的,这样就消除了多线程编程作用域共享带来的混乱。 当然,所有权传递是可以双向进行的。
共享 Worker: 整个站点或 app 的所有页面实例都可以共享的中心 Worker;
如果有某个端口连接终止而其他端口连接仍然活跃,那么共享 Worker 不会 终止。而对专用 Worker 来说,只要到实例化它的程序的连接终止,它就会 终止。
第 6 章 性能测试和调优

6.1 性能测试

用重复运行,然后取平均值去测试性能误差会比较大,应该基于统计学去实践,比如 Benchmark.js 库;
6.2 环境为王

测试不真实的代码只能得出不真实的结论。如果有实际可能的话,你 应该测试实际的而非无关紧要的代码,测试条件与你期望的真实情况越接近越好。只有这 样得出的结果才有可能接近事实。
6.3 jsPerf.com

如果你想要得到可靠的测试结论的话,就需要在很多不同的环境(桌面浏览 器、移动设备,等等)中测试汇集测试结果。
6.4 写好测试用例

写有意义的测试用例;
6.5 微性能

只考虑微性能是不合理的,可读性,包括一些浏览器不同处理问题也要考虑;
6.6 尾调用优化

a
尾调用:一个出现在另一个函数“结尾”处的函数调用;
调用一个新的函数需要额外的一块预留内存来管理调用 栈,称为栈;
尾调用优化(TCO):不需要创建一个新的栈帧,而是可以重用已 有的 bar(..) 的栈帧。这样不仅速度更快,也更节省内存
TCO 允许一个函数在结尾处调用另外一个函数来执行,不需要任何额外资源。这意味着,对递归算法来说,引擎不再需要限制栈深度。

来源:https://www.cnblogs.com/NSGUF/p/17213579.html
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!

举报 回复 使用道具