美文网首页
五 语法专题

五 语法专题

作者: 智勇双全的小六 | 来源:发表于2018-08-13 14:08 被阅读0次

    1 数据类型的转换

    https://wangdoc.com/javascript/features/conversion.html#number

    1.1 概述

    JS 是一种动态类型语言,变量没有类型限制,可以随时赋予任何值。
    即:

    let  a = 1;
    a = "hello";
    

    这种操作,在静态语言中是不允许的。
    虽然变量对变量类型没要求,但是运算符对数据类型有要求。如果运算符发现运算子的类型与预期不符合,就会进行类型转换。

    1.2 强制转换

    1.2.1 Number

    (1)原始类型的值

    // 数值:转化后还是原来的值
    Number(324) // 324
    // 字符串,如果可以被解析为数字,则转化为数字,否则为 NaN
    Number('324') // 324
    Number('324abc') // NaN
    // 空字符串、false 和 null 会被转为 0
    Number('') // 0
    Number(null) //0
    Number(false) // 0
    // undefined 转为 NaN
    Number(undefined) // NaN
    // 布尔值 true
    Number(true) // 1
    

    Number 函数将字符串转化为数值,要比 parseInt 严格很多。基本上只要有一个数字无法转为数值,整个字符串就会变为 NaN。

    let a = "34ca"
    Number(a) // NaN
    parseInt(a) // 34
    

    上面的代码中,parseInt 逐个解析字符串,而Number函数整体转化字符串的类型。
    当然, parseInt 和 Number 都会自动过滤一个字符串的前后的空格。

    parseInt("    23 ");  // 23
    Number(" 34 ") // 34
    

    (2)对象
    Number 方法的参数就是对象时,将返回 NaN,除非是包含单个数值的数组。

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

    Number 背后的转换规则比较复杂。

    1. 调用对象自身的 valueof 方法。如果返回的类型是原始类型的值,则直接对该值使用 Number 函数,不再进行后续方法。
    2. 如果 valueOf 返回的还是对象,则改为调用对象自身的 toString 方法。如果 toString 方法返回原始类型的值,则对改值使用 Number 函数。
    3. 如果 toString 返回的还是对象,则返回 NaN
      Number({}) // NaN
      Number([]) // 0

    1.2.2 String

    String 函数可以将任意类型的值转化为字符串,转化规则如下:
    (1) 原始类型值

    • 数值:转为相应的字符串
    • 字符串:转化后还是原来的值
    • 布尔值:true 转化为 “true”, false 转化为字符串 “false”。
    • undefined: 转化为字符串 "undefined"
    • null: 转化为字符串 "null"
    • NaN: 转化为字符串 "NaN"
    String(123) // "123"
    String('abc') // "abc"
    String(true) // "true"
    String(undefined) // "undefined"
    String(NaN) // "NaN"
    

    (2) 对象
    String 方法的参数如果是对象,则返回一个类型字符串;如果是数组,返回该数组的字符串形式。

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

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

    1. 先调用对象的自身的 toString 方法。如果返回原始类型的值,则对该值使用String函数,不再进行以下步骤。
    2. 如果 toString 方法返回的是对象,再调用原对象的 valueOf 方法。如果 valueOf 方法返回原始类型的值,则对该值使用 String 函数,不再进行以下步骤。
    3. 如果 valueOf 方法返回的是对象,就报错。
    String({a : 1}) // "[object object]"
    

    1.2.3 Boolean

    以下五个值的结果转化为 false,其他值全部为 true

    • undefined
    • null
    • -0 或 +0
    • NaN
    • ''
      注意,以下几个值的布尔值为 true
    Boolean({}) // true
    Boolean([]) // true
    Boolean(new Boolean(false)) // true
    

    空的对象在 Python 中就是 false

    1.3 自动转换

    遇到以下三种情况,JS 会自动进行数据类型转化。
    第一种,不同数据类型的数据互相运算。

    123 + "abc" // "123abc"
    

    d第二种,对非布尔值的数据求布尔值。(这种 Python也会发生数据类型转化,而GO就非常严格,要求类型转化必须是显示的)

    if("a"){
    }
    

    第三种,对于非数值类型的值使用一元运算符

    + "1" // 1
    + {a:1} // NaN
    

    1.3.1 自动转换为布尔值

    JS 遇到预期为布尔值的地方就会将非布尔值的参数自动转化为布尔值。
    以下五个值的布尔值都是 false

    NaN
    ''
    undefined
    0 -0
    null
    

    1.3.2 自动转化为字符串

    一般使用加法时,如果一个运算子是字符串,另外一个就会转化为字符串。具体规则是,先将复合类型的值转为原始类型的值,再将原始类型的值转为字符串。

    1.3.3 自动转化为数值

    除了加法运算符优先转化为字符串,其他运算符都会把运算子转化为数值。

    2 错误处理机制

    https://wangdoc.com/javascript/features/error.html

    2.1 Error 实例对象

    JS 解析或运行时,一旦发生错误,就会抛出错误对象。JS 原生提供 Error 构造函数,所有抛出的错误都是这个构造函数的实例。

    var err = new Error("出错了");
    err.message // 出错了
    

    Error 是一个构造函数,会生成一个实例对象。Error 构造函数接受一个参数,表示错误提示,可以从实例的 message 属性读到这个参数。

    除了message 属性,还提供 namestack 属性,分别表示错误的名称和错误的堆栈,但它们是非标准的,不是每种实现都有。

    • message: 错误提示信息
    • name: 错误名称(非标准属性)
    • stack:错误的堆栈(非标准属性)
      使用 namemessage 这两个属性,可以对发生什么错误有一个大概的了解。
    if (error.name){
      console.log(error.name + ": " + error.message);
    }
    

    使用 stack 属性来查看错误发生时的堆栈。

    function throwIt(){
      throw new Error('I am Error');
    }
    
    function catchIt(){
      try{
        throwIt();
      } catch(e){
        console.log(e.stack);
      }
    }
    
    catchIt();
    // Error: I am Error
    // at throwIt(~/examples/)
    // at catchIt()
    // at <anonymous>
    

    上面的代码中,错误堆栈的最内部是 throwIt 函数,然后是 catchIt 函数,最后是函数的运行环境

    2.2 原生错误类型

    Error 实例对象是最一般的错误类型,在此基础上,还派生了 6 种其他的错误对象。

    2.2.1 SyntaxError 对象

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

    var 1a;
    // Uncaught SyntaxError: Invalid or unexpected token
    

    2.2.2 ReferenceError 对象

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

    // 使用一个不存在的变量
    a 
    // Uncaught ReferenceError: unknownVariable is not defined
    

    另外一种触发场景是,将一个值分配给无法分配的对象,比如对函数的运行结果或者 this 赋值。

    // this 对象不能手动赋值
    this  = 1
    // ReferenceError: Invalid left-hand side in assignment
    

    2.2.3 RangeError 对象

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

    // 数组长度不能为负数
    new Array(-1)
    

    在 Python 中索引越界也会抛 一个 outOfRange 的错误,但是这种在 JS 中是不存在这种情况的。

    2.2.4 TypeError 对象

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

    new 123
    // Uncaught TypeError: 123 is not a constructor
    var obj = {};
    obj.a();
    // Uncaught TypeError: obj.a is not a function
    

    2.2.5 URIError 对象

    URIError 对象是 URI 相关函数的参数不正确时抛出的错误,encodeURI() / decodeURI / encodeURIComponent / decodeURICompoent() / escape()unescape() 这六个函数。

    decodeURI('%2')
    // Uncaught URIError: URI malformed
    

    2.2.6 EvalError 对象

    eval 函数没有被正确执行时,会抛出 EvalError 错误。该错误类型已经不再使用。保留只是为了跟代码兼容。

    2.2.7 总结

    以上 6 种错误类型连同原始的 Error 对象都是构造函数,开发者可以自定义函数。

    var err1 = new Error("自定义 Error");
    var err2 = new RangeError("I am Error");
    

    2.3 自定义错误

    function UserError(message){
      this.message = message || "默认信息";
      this.name = "UserError";
    }
    
    UserError.prototype = new Error();
    UserError.prototype.constructor = UserError;
    

    上面代码自定义一个错误对象 UserError , 让它继承 Error 对象。然后,就可以生成这种自定义类型的错误了。

    new UserError('这是自定义的错误');
    

    2.4 throw 语句

    throw 是 Python 中的 raise。
    但是 throw 想扔啥就扔啥。

    throw "Error"; // 字符串
    throw 42; // 数值
    throw true; // 布尔值
    

    2.5 try ... catch 结构

    python 和 JS 都支持这种写法,这是我以前用的比较少的:

    try{
    
    }catch (a){
    }catch (b){
    }catch (c){
    }
    

    2.6 finally 代码块

    3 编程风格

    https://wangdoc.com/javascript/features/style.html

    3.1 概述

    3.2 缩进

    我都是用 tab

    3.3 区块

    如果循环和判断只有一行,可以省略大括号。这种臭毛病在 JAVA 中也有。我不喜欢

    3.4 圆括号

    圆括号的作用有两种:一种表示函数的调用;另外一种是表达式的组合。

    // 圆括号表示函数的调用
    console.log("abc");
    // 圆括号表示表达式的组合
    (1 + 2) * 3
    

    表示表达式组合的时候,加空格比较好

    3.5 行尾的分号

    添加分号更好,我认为。

    3.5.1 不使用分号的情况

    1. for 和 while 循环
    2. if switch try
    3. 函数的声明
      但是注意,函数表达式是需要添加分号
    var f = ()=>{};
    

    3.5.2 分号的自动添加

    除了以上三种情况,JS 会自动添加。
    如果 continue / break / return 和 throw 这四个语句后面,直接跟换行符,则会自动添加分号。这意味着,如果return 语句返回的是一个对象的字面量,起首的大括号一定要写在同一行,否则达不到预期的效果。

    return 
    { first: 'jane'};
    
    // 解释成
    return;
    {first : 'jane'}
    
    // 实际案例
    function a(){
      return
      {first : 'Jane'};
    }
    

    另外不写分号,有些 JS 代码压缩工具不会自动添加分号,遇到没有分号结尾的代码就会让他保护原装。
    不写分号,可能会导致脚本合并除错。所以有的代码库,会在第一行语句开始前加上分号

    ;var a = 1;
    

    3.6 全局变量

    https://wangdoc.com/javascript/features/style.html#%E6%A6%82%E8%BF%B0
    全局变量对于任何一个代码块都是可读可写的,应该避免使用全局变量。

    3.7 变量的声明

    JS 会自动将变量声明提升到代码块的头部。

    if (!x){
      var x = {};
    }
    
    // 等同于
    var x;
    if (!x){
      x = {};
    }
    

    这意味着,变量 x 是 if 代码块之前就存在的。为了避免可能出现的问题,最好把变量声明都放在代码块的头部。

    for (var i = 0; i < 10; i++){
      //...
    }
    
    // 写成
    var i;
    for(i = 0;i<10;i++){
      // ...
    }
    

    这样的写法,就容易看出 i 是一个全局的循环变量 i。
    另外,所有函数都应该在使用之前定义。函数内部的变量声明,都应该放在函数的头部。(不然它也会提升到头部的。)

    3.8 with 语句

    最好不用 with

    3.9 相等和严格相等

    使用严格相等

    3.10 语句的合并

    没事合并语句就是有病

    3.11 自增和自减运算符

    不要用 ++ ,而是 += 1

    3.12 switch .. case 结构

    o ,朕知道了

    4 console 对象与控制台

    4.1 console 对象

    Chrome 控制台上的各个板块:

    • Elements: 查看网页的 HTML 源码和 CSS 代码。
    • Sources: 查看网页加载各种资源(代码文件/字体/css 文件),以及在硬盘上创建的各种内容(本地内容、Cookie、Local Storage 等)
    • Network: 查看网页的 HTTP 通信情况。
    • Performance: 查看网页的性能,比如 CPU 和内存消耗。
    • Console: 运行 JS 命令

    4.2 console 对象的静态方法

    4.2.1 console.log(), console.info(), console.debug()

    console.log 可以接受一个或多个参数,将它们连接起来输出。

    console.log(1,"2")
    

    console.log 还支持字符串格式化:

    console.log("%s + %s",1,2)
    // 1 + 2
    

    console.log 方法支持以下占位符,不同类型的数据必须使用对应的占位符。

    • %s 字符串
    • %d 整数
    • %i 整数
    • %f 浮点数
    • %o 对象的连接
    • %c CSS 格式字符串
    console.log("%cThis text is styled", 'color: red; background: yellow; font-size: 24px;')
    

    info / debug 都是不同的级别而已,debug 默认情况下在不显示,只有开启 All levels 才显示。

    4.2.2 console.warn(), console.error()

    warn 和 error 也是不同的日志级别而已, error 的出现会有一个小红叉,warn 的出现会有一个黄色的惊叹号

    4.2.3 console.table()

    可以把 对象/列表 以表格的形式打印出来。

    var a = [{a:0,b:1,c:2},{a:00,b:11,c:22},{a:000,b:111,c:222}]
    
    index a b c
    0 0 1 2
    1 00 11 22
    2 000 111 222

    4.2.4 console.count()

    count 方法用于计数,输出它被调用了多少次。

    function greet(user){
      console.count();
      return "hi " + user;
    }
    
    greet('bob');
    // default: 1
    // hi bob
    greet("jack");
    // default: 2
    // hi jack
    

    该函数还可以接受一个字符串作为参数,作为标签,可以区分不同参数的调用次数,真是挺神奇的。

    function greet(user){
      console.count(user);
      return "hi " + user;
    }
    
    greet('bob');
    bob: 1
    greet("jack");
    jack: 1
    

    4.2.5 console.dir(), console.dirxml()

    console.dir({f1: "foo", "f2": "bar"})
    // objct
    // f1: "foo"
    // f2: "bar"
    

    该方法对于输出 DOM 对象非常有用,会显示 DOM 对象的所有属性。

    console.dir(document.body);
    

    在 Node 的环境下,还可以指定以代码高亮的形式输出。

    console.dir(obj, {colors: true});
    

    dirxml 方法主要以目录数的形式,显示 DOM 节点。

    console.dirxml(document.body)
    

    如果不是 DOM 节点,而是普通的 JS 对象,则输出与 console.dir 相同。

    4.2.6 console.assert()

    console.assert 会对条件进行断言,如果被断言的语句为假,不会终端程序,而是输出一条错误日志。

    console.assert(false, "判断条件不成立");
    // Assertion failed: 判断条件不成立
    

    相当于:

    try{
      if (not false){
        throw new Error("判断条件不成立");
      }
    } catch(e){
        console.error(e);
    }
    

    下面一个例子,判断子节点的个数是不是大于等于 2。

    console.assert(document.body.childNodes.length < 0, "节点大于等于 2");
    

    4.2.7 console.time(), console.timeEnd()

    这两个方法用于计时,可以算出一个操作可以花费的准确事件。这两个方法是一对,传入的参数是计时器的名称

    console.time("Array");
    var array = new Array(1000000);
    for(var i = array.length - 1; i >= 0; i--){
      array[i] = new Object();
    }
    console.timeEnd("Array");
    // Array initialize: 1914.481 ms;
    

    4.2.8 console.group(), console.groupEnd(), console.groupCollapsed()

    console.group("g1");
    console.log("一级分组的内容")
    
    console.group("g2");
    console.log("二级分组内容");
    
    console.groupEnd(); // 二级分组结束
    console.groupEnd(); // 一级分组结束
    

    4.2.9 console.trace(), console.clear()

    console.trace 方法显示当前执行的代码在堆栈中的调用路径。

    console.trace()
    // 代码在堆栈中的路径
    

    console.clear用于清除当前控制台的所有输出,将光标回置到第一行。

    4.3 控制台命令 API

    浏览器控制台中,除了使用 console对象,还可以使用一些控制台自带的命令行。
    (1) $_
    $_ 属性返回上一个表达式的值。

    2 + 2 
    // 4
    $_
    // 4
    

    (2) 0-4
    ....
    感觉没啥用

    4.4 debugger 语句

    debugger 语句用于除错,设置断点。如果有正在运行的除错工具,程序运行到 debugger 语句时会自动停下。如果没有除错工具,debugger 语句不会产生任何结果,JS 引擎会自动跳过这一句。

    Chrome 浏览器中,当代码运行到debugger语句时,就会暂停运行,自动打开脚本源码界面。

    for(var i = 0; i < 5; i++){
      console.log(i);
      if (i === 2) {
        debugger;
      }
    }
    

    相关文章

      网友评论

          本文标题:五 语法专题

          本文链接:https://www.haomeiwen.com/subject/fyxxbftx.html