JavaScript 基础卷(一):基础语法

使用与输出

使用 JavaScript

  1. 内部使用:置于 <script></script>
    • HTML 中,JavaScript 脚本需置于 <script></script> 标签之间
    • 这部分内容可以放在 <head> 中,也可以放在 <body>
    • <head><body> 中的 JavaScript 有什么区别呢
      • 简单来说,区别就是执行顺序不同:<head> 中的 JavaScript 会 HTML 标签解析之前就执行,而 <body> 中的是在加载后才执行
      • 举个例子,脚本中的某个函数涉及到 DOM 操作,那么前提当然是这个 DOM 节点已经生成了,若放在 <head> 中,那么就会在 HTML 标签解析之前执行,也就没有对应的 DOM 对象可以操作了
    • 应该放在哪里呢
      • 需调用才执行的脚本或事件触发执行的脚本放在 HTML 的 <head> 部分中,这样可以保证在任何调用之前被加载
      • 些当页面被加载时才会执行的脚本应该放在 HTML 的 <body> 部分,放在 <body> 部分的脚本通常被用来生成页面的内容
      • 一般是把函数放入 <head> 部分中,或者放在页面底部,这样可以把它们安置到同一位置,不会干扰页面的内容
    • 参考链接(defer和async超出此卷的范围,之后另做讲解)
  2. 外部使用:置于外部 js 文件中,通过 <script src="xxx.js"></script> 引入
    • 通常这个引入 src 的地方也就是你原本打算在 <script> 中写入内容的地方

输出 JavaScript

  1. JavaScript 没有提供 任何打印或者显示的函数,我们可以选择以下几种辅助方案输出:

    • window.alert() 弹出警告框
    • document.write() 写入 HTML 文档中
    • innerHTML 写入 HTML 元素中
    • console.log() 写入浏览器的控制台中
  2. 下面我们详解这几个方法

    • window.alert()

    • document.write()

      • 基本用法请阅读:

      • document.write(exp1, exp2, exp3, ...) 可以接受一个或者多个参数,该方法会按照顺序写入由 document.open() 打开的一个文档流中(多个参数貌似不会换行显示)

      • 这里的参数可以是 变量、函数体、自执行函数、数字、字符串、表达式 等等,参数也同样支持 HTML 标签,只是需要用字符串形式

      • 例如:

        <script type="text/javascript">
          var a = 100;
          var b = function(){
              console.log('200')
          };
          //变量
          document.write(a,'<br/>');
          //函数体
          document.write(b,'<br/>');
          //自执行函数
          document.write(function(){
              return 300;
          }(),'<br/>');
          //数字
          document.write(400,'<br/>');
          //字符串
          document.write('500','<br/>');
          //表达式
          document.write((300*2),'<br/>');
          //HTML 标签
          document.write('<h1>700</h1>');
        </script>

        浏览器显示如下:

      • 注意,因为 document.write() 写入文档流,所以 在已加载或者已关闭的文档上调用 document.write() 会自动调用 document.open(),这将覆盖该文档,比如:

        <p>ABCDE</p>
        <button onclick="displayDate()">点击这里</button>
        <script type="text/javascript">
          function displayDate(){
              document.write('覆盖页面')
          }
        </script>

        此时,不管你的页面上有什么内容,点击按钮,就会立刻覆盖整个页面
        这种情况一般伴随着两个条件:1、在函数内部调用 document.write();2、通过按钮响应调用函数
        再次重复一遍!向一个已经加载,并且没有调用过 document.open() 的文档写入数据时,会自动完成调用 document.open() 的操作,一旦完成了数据写入,系统要求调用 document.close() 以告诉浏览器页面已经加载完毕,写入的数据会被解析到文档结构模型里,生成对应的 DOM 结点

      • 而直接嵌入 HTML 中,不会调用 document.open() 这也就是为什么我们上上个例子中连续多个 document.write() 没有被覆盖

      • 试一下:手动使用 document.close()

        <h2>AB</h2>
        <h2>CD</h2>
        <h2>EF</h2>
        <script type="text/javascript">
          document.write('document.close()之前','<br />');
          document.close();
          document.write('document.close()之后')
        </script>
        <h2>AB</h2>
        <h2>CD</h2>
        <h2>EF</h2>
        <script type="text/javascript">
          window.onload = function(){
              document.write('document.close()之前','<br />');
              document.close();
              document.write('document.close()之后')
          }
        </script>

        这是因为,window.onload 表达的是

        An event handler for the load event of a window.

        换言之,在文档加载完毕的时候,document.write() 在页面加载后调用,但在 W3C 规范中没有定义时,会发生自动的 document.open() 调用,所以页面才会被清除

      • 拓展:简书 — document.write()

    • innerHTML

      • innerHTML 有着双向功能:①获取 HTML 内容;②写入 HTML 内容。案例如下:
        <div id="x">
          <p>此为用 innerHTML 获取内容1<p>
          <p>此为用 innerHTML 获取内容2<p>
        </div>
        <button type="button" onclick="Inner()">点击</button>
        <script type="text/javascript">
          function Inner(){
              alert(document.getElementById('x').innerHTML);
              document.getElementById('x').innerHTML = '此为用 innerHTML 写入内容'
          }
        </script>
    • console.log()

      • alert() 不同的是,console.log() 并不会阻断线程,用法比较简单,在此不细说了,下面列举一些控制台常用方法
      • console.log() :输出普通信息
      • console.info() :输出提示信息
      • console.error() :输出错误信息
      • console.warn() :输出警示信息
      • console.debug() :输出调试信息

语句与表达式

语句

语句(statement) 是为了 完成某种任务 而进行的 操作 。

  1. 你可以把一个语句理解成一个动作,一个行为,比如 if语句、循环语句,如下的赋值语句:
      var a = 1;
    • 上述语句先用 var 命令声明了变量 a,再将 1+3 的运算结果赋值给变量 a
  2. 一个程序 是由 一系列语句 组成的
  3. JS 语句以分号结尾,JS是 弱类型 的语言,虽然分号有时可以省略,也请在省略时 注意是否会造成语法错误 ,但是一个分号就肯定表示着一个语句的结束,多个语句可以写在一行内。如:
     var a = 1;    var b = 2;
  4. 分号前面可以没有任何内容,JavaScript 引擎将其视为空语句。
     ;;;  //表示三条空语句
  5. 语句不存在返回值的说法
  6. 一般JavaScript的语句分为以下几种:
    • 声明语句:变量声明和函数声明
    • 赋值语句:在语句中比较重要的一类
    • 控制语句:能够 改变语句的执行顺序,包括条件语句和循环语句,还有比较特殊的标签语句
    • 表达式语句:是JS中最简单的语句,此类语句只由表达式组成,没有其他语法元素
      • 赋值、delete、函数调用 这三类既是表达式,又是语句,所以叫做表达式语句
      • 注:JS中某些需要语句的地方,若可以使用一个表达式来代替,那么此语句即是表达式语句。但反过来不可以,不能在一个需要表达式的地方放一个语句。比如,一个if语句不能作为一个函数的参数。

表达式

表达式 是由 运算符 和 运算元(可选) 构成的,并产生 运算结果 的语法结构 。

  1. 以下在 ES5 中,被称为 基本表达式(Primary Expression)
    • this、null、arguments 等 内置关键字
    • 变量,即一个已声明的标识符
    • 字面量,仅包括数字字面量、布尔值字面量、字符串字面量、正则字面量
    • 分组表达式,即用来表示立刻进行计算的
  2. 上述表达式均为 原子表达式,即无法再分解的表达式
  3. 除了基本表达式外,还有 复杂表达式,这类表达式 需要其他表达式的参与,如下:
    • 对象、数组初始化表达式:
      • 其实他们也算字面量的一种,但不把它们算作基本表达式,是因为对象字面量、数组字面量所包含的成员也都是表达式
      • 数组初始化表达式语法如下:
           [expression,expression,expression]
           //可以有 0个或多个子表达式
        初始化的结果是一个新创建的数组,数组的元素是逗号分隔的表达式的值。举例:
           []   //一个空数组;[]内留空即表示该数组没有任何元素
           [1+2,3+4]  //拥有两个元素的数组,第一个是3,第二个是7
      • 对象初始化表达式语法如下:
           {
              expression1: expression2,
              expression1: expression2,
              expression1: expression2,
           }
           //在ES5及其之前,expression1只能是字符串字面量,但ES6开始支持以下写法:
           {
               [expression1]: expression,
               [expression1]: expression,
               [expression1]: expression,
           }
           //expression1可以是任何返回值为字符串或Symbol类型的表达式
        初始化的结果是一个新创建的对象,举例:
            {
               foo: bar(3,5)
            }
           //不过同时,它也是一个完全合法的语句,这个语句的组成部分有:
           //    1.一个代码块:一个由大括号包围的语句序列;
           //    2.一个标签:你可以在任何语句前面放置一个标签.这里的foo就是一个标签.
           //    3.一条语句:表达式语句bar(3, 5).
    • 函数定义表达式(注意,需与函数声明语句区分开)
    • 属性访问表达式:
      • 属性访问表达式的两种语法如下:
           expression.identifier
           //expression可以是任意的表达式,identifier是属性名(必须合法)
           //注:跟在对象后面的句点'.'不是运算符,而是属性访问表达式的语法结构的一部分
        以及
           expression1[expression2]
           //其中,expression1与expression2都可以是任意表达式
           //而expression2的值会被转化为字符串(除非它是一个Symbol类型)
    • 调用表达式(分为 函数调用 和 方法调用):
      • 函数调用的语法如下:
           expression0([expression1[,expression2[,expression3]]])
           //expression0是一个返回值为函数对象的表达式
           //小括号内的是参数列表,其参数为0或多个,用逗号分隔
           //小括号并非操作符,而是调用语法的一部分
      • 方法调用的语法如下:
           expression0([[expression1[,expression2[,expression3]]])
           //其中,expression0 是一个返回值为函数对象的表达式
           //小括号提供一个逗号分隔的参数列表
    • 对象创建表达式:
      • 该表达式的语法如下:
           new expression0([expression1[,expression2[,expression3]]])
           //同调用表达式一样,//expression0是一个返回值为函数对象的表达式
           //小括号并非操作符而是语法的一部分
  4. 若表达式中未使用运算符,则称为 单值表达式,否则为 复合表达式
  5. JavaScript表达式必有返回值,单值表达式返回其值本身,复合表达式返回运算的结果值
  6. 总结出的表达式分类如下:
    • 单值表达式(不使用运算符)
      • 简单表达式(不能再分解)
      • 复杂表达式(需要其他表达式的参与)
    • 复合表达式(由运算符将多个单值表达式结合而成的表达式)

变量

相关概念

  1. 变量是对 “值” 的具名引用
  2. 变量的类型是没有限制的,所以你可以随时修改变量的类型,这也表明了 JavaScript是一种动态类型语言

标识符

  1. 标识符(identifier) 指的是用来识别各种值的合法名称,最常见的标识符就是变量名以及函数名,标识符对大小写敏感
  2. 命名规则如下:
    • 字母 或者 $ 或者 _ 开头,后面可包含 数字
    • 对大小写敏感
    • 中文是合法标识符,如:var 临时变量 = 1;
    • 保留字不能作标识符
  3. 保留字如下:

    arguments、break、case、catch、class、const、continue、debugger、default、delete、do、else、enum、eval、export、extends、false、finally、for、function、if、implements、import、in、instanceof、interface、let、new、null、package、private、protected、public、return、static、super、switch、this、throw、true、try、typeof、var、void、while、with、yield


变量声明

  1. 变量的声明与赋值是两个过程,如果只是声明变量而没有赋值,则该变量的值是 undefined,undefined是一个特殊的值,表示未定义
  2. 重复地声明变量是无效的
  3. 初识变量提升
    • 由于 JavaScript 引擎的工作方式是:先解析代码,获取所有被声明的变量再运行,所以,所有变量的声明都会被提到 作用域的顶端
    • 看几个例子:
      function test(){
            console.log(a);
            var a = 1;
      }
      test();  //undefined
      //上面的代码等效于:
      function test () {
            var a;
            console.log(a);
            a = 123;
      }
      test();  //undefined
      a = 1;
      var a;
      console.log(a);  //1
      看一道经典的面试题:
      console.log(v1);  //undefined
      var v1 = 100;
      function foo() {
            console.log(v1);  //undefined
            var v1 = 200;
            console.log(v1);  //200
      }
      foo();
      console.log(v1);  //100
    • 实际上,js 引擎并不会把 var a = 1; 当做一个过程,而是看做 var a;a = 1;,分别对应着声明与赋值,分别对应着编译阶段和执行阶段,而声明会被提到作用域的顶端
    • 如果理解了上面三个例子,那么已经成功认识了变量提升这个概念,彻底理解会在之后的作用域中详解
    • 参考阅读:
  4. 几种变量的声明:var、let、const
    • 先认识一个块级作用域:在 ES5 中,作用域有全局作用域和函数作用域,而在 ES6 中,增加了一个 块级作用域,块级作用域由 { } 包括(所以 if 语句等等也会形成块级作用域),举个栗子:
      //通过var定义的变量可以跨块级作用域访问到
      {
            var a = 1;
            console.log(a);  //1
      }
      console.log(a);  //1
      //通过var定义的变量不可以跨函数作用域访问到
      (function A(){
            var b = 2;
            console.log(b);  //2
      })();
      console.log(b);  //报错:b is not defined
      //if语句和for语句形成的是块级作用域而非函数作用域,因为var定义的变量可以在外部访问
      if(true){
            var c = 3;
      }
      console.log(c);  //3
      for(var i = 0; i < 4; i++){
            var d = 5;
      }
      console.log(i);  //4
      console.log(d);  //5
    • 承接之前的块级作用域,说说 var、let、const 的区别:
      • var 定义的变量,没有块级作用域的概念,所以可以跨块级作用域访问, 但不能跨函数作用域访问
      • let 定义的变量,只能在块级作用域里访问,不能跨块访问,也不能跨函数访问。
      • const 用来定义常量,使用时必须初始化(即必须赋值),只能在块作用域里访问,而且不能修改
      • 举例如下:
        //块级作用域
        {
              //const 不允许改变
              var a = 1;
              let b = 2;
              const c = 3;
              c = 4;  //报错:Assignment to constant variable
              //const 声明变量时必须初始化
              var A;
              let B;
              const C;  //报错:Missing initializer in const declaration
              //打印,观察
              console.log(a);  //1
              console.log(b);  //2
              console.log(c);  //3
              console.log(A);  //undefined
              console.log(B);  //undefined
        }
        console.log(a);  //1
        console.log(b);  //报错:b is not defined
        console.log(c):  //报错:b is not defined
        //函数作用域
        (function Fun(){
              var i = 5;
              let j = 6;
              const k = 7;
        })();
        //console.log(i);  //报错:i is not defined
        //console.log(j);  //报错:j is not defined
        //console.log(k);  //报错:k is not defined
      • 读到这里,已经初步认识了三者,接下来详细说明一下各自的用法
    • let 声明变量
      • 之前提到过,ES5 没有块级作用域,这导致很多场景只能通过闭包来解决不合理的冲突,ES6 出来之后,我们可以通过 let 即可实现
      • 注意,let 声明的变量只在 let 命令所在的代码块内有效,如上个例子中的 console.log(b);console.log(j); 均会报错 “未定义”,所以,for 循环就很适合 let,另外,说到 for 循环顺便提提它的特点:设置循环变量的那部分是一个 父作用域,而循环体内部是一个单独的 子作用域
        for (let i = 0; i < 3; i++) {
            let i = 'abc';
            console.log(i);
        }
        // abc
        // abc
        // abc
      • let 不存在变量提升,之前在 var 的变量提升总让人感觉逻辑怪怪的,现在在 let 中不存在变量提升了
      • 暂时性死区:只要块级作用域内存在 let 命令,它所声明的变量就 “绑定”(binding)这个区域,不再受外部的影响,例如:
        var a = 1;
        if(true){
              a = 'abc';
              let a;
        }
        上面代码中,var 先将 a 声明为全局变量,但是块级作用域内 let 又声明了一个局部变量 a,导致后者绑定这个块级作用域,所以在 let 声明变量前,对 a 赋值会报错
        因为 ES6 已经明确规定,如果区块中存在 let 和 const 命令,这个区块对这些命令声明的变量,从一开始就形成了 封闭作用域,不可在声明之前就使用这些变量
        也就是在用 let 和 const 声明变量之前,不可以使用该变量,这种语法称为 暂时性死区(temporal dead zone,简称 TDZ)
      • var 可以重复声明变量的,只不过无效而已,但是 let 是不允许重复声明同一变量的,例如:
        var a = 1;
        var a;
        console.log(a);
        let b = 1;
        let b;
        console.log(b);  //报错:Identifier 'b' has already been declared
        function fun(){
              var a = 1;
              let a;  //报错:Identifier 'a' has already been declared
        }
        //下面的不报错
        function fun(x){
              {
                      let x;
              }
        }
        fun();  //不报错
    • const 声明变量
      • const 声明一个 只读的常量,一旦声明,常量的值就不能改变,这也同时意味着,一旦用 const 声明变量,就必须立刻赋值,不能留到之后再赋值,例如:
        //不能改变值
        const a = 1;
        a = 2;  //报错:Assignment to constant variable.
        //必须立刻赋值
        const b;
        b = 3;  //报错:Missing initializer in const declaration
      • const 的作用域与 let 相同:只在声明所在的块级作用域内有效,同样的,const 声明的常量也不会有提升现象,而且也同样存在暂时性死区,只能在声明的位置后面使用
      • 参考阅读:
      • 拓展:const 真的不能修改么?
        • 先看个例子:
          const person = {
                name: 'A',
                age: 15
          }
          person.name = 'B';
          console.log(person.name)  //B
        • 因为对象是引用类型的,person 保存的仅仅是 对象的指针,这也就意味着,const 似乎只在乎指针有没有发生改变,而修改对象的类型是不会改变对象指针的,所以是被允许的
        • 也就是说,const 定义的引用类型只要指针不发生改变,其他的不论如何改变都是允许的,用下面的代码测试,果然报错:
          const person = {
                name: 'A',
                age: 15
          }
          person = {
                name: 'A',
                age: 16
          }
          console.log(person.name);  //报错:Assignment to constant variable.

本作品采用《CC 协议》,转载必须注明作者和本文链接
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!