视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001 知道1 知道21 知道41 知道61 知道81 知道101 知道121 知道141 知道161 知道181 知道201 知道221 知道241 知道261 知道281
问答文章1 问答文章501 问答文章1001 问答文章1501 问答文章2001 问答文章2501 问答文章3001 问答文章3501 问答文章4001 问答文章4501 问答文章5001 问答文章5501 问答文章6001 问答文章6501 问答文章7001 问答文章7501 问答文章8001 问答文章8501 问答文章9001 问答文章9501
使用JavaScript必须要知道的小细节
2020-11-27 19:58:57 责编:小采
文档

标签(label)

JavaScript 语言允许,语句的前面有标签(label),相当于定位符,用于跳转到程序的任意位置,标签可以是任意的标识符,但不能是保留字,语句部分可以是任意语句。

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

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 的作用范围是随着函数作用域的。我理解在这里尚未讲到函数作用域,不过可以提一句提醒读者注意,然后链接至作用域的部分进一步探讨;

  2. “Hoisting 只对 var 声明的变量有效”,不尽然如此。变量声明的提升并非 hoisting 的全部,JavaScript 有四种让声明在作用域内获得提升的途径(按优先级):

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

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

-函数声明。除了 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"]

    相信看了本文案例你已经掌握了方法,更多精彩请关注Gxl网其它相关文章!

    推荐阅读:

    React中有哪些类定义组件

    navigator.clipboard浏览器怎么制作原生剪贴板

    下载本文
    显示全文
    专题