js高级学习

面向对象编程介绍

1、面向过程编程(POP)

面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候再一个一个的依次调用就可以了。

2、面向对象编程OOP

面向对象是把事务分解成为一个个对象,然后由对象之间分工与合作。

以对象功能来划分问题,而不是步骤

在面向对象程序开发思想中,每一个对象都是功能中心,具有明确分工。
面向对象编程具有灵活、代码可复用、容易维护和开发的优点,更适合多人合作的大型软件项目。

面向对象的特性:封装、继承、多态

3、对比

面向过程
优点∶性能比面向对象高,适合跟硬件联系很紧密的东西,例如单片机就采用的面向过程编程。

缺点∶没有面向对象易维护、易复用、易扩展

面向对象
优点∶易维护、易复用、易扩展,由于面向对象有封装、继承、多态性的特性,可以设计出低耦合的系统,使系统更加灵活、更加易于维护
缺点︰性能比面向过程低

es6中的类和对象

1、对象

现实生活中︰万物皆对象,对象是一个具体的事物,看得见摸得着的实物。例如,一本书、一辆汽车、一个人可以是“对象”,一个数据库、一张网页、一个与远程服务器的连接也可以是“对象”。

在JavaScript 中,对象是一组无序的相关属性和方法的集合,所有的事物都是对象,例如字符串、数值、数组、函数等。

对象是由属性和方法组成的∶
属性∶事物的特征,在对象中用属性来表示(常用名词)

方法:事物的行为,在对象中用方法来表示(常用动词)

2、类class

在ES6中新增加了类的概念,可以使用class关键字声明一个类,之后以这个类来实例化对象。

抽象了对象的公共部分,它泛指某一大类(class )

对象特指某一个,通过类实例化一个具体的对象

思维特点:

1、抽取(抽象)对象共用的属性和行为组织(封装)成一个(模板)

2、对类进行实例化,获取类的对象

(1)创建类

1)constructor构造函数

**constructor()**方法是类的构造函数(默认方法),用于传递参数,返回实例对象,通过new命令生成对象实例时,自动调用该方法。如果没有显示定义,类内部会自动给我们创建一个constructor()

<script>
  //1、创建类class 创建一个 明星 类
  class Star{
    //类的共有方法放到constructor
      constructor(uname,age) {
          this.uname = uname;
          this.age = age
      }
    // (1) 我们类里面所有的函数不需要写function
    //(2) 多个函数方法之间不需要添加逗号分隔
    sing(song) {
      console.log(this.uname + song)
    }
  }
//  2、利用类创建对象 new
//  通过new命令生成对象实例时,自动调用 constructor方法
  var ldh = new Star('刘德华',18);
  var zxy = new Star('张学友',13);
  console.log(ldh)
  console.log(zxy)
  ldh.sing('冰雨')
  zxy.sing('理想栏')
  //(1) 通过class 关键字创建类, 类名我们还是习惯性定义首字母大写
  //(2) 类里面有个constructor 函数,可以接受传递过来的参数,同时返回实例对象
  //(3) constructor 函数 只要 new 生成实例时,就会自动调用这个函数, 如果我们不写这个函数,类也会自动生成这个函数
  //(4) 生成实例 new 不能省略
  //(5) 最后注意语法规范, 创建类 类名后面不要加小括号,生成实例 类名后面加小括号, 构造函数不需要加function
</script>
2)类的继承
extends关键字

子类可以继承父类的一些属性和方法

super关键字

super关键字用于访问和调用对象父类上的函数。可以调用父类的构造函数,也可以调用父类的普通函数

注意:子类在构造函数中使用super,必须放到this前面(必须先调用父类的构造方法,在使用子类构造方法)

<script>
  //1、类的继承
  /*class Father {
    constructor() {
    }
    money(){
      console.log(100)
    }
  }
  // 子类继承父类的属性和方法
  class Son extends Father{

  }
  var son = new Son()
  son.money()*/

  class Father {
    constructor(x,y) {
      this.x = x
      this.y = y
    }
    sum() {
      console.log(this.x + this.y)
    }
  }
  // 子类继承父类的属性和方法
  class Son extends Father{
    constructor(x,y) {
      super(x,y)  //调用了父类的构造函数
    }
  }
  var son = new Son(1,2)
  var son1 = new Son(11,21)
  son.sum()
  son1.sum()
</script>
<script>
  //super关键字调用父类普通函数
  class Father {
    say() {
      return '我是父亲'
    }
  }
  class Son extends Father{
    say() {
      // console.log('我是儿子')
      //调用父类的普通函数say:super.say()
      console.log(super.say() + '的儿子')
    }
  }
  var son = new Son()
  son.say()
  // 继承中的属性或者方法查找原则: 就近原则
  // 1. 继承中,如果实例化子类输出一个方法,先看子类有没有这个方法,如果有就先执行子类的
  // 2. 继承中,如果子类里面没有,就去查找父类有没有这个方法,如果有,就执行父类的这个方法(就近原则)
</script>
子类继承父类方法同时扩展自己方法
<script>
  //父类有加法方法
  class Father {
    constructor(x,y) {
      this.x = x
      this.y = y
    }
    sum() {
      console.log(this.x + this.y)
    }
  }
//  子类继承父类加法方法 同时  扩展减法方法
  class Son extends Father{
    constructor(x,y) {
      //  利用super调用父类中的构造函数
      //必须在子类this之前调用!!!
      super(x,y)
      this.x = x
      this.y = y
    }
    subtract(){
      console.log(this.x - this.y)
    }
  }
  var son = new Son(5,3)
  son.subtract()
  son.sum()
</script>

(2)实用类注意事项

<button>点击</button>
<script>
  var that
  var _that
  class Star {
    constructor(uname,age) {
      //constructor里面的this指向实例对象,方法里面的this指向这个方法的调用者
      //constructor 里面的this 指向的是 创建的实例对象
      that = this
      console.log(this)
      this.uname = uname
      this.age = age
      // this.sing()
//  2、类里面的共有的属性和方法一定要加this使用
      this.btn = document.querySelector('button')
      this.btn.onclick = this.sing
    }
    sing(){
      //这个sing方法里面的this 指向的是 btn这个按钮,因为这个按钮调用了这个函数
      console.log(this)
      // console.log('hi')
      //that里面存的就是constructor里的this
      console.log(that.uname)
    }
    dance(){
      //这个dance里面的this 指的是实例对象ldh,因为ldh调用了这个函数
      _that = this
      console.log(this)
    }
  }
  //  1、在ES6中,没有变量提升,所以必须先定义类,才能通过类实例化对象
  var ldh = new Star('刘德华')
  console.log(that === ldh) //true 表示constructor 里面的this 指向的是 创建的实例对象
  ldh.sing()
  ldh.dance()
  console.log(_that === ldh) //true

</script>

构造函数和原型

在典型的OOP的语言中(如Java ),都存在类的概念,类就是对象的模板,对象就是类的实例,但在ES6之前,JS中并没用引入类的概念。

ES6,全称ECMAScript 6.0,2015.06发版。但是目前浏览器的JavaScript是ES5版本,大多数高版本的浏览器也支持ES6,不过只实现了ES6的部分特性和功能。

在ES6之前,对象不是基于类创建的,而是用一种称为构造函数的特殊函数来定义对象和它们的特征。

1、构造函数

构造函数是一种特殊的函数,主要用来初始化对象,即为对象成员变量赋初始值,它总与new一起使用。我们可以把对象中一些公共的属性和方法抽取出来,然后封装到这个函数里面。
在JS中,使用构造函数时要注意以下两点:
1.构造函数用于创建某一类对象,其首字母要大写

2.构造函数要和new一起使用才有意义

new在执行时会做四件事情︰
1、在内存中创建一个新的空对象。
2、让 this指向这个新的对象。
3、执行构造函数里面的代码,给这个新对象添加属性和方法。
4、返回这个新对象( 所以构造函数里面不需要return ) .

<script>
    //1、利用new Object()创建对象
    var obj1 = new Object()
//    2、利用对象字面量创建对象
    var obj2 = {}
//    3、利用构造函数创建对象
    function Star(uname,age){
        this.uname = uname
        this.age = age
        this.sing = function (){
            console.log('我会唱歌')
        }
    }
    var ldh = new Star('刘德华',18)
    console.log(ldh)
</script>

2、静态成员和实例成员

<script>
  //构造函数中的属性和方法我们称为成员,成员可以添加
  function Star(uname,age){
    this.uname = uname
    this.age = age
    this.sing = function (){
      console.log('我会唱歌')
    }
  }
  var ldh = new Star('刘德华',18)
//  1、实例成员就是构造函数内部通过this添加的成员,uname,age,sing 就是实例成员
//  实例成员只能通过实例化的对象来访问
  console.log(ldh.uname)
  ldh.sing()
  // console.log(Star.uname)//undefined 不可以通过构造函数来访问实例成员
//  2、静态成员 在构造函数本身上添加的成员  sex就是静态成员
  Star.sex = '男'
//  静态成员只能通过构造函数来访问
  console.log(Star.sex) //男
  console.log(ldh.sex)  //undefined  不能通过对象来访问
</script>

3、构造函数原型 prototype

构造函数方法很好用,但存在浪费内存的问题

构造函数通过原型分配配的函数是所有对象所共享的。
JavaScript规定,每一个构造函数都有一个prototype属性,指向另一个对象。注意这个prototype就是一个对象,这个对象的所有属性和方法,都会被构造函数所拥有。

我们可以把那些不变的方法,直接定义在prototype对象上,这样所有对象的实例就可以共享这些方法。

<script>
  //    构造函数的问题
  function Star(uname,age){
    this.uname = uname
    this.age = age
    /*this.sing = function (){
      console.log('我会唱歌')
    }*/
  }
  //我们可以把那些不变的方法,直接定义在prototype对象上,这样所有对象的实例就可以共享这些方法
  Star.prototype.sing = function (){
    console.log('我会唱歌')
  }
  var ldh = new Star('刘德华',18)
  var zxy = new Star('张学友',18)
  //这两个对象的内容一样,但占了两个内存,比较浪费内存
  console.log(ldh.sing === zxy.sing) //方法放在构造函数里为false,放在原型对象里为true
  // console.dir(Star)
  ldh.sing()
  zxy.sing()
//  一般情况下,我们的公共属性定义到构造函数里面,公共的方法方法哦原型对象上
</script>

4、对象原型 __proto _

对象都会有一个属性__proto _**指向构造函数的prototype原型对象,**之所以我们对象可以使用构造函数prototype原型对象的属性和方法,就是因为对象有_proto_原型的存在。

_proto_对象原型和原型对象 prototype是等价的

proto_对象原型的意义就在于为对象的查找机制提供一个方向,或者说一条路线,但是它是一个非标准属性,因此实际开发中,不可以使用这个属性,它只是内部指向原型对象 prototype

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-i8GdDszg-1669643165010)(1668568961668.png)]

<script>
  function Star(uname,age){
    this.uname = uname
    this.age = age
  }
  Star.prototype.sing = function (){
    console.log('我会唱歌')
  }
  var ldh = new Star('刘德华',18)
  var zxy = new Star('张学友',18)
  ldh.sing()
  console.log(ldh) //对象身上系统自己添加一个_proto_指向我们构造函数的原型对象
  console.log(ldh.__proto__ === Star.prototype) //true _proto_对象原型和原型对象 prototype是等价的
//  方法的查找规则:首先看ldh对象身上是否有sing方法,如果有就执行这个对象的sing
//  如果没有sing方法,因为有__proto__的存在,就去构造函数原型对象prototype身上去找sing方法
</script>

5、constructor构造函数

对象原型(proto)和构造函数( prototype ) 原型对象里面都有一个属性constructor属性,constructor我们称为构造函数,因为它指回构造函数本身。

constructor主要用于记录该对象引用于哪个构造函数,它可以让原型对象重新指向原来的构造函数。

<script>
  function Star(uname,age){
    this.uname = uname
    this.age = age
  }
  //  很多情况下,需要手动利用constructor这个属性指向原来的构造函数
  /*Star.prototype.sing = function (){
    console.log('我会唱歌')
  }
  Star.prototype.movie = function (){
    console.log('演电影')
  }*/
  //这种={}的方法相当于赋值,赋值之后里面没有constructor属性,所以需要手动添加,利用constructor指向原来的构造函数
  Star.prototype = {
    // 如果我们修改了原来的原型对象,给原型对象赋值的是一个对象,
    // 则必须手动的利用constructor指回原来的构造函数
    constructor:Star,
    sing:function (){
      console.log('我会唱歌')
    },
    movie:function (){
      console.log('演电影')
    }
  }
  var ldh = new Star('刘德华',18)
  var zxy = new Star('张学友',18)
  console.log(Star.prototype)
  console.log(ldh.__proto__)
  //constructor指向构造函数本身
  console.log(Star.prototype.constructor)
  console.log(ldh.__proto__.constructor)
</script>

6、构造函数、实例、原型对象 三者之间的关系

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ewz4yz4N-1669643165013)(1668569975747.png)]

7、原型链

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gZEQ1Xx8-1669643165015)(1668570484909.png)]

<script>
  function Star(uname,age){
    this.uname = uname
    this.age = age
  }
  Star.prototype.sing = function (){
    console.log('我会唱歌')
  }
  var ldh = new Star('刘德华',18)
  var zxy = new Star('张学友',18)
// 1、 只要有对象就有__proto__原型,指向原型对象
  console.log(Star.prototype)
  //2、Star原型对象里面的__proto__原型指向的是Object.prototype
  console.log(Star.prototype.__proto__ === Object.prototype) //true
  //3、Object.prototype原型对象里面的__proto__原型 指向为null
  console.log(Object.prototype.__proto__) //null
</script>

8、JavaScript的成员查找机制(规则)

1、当访问一个对象的属性((包括方法)时,首先查找这个对象自身有没有该属性。

2、如果没有就查找它的原型(也就是_proto_指向的prototype原型对象)。

3、如果还没有就查找原型对象的原型( Object的原型对象)。

4、依此类推一直找到Object为止( null ) .

5、proto_对象原型的意义就在于为对象成员查找机制提供一个方向,或者说一条路线。

<script>
  function Star(uname, age) {
    this.uname = uname;
    this.age = age;
  }
  Star.prototype.sing = function() {
    console.log('我会唱歌');
  }
  Star.prototype.sex = '女' //2、ldh.__proto__ 找Star.prototype有没有sex
  // Object.prototype.sex = '男' //3、Star.prototype.__proto__找Object.prototype
  var ldh = new Star('刘德华', 18);
  ldh.sex = '男' //1、实例对象本身有sex
  console.log(ldh.sex) //多个都有sex成员的话,就近原则查找
  console.log(Object.prototype) //有toString
  console.log(ldh)
  console.log(Star.prototype)
  console.log(ldh.toString())
</script>

9、原型对象this指向

<script>
  function Star(uname, age) {
    this.uname = uname;
    this.age = age;
  }
  var that
  Star.prototype.sing = function() {
    console.log('我会唱歌');
    that = this
  }
  var ldh = new Star('刘德华',18)
//  1、在构造函数中里面的this指向的是对象实例 ldh
  ldh.sing()
  console.log(that === ldh) //true
//  2、原型对象函数里面的this 指向的是 实例对象 ldh
</script>

10、扩展内置对象

可以通过原型对象,对原来的内置对象进行扩展自定义的方法。比如给数组增加自定义求偶数和的功能。

<script>
  //原型对象的应用 扩展内置对象方法
  console.log(Array.prototype)
  //采取这种点的形式不会覆盖原来的内容 比较好
  Array.prototype.sum = function (){
    var sum = 0
    //this指向实例对象arr
    for (var i = 0;i<this.length;i++){
      sum += this[i]
    }
    return sum;
  }
  //覆盖了原来的原型对象 会报错 不建议采取赋值的形式
  /*Array.prototype = {
    sum:function (){
      var sum = 0
      //this指向实例对象arr
      for (var i = 0;i<this.length;i++){
        sum += this[i]
      }
      return sum;
    }
  }*/
  var arr = [1,2,3,1]
  console.log(arr.sum())
  console.log(Array.prototype)
  var arr1 = new Array(11,22,33)
  console.log(arr1.sum())
</script>

继承

ES6之前并没有给我们提供extends继承。我们可以通过构造f函数+原型对象模拟实现继承,被称为组合继承

call()修改this指向

调用这个函数并且修改函数运行时的this指向

fun.call (thisArg, arg1, arg2, —)

thisArg :当前调用函数this的指向对象

arg1 , arg2:传递的其他参数

<script>
  //call方法
  function fn(x,y){
    console.log('我想喝手磨咖啡')
    console.log(this)
    console.log(x + y)
  }
  var o = {
    name:'andy'
  }
  // fn();
//  1、call()方法可以调用函数
//   fn.call()
//  2、call()可以改变函数的this指向
  fn.call(o,1,2) //此时函数里的this指向o
</script>

借用构造函数继承父类型属性

核心原理:通过**call()**把父类型的this 指向子类型的this,这样就可以实现子类型继承父类型的属性。

<script>
  //借用父构造函数继承属性
//  1、父构造函数
  function Father (uname,age){
    //this指向父构造函数的对象实例
    this.uname = uname
    this.age = age
  }
//  2、子构造函数
  function Son (uname,age,score){
    //this指向子构造函数的对象实例
    //把Father的this指向改为子构造函数的this
    Father.call(this,uname,age)
    this.score = score
  }
  var son = new Son('刘德华',18,89)
  var father = new Father('张学友',32)
  console.log(son)
  console.log(father)
</script>

借用原型对象继承父类型方法

<script>
  //借用父构造函数继承属性
  //  1、父构造函数
  function Father (uname,age){
    //this指向父构造函数的对象实例
    this.uname = uname
    this.age = age
  }
  Father.prototype.money = function (){
    console.log('挣钱500万')
  }
  //  2、子构造函数
  function Son (uname,age,score){
    //this指向子构造函数的对象实例
    //把Father的this指向改为子构造函数的this
    Father.call(this,uname,age)
    this.score = score
  }
  //1、实现方法继承  这样直接赋值会有问题,如果修改了子原型对象,父原型对象也会跟着一起变化
  // Son.prototype = Father.prototype
  //2、实现方法继承,实例化一个对象后,对象有__proto__指向父亲的prototype原型对象,可以继承方法
  // 但原型对象改变了,需要利用constructor指回原来的构造函数
  Son.prototype = new Father()
  // 如果利用对象的形式修改了原型对象,别忘了利用constructor 指回原来的构造函数
  Son.prototype.constructor = Son
  //这个是子构造函数专门的方法
  Son.prototype.exam = function (){
    console.log('考试')
  }
  var son = new Son('刘德华',18,89)
  console.log(son)
  console.log(Father.prototype)
  console.log(Son.prototype.constructor)
</script>

类的本质

1.class本质还是function.
2.类的所有方法都定义在类的prototype属性上
3.类创建的实例,里面也有_proto_指向类的prototype原型对象
4.所以ES6的类它的绝大部分功能,ES5都可以做到,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。

5、所以ES6的类其实就是语法糖

6.语法糖:语法糖就是一种便捷写法.简单理解,有两种方法可以实现同样的功能,但是一种写法更加清晰、方便,那么这个方法就是语法糖

<script>
    // ES6 之前通过 构造函数+ 原型实现面向对象 编程
    //构造函数特点:
    // (1) 构造函数有原型对象prototype 
    // (2) 构造函数原型对象prototype 里面有constructor 指向构造函数本身
    // (3) 构造函数可以通过原型对象添加方法
    // (4) 构造函数创建的实例对象有__proto__ 原型指向 构造函数的原型对象
    // ES6 通过 类 实现面向对象编程 
    class Star {

    }
    console.log(typeof Star);
    // 1. 类的本质其实还是一个函数 我们也可以简单的认为 类就是 构造函数的另外一种写法
    // (1) 类有原型对象prototype 
    console.log(Star.prototype);
    // (2) 类原型对象prototype 里面有constructor 指向类本身
    console.log(Star.prototype.constructor);
    // (3)类可以通过原型对象添加方法
    Star.prototype.sing = function() {
        console.log('冰雨');
    }
    var ldh = new Star();
    console.dir(ldh);
    // (4) 类创建的实例对象有__proto__ 原型指向 类的原型对象
    console.log(ldh.__proto__ === Star.prototype);
    i = i + 1;
    i++
</script>

ES5新增的方法

1、数组方法

迭代(遍历)方法 :forEach()、map()、 filter()、 some()、every() ;

1)forEach

array.forEach ( function (currentvalue,index,arr))

currentValue:数组当前项的值
index:数组当前项的索引

arr:数组对象本身

<script>
  //forEach 迭代(遍历)数组
  var arr = [1,2,3,32,12,34]
  var sum = 0
  arr.forEach(function (value,index,array){
    console.log('每一个数组元素' + value)
    console.log('每一个数组元素的索引号' + index)
    console.log('数组本身' + array)
    sum += value
  })
  console.log(sum)
</script>

2)filter

array.filter (function(currentvalue,index,arr))
filter()方法创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素,主要用于筛选数组

注意它直接返回一个新数组

currentValue:数组当前项的值

index:数组当前项的索引

arr :数组对象本身

<script>
  //filter 筛选数组   直接返回一个新数组
  var arr = [12,33,121,321,12,32,90]
  var newArr = arr.filter(function (value,index,arr){
    // return value >= 50
    return value %2 === 0 //返回偶数
  })
  console.log(newArr)
</script>

3)some

array.some (function(currentvalue,index,arr))
some()方法用于检测数组中的元素是否满足指定条件.通俗点查找数组中是否有满足条件的元素
注意它返回值是布尔值 如果查找到这个元素,就返回true,如果查找不到就返回false,
如果找到第一个满足条件的元素,则终止循环不在继续查找.
currentValue:数组当前项的值
index:数组当前项的索引

arr:数组对象本身

<script>
  //some 查找数组中是否有满足条件的元素
  var arr = [12,32,1,43,54,26,98,91]
  var flag = arr.some(function (value,index,arr){
    // return value >= 20  //true
    return value < 0  //false
  })
  console.log(flag)
  var arr1 = ['red','pink','yellow','blue']
  var flag1 = arr1.some(function (value){
    return value === 'pink'
  })
  //如果找到第一个满足条件的元素,则终止循环不在继续查找.
  console.log(flag1)  //true
//  1、filter也是查找满足条件的元素,返回的是一个新数组 而且是把所有满足条件的元素返回
//  2、some也是查找满足条件的元素是否存在,返回的是布尔值, 如果查找到第一个满足条件的元素就终止循环
</script>

案例

<div class="search">
    按照价格查询: <input type="text" class="start"> - <input type="text" class="end">
    <button class="search-price">搜索</button>
    按照商品名称查询: <input type="text" class="product">
    <button class="search-pro">查询</button>
</div>
<table>
    <thead>
        <tr>
            <th>id</th>
            <th>产品名称</th>
            <th>价格</th>
        </tr>
    </thead>
    <tbody>


    </tbody>
</table>
<script>
    // 利用新增数组方法操作数据
    var data = [{
        id: 1,
        pname: '小米',
        price: 3999
    }, {
        id: 2,
        pname: 'oppo',
        price: 999
    }, {
        id: 3,
        pname: '荣耀',
        price: 1299
    }, {
        id: 4,
        pname: '华为',
        price: 1999
    }, ];
//    1、利用forEach把data里的数据渲染到tbody里
//(1)、获取相应元素
    var tbody = document.querySelector('tbody')
    setData(data)
//    (2)、把数据渲染到页面中
//    遍历数组--》创建行--》行里面添加单元格--》属性放在单元格里--》把行添加到tbody
    function setData(mydata){
        //先清除原来tbody的数据
        tbody.innerHTML = ''
        mydata.forEach(function (value){
            // console.log(value)
            var tr = document.createElement('tr')
            tr.innerHTML = '<td> '+ value.id +'</td><td>'+value.pname+'</td><td>'+value.price+'</td>'
            tbody.appendChild(tr);
        })
    }


//   2、 根据价格查询商品
//  当我们点击了按钮,就可以根据商品价格去筛选数组里面的对象
//    利用filter将满足条件的对象return到新数组里--》利用setData()把筛选完的对象渲染到页面中
    var start = document.querySelector('.start')
    var end = document.querySelector('.end')
    var search_price = document.querySelector('.search-price')
    search_price.addEventListener('click',function (){
        var newData = data.filter(function (value){
            // console.log(value)
            return value.price >= start.value && value.price <= end.value
        })
        // console.log(newData)
    //    把筛选完之后的对象渲染到页面中
        setData(newData)
    })

//    3、按照 商品名称 查询商品
//   如果查询数组中唯一的元素, 用some方法更合适,因为它找到这个元素,就不再进行循环,效率更高
//    利用some找到符合元素并打印--》返回true some不再循环--》渲染页面
    var search_pro = document.querySelector('.search-pro')
    var product = document.querySelector('.product')
    search_pro.addEventListener('click',function (){
        var arr = []
        data.some(function (value){
            if (value.pname === product.value){
                // console.log(value)
                arr.push(value)
                return true;  //return 后面必须写true
            }
        })
    //    把拿到的数据渲染到页面中
        setData(arr)
    })


</script>

2、字符串方法

trim()

trim()方法会从一个字符串的两端删除空白字符。
str.trim()
trim()方法并不影响原字符串本身,它返回的是一个新的字符串。

<input type="text"> <button>点击</button>
<div></div>
<script>
  //trim方法去除字符串两侧空格,不去中间的空格
  var str = '    an  dy    '
  console.log(str)
  var str1 = str.trim()
  console.log(str1)

  var input = document.querySelector('input')
  var btn = document.querySelector('button')
  var div = document.querySelector('div')
  btn.onclick = function (){
    var str = input.value.trim()
    if (str === ''){
      alert('请输入内容')
    }else{
      console.log(str)
      console.log(str.length)
      div.innerHTML = str
    }
  }
</script>

3、对象方法

(1)keys

Object.keys()用于获取对象自身所有的属性
object. keys(obj)
效果类似fon:.in
返回一个由属性名组成的数组

<script>
    // 用于获取对象自身所有的属性
    var obj = {
        id: 1,
        pname: '小米',
        price: 1999,
        num: 2000
    };
    //返回属性名组成的数组
    var arr = Object.keys(obj);
    console.log(arr);
    arr.forEach(function(value) {
        console.log(value);
    })
</script>

(2)defineProperty

Object.defineProperty()定义对象中新属性或修改原有的属性。object.defineProperty(obj,prop,descriptor)
obj :目标对象
prop:需定义或修改的属性的名字

descriptor:目标属性所拥有的特性

Object.defineProperty)第三个参数descriptor说明:**以对象形式{}**书写

value: 设置属性的值默认为undefined
writable:值是否可以重写。true | false 默认为false
enumerable:目标属性是否可以被枚举。true l false默认为 false
configurable:目标属性是否可以被删除或是否可以再次修改特性true | false 默认为false

<script>
    // Object.defineProperty() 定义新属性或修改原有的属性
    var obj = {
        id: 1,
        pname: '小米',
        price: 1999
    };
    // 1. 以前的对象添加和修改属性的方式
    // obj.num = 1000;
    // obj.price = 99;
    // console.log(obj);
    // 2. Object.defineProperty() 定义新属性或修改原有的属性
    //添加
    Object.defineProperty(obj,'num',{
        value:1000
    })
    console.log(obj)
    //修改
    Object.defineProperty(obj,'price',{
        value:999
    })
    console.log(obj)
    //限定不能修改
    Object.defineProperty(obj,'id',{
        //false 不允许修改属性值id
        writable:false
    })
    console.log(obj)
    obj.id = 2
    console.log(obj)

    Object.defineProperty(obj,'address',{
        value:'中国山东找蓝翔xx单元',
        writable:false,
        //enumerable:目标属性是否可以被枚举。true l false默认为 false
        enumerable:false,
        //configurable:目标属性是否可以被删除或是否可以再次修改特性true | false 默认为false
        configurable:false
    })
    console.log(obj)
    console.log(Object.keys(obj))
    delete obj.address
    console.log(obj)
    delete obj.pname
    console.log(obj)

    Object.defineProperty(obj,'address',{
        value:'中国山东找蓝翔xx单元',
        writable:true,
        //enumerable:目标属性是否可以被枚举。true l false默认为 false
        enumerable:true,
        //configurable:目标属性是否可以被删除或是否可以再次修改特性true | false 默认为false
        configurable:true
    })
    console.log(obj.address) //报错,因为上面不允许修改
</script>

函数进阶

1、函数定义和调用

(1)定义

1、函数声明方式function关键字(命名函数)

⒉、函数表达式(匿名函数)

3、 new Function()

Function里面参数都必须是字符串格式
第三种方式执行效率低,也不方便书写,因此较少使用

所有函数都是Function的实例(对象)
函数也属于对象

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-O5xHPBa0-1669643165016)(1668651439385.png)]

<script>
    //函数的定义方式
//    1、自定义函数(命名函数)
    function fn(){}
//    2、函数表达式(匿名函数)
    var fun = function (){}
//    3、利用 new Function(‘参数1’,‘参数2’,‘函数体’) 效率低
    var f = new Function('a','b','console.log(a+b)')
    f(1,3)
//    4、所有函数都是 Function 的实例对象
    console.dir(f)
    //5、函数也属于对象
    console.log(f instanceof Object)  //前面的f是否属于Object  true

</script>

(2)调用

<script>
  //函数的调用方式
  // 1. 普通函数
  function fn(){
    console.log('随便一句话')
  }
  //调用
  // fn();
  // fn.call()
  // 2. 对象的方法
  var o = {
    sayHi:function (){
      console.log('hi')
    }
  }
  o.sayHi()
  // 3. 构造函数
  function Star(){

  }
  new Star()
  // 4. 绑定事件函数
  btn.onclick = function (){}  //点击按钮就能调用
  // 5. 定时器函数
  setInterval(function (){},1000)  //定时器自动一秒钟调用函数
  // 6. 立即执行函数
          // 立即执行函数是自动调用
          (function (){
            console.log('立即执行')
          })()
</script>

2、this指向

(1)函数内部this指向

这些this的指向,是当我们调用函数的时候确定的。调用方式的不同决定了this的指向不同,一般指向我们的调用者.

调用方式this指向
普通函数调用window
构造函数调用实例对象 原型对象里面的方法也指向实例对象
对象方法调用该方法所属对象
事件绑定方法绑定事件对象
定时器函数window
立即执行函数window
<button>点击</button>
<script>
    // 函数的不同调用方式决定了this 的指向不同
    // 1. 普通函数 this 指向window
    function fn() {
        console.log('普通函数的this' + this);
    }
    //完整写法 window.fn()
    window.fn();
    // 2. 对象的方法 this指向的是对象 o
    var o = {
        sayHi: function() {
            console.log('对象方法的this:' + this);
        }
    }
    o.sayHi();
    // 3. 构造函数 this 指向 ldh 这个实例对象 原型对象里面的this 指向的也是 ldh这个实例对象
    function Star() {}
    Star.prototype.sing = function() {

    }
    var ldh = new Star();
    // 4. 绑定事件函数 this 指向的是函数的调用者 btn这个按钮对象
    var btn = document.querySelector('button');
    btn.onclick = function() {
        console.log('绑定事件函数的this:' + this);
    };
    // 5. 定时器函数 this 指向的也是window
    window.setTimeout(function() {
        console.log('定时器的this:' + this);
    }, 1000);
    // 6. 立即执行函数 this还是指向window
    (function() {
        console.log('立即执行函数的this' + this);
    })();
</script>

(2)改变函数内部this指向

JavaScript 为我们专门提供了一些函数方法来帮我们更优雅的处理函数内部 this 的指向问题,常用的有 bind()、call()、 apply()三种方法。

1)call()

call()方法调用一个对象。简单理解为调用函数的方式,但是它可以改变函数的 this指向。

<script>
  //改变函数内this指向  js三种方法  call() apply()  bind()
//  1、call()
//  call 第一个可以调用函数   第二个可以改变函数内的this指向
  var o = {
    name:'andy'
  }
  function fn(a,b) {
    console.log(this)
    console.log(a+b)
  }
  fn.call(o,1,2)
  //  call的主要作用可以实现继承
  function Father(uname,age,sex){
    this.uname = uname;
    this.age = age;
    this.sex = sex
  }
  function Son(uname,age,sex){
    //在调用父构造函数的同时,把Father的this指向改为 Son的this
    Father.call(this,uname,age,sex)
  }
  var son = new Son('刘德华',18,'男')
  console.log(son)
</script>

2)apply()

apply()方法调用一个函数。简单理解为调用函数的方式,但是它可以改变函数的 this 指向。
fun.apply(thisarg,[argsArray] )
thisArg :在fun函数运行时指定的 this值

argsArray :传递的值,必须包含在数组里面

返回值就是函数的返回值,因为它就是调用函数

<script>
  //2、apply()
  //1、apply 第一个可以调用函数   第二个可以改变函数内的this指向
  //但是参数必须是数组(伪数组)
  var o = {
    name:'andy'
  }
  function fn(arr){
    console.log(this)
    console.log(arr) //'pink'
  }
  fn.apply(o,['pink'])
//  2、apply的主要应用
//  可以利用apply 借助于数学内置对象 求数组的最大值、最小值
//   Math.max()
  var arr = [11,21,31,2,32,45,21,67,43,89,6]
  //因为不需要改变this指向,所以将this重新指回max的调用对象Math
  //把arr传递给max,
  var max = Math.max.apply(Math,arr)
  var min = Math.min.apply(Math,arr)
  console.log(max,min)
</script>

3)bind()

bind()方法不会调用函数。但是能改变函数内部this指向

fun. bind (thisArg,arg1,arg2, …)

thisArg :在fun函数运行时指定的 this值

arg1 , arg2:传递的其他参数

返回由指定的this值和初始化参数改造的原函数拷贝

<button>点击</button>
<button>点击</button>
<button>点击</button>
<script>
  //3、bind()
  var o = {
    name:'andy'
  }
  function fn(a,b){
    console.log(this)
    console.log(a+b)
  }
  //1、bind不会调用原来函数,可以改变函数this指向
  //2、返回的是函数改变this之后的新函数
  //接收新函数--》调用
  var f = fn.bind(o,1,2)
  f()
  // fn(1,2)
//  3、如果有的函数不需要立即调用 但又想改变这个函数内部的this指向  此时用bind
//  4、有一个按钮,点击之后禁用,三秒钟之后就禁用这个按钮
  /*var btn = document.querySelector('button')
  btn.onclick = function (){
      // var that = this
      this.disabled = true //这个this指向函数调用者:btn
      setTimeout(function (){
          // this.disabled = false //按钮不能恢复,因为定时器里的this指向的是window
          // that.disabled = false //这么写可以,但多了代码,浪费内存
          this.disabled = false //此时this指向btn
      //    相当于给定时器绑定了btn,把定时器的this改成了指向btn,并且不会马上就调用
      //    bind里的this是在定时器外面绑定的,所以this指向btn
      }.bind(this),3000)
  }*/
  var btns = document.querySelectorAll('button')
  for (var i = 0;i<btns.length;i++){
      btns[i].onclick = function (){
          this.disabled = true
          setTimeout(function (){
              this.disabled = false
          }.bind(this),2000)
      }
  }
</script>

call、apply、bind 总结

相同点:
都可以改变函数内部的this指向

区别点:

  1. call和apply 会调用函数,并且改变函数内部this指向.
  2. call和apply传递的参数不一样, call传递参数aru1, aru2…形式 apply必须数组形式[argl3. bind 不会调用函数,可以改变函数内部this指向.

主要应用场景:

1、call经常做继承.

2、apply经常跟数组有关系.比如借助于数学对象实现数组最大值最小值

3、bind不调用函数,但是还想改变this指向.比如改变定时器内部的this指向.

3、严格模式

JavaScript除了提供正常模式外,还提供了严格模式( strict mode )。ES5的严格模式是采用具有限制性JavaScript变体的一种方式,即在严格的条件下运行JS代码。

严格模式对正常的JavaScript语义做了一些更改
1、消除了Javascript语法的一些不合理、不严谨之处,减少了一些怪异行为。

2、消除代码运行的一些不安全之处,保证代码运行的安全。
3、提高编译器效率,增加运行速度。
4、禁用了在ECMAScript的未来版本中可能会定义的一些语法,为未来新版本的Javascript做好铺垫。比如一些保留字如: class, enum, export, extends, import, super 不能做变量名

(1)开启严格模式

严格模式可以应用到整个脚本个别函数中。因此在使用时,我们可以将严格模式分为为脚本开启严格模式和为函数开启严格模式两种情况。

1)为脚本开启严格模式

为整个脚本文件开启严格模式,需要在所有语句之前放一个特定语句“use strict”;(或‘use strict’ 😉

<!--为整个脚本(script)标签开启严格模式-->
<script>
  // 1、为整个脚本文件开启严格模式,需要在所有语句之前放一个特定语句“use strict”;(或‘use strict’ ;)
  //下面的js代码就会按照严格模式执行
  'use strict';
</script>

<script>
  // 2、有的script基本是严格模式,有的script脚本是正常模式,这样不利于文件合并,
// 所以可以将整个脚本文件放在一个立即执行的匿名函数之中。这样独立创建一个作用域而不影响其他script脚本文件。
  (function (){
    'use strict'
  })()
</script>

2)为函数开启严格模式

要给某个函数开启严格模式,需要把“use strict”;(或’use strict’;)声明放在函数体所有语句之前。

<!--为某个函数开启严格模式-->
<script>
  // 要给某个函数开启严格模式,需要把“use strict”;(或'use strict';)声明放在函数体所有语句之前。
  function  fn(){
    //只是给fn函数开启严格模式
    'use strict'
  }
  function fun(){
  //  里面的代码还是按照普通模式执行
  }
</script>

(2)严格模式的变化

严格模式对Javascript的语法和行为,都做了一些改变。
1、变量规定

  • 在正常模式中,如果一个变量没有声明就赋值,默认是全局变量。严格模式禁止这种用法,变量都必须先用var命令声明,然后再使用。
  • 严禁删除已经声明变量。例如,delete x;语法是错误的。

2、严格模式下this指向问题

  • 以前在全局作用域函数中的this指向window对象。
  • 严格模式下全局作用域中函数中的 this 是 undefined。
  • 以前构造函数时不加new也可以调用,当普通函数,this指向全局对象
  • 严格模式下,如果 构造函数不加new调用, this 指向的是undefined,this会报错
  • new实例化的构造函数this指向创建的对象实例
  • 定时器this还是指向window
  • 事件、对象里的this还是指向调用者

3、函数变化

  • 函数不能有重名的参数
  • 函数必须声明在顶层.新版本的JavaScript 会引入“块级作用域”(ES6中已引入)。为了与新版本接轨,不允许在非函数的代码块内声明函数。
<script>
  'use strict';
  // 1. 我们的变量名必须先声明再使用
  /*num = 10;
  console.log(num);*/
  var num = 10;
  console.log(num);
  // 2.我们不能随意删除已经声明好的变量
  // delete num;
  // 3. 严格模式下全局作用域中函数中的 this 是 undefined。
  /*function fn() {
      console.log(this); // undefined。
  }
  fn();*/
  // 4. 严格模式下,如果 构造函数不加new调用, this 指向的是undefined 如果给他赋值则 会报错.
  function Star() {
      this.sex = '男';
  }
  // Star();
  var ldh = new Star();
  console.log(ldh.sex);
  // 5. 定时器 this 还是指向 window
  setTimeout(function() {
      console.log(this);

  }, 2000);
  // 6. 严格模式下函数里面的参数不允许有重名
  /*function fn(a, a) {
      console.log(a + a);
  }
  fn(1, 2);*/
  //7、不允许在非函数的代码块内声明函数  比如不能在if,for的代码块里声明函数,可以在函数内部声明函数(嵌套)
  // function fn() {}
</script>

4、高阶函数

高阶函数是对其他函数进行操作的函数,它接收函数作为参数将函数作为返回值输出。

函数也是一种数据类型,同样可以作为参数,传递给另外一个参数使用。最典型的就是作为回调函数。

<div></div>
<script>
    // 高阶函数- 函数可以作为参数传递
    function fn(a, b, callback) {
        console.log(a + b);
        callback && callback();
    }
    fn(1, 2, function() {
        console.log('我是最后调用的');
    });
    $("div").animate({
        left: 500
    }, function() {
        $("div").css("backgroundColor", "purple");
    })
</script>

5、闭包

(1)变量作用域

变量根据作用域的不同分为两种:全局变量和局部变量。

1.函数内部可以使用全局变量。

2.函数外部不可以使用局部变量。

3.当函数执行完毕,本作用域内的局部变量会销毁

(2)什么是闭包

闭包( closure )指有权访问另一个函数作用域中变量的函数

简单理解就是,一个作用域可以访问另外一个函数内部的局部变量。

<script>
    // 闭包(closure)指有权访问另一个函数作用域中变量的函数。
    // 闭包: 我们fun 这个函数作用域 访问了另外一个函数 fn 里面的局部变量 num
    function fn() {
        var num = 10;

        function fun() {
            console.log(num);
        }
        fun();
    }
    fn();
</script>

(3)闭包的作用

闭包的主要作用: 延伸了变量的作用范围

<script>
    // 闭包(closure)指有权访问另一个函数作用域中变量的函数。
    // 一个作用域可以访问另外一个函数的局部变量 
    // fn 外面的作用域可以访问fn 内部的局部变量
    // 闭包的主要作用: 延伸了变量的作用范围
    function fn() {
        var num = 10;

        // function fun() {
        //     console.log(num);

        // }
        // return fun;
        return function() {
            console.log(num);
        }
    }
    var f = fn();
    f();
    // 类似于
    // var f = function() {
    //         console.log(num);
    //     }
    // var f =  function fun() {
    //         console.log(num);

    //     }
</script>

(4)闭包的应用

<script>
  //闭包引用-点击li输出当前li的索引号
//  1、利用动态添加属性的方式
  var lis = document.querySelector('.nav').querySelectorAll('li')
  /*for (var i = 0 ;i<lis.length;i++){
    lis[i].index = i
    lis[i].onclick = function (){
      // console.log(i)
      console.log(this.index)
    }
  }*/
//  2、利用闭包的方式得到当前小li的索引号
  for (var i = 0;i<lis.length;i++){
    //利用for循环创建4个立即执行函数
    // 立即执行函数也成为小闭包 因为立即执行函数里面的任何一个函数都可以使用它的i这变量
    //没有立即函数的话,点击for循环是同步任务,点击事件是异步任务,会造成for循环已经循环到i++到4了,
    // 点击之后输出的索引号都是4。有立即函数之后把i传进去
    (function (i){
      // console.log(i)
      lis[i].onclick = function (){
        console.log(i)
      }
    })(i)
  }

</script>

<ul class="nav">
    <li>榴莲</li>
    <li>臭豆腐</li>
    <li>鲱鱼罐头</li>
    <li>大猪蹄子</li>
</ul>
<script>
    // 闭包应用-3秒钟之后,打印所有li元素的内容
    var lis = document.querySelector('.nav').querySelectorAll('li')
    for (var i = 0;i<lis.length;i++){
        (function (i){
            setTimeout(function (){
                console.log(lis[i].innerHTML)
            },3000)
        })(i)
    }
</script>

<script>
    // 闭包应用-计算打车价格 
    // 打车起步价13(3公里内),  之后每多一公里增加 5块钱.  用户输入公里数就可以计算打车价格
    // 如果有拥堵情况,总价格多收取10块钱拥堵费
    // function fn() {};
    // fn();
    var car = (function() {
        var start = 13; // 起步价  局部变量
        var total = 0; // 总价  局部变量
        return {
            // 正常的总价
            price: function(n) {
                if (n <= 3) {
                    total = start;
                } else {
                    total = start + (n - 3) * 5
                }
                return total;
            },
            // 拥堵之后的费用
            yd: function(flag) {
                return flag ? total + 10 : total;
            }
        }
    })();
    console.log(car.price(5)); // 23
    console.log(car.yd(true)); // 33

    console.log(car.price(1)); // 13
    console.log(car.yd(false)); // 13
</script>

6、递归

如果一个函数在内部可以调用其本身,那么这个函数就是递归函数

简单理解:函数内部自己调用自己 这个函数就是递归函数

递归函数的作用和循环效果一样

由于递归很容易发生“栈溢出”错误( stack overflow ),所以必须要加退出条件return.

<script>
    // 递归函数 : 函数内部自己调用自己, 这个函数就是递归函数
    var num = 1;

    function fn() {
        console.log('我要打印6句话');

        if (num === 6) {
            return; // 递归里面必须加退出条件,否则会变成死递归
        }
        num++;
        fn();
    }
    fn();
</script>

1、利用递归求数学题

利用递归函数求1~n的阶乘 1 * 2 * 3 * 4 * …n

<script>
    // 利用递归函数求1~n的阶乘 1 * 2 * 3 * 4 * ..n
    function fn(n){
        if (n === 1){
            return 1
        }
        return n * fn(n-1)
    }

    console.log(fn(3))
    console.log(fn(13))
//    详细思路 加入用户输入3
    //return  3 * fn(2)
    //return  3 * (2 * fn(1))
    //return  3 * (2 * 1)
    //return  3 * (2)
    //return  6

</script>

2、利用递归函数求斐波那契数列(兔子序列)

<script>
    // 利用递归函数求斐波那契数列(兔子序列)  1、1、2、3、5、8、13、21...
        //前两项之和等于第三项的值
    // 用户输入一个数字 n 就可以求出 这个数字对应的兔子序列值
    // 我们只需要知道用户输入的n 的前面两项(n-1 n-2)就可以计算出n 对应的序列值
    function fb(n){
        if (n === 1 || n === 2){
            return 1
        }
        return fb(n-1) + fb(n-2)
    }
    console.log(fb(3))
    console.log(fb(8))
</script>

3、利用递归遍历数据

<script>
    var data = [{
        id: 1,
        name: '家电',
        goods: [{
            id: 11,
            gname: '冰箱',
            goods: [{
                id: 111,
                gname: '海尔'
            }, {
                id: 112,
                gname: '美的'
            }, ]
        }, {
            id: 12,
            gname: '洗衣机'
        }]
    }, {
        id: 2,
        name: '服饰'
    }];
    // 我们想要做输入id号,就可以返回的数据对象
    // 1. 利用 forEach 去遍历里面的每一个对象
    function getID(json,id){
        var  o = {}
        json.forEach(function (item){
            // console.log(item) //两个数组元素 id:1,2
            if (item.id === id){
                // console.log(item)
                o = item
                //    2、得到里层的数据 11、12 可以利用递归函数
            //    里面应该有goods这个数组,并且数组长度不为0
            }else if (item.goods && item.goods.length > 0){
                o = getID(item.goods,id)
            }
        })
        return o
    }

    console.log(getID(data,1))
    console.log(getID(data,2))
    console.log(getID(data,11))
    console.log(getID(data,12))
    console.log(getID(data,111))
</script>

2、浅拷贝和深拷贝

浅拷贝只是拷贝一层,更深层次对象级别的只拷贝引用

深拷贝拷贝多层,每一级别的数据都会拷贝

浅拷贝是拷贝最外层的标签,深拷贝可以把标签里面的内容一起拷贝过来

ES6新增浅拷贝方法:Object.assign(拷贝的对象,被拷贝给前一个参数)

<script>
    // 浅拷贝只是拷贝一层, 更深层次对象级别的只拷贝引用(地址).
    // 深拷贝拷贝多层, 每一级别的数据都会拷贝.
    var obj = {
        id: 1,
        name: 'andy',
        msg: {
            age: 18
        }
    };
    var o = {};
    //k是属性名  obj[k]是属性值
    //浅拷贝 1
    /*for (var k in obj){
        o[k] = obj[k]
    }
    console.log(o)
    o.msg.age = 20
    console.log(obj)*/

    console.log('-----------')
    //浅拷贝2
    Object.assign(o,obj)
    console.log(o)
</script>

深拷贝

<script>
    // 深拷贝拷贝多层, 每一级别的数据都会拷贝.
    var obj = {
        id: 1,
        name: 'andy',
        msg: {
            age: 18
        },
        color: ['pink', 'red']
    };
    var o = {};
    // 封装函数 
    function deepCopy(newobj, oldobj) {
        for (var k in oldobj){
        //    判断属性值属于那种数据类型
        //    1、获取属性值  oldobj[k]
            var item = oldobj[k]
        //    2、判断这个值是否是数组
            if (item instanceof Array){
                newobj[k] = []
                deepCopy(newobj[k], item)
            }else if (item instanceof Object){
                //    3、判断这个值是否是对象
                newobj[k] = {}
                deepCopy(newobj[k], item)
            }else{
                //    4、属于简单数据类型个
                newobj[k] = item
            }
        }
    }
    deepCopy(o, obj);
    console.log(o);

</script>

正则表达式

正则表达式(Regular Expression)是用于匹配字符串中字符组合的模式。在JavaScript中,正则表达式也是对象

通常用来查找、替换那些符合正则表达式的文本,许多语言都支持正则表达式。

表单验证(匹配)、过滤敏感词(替换)、字符串中提取需要的部分(提取)

**特点:**1.灵活性、逻辑性和功能性非常的强
2.可以迅速地用极简单的方式达到字符串的复杂控制。

1、定义规则—》 2、查找

语法

1、定义正则表达式语法

let 变量名 = /表达式/

其中//是正则表达式字面量

<script>
  //正则表达式在js中的使用
//  1、利用RegExp对象来创建  正则表达式
  var regexp = new RegExp(/123/)
  console.log(regexp)
//  2、利用字面量创建 正则表达式
  var rg = /123/
  console.log(rg)
</script>

2、判断是否有符合规则的字符串

test()方法,用来查看正则表达式与指定的字符串是否匹配

匹配成功,返回true 否则返回false

regObj.test(被检测的字符串)

<script>
  //定义正则表达式   reg里面存的是对象
  let reg = /前端/
  let reg1 = /java/
  let str = '我们大家都在学习前端,喜欢前端,工作前端'
  //检测是否匹配
  console.log(reg.test(str))
  console.log(reg1.test(str))
</script>

3、检索(查找)符合规则的字符串

exec()方法 在一个指定字符中执行一个搜索匹配

匹配成功返回一个数组,否则返回null

regObj.exec(被检测的字符串)

元字符

普通字符:
大多数的字符仅能够描述它们本身,这些字符称作普通字符,例如所有的字母和数字。

也就是说普通字符只能够匹配字符串中与它们相同的字符。

元字符(特殊字符)
是一些具有特殊含义的字符,可以极大提高了灵活性和强大的匹配功能。
比如,规定用户只能输入英文26个英文字母,普通字符的话abcdefghijklm…

但是换成元字符写法:[a-z]

1.边界符(表示位置,开头和结尾,必须用什么开头,用什么结尾)

正则表达式中的边界符(位置符)用来提示字符所处的位置,主要有两个字符

边界符说明
^表示匹配行首的文本(以谁开始)
$表示匹配行尾的文本(以谁结束)

如果^和$在一起,表示精确匹配

 <script>
        // 边界符 ^ $ 
        var rg = /abc/; // 正则表达式里面不需要加引号 不管是数字型还是字符串型
        // /abc/ 只要包含有abc这个字符串返回的都是true
        console.log(rg.test('abc'));
        console.log(rg.test('abcd'));
        console.log(rg.test('aabcd'));
        console.log('---------------------------');
        var reg = /^abc/;
        console.log(reg.test('abc')); // true
        console.log(reg.test('abcd')); // true
        console.log(reg.test('aabcd')); // false
        console.log('---------------------------');
        var reg1 = /^abc$/; // 精确匹配 要求必须是 abc字符串才符合规范
        console.log(reg1.test('abc')); // true
        console.log(reg1.test('abcd')); // false
        console.log(reg1.test('aabcd')); // false
        console.log(reg1.test('abcabc')); // false
    </script>

2.量词(表示重复次数)

量词用来 设定某个模式出现的次数

量词说明
*重复零次或更多次
+重复一次或更多次
?重复零次或一次
{n}重复n次
{n,}重复n次或更多次
{n,m}重复n到m次
<script>
        // 量词符: 用来设定某个模式出现的次数
        // 简单理解: 就是让下面的a这个字符重复多少次
        // var reg = /^a$/;

        //  * 相当于 >= 0 可以出现0次或者很多次 
        var reg = /^a*$/;
        console.log(reg.test('')); //true
        console.log(reg.test('a')); //true
        console.log(reg.test('aaaa')); //true

        //  + 相当于 >= 1 可以出现1次或者很多次
        // var reg = /^a+$/;
        // console.log(reg.test('')); // false
        // console.log(reg.test('a')); // true
        // console.log(reg.test('aaaa')); // true

        //  ?  相当于 1 || 0
        // var reg = /^a?$/;
        // console.log(reg.test('')); // true
        // console.log(reg.test('a')); // true
        // console.log(reg.test('aaaa')); // false

        //  {3 } 就是重复3次
        // var reg = /^a{3}$/;
        // console.log(reg.test('')); // false
        // console.log(reg.test('a')); // false
        // console.log(reg.test('aaaa')); // false
        // console.log(reg.test('aaa')); // true
        //  {3, }  大于等于3
        var reg = /^a{3,}$/;
        console.log(reg.test('')); // false
        console.log(reg.test('a')); // false
        console.log(reg.test('aaaa')); // true
        console.log(reg.test('aaa')); // true
        //  {3,6}  大于等于3 并且 小于等于6
        var reg = /^a{3,6}$/;
        console.log(reg.test('')); // false
        console.log(reg.test('a')); // false
        console.log(reg.test('aaaa')); // true
        console.log(reg.test('aaa')); // true
        console.log(reg.test('aaaaaaa')); // false
    </script>

3.字符类(比如\d表示0~9)

(1)[]匹配字符集合

字符类: [] 表示有一系列字符可供选择,只要匹配其中一个就可以了

后面的字符串只要包含abc中任意一个字符,都返回true

(2)[]里面加上-连字符

使用连字符,表示一个范围

比如:
[a-z]表示 a到z 26个英文字母都可以

[a-zA-Z]表示大小写都可以
[0-9]表示0~9的数字都可以

(3)[]里面加上^取反符号(了解)

[^a-z] 除了a-z

(4).匹配换行符之外的任何单个字符(了解)
<script>
        //var rg = /abc/;  只要包含abc就可以 
        // 字符类: [] 表示有一系列字符可供选择,只要匹配其中一个就可以了
        var rg = /[abc]/; // 只要包含有a 或者 包含有b 或者包含有c 都返回为true
        console.log(rg.test('andy')); //true
        console.log(rg.test('baby')); //true
        console.log(rg.test('color')); //true
        console.log(rg.test('red')); //false
        console.log('------------------1');

        var rg1 = /^[abc]$/; // 三选一 只有是a 或者是 b  或者是c 这三个字母才返回 true
        console.log(rg1.test('aa')); //false
        console.log(rg1.test('a')); //true
        console.log(rg1.test('b')); //true
        console.log(rg1.test('c')); //true
        console.log(rg1.test('abc')); //false
        console.log('------------------2');

        //【】加上连字符 -
        var reg = /^[a-z]$/; // 26个英文字母任何一个字母返回 true  - 表示的是a 到z 的范围  
        console.log(reg.test('a')); //true
        console.log(reg.test('z')); //true
        console.log(reg.test(1)); //false
        console.log(reg.test('A')); //false
        console.log(reg.test('ab')); //false
        console.log('------------------3');

        // 字符组合
        // 26个英文字母(大写和小写都可以)任何一个字母 或者数字,短横线,下划线  返回 true
        var reg1 = /^[a-zA-Z0-9_-]$/;
        console.log(reg1.test('a')); //true
        console.log(reg1.test('B')); //true
        console.log(reg1.test(8)); //true
        console.log(reg1.test('-')); //true
        console.log(reg1.test('_')); //true
        console.log(reg1.test('!')); //false
        console.log('----------------4');

        //取反符号 [^]
        // 如果中括号里面有^ 表示取反的意思 千万和 我们边界符 ^ 别混淆
        var reg2 = /^[^a-zA-Z0-9_-]$/;
        console.log(reg2.test('a'));  //false
        console.log(reg2.test('B'));  //false
        console.log(reg2.test(8));  //false
        console.log(reg2.test('-'));  //false
        console.log(reg2.test('_'));  //false
        console.log(reg2.test('!')); //true
    </script>

括号总结

大括号量词符.里面表示重复次数
中括号字符集合。匹配方括号中的任意字符.

小括号表示优先级

<script>
    // 中括号 字符集合.匹配方括号中的任意字符. 
    // var reg = /^[abc]$/;
    // a 也可以 b 也可以 c 可以  相当于 a ||b || c
    // 大括号  量词符. 里面表示重复次数
    var reg = /^abc{3}$/; // 没有中括号 它只是让c重复三次   abccc
    console.log(reg.test('abc')); //false
    console.log(reg.test('abcabcabc')); //false
    console.log(reg.test('abccc')); //true

    // 小括号 表示优先级
    // var reg = /^(abc){3}$/; // 它是让abc重复三次
    // console.log(reg.test('abc')); //false
    // console.log(reg.test('abcabcabc')); //true
    // console.log(reg.test('abccc')); //false
</script>

(5)预定义

指的是某些常见的简写方式

预定类说明
\d匹配0-9之间的任一数字,相当于[0-9]
\D匹配所有0-9以外的字符,相当于[^0-9]
\w匹配任意的字母、数字和下划线,相当于[A-Za-20-9]
\W除所有字母、数字和下划线以外的字符,相当于[^A-Za-z0-9_]
\s匹配空格(包括换行符、制表符、空格符等),相等于[ \t\r\n\v\f]
\S匹配非空格的字符,相当于[^\t\r\n\v\f]

日期格式:^ \d{4}-\d{1,2}-\d{1,2} 年月日

修饰符

修饰符约束正则执行的某些细节行为,如是否区分大小写、是否支持多行匹配等

i是单词ignore 的缩写,正则匹配时字母不区分大小写

g是单词global的缩写,匹配所有满足正则表达式的结果

gi :全局匹配,忽略大小写

替换replace

字符串.replace(/正则表达式/,‘替换的文本’)

<textarea name="" id="" cols="30" rows="10"></textarea>
<button>发布</button>
<div></div>
<script>
  let btn = document.querySelector('button')
  let textarea = document.querySelector('textarea')
  let div = document.querySelector('div')
  btn.addEventListener('click',function (){
    //过滤用户输入的内容
      //字符串.replace(/正则表达式/,'替换的文本')
    // g是单词global的缩写,匹配所有满足正则表达式的结果
    div.innerHTML = textarea.value.replace(/傻逼|二货/g,'**')
  })
</script>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值