|
第一部分 类型和语法
第一章 类型
JavaScript 有七种内置类型:
• 空值(null)
• 未定义(undefined)
• 布尔值( boolean)
• 数字(number)
• 字符串(string)
• 对象(object)
• 符号(symbol,ES6 中新增)- typeof undefined === "undefined"; // true
- typeof true === "boolean"; // true
- typeof 42 === "number"; // true
- typeof "42" === "string"; // true
- typeof { life: 42 } === "object"; // true
- // ES6中新加入的类型
- typeof Symbol() === "symbol"; // true
- typeof null === "object"; // true
- // 使用复合条件来检测 null 值的类型
- var a = null;
- (!a && typeof a === "object"); // true
- typeof function a(){ /* .. */ } === "function"; // true 是 object 的一个“子类型”
- typeof [1,2,3] === "object"; // true 也是 object 的一个“子类型”
复制代码 JavaScript 中的变量是没有类型的,只有值才有。变量可以随时持有任何类型的值。
已在作用域中声明但还没有赋值的变量,是 undefined 的。相反,还没有在作用域中声明过的变量,是 undeclared 的。
直接调用 undefined 的变量不会报错,但是直接调用 undeclared 的会报错,所以判断变量的 typeof 比直接判断变量更安全;如下;- var a;
- if (a) {} //这里会报错;
- if (typeof a === undefined) {} // 这里不会报错
- if (window.a) {} // 这种方式也可以
复制代码 第二章 值
数组
字符串键值能够被强制类型转换为十进制数字的话,它就会被当作数字索引来处理。如- var a = [];
- a["123"] = 23;
- a.length;// 124
复制代码 类数组转换成数组:
- Array.prototype.slice.call(arguments);
- Array.from(arguements);
字符串
JavaScript 中字符串是不可变的,而数组是可变的。
字符串不可变是指字符串的成员函数不会改变其原始值,而是创建并返回一个新的字符串。而数组的成员函数都是在其原始值上进行操作。- c = a.toUpperCase();
- a === c; // false
- a; // "foo"
- c; // "FOO"
- // 字符串反转
- var c = a
- // 将a的值转换为字符数组
- .split( "" )
- // 将数组中的字符进行倒转
- .reverse()
- // 将数组中的字符拼接回字符串
- .join( "" );
复制代码 数字
JavaScript 只有一种数值类型:number(数字),包括“整数”和带小数的十进制数。- // tofixed指定小数位数
- var a = 1.234
- a.toFixed(0);// 1;
- a.toFixed(4);// 1.2340
- // toPrecision(..) 方法用来指定有效数位的显示位数
- var a = 42.59;
- a.toPrecision( 1 ); // "4e+1"
- a.toPrecision( 2 ); // "43"
- a.toPrecision( 3 ); // "42.6"
- a.toPrecision( 4 ); // "42.59"
- a.toPrecision( 5 ); // "42.590"
- a.toPrecision( 6 ); // "42.5900"
- 42.toFixed( 3 ); // SyntaxError
- // 下面的语法都有效:
- (42).toFixed( 3 ); // "42.000"
- 0.42.toFixed( 3 ); // "0.420"
- 42..toFixed( 3 ); // "42.000"
- 42 .toFixed(3); // "42.000" 注意其中的空格
- var onethousand = 1E3; // 即 1 * 10^3
- var onemilliononehundredthousand = 1.1E6; // 即 1.1 * 10^6
- 0xf3; // 243的十六进制
- 0Xf3; // 同上
- 0363; // 243的八进制
- // 从 ES6 开始,严格模式(strict mode)不再支持 0363 八进制格式(新格式如
- //下)。0363 格式在非严格模式(non-strict mode)中仍然受支持,但是考虑到
- //将来的兼容性,最好不要再使用(我们现在使用的应该是严格模式)。
- 0o363; // 243的八进制
- 0O363; // 同上
- 0b11110011; // 243的二进制
- 0B11110011; // 同上
- 0.1 + 0.2 === 0.3 // false 合适因为在js中,二进制浮点数不是十分精确的
- console.log(.1 + .2); // 0.30000000000000004
复制代码 如何解决浮点数不精确的问题:最常见的方法是设置一个误差范围值,通常称为“机器精度”(machine epsilon),对 JavaScript 的数字来说,这个值通常是 2^-52 (2.220446049250313e-16)。从 ES6 开始,该值定义在 Number.EPSILON 中;- // polyfill
- if (!Number.EPSILON) {
- Number.EPSILON = Math.pow(2,-52);
- }
- function numbersCloseEnoughToEqual(n1,n2) {
- return Math.abs( n1 - n2 ) < Number.EPSILON;
- }
- var a = 0.1 + 0.2;
- var b = 0.3;
- numbersCloseEnoughToEqual( a, b ); // true
- 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;
整数检测:- // es6 是否是整数
- Number.isInteger( 42 ); // true
- Number.isInteger( 42.000 ); // true
- Number.isInteger( 42.3 ); // false
- // polyfill
- if (!Number.isInteger) {
- Number.isInteger = function(num) {
- return typeof num == "number" && num % 1 == 0;
- };
- }
- // es6 是否是安全的整数
- Number.isSafeInteger( Number.MAX_SAFE_INTEGER ); // true
- Number.isSafeInteger( Math.pow( 2, 53 ) ); // false
- Number.isSafeInteger( Math.pow( 2, 53 ) - 1 ); // true
- // 最大安全数为什么不安全?
- 2**53 //9007199254740992
- 2**53 + 1 //9007199254740992
- 2**53 + 2 //9007199254740994
- 2**53 + 3 //9007199254740996
- 2**53 + 4 //9007199254740996
- // polyfill
- if (!Number.isSafeInteger) {
- Number.isSafeInteger = function(num) {
- return Number.isInteger( num ) &&
- Math.abs( num ) <= Number.MAX_SAFE_INTEGER;
- };
- }
复制代码 特殊等式 Object.is- var a = 42;
- console.log( void a, a ); // undefined 42
复制代码 简单值通过值复制的方式来赋值 / 传递,包括 null、undefined、字符串、数字、布尔和 ES6 中的 symbol。
复合值(compound value)——对象(包括数组和封装对象)和函数,则总是通过引用复制的方式来赋值 / 传递。
第三章 原生函数
内部属性 [[Class]]- var a = 2 / "foo";
- a == NaN; // false
- a === NaN; // false
- var a = 2 / "foo";
- var b = "foo";
- a; // NaN
- b; "foo"
- window.isNaN( a ); // true
- window.isNaN( b ); // true——晕! 只要用数值除以某个变量,这个变量就是NaN
- 2/{};
- isNaN({})// true;
- // es6有Number.isNaN
- // polyfill
- if (!Number.isNaN) {
- Number.isNaN = function(n) {
- return (
- typeof n === "number" &&
- window.isNaN( n )
- );
- };
- }
- var a = 2 / "foo";
- var b = "foo";
- Number.isNaN( a ); // true
- Number.isNaN( b ); // false——好!
- // 还有个更为简单的方法 即利用 NaN 不等于自身这个特点
- if (!Number.isNaN) {
- Number.isNaN = function(n) {
- return n !== n;
- };
- }
复制代码 封装对象包装: 由于基本类型值没有 .length 和 .toString() 这样的属性和方法,需要通过封装对象才能访问,此时 JavaScript 会自动为基本类型值包装(box 或者 wrap)一个封装对象;
拆封:得到封装对象中的基本类型值,可以使用 valueOf() 函数;
构造函数 Array(..) 不要求必须带 new 关键字。不带时,它会被自动补上。因此 Array(1,2,3) 和 new Array(1,2,3) 的效果是一样的。
稀疏数组:将包含至少一个“空单元”的数组;- console.log(1 / 0); // Infinity
- console.log(-1 / 0); // -Infinity
- a = Number.MAX_VALUE;
- // 就近取整模式
- console.log(a); // 1.7976931348623157e+308
- console.log(a * 2); // Infinity
- console.log(a + Math.pow( 2, 969 )); // 1.7976931348623157e+308
- console.log(Infinity / Infinity); // NaN
- 1/Infinity // 0
- 1/-Infinity //-0
- Infinity / 1 //Infinity
- -Infinity / 1 //-Infinity
复制代码 永远不要创建和使用空单元数组
除非万不得已,否则尽量不要使用 Object(..)/Function(..)/RegExp(..)
RegExp(..) 有时还是很有用的,比如动态定义正则表达式时
Date(..) 和 Error(..)- // 加法和减法运算不会得到负零(negative zero)
- var a = 0 / -3;
- // 至少在某些浏览器的控制台中显示是正确的
- console.log(a); // -0
- // 但是规范定义的返回结果是这样!
- console.log(a.toString()); // "0"
- console.log(a + ""); // "0"
- console.log(String( a )); // "0"
- // JSON也如此,很奇怪
- console.log(JSON.stringify( a )); // "0"
- console.log(+"-0"); // -0
- console.log(Number( "-0") ); // -0
- console.log(JSON.parse( "-0" )); // -0 JSON.stringify(-0) 返回 "0",而 JSON.parse("-0") 返回 -0。
- var a = 0;
- var b = 0 / -3;
- console.log(a == b); // true
- console.log(-0 == 0); // true
- console.log(a === b); // true
- console.log(-0 === 0); // true
- console.log(0 > -0); // false
- console.log(a > b); // false
- // 是否是-0
- function isNegZero(n) {
- n = Number( n );
- return (n === 0) && (1 / n === -Infinity);
- }
- isNegZero( -0 ); // true
- isNegZero( 0 / -3 ); // true
- 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():去掉字符串前后的空格,返回新的字符串。
以上方法并不改变原字符串的值,而是返回一个新字符串。- var a = 2 / "foo";
- var b = -3 * 0;
- Object.is( a, NaN ); // true
- Object.is( b, -0 ); // true
- Object.is( b, 0 ); // false
- // polyfill
- if (!Object.is) {
- Object.is = function(v1, v2) {
- // 判断是否是-0
- if (v1 === 0 && v2 === 0) {
- return 1 / v1 === 1 / v2;
- }
- // 判断是否是NaN
- if (v1 !== v1) {
- return v2 !== v2;
- }
- // 其他情况
- return v1 === v2;
- };
- }
复制代码 第四章 强制类型转换
类型转换发生在静态类型语言的编译阶段,而强制类型转换则发生在动态类型语言的运行时(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 强制类型转换- Object.prototype.toString.call( [1,2,3] );
- // "[object Array]"
- Object.prototype.toString.call( /regex-literal/i );
- // "[object RegExp]"
- Object.prototype.toString.call( null );
- // "[object Null]"
- Object.prototype.toString.call( undefined );
- // "[object Undefined]"
- Object.prototype.toString.call( "abc" );
- // "[object String]"
- Object.prototype.toString.call( 42 );
- // "[object Number]"
- Object.prototype.toString.call( true );
- // "[object Boolean]"
复制代码 ToNumber
true 转换为 1,false 转换为 0。undefined 转换为 NaN,null 转换为 0。
为了将值转换为相应的基本类型值,抽象操作 ToPrimitive(参见 ES5 规范 9.1 节)会首先检查该值是否有 valueOf() 方法。如果有并且返回基本类型值,就使用该值进行强制类型转换。如果没有就使用 toString()的返回值(如果存在)来进行强制类型转换。如果 valueOf() 和 toString() 均不返回基本类型值,会产生 TypeError 错误。- var a = new Array( 3 );
- var b = [ undefined, undefined, undefined ];
- var c = [];
- c.length = 3;
- a.join( "-" ); // "--"
- b.join( "-" ); // "--"
- a.map(function(v,i){ return i; }); // [ undefined x 3 ]
- b.map(function(v,i){ return i; }); // [ 0, 1, 2 ]
- var a = Array.apply( null, { length: 3 } );// 等同于Array(undefined, undefined, undefined)
- a; // [ undefined, undefined, undefined ]
复制代码 ToBoolean
假值:
• undefined
• null
• false
• +0、-0 和 NaN
• ""
假值列表以外的值都是真值。
假值对象(falsy object)
document.all 浏览器自带的来判断浏览器是否是老版本的 IE。
:if(document.all) { /_ it’s IE _/ }- // Date.now的polyfill
- if (!Date.now) {
- Date.now = function(){
- return (new Date()).getTime();
- };
- }
复制代码 显式强制类型转换- var a = 'abcde';
- a.charAt(1) // 'b'
- a.charCodeAt(1) //98
- a.concat(1) //'abcde1'
- a.endsWith('e') // true
- a.indexOf('b') //1
- a.includes('b') //true
- a.lastIndexOf('d') //3
- a.match(/e/g) //['e']
- a.repeat()//''
- a.repeat(2)//'abcdeabcde'
- a.replace('d', 4)//'abc4e'
- a.replaceAll('e', '5')//'abcd5'
- a.search(/b/)//1
- a.slice(0, 1)//'a'
- a.split('')//(5) ['a', 'b', 'c', 'd', 'e']
- a.startsWith(1)//false
- a.substr(0,2) // 'ab' 第一个参数 起始下标 第二参数 长度
- a.substring(1,2)//'b' 第一个参数 起始下标 第二个参数 结束下标
- 'B'.toLowerCase()//'b'
- a.toUpperCase()//'ABCDE'
- ' fd '.trim() //'fd'
复制代码 一元运算符 - 和 + 一样,并且它还会反转数字的符号位。由于 -- 会被当作递减运算符来处理,所以我们不能使用 -- 来撤销反转,而应该像 - -"3.14" 这样,在中间加一个空格,才能得到正确结果 3.14。- JSON.stringify( undefined ); // undefined
- JSON.stringify( function(){} ); // undefined
- JSON.stringify(
- [1,undefined,function(){},4]
- ); // "[1,null,null,4]"
- JSON.stringify(
- { a:2, b:function(){} }
- ); // "{"a":2}"
- // 对包含循环引用的对象执行 JSON.stringify(..) 会出错。
- // Uncaught TypeError: Converting circular structure to JSON
- // --> starting at object with constructor 'Object'
- // | property 'c' -> object with constructor 'Object'
- var o = { };
- var a = {
- b: 42,
- c: o,
- d: function(){}
- };
- // 在a中创建一个循环引用
- o.e = a;
- // 循环引用在这里会产生错误
- // JSON.stringify( a );
- // 自定义的JSON序列化 toJSON() 应该“返回一个能够被字符串化的安全的 JSON 值”,而不是“返回一个 JSON 字符串”。
- a.toJSON = function() {
- // 序列化仅包含b
- return { b: this.b };
- };
- JSON.stringify( a ); // "{"b":42}"
- var a = {
- b: 42,
- c: "42",
- d: [1,2,3]
- };
- JSON.stringify( a, ["b","c"] ); // "{"b":42,"c":"42"}"
- JSON.stringify( a, function(k,v){
- if (k !== "c") return v;
- } );
- // "{"b":42,"d":[1,2,3]}"
- var a = {
- b: 42,
- c: "42",
- d: [1,2,3]
- };
- JSON.stringify( a, null, 3 );
- // "{
- // "b": 42,
- // "c": "42",
- // "d": [
- // 1,
- // 2,
- // 3
- // ]
- // }"
- JSON.stringify( a, null, "-----" );
- // "{
- // -----"b": 42,
- // -----"c": "42",
- // -----"d": [
- // ----------1,
- // ----------2,
- // ----------3
- // -----]
- // }"
复制代码 JavaScript 有一处奇特的语法,即构造函数没有参数时可以不用带 ()。于是我们可能会碰到 var timestamp = +new Date; 这样的写法。这样能否提高代码可读性还存在争议,因为这仅用于 new fn(),对一般的函数调用 fn() 并不适用。
奇特的 ~ 运算符
~x 大致等同于 -(x+1)。
用法- var a = {
- valueOf: function(){
- return "42";
- }
- };
- var b = {
- toString: function(){
- return "42";
- }
- };
- var c = [4,2];
- c.toString = function(){
- return this.join( "" ); // "42"
- };
- Number( a ); // 42
- Number( b ); // 42
- Number( c ); // 42
- Number( "" ); // 0
- Number( [] ); // 0
- Number( [ "abc" ] ); // NaN
复制代码 字位截除
~~x 能将值截除为一个 32 位整数,x | 0 也可以,而且看起来还更简洁。- var a = "false";
- var b = "0";
- var c = "''";
- var d = Boolean( a && b && c );// "''"是真值
复制代码 显式解析数字字符串
解析允许字符串中含有非数字字符,解析按从左到右的顺序,如果遇到非数字字符就停止。而转换不允许出现非数字字符,否则会失败并返回 NaN。解析字符串中的浮点数可以使用 parseFloat(..) 函数;- var a = 42;
- var b = String( a );// String(..) 遵循前面讲过的 ToString 规则
- // var b = a.toString(); 同上
- var c = "3.14";
- var d = Number( c ); // Number(..) 遵循前面讲过的 ToNumber 规则
- // var d = +c; 同上 +是运算符的一元(unary)形式
- b; // "42"
- d; // 3.14
- var c = "3.14";
- var d = 5+ +c;
- d; // 8.14
复制代码 ES5 之前的 parseInt(..) 有一个坑导致了很多 bug。即如果没有第二个参数来指定转换的基数(又称为 radix),parseInt(..) 会根据字符串的第一个字符来自行决定基数。从 ES5 开始 parseInt(..) 默认转换为十进制数,除非另外指定。如果你的代码需要在 ES5 之前的环境运行,请记得将第二个参数设置为 10。
解析非字符串- 1 + - + + + - + 1; // 2 负负得正
- +new Date();// 日期显式转换为数字
- Date.now();// 比较好的写法
- // Date.now的polyfill 不建议对日期类型使用强制类型转换,应该使用 Date.now() 来获得当前的时间戳,使用 new Date(..).getTime() 来获得指定时间的时间戳。
- if (!Date.now) {
- Date.now = function() {
- return +new Date();
- };
- }
复制代码 parseInt(1/0, 19) 实际上是 parseInt("Infinity", 19)。第一个字符是 "I",以 19 为基数时值为 18。第二个字符 "n" 不是一个有效的数字字符,解析到此为止,和 "42px" 中的 "p"一样
显式转换为布尔值: 使用 Boolean(a) 和 !!a 来进行显式强制类型转换
隐式强制类型转换: 代码可读性不好,但是也是可以减少冗余,让代码更简洁 抽象和隐藏那些细枝末节,有助于提高代码的可读性
字符串和数字之间的隐式强制类型转换- if (!~a.indexOf( "ol" )) { // true 这里~ 比 >= 0 和 == -1 更简洁。
- // 没有找到匹配!
- }
- ~12.1 // -13
- -(12.1+1) // -13.1
- ~~12.1 // 12
复制代码 a + "" 会对 a 调用 valueOf() 方法,然后通过 ToString 抽象操作将返回值转换为字符串。- Math.floor( -49.6 ); // -50
- ~~-49.6; // -49
- ~~1E20 / 10; // 166199296
- 1E20 | 0 / 10; // 1661992960
- (1E20 | 0) / 10; // 166199296
复制代码 布尔值到数字的隐式强制类型转换
如果其中有且仅有一个参数为 true,则 onlyOne(..) 返回 true。- var a = "42";
- var b = "42px";
- Number( a ); // 42
- parseInt( a ); // 42
- Number( b ); // NaN
- parseInt( b ); // 42
复制代码 || 和 &&:选择器运算符”(selector operators)或者“操作数选择器运算符”(operand selector operators)
在 a ? a : b 中,如果 a 是一个复杂一些的表达式(比如有副作用的函数调用等),它有可能被执行两次(如果第一次结果为真)。而在 a || b 中 a 只执行一次,其结果用于条件判断和返回结果(如果适用的话)。
Symbol 符号的强制类型转换- parseInt( 1/0, 19 ); // 18
- parseInt( 0.000008 ); // 0 ("0" 来自于 "0.000008")
- parseInt( 0.0000008 ); // 8 ("8" 来自于 "8e-7")
- parseInt( false, 16 ); // 250 ("fa" 来自于 "false")
- parseInt( parseInt, 16 ); // 15 ("f" 来自于 "function..")
- parseInt( "0x10" ); // 16
- 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。- var a = [1,2];
- var b = [3,4];
- 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 的结果。- var a = {
- valueOf: function() { return 42; },
- toString: function() { return 4; }
- };
- a + ""; // "42"
- String( a ); // "4"
复制代码 因为没有对应的封装对象,所以 null 和 undefined 不能够被封装(boxed),Object(null)和 Object() 均返回一个常规对象。NaN 能够被封装为数字封装对象,但拆封之后 NaN == NaN 返回 false,因为 NaN 不等于 NaN- function onlyOne() {
- var sum = 0;
- for (var i=0; i < arguments.length; i++) {
- // 跳过假值,和处理0一样,但是避免了NaN
- if (arguments[i]) {
- sum += arguments[i];
- }
- }
- return sum == 1;
- }
- var a = true;
- var b = false;
- onlyOne( b, a ); // true
- onlyOne( b, a, b, b, b ); // true
复制代码 安全运用隐式强制类型转换
如果两边的值中有 true 或者 false,千万不要使用 ==。
如果两边的值中有 []、"" 或者 0,尽量不要使用 ==。
抽象关系比较
比较双方首先调用 ToPrimitive,如果结果出现非字符串,就根据 ToNumber 规则将双方强制类型转换为数字来进行比较。- var s1 = Symbol( "cool" );
- String( s1 ); // "Symbol(cool)"
- var s2 = Symbol( "not cool" );
- s2 + ""; // TypeError
复制代码 4.6 生成器并发
通信顺序进程(Communicating Sequential Processes,CSP): 讲道理 这里没看懂 跳过吧
4.7 形式转换程序
形实转换程序(thunk):JavaScript 中的 thunk 是指一个用于调用另外一个函数的函数,没有任何参数- var a = 42;
- var b = "42";
- a === b; // false
- a == b; // true
- var x = true;
- var y = "42";
- x == y; // false
- var a = null;
- var b;
- a == b; // true
- a == null; // true
- b == null; // true
- a == false; // false
- b == false; // false
- a == ""; // false
- b == ""; // false
- a == 0; // false
- 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】 我们会及时删除侵权内容,谢谢合作! |
|