JavaScript小细节点罗列(2)

break 语句和 continue 语句

break语句和continue语句都具备跳转做用,可让代码不按既有的顺序执行。javascript

break语句用于跳出代码块或循环。java

var i = 0;

while(i < 100) {
  console.log('i 当前为:' + i);
  i++;
  if (i === 10) break;
}

continue语句用于当即终止本轮循环,返回循环结构的头部,开始下一轮循环。数组

var i = 0;

while (i < 100){
  i++;
  if (i % 2 === 0) continue;
  console.log('i 当前为:' + i);
}

注意除了break,continue语句以外,还有一个return语句表示从被调函数返回到主调函数继续执行,返回时可附带一个返回值,由return后面的参数指定。return后函数就结束了,后面的语句再也不执行.函数

标签(label)

JavaScript 语言容许,语句的前面有标签(label),至关于定位符,用于跳转到程序的任意位置,标签能够是任意的标识符,但不能是保留字,语句部分能够是任意语句。this

标签一般与break语句和continue语句配合使用,跳出特定的循环。code

top:
  for (var i = 0; i < 3; i++){
    for (var j = 0; j < 3; j++){
      if (i === 1 && j === 1) break top;
      console.log('i=' + i + ', j=' + j);
    }
  }
// i=0, j=0
// i=0, j=1
// i=0, j=2
// i=1, j=0

变量提高(Hoisting)

  1. Hoisting 的做用范围是随着函数做用域的。我理解在这里还没有讲到函数做用域,不过能够提一句提醒读者注意,而后连接至做用域的部分进一步探讨;orm

  2. “Hoisting 只对 var 声明的变量有效”,不尽然如此。变量声明的提高并不是 hoisting 的所有,JavaScript 有四种让声明在做用域内得到提高的途径(按优先级):对象

-语言定义的声明,如 this 和 arguments。你不能在做用域内从新定义叫作 this 的变量,是由于 this 是语言自动定义的声明,而且它的优先级最高,也就是被 Hoisting 到最顶上了,没人能覆盖它继承

-形式参数。虽然咱们无需用 var 来修饰形式参数,可是形式参数的确也是变量,而且被自动提高到次高的优先级ip

-函数声明。除了 var 之外,function declaration 也能够定义新的命名,而且一样会被 hoisting 至做用域顶部,仅次于前二者

-最后,是本文提到的常规变量,也就是 var 声明的变量

整数和浮点数

JavaScript 内部,全部数字都是以64位浮点数形式储存,即便整数也是如此。因此,1与1.0是相同的,是同一个数。

这就是说,JavaScript 语言的底层根本没有整数,全部数字都是小数(64位浮点数)。容易形成混淆的是,某些运算只有整数才能完成,此时 JavaScript 会自动把64位浮点数,转成32位整数,而后再进行运算。

因为浮点数不是精确的值,因此涉及小数的比较和运算要特别当心。

0.1 + 0.2 === 0.3
// false

0.3 / 0.1
// 2.9999999999999996

(0.3 - 0.2) === (0.2 - 0.1)
// false

精度最多只能到53个二进制位,这意味着,绝对值小于等于2的53次方的整数,即-253到253,均可以精确表示。

Math.pow(2, 53)
// 9007199254740992
 
Math.pow(2, 53) + 1
// 9007199254740992

Math.pow(2, 53) + 2
// 9007199254740994

Math.pow(2, 53) + 3
// 9007199254740996

Math.pow(2, 53) + 4
// 9007199254740996

上面代码中,大于2的53次方之后,整数运算的结果开始出现错误。因此,大于2的53次方的数值,都没法保持精度。因为2的53次方是一个16位的十进制数值,因此简单的法则就是,JavaScript 对15位的十进制数均可以精确处理。

Math.pow(2, 53)
// 9007199254740992

// 多出的三个有效数字,将没法保存
9007199254740992111
// 9007199254740992000

根据标准,64位浮点数的指数部分的长度是11个二进制位,意味着指数部分的最大值是2047(2的11次方减1)。也就是说,64位浮点数的指数部分的值最大为2047,分出一半表示负数,则 JavaScript 可以表示的数值范围为21024到2-1023(开区间),超出这个范围的数没法表示。

若是一个数大于等于2的1024次方,那么就会发生“正向溢出”,即 JavaScript 没法表示这么大的数,这时就会返回Infinity。

Math.pow(2, 1024) // Infinity
Math.pow(2, -1075) // 0

parseInt()

对于那些会自动转为科学计数法的数字,parseInt会将科学计数法的表示方法视为字符串,所以致使一些奇怪的结果。

parseInt(1000000000000000000000.5) // 1
// 等同于
parseInt('1e+21') // 1

parseInt(0.0000008) // 8
// 等同于
parseInt('8e-7') // 8

parseInt方法还能够接受第二个参数(2到36之间),表示被解析的值的进制,返回该值对应的十进制数。默认状况下,parseInt的第二个参数为10,即默认是十进制转十进制。

parseInt('1000', 2) // 8
parseInt('1000', 6) // 216
parseInt('1000', 8) // 512

isFinite()

isFinite方法返回一个布尔值,表示某个值是否为正常的数值。

isFinite(Infinity) // false
isFinite(-Infinity) // false
isFinite(NaN) // false
isFinite(-1) // true

with语句

它的做用是操做同一个对象的多个属性时,提供一些书写的方便。

var obj = {
  p1: 1,
  p2: 2,
};
with (obj) {
  p1 = 4;
  p2 = 5;
}
// 等同于
obj.p1 = 4;
obj.p2 = 5;

注意,若是with区块内部有变量的赋值操做,必须是当前对象已经存在的属性,不然会创造一个当前做用域的全局变量。

var obj = {};
with (obj) {
  p1 = 4;
  p2 = 5;
}

obj.p1 // undefined
p1 // 4

位运算符

二进制或运算符(or):符号为|,表示若两个二进制位都为0,则结果为0,不然为1。
二进制与运算符(and):符号为&,表示若两个二进制位都为1,则结果为1,不然为0。
二进制否运算符(not):符号为~,表示对一个二进制位取反。
异或运算符(xor):符号为^,表示若两个二进制位不相同,则结果为1,不然为0。
左移运算符(left shift):符号为<<
右移运算符(right shift):符号为>>
带符号位的右移运算符(zero filled right shift):符号为>>>

void 运算符

void运算符的做用是执行一个表达式,而后不返回任何值,或者说返回undefined。

下面是一个更实际的例子,用户点击连接提交表单,可是不产生页面跳转。

<a href="javascript: void(document.form.submit())">
  提交
</a>

圆括号的做用

圆括号不是运算符,而是一种语法结构。它一共有两种用法:一种是把表达式放在圆括号之中,提高运算的优先级;另外一种是跟在函数的后面,做用是调用函数。

Number(Object)

简单的规则是,Number方法的参数是对象时,将返回NaN,除非是包含单个数值的数组。

Number({a: 1}) // NaN
Number([1, 2, 3]) // NaN
Number([5]) // 5

之因此会这样,是由于Number背后的转换规则比较复杂。

第一步,调用对象自身的valueOf方法。若是返回原始类型的值,则直接对该值使用Number函数,再也不进行后续步骤。

第二步,若是valueOf方法返回的仍是对象,则改成调用对象自身的toString方法。若是toString方法返回原始类型的值,则对该值使用Number函数,再也不进行后续步骤。

第三步,若是toString方法返回的是对象,就报错。

String(Object)

String方法的参数若是是对象,返回一个类型字符串;若是是数组,返回该数组的字符串形式。

String({a: 1}) // "[object Object]"
String([1, 2, 3]) // "1,2,3"

String方法背后的转换规则,与Number方法基本相同,只是互换了valueOf方法和toString方法的执行顺序。

先调用对象自身的toString方法。若是返回原始类型的值,则对该值使用String函数,再也不进行如下步骤。

若是toString方法返回的是对象,再调用原对象的valueOf方法。若是valueOf方法返回原始类型的值,则对该值使用String函数,再也不进行如下步骤。

若是valueOf方法返回的是对象,就报错。

原生错误类型

  • SyntaxError 对象是解析代码时发生的语法错误。

  • ReferenceError 对象是引用一个不存在的变量时发生的错误。

  • angeError 对象是一个值超出有效范围时发生的错误。主要有几种状况,一是数组长度为负数,二是Number对象的方法参数超出范围,以及函数堆栈超过最大值。

  • TypeError 对象是变量或参数不是预期类型时发生的错误。好比,对字符串、布尔值、数值等原始类型的值使用new命令,就会抛出这种错误,由于new命令的参数应该是一个构造函数。

  • URIError 对象是 URI 相关函数的参数不正确时抛出的错误,主要涉及encodeURI()、decodeURI()、encodeURIComponent()、decodeURIComponent()、escape()和unescape()这六个函数。

  • eval 函数没有被正确执行时,会抛出EvalError错误。该错误类型已经再也不使用了,只是为了保证与之前代码兼容,才继续保留。

var err1 = new Error('出错了!');
var err2 = new RangeError('出错了,变量超出有效范围!');
var err3 = new TypeError('出错了,变量类型无效!');

err1.message // "出错了!"
err2.message // "出错了,变量超出有效范围!"
err3.message // "出错了,变量类型无效!"

finally 代码块

try...catch结构容许在最后添加一个finally代码块,表示不论是否出现错误,都必需在最后运行的语句。

function cleansUp() {
  try {
    throw new Error('出错了……');
    console.log('此行不会执行');
  } finally {
    console.log('完成清理工做');
  }
}

cleansUp()
// 完成清理工做
// Error: 出错了……

map()

map方法不只能够用于数组,还能够用于字符串,用来遍历字符串的每一个字符。可是,不能直接使用,而要经过函数的call方法间接使用,或者先将字符串转为数组,而后使用。

var upper = function (x) {
  return x.toUpperCase();
};

[].map.call('abc', upper)
// [ 'A', 'B', 'C' ]

// 或者
'abc'.split('').map(upper)
// [ 'A', 'B', 'C' ]

其余相似数组的对象(好比document.querySelectorAll方法返回DOM节点集合),也能够用上面的方法遍历。

map方法还能够接受第二个参数,表示回调函数执行时this所指向的对象。

var arr = ['a', 'b', 'c'];

[1, 2].map(function(e){
  return this[e];
}, arr)
// ['b', 'c']

若是数组有空位,map方法的回调函数在这个位置不会执行,会跳过数组的空位。

Array(2).map(function (){
  console.log('enter...');
  return 1;
})
// [, ,]

reduce(),reduceRight()

reduce方法和reduceRight方法依次处理数组的每一个成员,最终累计为一个值。

它们的差异是,reduce是从左到右处理(从第一个成员到最后一个成员),reduceRight则是从右到左(从最后一个成员到第一个成员),其余彻底同样。

这两个方法的第一个参数都是一个函数。该函数接受如下四个参数。

累积变量,默认为数组的第一个成员
当前变量,默认为数组的第二个成员
当前位置(从0开始)
原数组

[1, 2, 3, 4, 5].reduce(function(x, y){
  console.log(x, y)
  return x + y;
});
// 1 2
// 3 3
// 6 4
// 10 5
//最后结果:15

Object.keys(),Object.getOwnPropertyNames()

Object.keys方法和Object.getOwnPropertyNames方法都用来遍历对象的属性。

Object.keys方法的参数是一个对象,返回一个数组。该数组的成员都是该对象自身的(而不是继承的)全部属性名。

Object.getOwnPropertyNames方法与Object.keys相似,也是接受一个对象做为参数,返回一个数组,包含了该对象自身的全部属性名。

对于通常的对象来讲,Object.keys()和Object.getOwnPropertyNames()返回的结果是同样的。只有涉及不可枚举属性时,才会有不同的结果。Object.keys方法只返回可枚举的属性,Object.getOwnPropertyNames方法还返回不可枚举的属性名。

var a = ['Hello', 'World'];

Object.keys(a) // ["0", "1"]
Object.getOwnPropertyNames(a) // ["0", "1", "length"]
相关文章
相关标签/搜索