JavaScript高级第01天笔记

# JavaScript高级第01天笔记

# 1.面向过程与面向对象

# 1.1面向过程

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

# 1.2面向对象

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

# 1.3面向过程与面向对象对比

面向过程 面向对象
优点 性能比面向对象高,适合跟硬件联系很紧密的东西,例如单片机就采用的面向过程编程。 易维护、易复用、易扩展,由于面向对象有封装、继承、多态性的特性,可以设计出低耦合的系统,使系统 更加灵活、更加易于维护
缺点 不易维护、不易复用、不易扩展 性能比面向过程低

# 2.对象与类

# 2.1对象

对象是由属性和方法组成的:是一个无序键值对的集合,指的是一个具体的事物

  • 属性:事物的特征,在对象中用属性来表示(常用名词)
  • 方法:事物的行为,在对象中用方法来表示(常用动词)

# 2.1.1创建对象

//以下代码是对对象的复习
//字面量创建对象
var ldh = {
    name: '刘德华',
    age: 18
}
console.log(ldh);

//构造函数创建对象
  function Star(name, age) {
    this.name = name;
    this.age = age;
 }
var ldh = new Star('刘德华', 18)//实例化对象
console.log(ldh);	
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

如上两行代码运行结果为:

# 2.2类

  • 在 ES6 中新增加了类的概念,可以使用 class 关键字声明一个类,之后以这个类来实例化对象。类抽象了对象的公共部分,它泛指某一大类(class)对象特指某一个,通过类实例化一个具体的对象

# 2.2.1创建类

  1. 语法:
//步骤1 使用class关键字
class name {
  // class body
}     
//步骤2使用定义的类创建实例  注意new关键字
var xx = new name();     
1
2
3
4
5
6
  1. 示例
 // 1. 创建类 class  创建一个 明星类
 class Star {
   // 类的共有属性放到 constructor 里面
   constructor(name, age) {
   this.name = name;
   this.age = age;
   }
 }
   // 2. 利用类创建对象 new
   var ldh = new Star('刘德华', 18);
   console.log(ldh);
1
2
3
4
5
6
7
8
9
10
11

以上代码运行结果:

通过结果我们可以看出,运行结果和使用构造函数方式一样

# 2.2.2类创建添加属性和方法

 // 1. 创建类 class  创建一个类
class Star {
    // 类的共有属性放到 constructor 里面 constructor是 构造器或者构造函数
    constructor(uname, age) {
      this.uname = uname;
      this.age = age;
    }//------------------------------------------->注意,方法与方法之间不需要添加逗号
    sing(song) {
      console.log(this.uname + '唱' + song);
    }
}
// 2. 利用类创建对象 new
var ldh = new Star('刘德华', 18);
console.log(ldh); // Star {uname: "刘德华", age: 18}
ldh.sing('冰雨'); // 刘德华唱冰雨
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

以上代码运行结果:

注意哟:

  1. 通过class 关键字创建类, 类名我们还是习惯性定义首字母大写
  2. 类里面有个constructor 函数,可以接受传递过来的参数,同时返回实例对象
  3. constructor 函数 只要 new 生成实例时,就会自动调用这个函数, 如果我们不写这个函数,类也会自动生成这个函数
  4. 多个函数方法之间不需要添加逗号分隔
  5. 生成实例 new 不能省略
  6. 语法规范, 创建类 类名后面不要加小括号,生成实例 类名后面加小括号, 构造函数不需要加function

# 2.2.3类的继承

  1. 语法
// 父类
class Father{   
} 

// 子类继承父类
class  Son  extends Father {  
}       
1
2
3
4
5
6
7
  1. 示例
class Father {
      constructor(surname) {
        this.surname= surname;
      }
      say() {
        console.log('你的姓是' + this.surname);
       }
}

class Son extends Father{  // 这样子类就继承了父类的属性和方法
}
var damao= new Son('刘');
damao.say();      //结果为 你的姓是刘
1
2
3
4
5
6
7
8
9
10
11
12
13

以上代码运行结果:

  • 子类使用super关键字访问父类的方法

    //定义了父类
    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); //使用super调用了父类中的构造函数
        	}
        }
        var son = new Son(1, 2);
        son.sum(); //结果为3
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18

    注意:

    1. 继承中,如果实例化子类输出一个方法,先看子类有没有这个方法,如果有就先执行子类的

    2. 继承中,如果子类里面没有,就去查找父类有没有这个方法,如果有,就执行父类的这个方法(就近原则)

    3. 如果子类想要继承父类的方法,同时在自己内部扩展自己的方法,利用super 调用父类的构造函数,super 必须在子类this之前调用

       // 父类有加法方法
       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 调用父类的构造函数 super 必须在子类this之前调用,放到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(); //2
      son.sum();//8
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26

      以上代码运行结果为:

    4. 时刻注意this的指向问题,类里面的共有的属性和方法一定要加this使用.

      1. constructor中的this指向的是new出来的实例对象
      2. 自定义的方法,一般也指向的new出来的实例对象
      3. 绑定事件之后this指向的就是触发事件的事件源
    5. 在 ES6 中类没有变量提升,所以必须先定义类,才能通过类实例化对象

      #

# 3.面向对象版tab 栏切换

# 3.1功能需求

  1. 点击 tab栏,可以切换效果.
  2. 点击 + 号, 可以添加 tab 项和内容项.
  3. 点击 x 号, 可以删除当前的tab项和内容项.
  4. 双击tab项文字或者内容项文字可以修改里面的文字内容

# 3.2案例准备

  1. 获取到标题元素
  2. 获取到内容元素
  3. 获取到删除的小按钮 x号
  4. 新建js文件,定义类,添加需要的属性方法(切换,删除,增加,修改)
  5. 时刻注意this的指向问题

# 3.3切换

  • 为获取到的标题绑定点击事件,展示对应的内容区域,存储对应的索引

     this.lis[i].index = i;
     this.lis[i].onclick = this.toggleTab;
    
    1
    2
  • 使用排他,实现只有一个元素的显示

     toggleTab() {
       //将所有的标题与内容类样式全部移除
         for (var i = 0; i < this.lis.length; i++) {
         this.lis[i].className = '';
         this.sections[i].className = '';
         }
       //为当前的标题添加激活样式
         this.className = 'liactive';
        //为当前的内容添加激活样式
         that.sections[this.index].className = 'conactive';
      }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11

# 3.4添加

  • 为添加按钮+ 绑定点击事件

     this.add.onclick = this.addTab;
    
    1
  • 实现标题与内容的添加,做好排他处理

    addTab() {
        that.clearClass();
        // (1) 创建li元素和section元素 
        var random = Math.random();
        var li = '<li class="liactive"><span>新选项卡</span><span class="iconfont icon-guanbi">				</span></li>';
        var section = '<section class="conactive">测试 ' + random + '</section>';
        // (2) 把这两个元素追加到对应的父元素里面
        that.ul.insertAdjacentHTML('beforeend', li);
        that.fsection.insertAdjacentHTML('beforeend', section);
        that.init();
        }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11

# 3.5删除

  • 为元素的删除按钮x绑定点击事件

     this.remove[i].onclick = this.removeTab;
    
    1
  • 获取到点击的删除按钮的所在的父元素的所有,删除对应的标题与内容

     removeTab(e) {
         e.stopPropagation(); // 阻止冒泡 防止触发li 的切换点击事件
         var index = this.parentNode.index;
         console.log(index);
         // 根据索引号删除对应的li 和section   remove()方法可以直接删除指定的元素
         that.lis[index].remove();
         that.sections[index].remove();
         that.init();
         // 当我们删除的不是选中状态的li 的时候,原来的选中状态li保持不变
         if (document.querySelector('.liactive')) return;
         // 当我们删除了选中状态的这个li 的时候, 让它的前一个li 处于选定状态
         index--;
         // 手动调用我们的点击事件  不需要鼠标触发
         that.lis[index] && that.lis[index].click();
     }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15

# 3.6编辑

  • 为元素(标题与内容)绑定双击事件

     this.spans[i].ondblclick = this.editTab;
     this.sections[i].ondblclick = this.editTab;
    
    1
    2
  • 在双击事件处理文本选中状态,修改内部DOM节点,实现新旧value值的传递

    editTab() {
        var str = this.innerHTML;
        // 双击禁止选定文字
        window.getSelection ? window.getSelection().removeAllRanges() : 				    document.selection.empty();
        // alert(11);
          this.innerHTML = '<input type="text" />';
          var input = this.children[0];
          input.value = str;
          input.select(); // 文本框里面的文字处于选定状态
          // 当我们离开文本框就把文本框里面的值给span 
          input.onblur = function() {
          this.parentNode.innerHTML = this.value;
          };
          // 按下回车也可以把文本框里面的值给span
          input.onkeyup = function(e) {
          if (e.keyCode === 13) {
          // 手动调用表单失去焦点事件  不需要鼠标离开操作
          this.blur();
          }
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21

# JavaScript高级第02天笔记

# 1.构造函数和原型

# 1.1对象的三种创建方式--复习

  1. 字面量方式

    var obj = {};
    
    1
  2. new关键字

    var obj = new Object();
    
    1
  3. 构造函数方式

    function Person(name,age){
      this.name = name;
      this.age = age;
    }
    var obj = new Person('zs',12);
    
    1
    2
    3
    4
    5

# 1.2静态成员和实例成员

# 1.2.1实例成员

实例成员就是构造函数内部通过this添加的成员 如下列代码中uname age sing 就是实例成员,实例成员只能通过实例化的对象来访问

 function Star(uname, age) {
     this.uname = uname;
     this.age = age;
     this.sing = function() {
     console.log('我会唱歌');
    }
}
var ldh = new Star('刘德华', 18);
console.log(ldh.uname);//实例成员只能通过实例化的对象来访问
1
2
3
4
5
6
7
8
9

# 1.2.2静态成员

静态成员 在构造函数本身上添加的成员 如下列代码中 sex 就是静态成员,静态成员只能通过构造函数来访问

 function Star(uname, age) {
     this.uname = uname;
     this.age = age;
     this.sing = function() {
     console.log('我会唱歌');
    }
}
Star.sex = '男';
var ldh = new Star('刘德华', 18);
console.log(Star.sex);//静态成员只能通过构造函数来访问
1
2
3
4
5
6
7
8
9
10

# 1.3构造函数的问题

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

# 1.4构造函数原型prototype

构造函数通过原型分配的函数是所有对象所共享的。

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

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

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('张学友', 19);
ldh.sing();//我会唱歌
zxy.sing();//我会唱歌
1
2
3
4
5
6
7
8
9
10
11

# 1.5对象原型

对象都会有一个属性 __proto__ 指向构造函数的 prototype 原型对象,之所以我们对象可以使用构造函数 prototype 原型对象的属性和方法,就是因为对象有 __proto__ 原型的存在。
__proto__对象原型和原型对象 prototype 是等价的
__proto__对象原型的意义就在于为对象的查找机制提供一个方向,或者说一条路线,但是它是一个非标准属性,因此实际开发中,不可以使用这个属性,它只是内部指向原型对象 prototype
1
2
3

# 1.6constructor构造函数

对象原型( __proto__)和构造函数(prototype)原型对象里面都有一个属性 constructor 属性 ,constructor 我们称为构造函数,因为它指回构造函数本身。
constructor 主要用于记录该对象引用于哪个构造函数,它可以让原型对象重新指向原来的构造函数。
一般情况下,对象的方法都在构造函数的原型对象中设置。如果有多个对象的方法,我们可以给原型对象采取对象形式赋值,但是这样就会覆盖构造函数原型对象原来的内容,这样修改后的原型对象 constructor  就不再指向当前构造函数了。此时,我们可以在修改后的原型对象中,添加一个 constructor 指向原来的构造函数。
1
2
3

如果我们修改了原来的原型对象,给原型对象赋值的是一个对象,则必须手动的利用constructor指回原来的构造函数如:

 function Star(uname, age) {
     this.uname = uname;
     this.age = age;
 }
 // 很多情况下,我们需要手动的利用constructor 这个属性指回 原来的构造函数
 Star.prototype = {
 // 如果我们修改了原来的原型对象,给原型对象赋值的是一个对象,则必须手动的利用constructor指回原来的构造函数
   constructor: Star, // 手动设置指回原来的构造函数
   sing: function() {
     console.log('我会唱歌');
   },
   movie: function() {
     console.log('我会演电影');
   }
}
var zxy = new Star('张学友', 19);
console.log(zxy)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

以上代码运行结果,设置constructor属性如图:

如果未设置constructor属性,如图:

# 1.7原型链

​ 每一个实例对象又有一个__proto__属性,指向的构造函数的原型对象,构造函数的原型对象也是一个对象,也有__proto__属性,这样一层一层往上找就形成了原型链。

# 1.8构造函数实例和原型对象三角关系

1.构造函数的prototype属性指向了构造函数原型对象
2.实例对象是由构造函数创建的,实例对象的__proto__属性指向了构造函数的原型对象
3.构造函数的原型对象的constructor属性指向了构造函数,实例对象的原型的constructor属性也指向了构造函数
1
2
3

# 1.9原型链和成员的查找机制

任何对象都有原型对象,也就是prototype属性,任何原型对象也是一个对象,该对象就有__proto__属性,这样一层一层往上找,就形成了一条链,我们称此为原型链;

当访问一个对象的属性(包括方法)时,首先查找这个对象自身有没有该属性。
如果没有就查找它的原型(也就是 __proto__指向的 prototype 原型对象)。
如果还没有就查找原型对象的原型(Object的原型对象)。
依此类推一直找到 Object 为止(null)。
__proto__对象原型的意义就在于为对象成员查找机制提供一个方向,或者说一条路线。
1
2
3
4
5

# 1.10原型对象中this指向

构造函数中的this和原型对象的this,都指向我们new出来的实例对象

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
console.log(that === ldh);//true
// 2.原型对象函数里面的this 指向的是 实例对象 ldh
1
2
3
4
5
6
7
8
9
10
11
12
13

# 1.11通过原型为数组扩展内置方法

 Array.prototype.sum = function() {
   var sum = 0;
   for (var i = 0; i < this.length; i++) {
   sum += this[i];
   }
   return sum;
 };
 //此时数组对象中已经存在sum()方法了  可以始终 数组.sum()进行数据的求
1
2
3
4
5
6
7
8

# 2.继承

# 2.1call()

  • call()可以调用函数
  • call()可以修改this的指向,使用call()的时候 参数一是修改后的this指向,参数2,参数3..使用逗号隔开连接
 function fn(x, y) {
     console.log(this);
     console.log(x + y);
}
  var o = {
  	name: 'andy'
  };
  fn.call(o, 1, 2);//调用了函数此时的this指向了对象o,
1
2
3
4
5
6
7
8

# 2.2子构造函数继承父构造函数中的属性

  1. 先定义一个父构造函数
  2. 再定义一个子构造函数
  3. 子构造函数继承父构造函数的属性(使用call方法)
 // 1. 父构造函数
 function Father(uname, age) {
   // this 指向父构造函数的对象实例
   this.uname = uname;
   this.age = age;
 }
  // 2 .子构造函数 
function Son(uname, age, score) {
  // this 指向子构造函数的对象实例
  3.使用call方式实现子继承父的属性
  Father.call(this, uname, age);
  this.score = score;
}
var son = new Son('刘德华', 18, 100);
console.log(son);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 2.3借用原型对象继承方法

  1. 先定义一个父构造函数
  2. 再定义一个子构造函数
  3. 子构造函数继承父构造函数的属性(使用call方法)
// 1. 父构造函数
function Father(uname, age) {
  // this 指向父构造函数的对象实例
  this.uname = uname;
  this.age = age;
}
Father.prototype.money = function() {
  console.log(100000);
 };
 // 2 .子构造函数 
  function Son(uname, age, score) {
      // this 指向子构造函数的对象实例
      Father.call(this, uname, age);
      this.score = score;
  }
// Son.prototype = Father.prototype;  这样直接赋值会有问题,如果修改了子原型对象,父原型对象也会跟着一起变化
  Son.prototype = new Father();
  // 如果利用对象的形式修改了原型对象,别忘了利用constructor 指回原来的构造函数
  Son.prototype.constructor = Son;
  // 这个是子构造函数专门的方法
  Son.prototype.exam = function() {
    console.log('孩子要考试');

  }
  var son = new Son('刘德华', 18, 100);
  console.log(son);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

如上代码结果如图:

# 3.ES5新增方法

# 3.1数组方法forEach遍历数组

 arr.forEach(function(value, index, array) {
       //参数一是:数组元素
       //参数二是:数组元素的索引
       //参数三是:当前的数组
 })
  //相当于数组遍历的 for循环 没有返回值
1
2
3
4
5
6

# 3.2数组方法filter过滤数组

  var arr = [12, 66, 4, 88, 3, 7];
  var newArr = arr.filter(function(value, index,array) {
  	 //参数一是:数组元素
     //参数二是:数组元素的索引
     //参数三是:当前的数组
     return value >= 20;
  });
  console.log(newArr);//[66,88] //返回值是一个新数组
1
2
3
4
5
6
7
8

# 3.3数组方法some

some 查找数组中是否有满足条件的元素 
 var arr = [10, 30, 4];
 var flag = arr.some(function(value,index,array) {
    //参数一是:数组元素
     //参数二是:数组元素的索引
     //参数三是:当前的数组
     return value < 3;
  });
console.log(flag);//false返回值是布尔值,只要查找到满足条件的一个元素就立马终止循环
1
2
3
4
5
6
7
8
9

# 3.4筛选商品案例

  1. 定义数组对象数据

    var data = [{
                id: 1,
                pname: '小米',
                price: 3999
            }, {
                id: 2,
                pname: 'oppo',
                price: 999
            }, {
                id: 3,
                pname: '荣耀',
                price: 1299
            }, {
                id: 4,
                pname: '华为',
                price: 1999
            }, ];
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
  2. 使用forEach遍历数据并渲染到页面中

    data.forEach(function(value) {
      var tr = document.createElement('tr');
      tr.innerHTML = '<td>' + value.id + '</td><td>' + value.pname + '</td><td>' + value.price + '</td>';
      tbody.appendChild(tr);
     });
    
    1
    2
    3
    4
    5
  3. 根据价格筛选数据

    1. 获取到搜索按钮并为其绑定点击事件

      search_price.addEventListener('click', function() {
      });
      
      1
      2
    2. 使用filter将用户输入的价格信息筛选出来

      search_price.addEventListener('click', function() {
            var newDate = data.filter(function(value) {
              //start.value是开始区间
              //end.value是结束的区间
            	return value.price >= start.value && value.price <= end.value;
            });
            console.log(newDate);
       });
      
      1
      2
      3
      4
      5
      6
      7
      8
    3. 将筛选出来的数据重新渲染到表格中

      1. 将渲染数据的逻辑封装到一个函数中

        function setDate(mydata) {
              // 先清空原来tbody 里面的数据
          tbody.innerHTML = '';
          mydata.forEach(function(value) {
            var tr = document.createElement('tr');
            tr.innerHTML = '<td>' + value.id + '</td><td>' + value.pname + '</td><td>' + value.price + '</td>';
              tbody.appendChild(tr);
          });
         }
        
        1
        2
        3
        4
        5
        6
        7
        8
        9
      2. 将筛选之后的数据重新渲染

         search_price.addEventListener('click', function() {
             var newDate = data.filter(function(value) {
             return value.price >= start.value && value.price <= end.value;
             });
             console.log(newDate);
             // 把筛选完之后的对象渲染到页面中
             setDate(newDate);
        });
        
        1
        2
        3
        4
        5
        6
        7
        8
    4. 根据商品名称筛选

      1. 获取用户输入的商品名称

      2. 为查询按钮绑定点击事件,将输入的商品名称与这个数据进行筛选

         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  
               }
             });
             // 把拿到的数据渲染到页面中
             setDate(arr);
        })
        
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12

# 3.5some和forEach区别

  • 如果查询数组中唯一的元素, 用some方法更合适,在some 里面 遇到 return true 就是终止遍历 迭代效率更高
  • 在forEach 里面 return 不会终止迭代

# 3.6trim方法去除字符串两端的空格

var str = '   hello   '
console.log(str.trim()//hello 去除两端空格
var str1 = '   he l l o   '
console.log(str.trim()//he l l o  去除两端空格
1
2
3
4

# 3.7获取对象的属性名

Object.keys(对象) 获取到当前对象中的属性名 ,返回值是一个数组

 var obj = {
     id: 1,
     pname: '小米',
     price: 1999,
     num: 2000
};
var result = Object.keys(obj)
console.log(result)//[id,pname,price,num]
1
2
3
4
5
6
7
8

# 3.8Object.defineProperty

Object.defineProperty设置或修改对象中的属性

Object.defineProperty(对象,修改或新增的属性名,{
		value:修改或新增的属性的值,
		writable:true/false,//如果值为false 不允许修改这个属性值
		enumerable: false,//enumerable 如果值为false 则不允许遍历
        configurable: false  //configurable 如果为false 则不允许删除这个属性 属性是否可以被删除或是否可以再次修改特性
})	
1
2
3
4
5
6

# day3

构造函数的原型是prototype ,实例对象的原型是__proto__

# 构造函数、原型对象、实例对象的三角关系

  • 构造函数的prototype属性指向了构造函数原型对象
  • 实例对象是由构造函数创建的,实例对象的__proto__属性指向了构造函数的原型对象
  • 构造函数的原型对象的constructor属性指向了构造函数,实例对象的原型的constructor属性也指向了构造函数

# 1、函数的调用方式:

  • 普通函数的调用
  • 对象中的函数调用
  • 构造函数的调用
  • DOM元素绑定事件调用函数
  • 定时器中调用函数
  • 立即执行函数

# 2、this指向

  • 普通函数 this 指向window
  • 对象的方法 this指向的是对象
  • 构造函数 this 指向 构造函数new出来的实例对象
  • 绑定事件函数 this 指向的是函数的调用者
  • 定时器函数 this 指向的是window
  • 立即执行函数 this指向的是window

# 3、call方法、apply、bind

# (1)call

  • call可以调用函数
  • call可以改变函数内部的this指向
  • 使用call方法传递参数使用逗号隔开
  • call方法一般应用于继承

# (2)apply

  • apply可以调用函数
  • apply可以改变函数内部的this指向
  • 使用apply方法传递参数使用数组传递
  • apply方法一般应用于操作数组数据

# (3)bind

  • bind不会调用原来的函数 可以改变原来函数内部的this 指向
  • 返回的是原函数改变this之后产生的新函数
  • 应用场景:如果有的函数我们不需要立即调用,但是又想改变这个函数内部的this指向此时用bind

# (4)异同点

  • 相同点:都可以改变this指向
  • 不同点:
    • call 和 apply 会调用函数, 并且改变函数内部this指向
    • call传递参数使用逗号隔开传递,apply传递参数使用数组传递
    • bind 不会调用函数
  • 应用场景:
    • call 经常做继承
    • apply 经常跟数组有关系. 比如借助于数学对象实现数组最大值最小值
    • bind 不调用函数,但是还想改变this指向. 比如改变定时器内部的this指向

# 4、严格模式

  • 严格模式的产生:
    • 消除了Javascript 语法的一些不合理、不严谨之处,减少了一些怪异行为。
    • 消除代码运行的一些不安全之处,保证代码运行的安全。
    • 提高编译器效率,增加运行速度。
  • 开启严格模式:使用‘use strict’。
  • 严格模式下
    • 变量:
      • 在正常模式中,如果一个变量没有声明就赋值,默认是全局变量.严格模式禁止这种用法,变量都必须先用var命令声明,然后再使用
      • 严禁删除已经声明变量。例如,delete x; 语法是错误的
    • this 指向问题:
      • 严格模式下全局作用域中函数中的 this 是 undefined
      • 严格模式下,如果 构造函数不加new调用, this 指向的是undefined 如果赋值,会报错
      • new 实例化的构造函数指向创建的对象实例。
      • 定时器 this 还是指向 window
      • 事件、对象还是指向调用者
    • 函数变化
      • 函数不能有重名的参数
      • 函数必须声明在最前面.新版本的JavaScript会引入“块级作用域”( ES6 中已引入)为了与新版本接轨,不允许在非函数的代码块内声明函数

# 5、高阶函数

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

# 6、闭包

闭包(closure)指有权访问另一个函数作用域中变量的函数,一个作用域可以访问另外一个函数的局部变量。

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

# 7、递归

  • 一个函数在内部可以调用其本身,那么这个函数就是递归函数
  • 递归函数一定要有跳出条件

# JavaScript高级第04天笔记

# 1.正则表达式概述

# 1.1什么是正则表达式

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

正则表通常被用来检索、替换那些符合某个模式(规则)的文本,例如验证表单:用户名表单只能输入英文字母、数字或者下划线, 昵称输入框中可以输入中文(匹配)。此外,正则表达式还常用于过滤掉页面内容中的一些敏感词(替换),或从字符串中获取我们想要的特定部分(提取)等 。

其他语言也会使用正则表达式,本阶段我们主要是利用JavaScript 正则表达式完成表单验证。

# 1.2 正则表达式的特点

  1. 灵活性、逻辑性和功能性非常的强。
  2. 可以迅速地用极简单的方式达到字符串的复杂控制。
  3. 对于刚接触的人来说,比较晦涩难懂。比如:^\w+([-+.]\w+)@\w+([-.]\w+).\w+([-.]\w+)*$
  4. 实际开发,一般都是直接复制写好的正则表达式. 但是要求会使用正则表达式并且根据实际情况修改正则表达式. 比如用户名: /^[a-z0-9_-]{3,16}$/

# 2.正则表达式在js中的使用

# 2.1正则表达式的创建

在 JavaScript 中,可以通过两种方式创建一个正则表达式。

方式一:通过调用RegExp对象的构造函数创建

var regexp = new RegExp(/123/);
console.log(regexp);
1
2

方式二:利用字面量创建 正则表达式

 var rg = /123/;
1

# 2.2测试正则表达式

test() 正则对象方法,用于检测字符串是否符合该规则,该对象会返回 true 或 false,其参数是测试字符串。

var rg = /123/;
console.log(rg.test(123));//匹配字符中是否出现123  出现结果为true
console.log(rg.test('abc'));//匹配字符中是否出现123 未出现结果为false
1
2
3

# 3.正则表达式中的特殊字符

# 3.1正则表达式的组成

一个正则表达式可以由简单的字符构成,比如 /abc/,也可以是简单和特殊字符的组合,比如 /ab*c/ 。其中特殊字符也被称为元字符,在正则表达式中是具有特殊意义的专用符号,如 ^ 、$ 、+ 等。

特殊字符非常多,可以参考:

MDN (opens new window)

jQuery 手册:正则表达式部分

[正则测试工具]( <http://tool.oschina.net/regex)

# 3.2边界符

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

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

如果 ^和 $ 在一起,表示必须是精确匹配。

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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 3.3字符类

字符类表示有一系列字符可供选择,只要匹配其中一个就可以了。所有可供选择的字符都放在方括号内。

# 3.3.1 [] 方括号

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

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
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'));//true
----------------------------------------------------------------------------------
var reg = /^[a-z]$/ //26个英文字母任何一个字母返回 true  - 表示的是a 到z 的范围  
console.log(reg.test('a'));//true
console.log(reg.test('z'));//true
console.log(reg.test('A'));//false
-----------------------------------------------------------------------------------
//字符组合
var reg1 = /^[a-zA-Z0-9]$/; // 26个英文字母(大写和小写都可以)任何一个字母返回 true  
------------------------------------------------------------------------------------
//取反 方括号内部加上 ^ 表示取反,只要包含方括号内的字符,都返回 false 。
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('!'));//true
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

# 3.3.2量词符

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

量词 说明
* 重复0次或更多次
+ 重复1次或更多次
? 重复0次或1次
{n} 重复n次
{n,} 重复n次或更多次
{n,m} 重复n到m次

# 3.3.3用户名表单验证

功能需求:

  1. 如果用户名输入合法, 则后面提示信息为: 用户名合法,并且颜色为绿色
  2. 如果用户名输入不合法, 则后面提示信息为: 用户名不符合规范, 并且颜色为红色

分析:

  1. 用户名只能为英文字母,数字,下划线或者短横线组成, 并且用户名长度为6~16位.
  2. 首先准备好这种正则表达式模式/$[a-zA-Z0-9-_]{6,16}^/
  3. 当表单失去焦点就开始验证.
  4. 如果符合正则规范, 则让后面的span标签添加 right类.
  5. 如果不符合正则规范, 则让后面的span标签添加 wrong类.
<input type="text" class="uname"> <span>请输入用户名</span>
 <script>
 //  量词是设定某个模式出现的次数
 var reg = /^[a-zA-Z0-9_-]{6,16}$/; // 这个模式用户只能输入英文字母 数字 下划线 中划线
 var uname = document.querySelector('.uname');
 var span = document.querySelector('span');
 uname.onblur = function() {
   if (reg.test(this.value)) {
   console.log('正确的');
   span.className = 'right';
   span.innerHTML = '用户名格式输入正确';
   } else {
   console.log('错误的');
   span.className = 'wrong';
   span.innerHTML = '用户名格式输入不正确';
   }
 }
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 3.3.4 括号总结

1.大括号 量词符. 里面表示重复次数

2.中括号 字符集合。匹配方括号中的任意字符.

3.小括号表示优先级

正则表达式在线测试 (opens new window)

# 3.4预定义类

预定义类指的是某些常见模式的简写方式.

案例:验证座机号码

var reg = /^\d{3}-\d{8}|\d{4}-\d{7}$/;
var reg = /^\d{3,4}-\d{7,8}$/;
1
2

表单验证案例

//手机号验证:/^1[3|4|5|7|8][0-9]{9}$/;
//验证通过与不通过更换元素的类名与元素中的内容
 if (reg.test(this.value)) {
    // console.log('正确的');
    this.nextElementSibling.className = 'success';
    this.nextElementSibling.innerHTML = '<i class="success_icon"></i> 恭喜您输入正确';
   } else {
       // console.log('不正确');
      this.nextElementSibling.className = 'error';
      this.nextElementSibling.innerHTML = '<i class="error_icon"></i>格式不正确,请从新输入 ';
 }
1
2
3
4
5
6
7
8
9
10
11
//QQ号验证: /^[1-9]\d{4,}$/; 
//昵称验证:/^[\u4e00-\u9fa5]{2,8}$/
//验证通过与不通过更换元素的类名与元素中的内容 ,将上一步的匹配代码进行封装,多次调用即可
 function regexp(ele, reg) {
    ele.onblur = function() {
      if (reg.test(this.value)) {
        // console.log('正确的');
        this.nextElementSibling.className = 'success';
        this.nextElementSibling.innerHTML = '<i class="success_icon"></i> 恭喜您输入正确';
   } else {
     // console.log('不正确');
     this.nextElementSibling.className = 'error';
     this.nextElementSibling.innerHTML = '<i class="error_icon"></i> 格式不正确,请从新输入 ';
            }
        }
 };
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//密码验证:/^[a-zA-Z0-9_-]{6,16}$/
//再次输入密码只需匹配与上次输入的密码值 是否一致
1
2

# 3.5正则替换replace

replace() 方法可以实现替换字符串操作,用来替换的参数可以是一个字符串或是一个正则表达式。

var str = 'andy和red';
var newStr = str.replace('andy', 'baby');
console.log(newStr)//baby和red
//等同于 此处的andy可以写在正则表达式内
var newStr2 = str.replace(/andy/, 'baby');
console.log(newStr2)//baby和red
//全部替换
var str = 'abcabc'
var nStr = str.replace(/a/,'哈哈')
console.log(nStr) //哈哈bcabc
//全部替换g
var nStr = str.replace(/a/a,'哈哈')
console.log(nStr) //哈哈bc哈哈bc
//忽略大小写i
var str = 'aAbcAba';
var newStr = str.replace(/a/gi,'哈哈')//"哈哈哈哈bc哈哈b哈哈"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

案例:过滤敏感词汇

<textarea name="" id="message"></textarea> <button>提交</button>
<div></div>
<script>
    var text = document.querySelector('textarea');
    var btn = document.querySelector('button');
    var div = document.querySelector('div');
    btn.onclick = function() {
    	div.innerHTML = text.value.replace(/激情|gay/g, '**');
    }
</script>
1
2
3
4
5
6
7
8
9
10

Last Updated: 2022/12/5 16:36:25