js 总结

136 阅读12分钟

1.数组

1.2 创建方式

   var arr = new Array(); // 不定长度
   var arr = new Array(10) // 定长为10
   var arr = [];

1.3 基本操作

  • 获取数据、增加数据取的是下标
  • in 遍历 返回的是下标
  • for of 遍历,返回的是值
  • delete 删除数据会存在empty占位符
     let arr = [1,2,3];
      arr[3] = 4 ;
      console.log(arr); // [1,2,3,4]
      console.log(arr[0]);
      delete arr[3];
      console.log(arr); // [1,2,3,empty]

    // in 遍历
  for(var i in arr) {
    // i 是下标索引
    console.log(arr[i])
  }
  

  for(let i= 0;i<arr.length;i++) {
    console.log(arr[i]) // 输出4个,delete 引发的
  }

    // of 遍历,返回的是值
  for(let i of arr) {
    console.log('of',i) //1,2,3,undefined
  }
   

1.4 数组的属性

  • length 返回数组的长度
  • constructor 处理实例接受的外部参数
  • prototype 用户扩展额外的方法属性和值属性
  console.log('constructor',arr.constructor(1,2,3)) // [1,2,3]

  const a = new Array(1,2,3);

  console.log('a',a) // [1,2,3]

  Array.prototype.getLegnth = function () {
    return this.length;
  };

   let b = [];

  console.log('a-getLength',b.getLegnth()) // 0

1.5数组操作方法

// 修改原数组
popArr.push(4); // 改变原数组
popArr.unshift(1); // 改变原数组
popArr.pop();    // 修改原数组
popArr.shift();  // 修改原数组
popArr.splice(1,1,9); // 在1的位置,删除一项,并加入一项9
popArr.reverse(); // 修改原数组

// 不修改原数组
popArr.concat([5,6]) // 不修改原数组
popArr.slice(1,2); // 选择某些连续的项,从下表1开始,到下标2为止,返回长度2-1.
popArr.sort(); // 数字排序

// 转化为以,分割的字符串
console.log('popArr',popArr);
console.log('popArr',popArr.toString()); // '1,3,9'
console.log('popArr',popArr.toLocaleString()); // '1,3,9'
console.log('popArr',popArr.join()); // '1,3,9'

// 迭代方法

const every =  popArr.every(i => i>2);
console.log('every',every); // false

const filter =  popArr.filter(i => i>10);
console.log('filter',filter); // []

const find = popArr.find(i => i==100);
console.log('find',find) // undefind ,找到返回值

const some = popArr.some((item,index,arr)=> { // 三个参数,item 每一项,index索引,arr原数组

 console.log('---',item,index,arr);

 return item>1;

});


// 缩小方法 popArr.reduceRight

const a= popArr.reduce((prev,next,index,arr) => { // 2个参数,第一个是一个函数(4个参数,第一个,下一个,索引),返回值是对最后一次处理的next
   console.log(prev,next,index,arr)
   return next -1;
 },100)

 console.log('a---',a)

2 函数

2.1 函数的定义

  • 静态方法 function a () {}
  • 直接量方法 const a = function() {};

2.2 调用方式

 - 直接调用 a()
 - 在链接中调用 <a herf = 'javascript:a()'>
 - 事件调用  = "a()"
 - 递归调用

2.3 方法

  • apply 将函数作为对象的方法来调用,也就是绑定this的指向,参数为[]
  • call 将函数作为对象的方法调用 ,也就是绑定this的指向 ,参数为一个个
  • bind 将函数绑定,返回一个函数代执行
  • toString 返回函数的字符串表示

2.4对象属性

  • arguments 存放实参数的参数,是一个类数组对象 arguments.length 获取函数实参的长度 arguments.callee 返回正在执行的函数 arguments.caler 返回正在执行函数的函数名字

  • this 指向当前对象

  • prototype 指向参数集合所属函数

2.5原型和原型链

  • 什么是构造函数? 函数名字大写 function Persion() {}

  • prototype 是什么,指向什么? 每一个函数都有一个prototype属性,指向 执行 构造函数 创建 的 实例的原型

    let persion1 = new Persion(); Persion.prototype 指向 persion1的原型 因此概念为,构造函数(Persion) 实例(persion1) 和实例原型(Persion.prototype)

  • 如何描述实例(persion1)和实例原型(Persion.prototype)的关系 每一个js对象,除了了null,都有一个属性__proto__ ,指向实例原型,也就是该对象的原型

    persion1.proto === Persion.prototype // true

    实例(persion1)对象通过__proto__ 指向原型; 构造函数(Persion)通过prototype 指向原型

  • 那实例原型是否有属性指向构造函数和实例呢? 指向实例没有,因为实例可能很多个,实例原型只有一个; 指向构造函数有的,每一个实例原型都可以通过 constructor 指向构造函数

Persion.prototype.constructor === Persion ===persion1.constructor // true

实例 实例原型 和构造函数之间的关系就是这些。

  • 实例和原型之间的关系 实例通过 proto 找到原型,如果找不到就去找原型的原型,一直往上找,最顶层是 Object.prototype

    Persion.prototype.proto === Object.prototype

    Object.prototype 的原型是什么呢? Object.prototype.proto 为 null

    Object.prototype.proto === null // true

    一直往上找的行为会形成一个链条,称为原型链。

    表现为: persion1.proto ----> Object.prototype Object.prototype.proto ---> null

  • proto 是什么 浏览器支持的非官方的一个实例属性去指向原型,内部是执行了 Object.getProrotypeOf(persion1)

    Object.getPrototypeOf(persion1) === Persion.prototype // true

2.6函数作用域 和执行上下文

  • 函数作用域:全局作用域、函数作用域、 eval
  • 执行上下文:当代码执行的时候, 会先进行准备工作(变量提升、函数提升),这个准备工作就叫做 执行上下文。
  • 如何管理执行上下文: 当执行全局代码,js引擎会创建执行上下文栈,会创建执行上下文栈,这个栈是一个数组。 当遇见函数执行的时候,根据调用,依次会把函数压栈,最上面的先出栈执行,最后是全局(global栈)
  • 每个执行上下文都会产生3个重要的对象 变量对象、作用域链、this 全局变量对象就是 window 和 this.window 是一样的
  • 作用域链: 查找变量的时候,会先从当前上下文的变量对象中查找,如果没有找到,就会从父级(词法层面上的父级)执行上下文的变量对象中查找,一直找到全局上下文的变量对象,也就是全局对象。这样由多个执行上下文的变量对象构成的链表就叫做作用域链

2.7闭包

  • 可以访问自由变量的函数(不是参数和函数内部声明的); 即使销毁了函数,也可以通过作用域链访问到变量

2.8 参数按照值传递还是引用传递

var value = 1;
function foo(v) {
    v = 2;
    console.log(v); //2
}
foo(value);
console.log(value) // 1


   var obj = {
  value: 1
  };
  function foo(o) {
      o.value = 2;
      console.log(o.value); //2
  }
  foo(obj);
  console.log(obj.value) // 2

  var obj = {
  value: 1
  };
  function foo(o) {
      o = {value:'ddd'};
      console.log(o.value); //ddd
  }
  foo(obj);
  console.log(obj.value) // 1

   按引用传递是传递对象的引用,而按共享传递是传递对象的引用的副本!

  所以修改 o.value,可以通过引用找到原值,但是直接修改 o,并不会修改原值。所以第二个和第三个例子其实都是按共享传递。  

2.9 创建对象的方式和优缺点

  • 工厂模式
   function createPersion(name) {
     let o = new Object();
     o.name = name;
     o.sayName = function() {}

     return o;
   }

   缺点:对象无法识别,所有实例都指向同一个原型
  • 构造函数模式
   function Persion(name) {
     this.name = name;
     this.getName = function() {}
   }

优点:实例可以识别为一个特定的类型

缺点:每次创建实例时,每个方法都要被创建一次

优化:
function Person(name) {
 this.name = name;
 this.getName = getName;
 }

 function getName() {
     console.log(this.name);
 }

 var person1 = new Person('kevin');

 失去了封装性

  • 原型模式
   function Person(name) {

   }

   Person.prototype.name = 'keivn';
   Person.prototype.getName = function () {
       console.log(this.name);
   };

   var person1 = new Person();

 优点:方法不会重新创建

 缺点:1. 所有的属性和方法都共享 2. 不能初始化参数

 优化:

 function Person(name) {

 }

 Person.prototype = {
     constructor: Person,
     name: 'kevin',
     getName: function () {
         console.log(this.name);
     }
 };

 var person1 = new Person();

 原型的缺点还是没有避免


  • 组合模式 构造函数和原型的组合模式,也是使用比较广泛的一种
```javascript
   function Persion(name) {
     this.name = name;
   }
   Persion.prototype = {
     constructor:Persion,
     getName:function() {}
   }
优点:该共享的共享,该私有的私有,使用最广泛的方式

缺点:有的人就是希望全部都写在一起,即更好的封装性  
```
  • 动态原型模式
  方式1:
   function Persion(name) {
     this.name = name;
     if(typeof this.getName !== 'function') {
       Persion.prototype.getName = function() {}
     }
   } 
   方式2
   function Person(name) {
       this.name = name;
       if (typeof this.getName != "function") {
           Person.prototype = {
               constructor: Person,
               getName: function () {
                   console.log(this.name);
               }
           }

           return new Person(name);
       }
   }
  • 寄生构造函数模式

       function Persion(name){
         var o = new Object();
         o.name = name;
         o.sayName =function() {}
         return o;
       }
      什么时候使用,假设想要一个数组,有个方法,返回以| 连接数组的每一项的字符串
    
      function SpecialArray() {
        var values = new Array();
        for (var i = 0, len = arguments.length; i < len; i++) {
            values.push(arguments[i]);
        }
    
        values.getSpiString =function() {
          return this.join('|');
        }
      }
    
    
  • 稳妥模式 没有公共属性,也不引用this

function person(name){
 var o = new Object();
 o.sayName = function(){
     console.log(name);
 };
 return o;
}

2.10 继承方式

  • 原型链继承 原理: 子的原型指向父级的实例原型 const A.prototype = new Parent(); 缺点: 1. 引用类型的属性被所有实例共享 2.在创建 Child 的实例时,不能向Parent传参

       function Parent() {};
       function Child() {};
       Child.prototype = new Parent();
    
    
  • 借助构造函数 原理: 子构造函数里面调用父构造函数,父构造函数调用绑定子的this 优点: 1.避免了引用类型的属性被所有实例共享 2.可以在 Child 中向 Parent 传参 缺点: 方法都在构造函数中定义,每次创建实例都会创建一遍方法。

      function Parent(name) {}
      function Child(name) {
        Parent.call(this,name)
      }
    
  • 组合继承 原理:原型式继承和组合继承结合的方式, 父级方法放在原型上,可以解决构造函数的缺点 优点:解决了原型链继承和原型式继承 组合继承最大的缺点是会调用两次父构造函数。

    一次是设置子类型实例的原型的时候:

    Child.prototype = new Parent(); 一次在创建子类型实例的时候:

    var child1 = new Child('kevin', '18');

       function Parent(name){}
       Parent.prototype.sayName = function() {}
       function Child(name) {
         Parent.call(this,name)
       }
       Child.prototype = new Parent();
       Child.prototype.constructor = Child;
    
  • 原型式继承 原理:传入的对象作为创建的对象的原型,就是 ES5 Object.create 的模拟实现 缺点:和原型链继承是一样的

       function createObj(o) {
         function F () {}
         F.prototype = o;
         return new F()
       }
    
  • 寄生式继承 原理: 创建一个仅用于封装继承过程的函数,该函数在内部以某种形式来做增强对象,最后返回对象。

        function createO(o) {
          let obj = Object.create(o);
          obj.sayName = function() {}
          return obj;
        } 
    
  • 组合寄生式继承 原理: 组合模式下,如果让Child.prototype 间接的指向 Parent.prototype

      function Parent(name){}
      function Child(name) {
        Parent.call(this,name)
      }
      let F = function() {}
      F.prototype = Parent.prototype;
    
      Child.prototype = new F();
    
      最后把这个方法封装一下
      function prototype(child, parent) {
        var prototype = Object.create(parent.prototype);// 创建过度的对象
        prototype.constructor = child; // 对象的构造函数指向 child
        child.prototype = prototype; // 指向中间过渡原型
      }
    
      // 当我们使用的时候:
      prototype(Child, Parent);
    
    

web浏览器的事件注册

事件挂在

Element,Document和Window

  • 方式1: 通过把函数赋给某个特殊属性 缺点:对于某个事件只能注册一个事件处理器。也就是说, 一不小心就会将上一个事件处理器改写掉

       window.onload =funciton() {}
       document.body.onclick = function() {}
    
  • 方式2: 通过addEventListener 可以注册多个事件

      window.addEventListener('click',function(e,i) {
          console.log(e,i)
      });
      document.body.addEventListener('click',function(e) {
        console.log(e)
      })
    

3 编程方式

3.1命令式编程 VS 面对对象编程 VS 函数式编程

  • 命令式编程: 用控制语句(if else while return break)等,一步步告诉计算机先做什么,后做什么;
  • 面对对象编程:告诉计算机应该做什么,但是不需要告诉计算机如何做。
  • 函数式编程:将计算机计算看成是函数的计算。 这几种编程思想,并不是割裂的独立的,只是一定程度的上的代码思想的转化,命令式适合做流程控制(switch); 面对对象适合构建实例,操作并维护一个仓库;函数式编程基于函数为基础去编程
  // 命令式
trim(reverse(toUpperCase(map(arr))));
// 面向对象
arr.map().toUpperCase().reverse().trim();
// 函数式
const result = componse(trim,reverse,toUpperCase,map)

3.2什么是函数式编程

原子组合:把原本的逻辑组合,并不影响最后的结果(加法结合律 | 因式分解 | 完全平方公式)

3.3函数式理论思想

  1. 函数式一等公民
  2. 逻辑功能落脚点就是函数
  3. 实现函数和拼接函数
  4. 声明一个需求,尽量语义化

3.4函数式的使用场景

函数组合的形式

  • 函数组合流水线有顺序的(后面的输入依赖前面的输出,违背了函数式原则,但是还是能优化点代码)
  • 函数组合流水线无顺序的(函数前后调用返回结果不变,arr.map.filter)
   /**
   *
   *  // 第一次需求: 将这个人的名字加个加个企业名字
   *  // 第二次需求: 将这个人名字加上后缀 属于企业有限责任公司
   *  // 第三次需求: 将这个人的公司名称隐藏 **
   *  
   *  // 函数式组合的概念:
   *   1. 首先创建一个组合函数,本次的输出作为下次的输入(reduce)
   *   2. 返回一个缓存函数
   *   3. 执行缓存函数即可
   */

   const componse = (...fns) => {
     return (value) => {
       return fns.reduce((v,fn) => fn(v),value)
     }

   }

   const addName = (str) => {
     return  `${str}-属于企业有限责任公司`
   }

   const hideName = (str) => {
     return  str.replace(/公司/ig,'**')
   }

   const elipName = (str) => {
     return str >=10?`${str}...`:str;
   }

   const comData = componse(addName,hideName,elipName);

   console.log('comData', comData('ffsdfsafjf'))
  // 上面一个例子都有个问题,就是不够封闭性,尝试将企业的名字动态传入
 const newAddName =(companyName) => {
   return (value) => value + companyName
 };

      const comData = componse(newAddName('测试企业名字'),hideName,elipName);
  

偏函数

  概念:缓存一部分参数,然后让另外一些参数在使用时传入

 const newAddName =(companyName) => {
   return (value) => value + companyName
 };
#### 柯里化
 概念:将参数放到一起,最后一起处理.
 
```js
   // 实现一个去重数据的函数
  //  f([1,2])([3,4])([1,2]) => [1,2,3,4]
   const unquieArrFun =(value=[]) => {
      let arr =[...value];
      const innerFun =(innerValue=[]) => {
        arr = [...arr,...innerValue];
        return innerFun
      }

      innerFun.toString =() => {
        return [...new Set(arr)];
      }
      return innerFun;

    }
    const data = unquieArrFun([1,2])([3,4])([1,2])
    console.log('unquieArrFun',data.toString())

    // 实现一个累加
    // f(1)(2)(3)(4)
    const addFun =(value:number) => {
    const arr =[value];
    const innerFun =(val:number ) => {
      arr.push(val);
      return innerFun;
    }

    innerFun.toString =() => {
      return arr.reduce((prev,next) => prev+next,0)
    }

    return innerFun;
    }
    const sum = addFun(1)(2)(3);

    console.log('sum',sum.toString())
```

惰性函数

概念:调用一次之后,后面再调用一直都是同一个函数
   // 需求:取一个基础时间点,任何时候都要以此时间点计算
   const getTime1 = () => {
       return new Date().getTime();
   }
   const getTime = () => {
     let time = new Date().getTime();
     const getTime = () => {
       return time;
     }
     return getTime();
   }


   setTimeout(() => {
     console.log('----',  getTime1())
     console.log('----',  getTime())

   },200)
   setTimeout(() => {
     console.log('----2',  getTime1())
     console.log('----2',  getTime())

   },300)

   // 需求:缓存请求

   let cacheApi = async () => {

     const data = await new Promise((resolve,reject) => {
         setTimeout(() => {
           resolve('eeee');
         },2000)
     })
     console.log('数据只会执行一次')
     cacheApi =() => {
       return data;
     }

     return cacheApi();
   }
   console.log('cacheApi', cacheApi())
   setTimeout(() => {
     console.log('cacheApi', cacheApi())
   },3000)


   // 惰性函数
   const program = name => {
     if(name === 'progressive') {
        return program = () => {
          console.log('progressive')
        } 
     } else if(name === 'obj') {
       return program =() => {
         console.log('obj')
       }
     } else {
       return program =() => {
         console.log('func')
       }
     }
   }
   program('progressive')();
   console.log('lazy');
   program();
   // progressive lazy progerssive

3. 无状态和副作用(rxjs、lodash)

  • 无状态 - 幂等,每次输入,得到都是相同的输出
  • 无副作用 函数内部不应该对整个系统任何参数变量的改动
   if(a === '1' || a ==='2') {
     // doSomething
   }
   
   // 优化1:
   if(['1','2'].includes(a)) {
     // doSomething
   }

   // 优化2:
   const functionCode =(a,arr=[]) => { // 消除全局影响
     return arr.includes(a)
   }
   // 优化3:
    const functionCode =(a:string| number,arr?:any []):boolean=>(a,arr=intialArr) => { // 全局枚举集合
     return arr.includes(a)
   }


  

3.1 实际开发

3.2 纯函数改造

   const _class = {
     name:"obj"
   }
   // 函数内部引入了外部变量了, -- 违反了无状态规则
   const score = str => _calss.name + ':' + str;
  
   // 违反了修改传入参数
   const changeClass = (obj,name) => {
     obj.name = name;
   }

   changeClass(_class,'fucntion');
   score('good!');

   //修改----------------
   const _class = {
     name:"ob"
   }
   const score =(obj,str) => obj.name + ':' +str; // 不依赖外部变量
   const changeClass = (obj,name) => ({...obj,name});// 未修改外部变量
   changeClass(_class,'fucntion');
   score('good!');

3.3流水线组装

实现构造函数实现可拆分传递参数的累加函数

   // 1. 构造函数科里化结构
   // 2. 传入参数arguments
   // 3. 递归参数无限拓展
   // 4. 主功能实现 => 累加
   // 5. 输出
   const add = function() {
     let args = Array.prototype.slice.call(arguments);

     // 内层处理
     const inner = function() {
       args.push(...arguments);
       return inner;
     }
     // 递归保证的是都一样,最后输出值,用toString方法复写
     inner.toString = function() {
       return args.reduce(prev,cur) => {
         return prev + cur;
       }
     }
     return inner
   }

4正则

4.1 正则的概念

正则是匹配模式,要么是匹配字符,要不匹配位置。

4.2 横向模糊匹配

  • 概念: 一个正则可匹配的字符串的长度不是固定的,可以是多种情况的
  • 实现方式,使用量词{m,n} 最少m个,最多n个
   const patter = /ab{2,5}c/ig;
   'abbc abbbc'.match(patter); // [abbc,abbbc]

4.3 纵向模糊匹配

  • 概念:一个正则匹配的字符串,具体到某一位字符时,它可以不是某个确定的字符,可以有多种 可能。
  • 实现方式:[123] 可能是1,2,3中的任意一个
  • 范围表示法[1-6a-zA-Z],表示其中的任意一个,如果仅仅想表示az-的3个字符中任意一个,可以[az-] [za-] [-az]
   const patter = /a[123]b/g;
   'a0b a1b a2b'.match(patter) // [a1b,a2b]
  • 排除字符组 [^abc] 不是abc中的任意一个

image.png

  • 匹配任意字符 [\s\S] [\d\D] [\s\S] [^]

4.4 量词

{m,n} 最少m次,最多n次 {m,} 最少m次 {m} 出现m次 ? {0,1} 记忆表示有吗

  • {1,} 至少有一次 + ,多余一个才加
  • {0,} 表示0到多个,* 星星,天上可能看不到,也可能看到很多
  • 贪婪匹配 vs 惰性匹配
      let  regex = /\d{2,5}/g; // 能者多劳 2-5个
      let regex1 = /\d{2,5}?/g; // ? 你知足了吗,当为2就不再往下匹配了
    
      '12432,542'.match(regex1) // [12,43,52] 
    
    

4.5 多选分支

  • (p1|p2|p3) 惰性匹配
var regex = /good|goodbye/g;
var string = "goodbye";
console.log( string.match(regex) );
// => ["good"]

4.6 ------------------案例分析--------------------

  1. 匹配颜色值 #开头 固定为6位或者3位的 数字和大小写字符组成
   /#[0-9a-fA-F]{6}|[0-9a-fA-F]{3}/
  1. 匹配时间 第一个是0-2; 第二位0-9 ;;第三位0-5,第四位0-9
  /^([01][0-9]|[02][0-3]):([0-5][0-9])$/
  23:35

如果要求匹配 7:9, 十分的0可以省略 ,枚举所有的可能性 /^(0?[0-9]|1[0-9]|2[0-3]):(0?[0-9]|[1-5][0-9])$/

正则匹配位置攻略

如何理解: 出现这几个,或者想操作字符位置的话,用这几个 ^ 开头
结尾\b是单词边界,具体就是\w\W之间的位置,也包括\w间的位置,和\w结尾 \b 是单词边界,具体就是 \w 与 \W 之间的位置,也包括 \w 与 ^ 之间的位置,和 \w 与 之间的位置。 \B 非单词边界 (?=p) 子模式 匹配到p前面的位置 (?!p) 非子模式 匹配到非p前面的位置

'[sa]saf.32f'.replace(/\b/g,'#'); // '[#sa#]#saf#.#32f#'
'[eea]sdjdd'.replace(/(?=dj)/g,'#')// '[eea]s#djdd'
  • 实现数字按照正则匹配 '1241214' 以,千分位分割 每三位是一个模式,从后面开始

       '1241214'.replace(/(?=(\d{3})$)/g,',') // '1241,214'
    

    每一个三位都要有

       '1241214'.replace(/(?=(\d{3})+$)/g,',') // '1,241,214'
    

    3的倍数,就会有问题,前面多一个,要求这个位置不能是开头 (?!^)

       '1241214'.replace(/(?!^)(?=(\d{3})+$)/g,',') // '1,241,214'
    
  • 密码验证 必须包含数字和字母 必须包含s数字 (?=.[0-9]) 必须包含字母 (?=.[a-z])

    ?=.*[0-9] // .*任意字符后面必须是数字

    /(?=.*[0-9])^[0-9A-Za-z]{6,12}$/

  • 反向引用

2016-06-12 2016/06/12 2016.06.12

/\d{4}(/|.|-)\d(2)(/|.|-)\d{2}/g;

也会匹配到 2016.06-12 这样的内容

/\d{4}(/|.|-)\d(2)\1\d{2}/g; // \1表示上一个匹配到的分组

正则表达式括号的使用

  • 括号提供了分组,可以在js中引用或者在括号中引用
  • (ab)+ 表示以ab出现最少一次;
  • (ab|abc) ab 或者 abc
  • 匹配到的分组可以用表示 11 2 (api分组)
'2027-12-34'.replace(/(\d{4})-(\d{2})-(\d{2})/,'$3.$2.$1'); // '34.12.2027'

  • 反向引用 含义:让你少写一些一模一样的正则, 在正则里面使用 \1 \2 \n 表示匹配到的分组的简写形式 \10 表示第十个分组

    /(\d{1,})(-)\1\2/g.test('123-123-') // true
    
     (\d{1,}) 用\1代替
     (-) 用\2代替 
    
    
    • 非捕获括号 只想要匹配到最原始的功能,可以使用非捕获括号 (?:P)
      let reg = /(?:ab)+/g;
      'ababc abbbb ababab'.match(reg); // ['abab','ab','ababab'] 只要ab连续子串
    

正则表达式的拆分

正则执行的优先级 \ ---> () [] ---> {m,n} ? * + ----> ^ $ \1 \b \B ---> |

元字符转译

需要,但是不完全需要,为避免错误,尽量都转译

/[abc]/g === /[abc]/g 因为后面一个构不成字符组[],因此不需要

是否使用正则

  • 正则不能完成所有事情,能用字符串方法,不用正则
  • 构建小而简单的正则,去判断处理,不需要构建一个复杂看不懂的正则
  • 准确性,一般是知道预期的目标,而不匹配非预期的目标

正则表达式回溯原理

  • 概念 从一种状态出发,搜索这种状态的所有可能,当一条路走不通,再后退一步或者若干步。这种不断前进,不断后退的方法,称为回溯法。

  • 发生在哪些地方 贪婪量词 {m,n} 惰性量词 {m,n}? 分支结构 |

5 字符串

5.1 字符串函数

5.2 根据位置查找字符函数

  • charAt(n) 返回字符串中第n字符,从字符串的下标0开始
  • charCodeAt(n) 返回字符串中n哥字符的code 是多少,返回是数字
  'ddddasf'.charAt(5) // s
  'ddddasf'.charCodeAt(5) // 115

5.3根据字符查找位置函数

-indexOf() 从前往后找,找到第一个符合的 没找到返回-1 -lastIndexOf() //从后往前找,找到第一个符合的没找到返回-1

  'ddddasf'.indexOf(s) // 5
  'ddddasf'.lastIndexOf(s) // 5

5.4匹配函数

  • match 找到一个或者多个正则表达式或者字符串的匹配

     '141414'.match(/14/) // ['14', index: 0, input: '141414', groups: undefined]
     'fada'.match(/a/ig) // ['a','a'] 全局匹配额时候
     'afda'.match('a') //  ['a', index: 0, input: 'afda', groups: undefined]
    
  • search 找到第一个要查找的字符串,没找到返回 -1

    'afda'.search(/a/) // 0 
    'afda'.search('a') // 0
    
  • replace 替换参数,接受一个字符或者正则表达式

      '14124'.replace('1','s')// 's4124' 替换一个
      '14124'.replace(/1/,'s')// 's4124' 替换一个
      '14124'.replace(/1/ig,'s') // 全替换 必须加g
      '14124'.replace(/1/g,function(){ return 'j' }) // 'j4j24' 接受函数
      '1,4124'.replace(/(1),(4)/g,'$2,$1') // $n 表示匹配到的分组占位符
    
  • split 以一定的规则分割成数组

   'daf'.split('') // [d,a,f]
  • concat 链接字符串,功能和 + 一样
   'a'.concat('2') // a2 

5.5截取方法

  • slice subString 通过下标获取字符串方法
  • substr 根据长度截取
 'a23456'.slice(1,2) // 2 
 'a23456'.subStrig(1,2) // 2 
 'a23456'.substr(1,2) // 23 
 

5.6去掉空格

  • trim() 首尾去掉
  • trimLeft() 去掉左边
  • trimRight()

5.7 编码方法

  • escape unescape 对字符串进行uncoide 编码

  • encodeURI uncodeURI 对url编码,不会对 / ?,等编码

  • encodeURIComponent uncodeURLComponent 对所有的URI编码,会导致路由不能用

    变量类型

    基础类型

    类型: null boolean string number undefined Symbol Bigint 检查: typeof 存储: 栈中,值类型 typeof返回的值: string number undefined boolean function object symbol bigint(全是小写)

    对象类型

    类型: null boolean string number undefined Symbol Bigint 检查: instanceof 存储: 堆中,引用类型 instanceof返回的值: Object Array Function String Date RegExp Number Set WeakSet Map WeakMap(全是大些)

变量检查类型

基础类型检查

const a_1 = null;
const a_2 = 'tedt';
const a_3 = 12;
const a_4 = true;
const a_5 = undefined;
const a_6 = Symbol('ll');
const a_7 = BigInt('12');
const a_8 =  () => {};
const a_9 = {};
const a_10 = [];

console.log(typeof a_1 === 'object') //true
console.log(typeof a_2 === 'string') //true
console.log(typeof a_3 === 'number') //true
console.log(typeof a_4 === 'boolean') //true
console.log(typeof a_5 === 'undefined') //true
console.log(typeof a_6 === 'symbol') //true
console.log(typeof a_7 === 'bigint') //true
console.log(typeof a_8 === 'function') //true
console.log(typeof a_9 === 'object') //true
console.log(typeof a_10 === 'object') //true

复杂类型检查

    const b_1 = {};
    const b_2 = [];
    const b_3 = new Date();
    const b_4 = /qwe/g;
    const b_5 = () => {};
    const b_6 = new String('123');
    const b_7 = new Number('123');
    const b_8 = new Set([1]);
    const b_9 = new WeakSet();
    const b_10 = new Map();
    const b_11 = new WeakMap();

    console.log('-----------')
    console.log(b_1 instanceof Object); // true
    console.log(b_2 instanceof Object); // true
    console.log(b_2 instanceof Array); // true
    console.log(b_3 instanceof Date); // true
    console.log(b_4 instanceof RegExp); // true
    console.log(b_5 instanceof Function); // true
    console.log(b_6 instanceof String); // true
    console.log(b_7 instanceof Number); // true
    console.log(b_8 instanceof Set); // true
    console.log(b_9 instanceof WeakSet); // true
    console.log(b_10 instanceof Map); // true
    console.log(b_11 instanceof WeakMap); // true

当判断一个引用类型,是不是对象时,可能是很多种,因此需要一个准确判断方法。Object.prototype.toString() 判断格式: '[object Type]' object 是小写的,Type是大写的

console.log(Object.prototype.toString.call(a_1) === '[object Null]'); // true
console.log(Object.prototype.toString.call(a_2) === '[object String]'); // true
console.log(Object.prototype.toString.call(a_3) === '[object Number]'); // true
console.log(Object.prototype.toString.call(a_4) === '[object Boolean]'); // true
console.log(Object.prototype.toString.call(a_5) === '[object Undefined]'); //true
console.log(Object.prototype.toString.call(a_6) === '[object Symbol]'); // true
console.log(Object.prototype.toString.call(a_7) === '[object BigInt]'); //true
console.log(Object.prototype.toString.call(a_8) === '[object Function]'); // true
console.log(Object.prototype.toString.call(a_9) === '[object Object]'); //true
console.log(Object.prototype.toString.call(a_10) === '[object Array]'); //true
console.log(Object.prototype.toString.call(b_1) === '[object Object]'); //true
console.log(Object.prototype.toString.call(b_2) === '[object Array]'); //true
console.log(Object.prototype.toString.call(b_3) === '[object Date]'); //true
console.log(Object.prototype.toString.call(b_4) === '[object RegExp]'); //true
console.log(Object.prototype.toString.call(b_5) === '[object Function]'); //true
console.log(Object.prototype.toString.call(b_6) === '[object String]'); //true
console.log(Object.prototype.toString.call(b_7) === '[object Number]'); //true
console.log(Object.prototype.toString.call(b_8) === '[object Set]'); //true
console.log(Object.prototype.toString.call(b_9) === '[object WeakSet]'); //true
console.log(Object.prototype.toString.call(b_10) === '[object Map]'); //true
console.log(Object.prototype.toString.call(b_11) === '[object WeakMap]'); //true

变量的作用域

全局变量 位置:全局var、函数体内无var 调用:任意位置 生命周期:除非显式的删除、否则一直存在,挂在在window上 局部变量 位置:函数参数、函数体内的var声明 调用:当前的函数体内 生命周期:自函数执行起或者函数 优先级: 局部变量同名高于全局变量同名 参数同名高于全局变量参数同名 局部变量同名高于参数同名

  var widowA = '全局'
  function testName (widowA) {
    widowA = '局部';
    console.log('widowA',widowA) // 局部

    return widowA;
  };

  testName('参数');
  console.log('widowA',widowA) // 全局

  var b = 'ddd';
  function testB() {
    b = 'ccc';
  };
  testB();

  console.log('b',b); // ccc

作用域链: 函数内层能访问外层和全局的变量 外层函数不能访问内层局部变量

声明提升: 函数声明要高于变量声明

console.log(m);  // f() {}
var m =''; 
function m(){};

6 变量隐式类型转换

6.1 概念: 通过显示的 Number('123') 称为显示类型转换;

   一些情况下,来种值会发生相互转化,称为隐式类型转换;
   === 不会发生隐式类型转换。

6.2 规则:

 规则_1:js中只有三种类型转换  (to string ; to boolean ; to number )
 规则_2:原始类型和对象类型转化是不同的,但是只会转成上面三种类型

6.3 转化为string:

显式: String('12') new String('12')不是值,是一个对象

基础类型 隐式转换 格式: [p] + ''

  console.log('to string',12 + ''); // '12'
  console.log('to string','12' + ''); // '12'
  console.log('to string',null + ''); // 'null'
  console.log('to string',undefined + ''); // 'undefined'
  console.log('to string',true + ''); // 'true'
  console.log('to string',BigInt(1) + '1'); // '11'
  console.log('to string',String(Symbol('d'))); // 'Symbol(d)' Symbol 必须显式转换

6.4 转换为boolean

不在以下情况的都转化为true

      Boolean('')           // false
      Boolean(0)            // false  
      Boolean(-0)           // false
      Boolean(NaN)          // false
      Boolean(null)         // false
      Boolean(undefined)     // false
      Boolean(false)        // false

发生隐式转换的情况

if('2' || '')  // || 和 && 会发生内部转换,可以判断,但是返回的是值, 
!!2 

6.5 number 转化

比较复杂, 比较操作(>, <, <=, >=) 按位操作(| & ^ ~) 算数操作(- + * / %), 注意,当 + 操作存在任意的操作数是 string 类型时,不会触发 number 类型的隐式转换 一 元 + 操作 非严格相等操作(== 或者 != ),注意,== 操作两个操作数都是 string 类型时,不会发生 number 类型的隐式转换

== 的转化规则,当位null 和 undefined 时,不会发生转化。null和undefined 只等于自己本身

  + '124'  // 124
  '123' - 0 // 123
  Number(true) // 1
  Number(undefined) // NaN

Object 类型转换 规则: -Symbol.toPrimitive 存在,执行这个,不走valueOf、toString ,是原始类型就返回,还不是原始类型就保存。 -number 先调用 valueOf() 后调用toString() ; string 先调用 toString() 后调用 valueOf()

    let testObjToNumber  ={
    // [Symbol.toPrimitive](hite) {
    //  if(hite  ==='number') {
    //    return 3;
    //  }
    //  if(hite === 'string') {
    //    return 4
    //  }
    // },
    a:1,
    valueOf() {
      return this.a +1;
    },
    toString() {
      return this.a+'1'
    }
  };

  console.log('testObjToNumber',+testObjToNumber) // 2, 放开3 
  console.log('testObjToNumber',`${testObjToNumber}`) // 11 ,放开返回是4