JavaScript 进阶知识 | 青训营

81 阅读13分钟

JavaScript 进阶知识

一、 JavaScript 程序规范

1.1 Javascript 的语法规则特点
  • 1.1.1 句末分号

JS并不想C语言等编译类语言需要严格地使用分号来表征换行,句末分号可以省略。在初学阶段,为了易于理解语句逻辑,推荐将分号带上

  • 1.1.2 大小写与中英文

    • 在JS中一切的命名(标识符)、关键字和操作符等,都是区分大小写的,比如:
 function OpenDoor(){
        console.log("OpenDoor")
    }
    function opendoor(){
        console.log("opendoor")
    }  

当调用OpenDoor,只会执行第一个函数,因此可以利用区分大小写的特性对标识符的命名进行规范化命名。

  • 1.1.2 严格模式

'use strict';

  • 1.1.3 规范化标识符

    • 驼峰法

      • 每一个单词为单位,每一个新单词,eg. nameList
    • 下划线法

      • 单词统一使用小写,单词之间用下划线连接,eg. name_list
    • 3.3.4 注释

    •              // 单行注释
                    /*
                      多行注释
                    */
      
  • 1.1.5 函数

-   普通函数(关键字function)
-   ```
            function 函数名(参数名1,参数名2,...){
              return 参数名1;//返回值 可有可无
            }
    ```

-   箭头函数(ES6以后的语法)
     //匿名函数写法
        (参数名1,参数名2,...)=>{
          return 参数名1;//返回值 可有可无
        }
      //可以将函数赋给一个常量
      const fn = (参数名1,参数名2,...)=>{
          return 参数名1;//返回值 可有可无
        }
1.2 ES6中标识符初始化
  • 1.2.1 作用域(scope)

    • 作用域包括全局(global)与局部(part)两种

      • 全局作用域在页面打开时创建,页面关闭时销毁
      • 局部作用域是在函数内创建的作用域,函数执行完毕,局部作用域会销毁
   var num = 10;
          function nu(){
          var num = 20;
          console.log(num);   
          }
         nu();
         console.log(num);     
  • 1.2.2 var(目前var声明已经弃用 建议都选用let)

    • 用于声明一个可变变量,因为是“弱类型”声明,该变量可以赋为任意值。var 声明初始化可以在分离也可以同时完成,但是要注意的是,在未赋值时,变量默认为undefined。

    •              var a; 
                  console.log(a) //undefined
                  a = "1";
                  var b = "2";
                  console.log(a,b)  //1 2
                  var c = 3;
                  console.log(a + c) //13
                  console.log(c + a) //4  
      
    • var 的变量提升

      • 以var为界以下到结束,为var声明变量的作用域,但函数体会自动提升到作用域顶层,变量可以先于声明语句调用。
            ()=>{
                console.log(b)
                var b = 2;
              }
  • 1.2.3 let

    • let的用法与var很接近,初学只需要记得三点区别
    • var声明范围是函数 作用域, 而let声明的范围是块级作用域
    •              if(true){
                        var name = 'LanMei';
                        console.log (name);  //LanMei
                     }
                      console.log (name);  //LanMei
                      if(true){
                          let birth = 1010;
                          console.log (birth);  //1010
                      }
                      console.log (birth);  //Uncaught ReferenceError: birth is not defined
      
    • 全局作用域下,var变量会指向window,而let变量只会指向变量本身
    •                 var name = 'LanMei';
                       let birth = 1010;
                       console.log(window.name)            //LanMei
                       console.log(window.birth)           //undefined
                       console.log(birth)               //1010
      
    • var可以重复声明同一变量,但是let不能
    •              let birth = 1010;
                      let birth = 1030;
                      console.log (birth);  //Uncaught SyntaxError: Identifier 'birth' has already been declared
                      var name = 'LanMei';
                      var name = 'HongMei';
                      var name = 'HuangMei';
                      console.log (name);  //HuangMei
      
    • 若一定要用let,也可以使用如下写法:
    •    ()=>{
                    let birth = 1010;
                    console.log(birth)
                    }
                   let birth = 1030;   
                   console.log(birth);
      
  • 1.2.4 const

    • const 声明一个常量,常量不可修改,即无法被重新赋值,并且常量声明时必须初始化
        const birth = 1010;
          birth = 1030;/Uncaught TypeError: Assignment to constant variable.
           const name;//Uncaught SyntaxError: Missing initializer in const declaration
1.2 Javascript 六大基本 数据类型
  • 1.2.1 Number

    • 数值型
    • JS不像C/C++、Java、Python等语言严格区分出浮点型和整型,JS会根据声明情况及运算调整精度位,浮点数和整数都归入数值型数据。
    • 创建一个数值型
              a = new Number()
              console.log(a)
               console.log(typeof(a))
              b = 1
              a = 2.333
                console.log(a)
                console.log(b)
              console.log(typeof(b))
  • 1.2.2 String

    • 字符串型

      • JS中不对字符串的长度做约束,可以声明空字符串。
      • 创建一个字符串
    •            var str1 = " "
                     console.log(typeof(str1)) //null
                    var str2 = '12356'
                    console.log(typeof(str2))  //string
      
    • [字符串操作方法]

eg. str1 = "123876" str2 = "3345866"

方法名执行含义实例用法结果
indexOf(x)返回x在字符串里的位置str1.indexOf('1')0
lastIndexOf(x)返回最后一个x在字符串里的位置str2.indexOf('3')0
search()搜索匹配的字符串,返回第一个匹配字符串段的首字符位置str2.search('3')0
slice(start,end)切割出下标为start到end-1的字符串str1.slice(1,3)"23"
slice(start)切割出下标从start开始到最后的字符串str1.slice(3)"876"
substring(a,b)切割出下标在a,b间的字符串(以较小的为起点)str1.substring(3,1)"23"
substring(a)切割出下标从a开始到最后的字符串str1.substring(3)"876"
substr(start,length)切割出下标从a开始到长度为length的字符串str1.substr(3,2)"87"
replace(regexp,replacement)替换符合正则表达式reg的字符串为replacementstr2.replace(/3/g, "66");6645866
replace(substr,replacement)替换字符串substr为replacementstr2.replace(33, "66");6645866
  • 1.2.3 Boolean

    • 布尔型表示变量的真值,只有true和false两种状态。
    • 创建一个布尔型
       var bool = true
          var beel = false
          var Bool = new Boolean()
          Bool = false;
  • 1.2.4 null

    • 空类型,顾名思义,没有类型指向,当赋值后,类型和传入值的类型相同。
    • 创建一个null类
            var hhh = null
          console.log(typeof hhh); //Object
          hhh = "hhh"
          console.log(typeof hhh)

Null变量创建后仍会声明一个空间用于存储空对象。

  • 1.2.5 undefined

    • 创建一个undefined类型
  let hhh;
            console.log(hhh);  //undefined
  • 1.2.6 Object

    • 对象成员

    • ECMAScript中的对象是一组数据和功能的集合,集合内容可以是键值对也可以是函数。创建对象有五种方法:

      • 1.使用new操作符直接创建
      •                  var obj = new Object()
                                  //逐个添加属性(键值对)或方法
                            obj.name = 'Tom'
                            obj.age = 18
                            obj.setName = function(name){
                              this.name = name 
                            }
        
      • 2.使用对象字面量模式的创建
      •     var obj = {
                            name = 'Tom'
                            age = 18
                            setName: function(name){
                              this.name = name
                            }
                           }
        
      • 3.构造函数方式创建
      •     function Person(name, age){
                                    this.name = name
                                    this.age = age
                                    this.setName = function(name){
                                      this.name = name
                                    }
                                  }
                              var obj1 = new Person('Tom',18)
                              var obj2 = new Person('Mike',20)
        
      • 4.工厂模式创建
      •                  function newPerson(name,age){
                                    var obj = {
                                      name: name,
                                      age: age,
                                      setName: function(name){
                                      this.name = name
                                      }
                                    }
                                    return obj
                                  }
        
                           var obj1 = newPerson('Tom',18)
                           var obj1 = newPerson('Mike',20)
        
      • 5.使用原型构造函数创建(Object.create())
            function Person(name, age){
                  this.name = name
                  this.age = age
                }
                Person.prototype.setName = function(name){
                  this.name = name
                }
                var obj1 = new Person('Tom',18)
  • 数组(Array)

    • 数组是一组有序的数据。数组的函数方法使得操作数组增删查改很便捷,可以实现动态操作。

    • 创建一个数组

      • 使用数组构造函数Array(),与c语言相似素组拥有下标(数组索引)。

      • 分离式

      •                      var fruits = new Array(3); //创建一个长度为3的空数组
                            console.log(fruits);
                            fruit[0] = apple;      
                            fruit[1] = banana;
                            fruit[2] = pear;
                            console.log(fruits)
        
      • 直接式

      •        var fruits = new Array("apple","banana"); //创建一个包含元素"apple","banana"的数组
               console.log(fruits);
        
      • 数字字面量模式创建(✨推荐)

      •          var fruits = ["apple","banana"]; //创建一个包含元素"apple","banana"的数组
                  console.log(fruits);
        
      • 使用from()方法创建模板

      •    let fruits = ["apple","banana"];
                              let cpFruits1 = Array.from(fruits);
                              let cpFruits2 = Array.from(fruits);
                              console.log(cpFruits1,cpFruits2);
        
      • 数组空位

        • 使用数组字面量初始化数组时,可以使用一串逗号来创建空位。
      •                 let arr =[,,,,,];
                            console.log(arr[0]);  //undefined
                            console.log(arr.length);  //5
        
      • 数组操作方法

              //eg. 
                  arr1 = [3,4,5,6]  arr2 = ["app","bpp","cpp"] arr3 = [0,9,2,3]
                  function multi(item){
                    return 10*item
                  }
方法名执行含义实例用法结果
indexOf(x)返回x在数组里的位置str1.indexOf('3')0
join()将所有元素合并为字符串str1.join('3')"3456"
isArray()判断是否为数组str1.isArraytrue
forEach(function)顺序遍历每个元素执行函数functionstr1.forEach((item)=>console.log(item))3 4 5 6
map(function)散列遍历每个元素执行函数function后创建新的数组str1.map(multi)[30,40,50,60]
push(element)将元素element添加到数组的末尾str1.push(7)[3,4,5,6,7]
pop(element)删除最后一个元素并返回该元素str1.pop(7)[3,4,5],[6]
unshift(a,b,...)向数组开头添加一个或多个元素arr1.unshift(1,2)[1,2,3,4,5,6]
shift(a,b)删除数组开头一个或多个元素,并返回新的数组长arr1.shift(3,4)[5,6]
splice(key,len,element)在key位置向后插入element元素,并从key位置删除长度为len的元素arr2.splice(1,2,"dpp","epp")["app","dpp","epp"]
slice(start,end)截取数组中从key位置到end-1的元素组成新的数组arr2.slice(1,2)arr2.slice(1,2)["cpp"]
sort()对数组进行排序(快速排序、ASCII值)arr3.sort()[0,2,3,9]
reverse()对数组中的元素按照序号进行反序arr3.reverse()[3,2,9,0]

二、 JavaScript 程序结构设计

2.1 JavaScript 中的函数
  • 2.1.1 JS中的函数

    • JS中的函数是一种特殊的对象,可以存储在Object类型数据内,也是最常用的对象。
  • 2.1.2 四种创建函数的方式

    • 除了上文中普通函数和箭头函数外,还有函数表达式和构造函数new Function:

    • 语法:function 函数名(参数1,参数2){ 函数体 }

    •                 function sum (num1,num2){
                          return num1+num2;
                      }
                      const res = sum(1,2);
                      console.log (res);  //3
      
    • 语法:let/const 变量 = function(参数1,参数2){ 函数体 }

    •             const sum = function (num1,num2){
                      return num1+num2;
                  }
                  const res = sum(1,2);
                  console.log (res);  //3
      
    • 语法:let/const 变量 = (参数1,参数2)=>{ 函数体 }

    •             const sum = (num1,num2)=>{
                      return num1+num2;
                  }
                  const res = sum(1,2);
                  console.log (res);  //3
      
    • 语法:let/const 变量 = new Function(参数1,参数2,...,返回值); (不常用,过多影响性能)

    •             let sum = new Function("num1","num2","return num1+num2");
                    const res = sum(1,2);
                    console.log (res);  //3
      
    • 函数的属性

      • 每个函数都有两个属性,length和prototype,符合对象的要求。
      • prototype的知识前面我们已经讲过,length为函数定义的形参总数。
  • 匿名函数

    • 语法
        (function(){
            console.log("1") //块级作用域 存放语句
          })()

下面大部分内容与C语言基础类似,供大一上还没有开C语言课的专业同学学习

2.2 顺序结构
  • 2.2.1 赋值语句

    • 变量名 = 表达式;
   var a, b;
        a = 1;                  //单赋值
        a = b = 1;            //传递赋值
        ({a, b} = {a: 1, b: 2});//无声明赋值
  • 2.2.2返回语句

    • 在使用 return 语句时,函数会停止执行,并返回指定的值
    • 如果函数没有 return ,返回的值是 undefined
    • 返回值可以是六种基本数据类型,也可以是函数,但只能返回一条表达式。
 function getMax(num1, num2) {
          return num1 > num2 ? num1 : num2;  //返回值为三目表达式
      }
        console.log(getMax(1, 2));
        console.log(getMax(11, 2));
2.3 选择结构
  • 2.3.1 if-else分支语句

    • if 语句

    •         if (condition) {
                  当条件为 true 时执行的代码
              }
      
    • if-else语句

    •   if (condition)
              {
                  当条件为 true 时执行的代码
              }
              else
              {
                  当条件不为 true 时执行的代码
              }
      
    • if-else-else if语句

    •          if (condition1)
              {
                  当条件1true 时执行的代码
              }
               if (condition2)
              {
                  当条件2true 时执行的代码
              }
              else
              {
                  当条件12不为 true 时执行的代码
              }
      
    • 4.3.2 switch-case分支语句

      • switch-case
      switch(n)
        {
            case 1:
                执行代码块 1
                break;
            case 2:
                执行代码块 2
                break;
            default:
                与 case 1case 2 不同时执行的代码
        }
2.4 循环结构
  • 2.4.1 for循环
   for (i = 0; i < 5; i++) {
           console.log("Lanshan!")
      }
  • 2.4.2 for-in循环
       let arr = ['a', 'b', 'c']
        //使用for-in遍历获得索引序号
        for(let item in arr) {
            //打印数组索引(数组的索引就是对象的键名)
            console.log(item);  //0  //1  //2
        }
  • 2.4.3 for-of循环
   let arr = ['a', 'b', 'c']
        //使用for-in遍历获取元素
        for(let item of arr) {
            console.log(item);  //a  //b  //c
        }  
  • 2.4.4 while循环
    i = 0;
        while (i<5){
            i++;
            console.log("Lanshan!")
        }
  • 2.4.5 do-while循环
   do{  
         i++;
         console.log("Lanshan!")
      }
     while (i<5);
  • 2.4.6 break 和 continue
        i = 0;
        while (i<10){
            i++;
             if(i==2){
            continue;
          }
            else if(i==8){
              break;
            }
            console.log(i)
        }
  • break 语句用于跳出循环。
  • continue 用于跳过循环中的一个迭代。
2.5 函数 递归
  • 2.5.1什么是递归:

    • 如果一个函数在内部可以调用其本身,那么这个函数就是递归函数。简单理解:函数内部自己调用自己, 这个函数就是递归函数。
    • 递归函数是一种高阶函数,后续课程会再展开。
  • 2.5.2基本结构:

      function fun(n){
             fun();
        }
        console.log(fun());  
2.6 函数嵌套
  • 2.6.1 什么是嵌套:

    • 如果一个函数在内部定义或者调用了其他函数,叫做嵌套函数,JS中允许多个函数的嵌套。
  • 2.6.2 基本结构:

    function foo() {
          var x = 1;
              function bar() {
                  var y =  x + 1;
                  console.log(y);
              }
          console.log(x);
        }
2.7 函数闭包
  • 2.7.1 什么是闭包:

    • 当一个函数的返回值是另外一个函数,而返回的那个函数如果调用了其父函数内部的其它变量,如果返回的这个函数在外部被执行,就产生了闭包
  • 2.7.2 函数闭包的特点:

    • 引用了外部的函数的作用域。
    • 可以创建私有作用域和私有变量,且不会被回收内存。
  • 2.7.3 与正常函数的区别:

正常情况下,当一个函数调用结束后,函数内部定义的变量占用的内存会被清理掉,

  • (1)而当函数内部 存在闭包函数时,
  • (2)且在函数执行完,闭包函数在外部被使用的时候,

那么在外部函数调用结束之后,由于闭包函数引用了外部函数的作用域,所以外部函数的作用域会一直在内存中,不会被处理掉。

  • 2.7.4 闭包的弊端:

  • 容易导致内存泄露

2.8 JS 程序设计[ 基本原则 ]
  • 2.8.1 单一职责原则(S):

一个类只做一件事,一个类应该只有一个引起它修改的原因,应该只有一个职责。每一个职责都是变化的一个轴线,如果一个类有一个以上的职责,这些职责就耦合在了一起。导致脆弱的设计。例如:要实现逻辑和界面的分离

  • 2.8.2 开闭原则(O):

一个软件实体如类,模块和函数应该 对扩展开放,对修改封闭,即在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果,应该尽可能减少闭包。

  • 2.8.3 里式替换原则(L):

子类应该可以完全替换父类。也就是说在使用继承时,只扩展新功能,不要破坏子类原有的功能。

  • 2.8.4 接口隔离原则(I):

使用多个隔离的接口,比使用单个接口要好。降低类之间的耦合度。换句话说客户端不应依赖它不需要的接口,比如一个接口在实现时,部分方法由于冗余被客户端实现,则应该将接口拆分,让实现类只需依赖自己需要的接口方法。

  • 2.8.5 依赖倒置原则(D):

细节应该依赖于抽象 ,抽象不依赖于细节。把抽象层放在程序设计的最高层,并保持稳定,程序的细节变化由低层的实现层来完成。(例如实现一个组件基类,存放组件的id、apperance等信息,具体的组件类继承基类,实现具体的UI及功能 )

  • 2.8.6 迪米特法则:

又名 [最少知道原则],一个类不应该知道自己操作的类的细节,换言之只和朋友谈话,不和朋友的朋友谈话。