JavaScript教程(高级)

news2024/11/23 15:16:49

面向对象编程介绍

两大编程思想

(1)、 面向过程编程

  • (缩写 POP)( Process-oriented programming)面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候再一个一个的依次调用就可以了;
  • 面向过程,就是按照我们分析好了的步骤,按照步骤解决问题;
    (2)、 面向对象
  • (缩写OOP)(Object oriented programming)面向对象就是把事务分解成为一个一个对象,然后由对象之间分工与合作;
  • 面向对象是以对象功能来划分问题,而不是步骤;
  • 在面向对象程序开发思想中,每一个对象都是功能中心,具有明确分工;
  • 面向对象编程具有灵活、代码可复用、容易维护和开发的优点,更适合多人合作的大型软件项目;
  • 面向对象的特性:【封装性】、【继承性】、【多态性】;

面向过程和面向对象的对比

面向过程

  • 优点:性能比面向对象高,适合跟硬件联系很密切的东西,例如单片机就采用的面向过程编程;
  • 缺点:没有面向对象易维护、易复用、易扩展;

面向对象

  • 优点:易维护、易复用、易扩展,由于面向对象有封装、继承、多态性的特性,可以设计出低耦合的系统,使系统更加灵活、更加易于维护;
  • 缺点:性能比面向过程低;
    注意:面向对象和面向过程是两种不同的编程思想,都有适合各自的使用场景,谁也不能替代谁;

ES6中的类和对象

  • 面向对象的思维特点:
    (1)、抽取(抽象)对象共用的属性和行为组织(封装)成一个类(模版);
    (2)、对类进行实例化,获取类的对象;
  • 面向对象编程我们考虑的是有哪些对象,按照面向对象的思维特点,不断的创建对象,使用对象,指挥对象做事情;

对象

  • 现实生活中:对象是一个具体的事务;
  • JavaScript中,对象是一组无序的相关属性和方法的集合,所有的事务都是对象,例如:字符串、数值、数组、函数等;
  • 对象是由【属性】和【方法】组成的;
  • 【属性】:事物的特征,在对象中用属性来表示(常用名词);
  • 【方法】:事物的行为,在对象中用方法来表示(常用动词);

类(class

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

创建类和生成实例

  • 语法:
class name(){
	//class body
}
  • 创建实例:
var xx = new name();
  • 注意:类必须使用new实例化对象;

constructor()(构造函数 又叫 构造器)

  • constructor()方法是类的构造函数(默认方法),用于传递参数,返回实例对象,通过new命令生成对象实例时,自动调用该方法。如果没有显示定义,类内部会自动给我们创建一个constructor()
//1、创建类 class 创建一个明星类
class Star{
	//类的共有属性放到 constructor 里面;
	constructor(uname){//接收实例传递过来的参数并返回实例对象;
		this.uname = uname;
	}
}
//2、利用类创建对象 new
var ldh = new Star('刘德华');
console.log(ldh.uname);//刘德华
  • 1、通过class关键字创建类,类名我们还是习惯性定义首字母大写;
  • 2、类里面有一个constructor()函数,可以接受传递过来的参数,同时返回实例对象;
  • 3、constructor函数只要new生成实例时,就会自动调用这个函数,如果我们不写这个函数,类也会自动生成这个函数;
  • 4、生成实例new不能省略;
  • 5、最后注意语法规范,创建类,类名后面不要加小括号,生成实例,类名后面加小括号,构造函数不需要加function

类中添加共有方法

  • 语法:
class Person{
	constructor(name,age){//constructor 构造器 或 构造函数
		this.name = name;
		this.age = age;
	}
	say(){
		console.log(this.name + '你好');
	}
}
//1、类里面所有的函数不需要写function;
//2、多个函数方法之间不需要添加逗号分隔;

类继承extendssuper关键字

extends关键字

  • 子类可以继承父类的一些属性和方法;
  • 语法:
class Father{//父类
	constructor(){
		
	}
	money(){
		console.log(100);
	}
}
class Son extends Father{//子类继承父类
}
var son = new Son();
son.money();//100

super关键字

  • 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);//调用了父类中的构造函数
	}
}
var son = new Son(1,2);
son.sum();
  • super关键字调用父类的普通函数;
class Father{
	say(){
		return '我是爸爸';
	}	
}
class Son extends Father{
	say(){
		console.log(super.say() + '的儿子');//调用父类的say方法
	}
}
var son = new Son();
son.say();
  • 继承中的属性或者方法查找原则:(就近原则)
    (1)、继承中,如果实例化子类输出一个方法,先看子类有没有这个方法,如果有就先执行子类的;
    (2)、继承中,如果子类里面没有,就去查找父类有没有这个方法,如果有,就执行父类的这个方法(就近原则);
  • super必须放到子类this之前;
  • 子类在构造函数中使用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(x,y);//super必须在子类this之前调用,否咋会报错
		this.x = x;
		this.y = y;
		//利用super 调用父类的构造函数;	
	}
	subtract(){
		console.log(this.x - this.y);
	}
}
var son = new Son(5,3);
son.subtract();

使用类的3个注意点

  • 1、在ES6中类没有变量提升,所以必须先定义类,才能通过类实例化对象;
  • 2、类里面的共有的属性和方法一定要加this使用;(this指向是实例化对象)
  • 3、类里面this指向问题;
//案例:点击button调用sing方法
<button>点击</button>
var that;
var _that;

class Star{
	constructor(uname,age){
	//constructor里面的this指向的是【创建的实例对象】;
		that = this;
		
		this.uname = uname;
		this.age = age;
		//this.sing();
		this.btn = document.querySelector('button');
		//实例对象的btn,所以要加this;
		//点击会输出undefined;原因:sing里面的this指向的是btn;而btn里面是没有uname属性的,所以打印出来的是undefined;
		this.btn.onclick = this.sing;//此处sing后面不需要加小括号,因为不需要立马就调用,而是要点击按钮再调用;
	}
	sing(){
		//这个sing方法里面的this指向的是btn这个按钮,因为这个按钮调用了这个函数;
		console.log(this.uname);//从实例上拿uname,所以需要加this
		console.log(that.uname);//that里面存储的是constructor里面的this;所以打印出来的是刘德华;
		
	}
	dance(){
		//这个dance里面的this,指向的是实例对象 ldh,因为ldh调用了这个函数;
		_that = this;
	}
}
var ldh = new Star('刘德华');

类里面this指向问题;

  • constructor里面的this指向实例对象,方法里面的this指向这个方法的调用者;

案例:面向对象tab栏切换

构造函数和原型

构造函数

  • 构造函数是一种特殊的函数,主要用来初始化对象,即为对象成员变量赋初始值,它总与new一起使用。我们可以把对象中一些公共的属性和方法抽取出来,然后封装到这个函数里面;
  • JS中,使用构造函数时要注意以下两点:
    (1)、构造函数用于创建某一类对象,其首字母要大写;
    (2)、构造函数要和new一起使用才有意义;
  • new在执行时会做四件事:
    (1)、在内存中创建一个新的空对象;
    (2)、让this指向这个新的对象;
    (3)、执行构造函数里面的代码,给这个新对象添加属性和方法;
    (4)、返回这个新对象(所以构造函数里面不需要return);

利用构造函数创建对象

  • 在典型的OOP的语言中(如java),都存在类的概念,类就是对象的模版,对象就是类的实质,但在ES6之前,JS中并没有引入类的概念;
  • ES6全称ECMAScript6.02015.06发布。但是目前浏览器的JavaScriptES5版本,大多数高版本的浏览器也支持ES6,不过只实现了ES6的部分特性和功能;
  • ES6之前,对象不是基于类创建的,而是用一种称为【构建函数】的特殊函数来定义对象和他们的特征;
  • 复习:创建对象可以通过以下三种方式:
    (1)、对象字面量;(对象的字面量是花括号{}
    (2)、new Object()
    (3)、自定义构造函数;(把对象公共的属性和方法抽取出来放到构造函数中,利用new关键字创建实例对象;)
// 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('唱歌');
	}
}
//利用new关键字创建实例对象;
var ldh = new Star('ldh',13);
console.log(ldh);

静态成员和实例成员(构造函数的属性和方法我们称为【成员】,成员可以添加)

  • JavaScript的构造函数中可以添加一些成员,可以在构造函数本身上添加,也可以在构造函数内部的this上添加。通过这两种方式添加的成员,就分别称为【静态成员】和【实例成员】。
  • 静态成员:在构造函数本身上添加的成员称为静态成员(如:Star.sex='男'sex就是静态成员),只能通过构造函数来访问(如:Star.sex);
  • 实例成员:在构造函数内部通过this添加的成员称为实例成员(如上面代码中的uname agesing),只能由实例化的对象来访问(如:ldh.uname);不可以通过构造函数(Star.uname)来访问实例成员;

构造函数原型对象prototype

  • 构造函数方法很好用,但是存在浪费内存的问题;(同一个方法在不同的实例对象中都需要开辟新的内存地址)
    -
  • 我们希望所有的对象使用同一个函数,这样就比较节省内存。
  • 构造函数通过原型prototype分配的函数是所有对象所共享的;
  • javascript规定,每一个构造函数都有一个prototype属性,指向另一个对象。注意这个prototype就是一个对象(所以就称为原型对象),这个对象的所有属性和方法,都会被构造函数所拥有;
  • 我们可以把那些不变的方法,直接定义在prototype对象上,这样所有对象的实例就可以共享这些方法;
function Star(uname,age){
	this.uname = uname;
	this.age = age;
}
//可以把那些不变的方法,直接定义在`prototype`原型对象上;
Star.prototype.sing = function(){
	console.log('唱歌');
}
var ldh = new Star('ldh',19);
var zxy= new Star('zxy',19);
console.log(ldh.sing === zxy.sing);//true  比较的是方法的内存地址
ldh.sing();
zxy.sing();
  • 原型是什么?一个对象,我们也称为prototype为原型对象;
  • 原型的作用是什么?共享方法;
  • 一般情况下,我们的公共属性定义到构造函数里面,公共的方法我们放到原型对象身上;

对象的原型__proto__

  • 对象都会有一个属性__proto__,指向构造函数的prototype原型对象,之所以我们对象可以使用构造函数prototype原型对象的属性和方法,就是因为对象有__proto__原型的存在;
  • __proto__对象原型和原型对象prototype是等价的;
  • __proto__对象原型的意义就在于为对象的查找机制提供一个方向,或者说一条路线,但是它是一个非标准属性,因此实际开发中,不可以使用这个属性,它只是内部指向原型对象prototype;
console.log(ldh.__proto__ === Star.prototype);//true
/*
方法的查找规则:
 (1)、首先看ldh对象身上是否有sing方法,如果有就执行这个对象上的sing;
 (2)、如果没有sing这个方法,因为有__proto__的存在,就去构造函数原型对象prototype身上去查找sing这个方法;
*/

在这里插入图片描述

constructor构造函数

  • 对象原型(__proto__)和构造函数原型对象(prototype)里面都有一个属性constructor属性,constructor我们称为构造函数,因为它指回构造函数本身;
  • constructor主要用于记录该对象引用于哪个构造函数,它可以让原型对象重新指向原来的构造函数;
function Star(uname,age){
	this.uname = uname;
	this.age = age;
}
//Star.prototype.sing = function(){
//	console.log('唱歌');
//}
//Star.prototype.movie= function(){
//	console.log('演电影');
//}

//很多情况下,我们需要手动的利用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__);

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

在这里插入图片描述

原型链

  • 主要是对象就有__proto__原型,指向原型对象;
  • 我们Star原型对象里面的__proto__原型指向的是Object.prototype
  • 我们Object.prototype原型对象里面的__proto__原型,指向为null
    在这里插入图片描述

JavaScript的对象成员查找规则(机制)

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

原型对象this指向

  • this到底指向谁只有调用的时候才会确定;
  • 一般情况下,this指向的是调用者;
function Star(uname,age){
	this.uname = uname;
	this.age = age;
}
var that;
Star.prototype.sing = function(){
	console.log('唱歌');
	that = this;
}
var ldh = new Star('刘德华',19);

//1、在构造函数中,里面的this指向的是 实例对象 ldh;
ldh.sing();
console.log(that === adh);
//2、原型对象函数里面的this 指向的是 实例对象 ldh;

原型对象的应用:利用原型对象扩展内置对象方法

  • 可以通过原型对象,对原来的内置对象进行扩展自定义的方法。比如:给数组增加自定义求偶数和的功能;
console.log(Array.prototype);//打印数组的原型

Array.prototype.sum = function(){
	var sum = 0;
	for(var i = 0;i < this.length;i++ ){
		sum += this[i];
	}
	return sum;
}
var arr = [1,2,3];
console.log(arr.sum());//6

var arr1 = new Array(11,22,33);
console.log(arr1.sum());//66
  • 注意:数组和字符串内置对象不能给原型对象覆盖操作Array.prototype = {},只能是Array.prototype.xxx = function(){}的方式;

继承

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

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

  • 核心原理:通过call把父类型的this指向子类型的this,这样就可以实现子类型继承父类型的属性;
 <script>
        // 借用父构造函数继承属性;
        // 1、父构造函数
        function Father(uname,age){
            // this 指向父构造函数的对象实例;
            this.uname = uname;
            this.age = age;
        }

        // 2、子构造函数
        function Son(uname,age,score){
            // this 指向子构造函数的实例对象;
            Father.call(this,uname,age);//调用父构造函数,把父构造函数中的this改为子构造函数中的this;
            this.score = score;
        }
        var son = new Son('刘德华',12,90);
        console.log(son);//Son {uname: '刘德华', age: 12, score: 90}
 </script>

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

<script>
        // 一、借用父构造函数继承属性;
        // 1、父构造函数
        function Father(uname,age){
            // this 指向父构造函数的对象实例;
            this.uname = uname;
            this.age = age;
        }
        // 二、借用原型对象继承父类型方法
        Father.prototype.money = function(){
            console.log(10);
        }

        // 2、子构造函数
        function Son(uname,age,score){
            // this 指向子构造函数的实例对象;
            Father.call(this,uname,age);//调用父构造函数,把父构造函数中的this改为子构造函数中的this;
            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('刘德华',12,90);
        console.log(son);//Son {uname: '刘德华', age: 12, score: 90}
        console.log(Father.prototype);
        console.log(Son.prototype.constructor);
</script>

在这里插入图片描述

call方法的作用

  • 调用这个函数,并且修改函数运行时的this指向;
  • fun.call(thisArg,arg1,arg2,...)
  • thisArg:当前调用函数this的指向对象;
  • arg1,arg2:传递的其他参数;
 <script>
        // call()
        function fn(x, y) {
            console.log('1111');
            console.log(this);//Window {window: Window, self: Window, document: document, name: '', location: Location, …}
            console.log(x + y);//3
        }
        var o = {
            name: 'andy'
        };
        //fn();

        // 1、call() 可以调用函数;
        fn.call();

        // 2、call() 可以改变这个函数的this指向;
        fn.call(o, 1, 2);//让this指向o这个对象;(o后面可以写需要传递的参数,如此处:1传给了x,2传给了y)
</script>

ES6类的本质

  • 类的本质还是function
  • 类的所有方法都定义在类的prototype属性上;
  • 类创建的实例,里面也有__proto__指向类的prototype原型对象;
  • 所以ES6的类它的绝大部分功能,ES5都可以做到,新的class写法只是让对象原型的写法更加清晰、更像面向对象编辑的语法而已;
  • 所以,ES6的类其实就是语法糖;
  • 语法糖:就是一种便捷写法。简单理解,有两种方法可以实现同样的功能,但是一种写法更加清晰、方便,那么这个方法就是语法糖;
  <script>
        // 1、ES6之前,通过 构造函数 + 原型 实现面向对象 编程;
        /*
          构造函数的特点:
            1、构造函数有原型对象prototype;
            2、构造函数原型对象prototype里面有constructor,指向构造函数本身;
            3、构造函数可以通过原型对象添加方法;
            4、构造函数创建的实例对象有__proto__原型 指向 构造函数的原型对象;
        */ 

        // 2、ES6通过 类 实现面向对象编程;
        class Star{

        }
        console.log(typeof Star);//function
        // 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);//true
</script>

ES5中的新增方法

  • ES5中给我们新增了一些方法,可以很方便的操作数组或者字符串,这些方法包括:数组方法、字符串方法、对象方法;

数组方法

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

遍历数组forEach()方法

  • array.forEach(function(currentValue,index,arr){})
  • currentValue:数组当前项的值;
  • index:数组当前项的索引;
  • arr:数组对象本身;
 <script>
        // forEach() 迭代(遍历)数组;
        var arr = [1,2,3];
        var sum = 0;
        arr.forEach(function(value,index,array){
            console.log('数组元素' +  value);
            console.log('数组元素的索引号' + index);
            console.log('数组本身' + array);
            sum +=value;//求和
        })
        console.log(sum);//6
    </script>

筛选数组filter()方法

  • array.filter(function(currentValue,index,arr){})
  • filter()方法创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素,主要用于筛选数组;
  • 注意:它直接返回一个新数组;
  • currentValue:数组当前项的值;
  • index:数组当前项的索引;
  • arr:数组对象本身;
 <script>
        var arr = [23,40,1,14,39,99];
        var newArr = arr.filter(function(value,index,arr){
            // return value >= 20;//筛选出大于20的数;
            return value %  2 == 0;//筛选出偶数;
        })
        console.log(newArr);
</script>

查找数组中是否有满足条件的元素some()方法

  • array.some(function(currentValue,index,arr){})
  • some()方法用于检测数组中的元素是否满足指定条件,通俗点,查找数组中是否有满足条件的元素;
  • 注意它返回值是布尔值,如果查找到这个元素,就返回true,如果查找不到就返回false
  • 如果找到第一个满足条件的元素,则终止循环,不再继续查找;
  • currentValue:数组当前项的值;
  • index:数组当前项的索引;
  • arr:数组对象本身;
<script>
        // some() 查找数组中是否有满足条件的元素;
        // 查找数组中是否有大于等于20的元素;
        var arr = [10,30,4];
        var flag = arr.some(function(value,index,arr){
            return value >= 20;
        })
        console.log(flag);//true

        // 查找数组中是否有包含pink的元素;
        var arr1 = ['pink','plue','red'];
        var flag1 = arr1.some(function(value,inex,arr){
            return value === 'pink';
        })
        console.log(flag1);//true
        /*
            1、filter 是查找满足条件的元素,返回的是一个数组,而且是把所有满足条件的元素返回回来;
            2、some 是查找满足条件的元素是否存在,返回的是一个布尔值,如果查找到第一个满足条件的元素就终止循环;
        */ 
    </script>

案例:查询商品案例-利用数组新增方法操作数据

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        table {
            width: 400px;
            border: 1px solid #000;
            border-collapse: collapse;
            margin: 0 auto;
        }

        td,
        th {
            border: 1px solid #000;
            text-align: center;
        }

        input {
            width: 50px;
        }

        .search {
            width: 600px;
            margin: 20px auto;
        }
    </style>
</head>

<body>
    <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>
            <!-- <tr>
                <td>1</td>
                <td>小米</td>
                <td>3999</td>
            </tr> -->
        </tbody>
    </table>
    <script>
        var data = [
            {
                id: 1,
                pname: '小米',
                price: '299'
            },
            {
                id: 2,
                pname: '大米',
                price: '399'
            },
            {
                id: 3,
                pname: '中米',
                price: '499'
            }
        ]
        // 1、获取相应的元素;
        var tbody = document.querySelector('tbody');
        setData(data);
        // 2、把数据渲染到页面中;
        function setData(myData) {
            // 先清空原来tbody里面的数据;
            tbody.innerHTML = '';
            // 再渲染新的数据;
            myData.forEach(function (value, index, arr) {
                var tr = document.createElement('tr');
                tr.innerHTML = `<td>${value.id}</td><td>${value.pname}</td><td>${value.price}</td>`;
                tbody.appendChild(tr);
            })
        }

        // 3、根据价格查询商品(当我们点击了按钮,就可以根据我们的商品价格去筛选数组里面的对象);
        var start = document.querySelector('.start');
        var end = document.querySelector('.end');
        var searchPrice = document.querySelector('.search-price');
        searchPrice.addEventListener('click', function () {
            var newData = data.filter(function (value) {
                return value.price >= start.value && value.price <= end.value;
            })
            // console.log(newData);
            // 把筛选完之后的对象渲染到页面中;
            setData(newData);
        })
        // 4、根据商品名称查询;
        var product = document.querySelector('.product');
        var searchPro = document.querySelector('.search-pro');
        searchPro.addEventListener('click', function () {
            // var newDataByName = data.filter((item) => {
            //     return item.pname === product.value;
            // })
            // console.log(newDataByName);//some方法返回的是布尔值;
            // setData(newDataByName);

            // 如果查询数组中唯一的元素,用some方法更合适,因为它找到这个元素,就不再进行循环,效率更高;
            var arr = [];
            var newDataByName = data.some(function (value) {
                if (value.pname === product.value) {
                    // console.log(value);
                    arr.push(value);
                    return true;//return后面必须写true;
                };
            })
            setData(arr);
        })
    </script>
</body>

</html>

someforEach区别

<script>
        var arr = ['red', 'green', 'blue', 'pink'];
        // 1、forEach迭代 遍历
        // arr.forEach(function (value) {
        //     if (value == 'green') {
        //         console.log('找到该元素了');
        //         return true; // 在forEach里面 return true 不会终止迭代;
        //     }
        //     console.log(11);//打印了3次11,'red','blue', 'pink'各打印了一次;
        // })

        // 2、some 迭代 遍历(如果查询数组中唯一的元素,用some方法更合适);
        arr.some(function (value) {
            if (value == 'green') {
                console.log('找到该元素了');
                return true; // 在some里面 return true 就是终止遍历,效率更高;
            }
            console.log(11);//只打印了1次11,'red'打印的,找到'green'后就不再往下执行了;
        })
    </script>

trim方法去除字符串两侧空格

  • trim()方法会从一个字符串的两端删除空白字符;
  • str.trim()
  • trim()方法并不会影响原字符串本身,它返回的是一个新的字符串;
<body>
    <input type="text"><button>点击</button>
    <div></div>
    <script>
        // trim方法去除字符串两侧空格
        var str = '   andy  ';
        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.addEventListener('click', function () {
            var str = input.value.trim();
            if (str === '') {
                alert('请输入内容');
            } else {
                div.innerHTML = str;
            }
        })
    </script>
</body>

对象方法:Object.defineProperty方法

  • Object.defineProperty()定义对象中新属性或修改原有的属性;
  • Object.defineProperty(obj,prop,descriptor)
  • obj:必需,目标对象;
  • prop:必需,需定义或修改的属性的名字;
  • descriptor:必需,目标属性所拥有的特性;
  • 第三个参数descriptor说明:以对象形式{}书写
    (1)、value:设置属性的值,默认为undefined
    (2)、writable:值是否可以重写。true | false,默认为false
    (3)、enumerable:目标属性是否可以被枚举(遍历)。true | false,默认为false
    (4)、configurable:目标属性是否可以被删除或是否可以再次修改特性。true | false,默认为false
<script>
        // Object.defineProperty() 定义新属性或修改原有的属性
        var obj = {
            id: 1,
            pname: "小米",
            price: 1999
        };
        // 1、以前的对象添加和修改属性的方式;
        // obj.num = 10000;
        // obj.price = 99;
        // console.log(obj);

        // 2、Object.defineProperty() 定义新属性或修改原有的属性
        Object.defineProperty(obj, 'num', {
            value: 9000
        });
        // console.log(obj);

        // 如果对象中原来就有该属性,则会修改原属性;如果对象中原来没有该属性,就添加该属性;
        Object.defineProperty(obj, 'price', {
            value: 9.9
        });
        // console.log(obj);

        Object.defineProperty(obj, 'id', {
            // 如果值为false,则不允许重写(不允许修改该属性值);
            writable: false,
        });
        obj.id = '999';
        // console.log(obj);

        Object.defineProperty(obj, 'address', {
            value: '南京',
            // 如果值为false,则不允许重写(不允许修改该属性值);
            writable: false,
            // enumerable 如果值为false,则不允许遍历,默认的值是 false;
            enumerable: false,
            // configurable 如果为false则不允许删除某个属性,并且不允许再次修改第三个参数里面的【特性】;
            configurable: false,
        });
        console.log(Object.keys(obj));
        delete obj.address // 删除对象中的某个属性;
    </script>

函数进阶

函数的定义和调用

函数的定义方式

  • 函数声明方式function关键字(命名函数);
  • 函数表达式(匿名函数);
  • new Function()(此处的Function()是一个构造函数);
  • var fn= new Function('参数1','参数2',...,'函数体');
  • 注意:Function里面参数都必须是字符串格式;
  • 第三种方式执行效率低,也不方便书写因此较少使用;
  • 所有函数都是Function的实例(对象);
  • 函数也属于对象;
    在这里插入图片描述
<script>
        // 函数的定义方式
        // 1、自定义函数(命名函数);
        function fn() { }

        // 2、函数表达式(匿名函数);
        var fun = function () { };

        // 3、利用 new Function('参数1','参数2','函数体');
        var f = new Function('console.log(123)');
        f();

        var t = new Function('a', 'b', 'console.log(a + b)');
        t(1, 2);

        // 4、所有函数都是 Function 的实例(对象);
        console.dir(f);
        // 函数也属于对象;
        console.log(f instanceof Object); // true  // instanceof 用于检测前者是否属于后者;
    </script>

函数的调用方式

1、普通函数

2、对象的方法

3、构造函数

4、绑定事件函数

5、定时器函数

6、立即执行函数

<script>
        // 1、普通函数
        function fn() {
            console.log(111);
        }
        // fn();  或  fn.call();

        // 2、对象的方法(把函数放到对象里面叫方法);
        var o = {
            sayHi: function () {
                console.log(2222);
            }
        }
        o.sayHi();
        // 3、构造函数
        function Star() { };
        new Star();// 产生一个新的实例对象;
        // 4、绑定事件函数
        btn.onClick = function () { };//点击了按钮就可以调用这个函数;
        // 5、定时器函数
        setInterval(function () { }, 1000);//这个函数是定时器自动1秒钟调用一次;
        // 6、立即执行函数(立即执行函数是自动调用);
        (function () {
            console.log(3333);
        })()
    </script>

函数内部的this指向

在这里插入图片描述

改变函数内部this指向(call()apply()bind()

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

call()方法

  • call()方法调用一个对象。简单理解为调用函数的方式,但是它可以改变函数的this指向;
  • fun.call(thisArg,arg1,arg2,...)
<script>
        // 改变函数内this指向,js提供了三种方法 call()、apply()、bind();

        // 1、call(),第一个可以调用函数,第二个可以改变函数内的this指向;
        var o = {
            name: 'andy'
        }
        function fn(a, b) {
            console.log(a);
            console.log(b);
            // console.log(this);
            console.log(a + b);
        }
        fn(); //this 指向 window;
        fn.call(o, 1, 6) // this 指向 o 对象;
        // call() 的主要作用可以实现继承;
        function Father(uname, age, sex) {
            this.uname = uname;
            this.age = age;
            this.sex = sex;
        }
        function Son(uname, age, sex) {
            Father.call(this, uname, age, sex)//调用Father函数,并将Father的this指向改为Son的this;
        }
        var son = new Son('刘德华', 18, '男');
        // console.log(son);
    </script>

apply()方法

  • apply()方法调用一个函数,简单理解为调用函数的方式,但是它可以改变函数的this指向;
  • fun.apply(thisArg,[argsArray])
  • thisArg:在fun函数运行时指定的this值;
  • argsArray:传递的值,必须包含在数组里面;
  • 返回值就是函数的返回值,因为它就是调用函数;
<script>
        // 2、apply() 应用、运用的意思
        var o = {
            name: 'andy',
        };
        function fn(arr) {
            console.log(this);
            console.log(arr); // pink(注意:打印出来的是一个字符串,不是数组)
        }
        fn();// this 指向 window;
        fn.apply(o, ['pink']); // this 指向 o ;
        // 1、也是调用函数,第二个可以改变函数内部的this指向;
        // 2、但是它的参数必须是数组形式的(伪数组);
        // 3、apply的主要应用:比如说我们可以利用apply借助于数学内置对象求最大值;
        // Math.max();
        var arr = [1, 33, 55, 99, 8];
        // var max = Math.max.apply(null, arr);// null表示不需要改变this指向;
        var max = Math.max.apply(Math, arr);//this指向方法max的调用者Math,所以写成 Math 比 null 合适;
        console.log(max);// 99
    </script>

bind()方法

  • bind()方法不会调用函数,但是能改变函数内部this指向;
  • fun.bind(thisArg,arg1,arg2,...)
  • thisArg:在fun函数运行时指定的this值;
  • arg1,arg2:传递的其他参数;
  • 返回由指定的this值和初始化参数改造的原函数拷贝;
<body>
    <button>点击</button>
    <button>点击</button>
    <button>点击</button>
    <script>
        // bind 绑定 捆绑 的意思;
        // var o = {
        //     name: 'andy'
        // };
        // function fn(a, b) {
        //     console.log(a + b);
        //     console.log(this);
        // }
        // var f = fn.bind(o, 4, 12);// 没调用函数,所以没有打印;而是返回一个函数
        // f();//调用返回的函数;

        // 1、不会调用原来的函数,可以改变原来函数内部的this指向;
        // 2、返回的是原函数改变this之后产生的新函数;
        // 3、如果有的函数我们不需要立即调用,但是又想改变这个函数内部的this指向,此时用bind;
        // 4、bind()方法应用案例:我们有一个按钮,当我们点击了之后,就禁用这个按钮,3秒钟之后开启这个按钮;
        var btn = document.querySelector('button');
        // btn.addEventListener('click', function () {
        //     this.disabled = true;
        //     // 方法一:用变量来保存this指向(需要开辟新的内存空间,浪费内存);
        //     var that = this;
        //     setInterval(function () {//定时器函数里面的this指向的是window;
        //         that.disabled = false;
        //     }, 3000)
        // })

        // btn.addEventListener('click', function () {
        //     // 方法二:用bind更方便;
        //     this.disabled = true;
        //     // setInterval(function () {//定时器函数里面的this指向的是window;
        //     //     this.disabled = false;
        //     // }.bind(btn), 3000)
        //     setInterval(function () {//定时器函数里面的this指向的是window;
        //         this.disabled = false;
        //     }.bind(this), 3000)//这个this指向的是btn这个对象,用this可以防止后期btn改变或有多个button的情况;
        // })

        // 当有多个按钮时
        var btns = document.querySelectorAll('button');
        for (let i = 0; i < btns.length; i++) {
            btns[i].onclick = function () {
                this.disabled = true;
                setInterval(function () {
                    this.disabled = false;
                }.bind(this), 1000)
            }
        }
    </script>
</body>
call() apply() bind()总结
  • 相同点:都可以改变函数内部this指向;
  • 区别点:
    (1)、call()apply()会调用函数,并且改变函数内部this指向;
    (2)、call()apply()传递的参数不一样,call()传递参数arg1arg2… ,apply()必须是数组形式[arg]
    (3)、bind()不会调用函数,可以改变函数内部this指向;
  • 主要应用场景:
    (1)、call()经常做继承;
    (2)、apply()经常跟数组有关系,比如借助于数学对象实现数组最大值最小值;
    (3)、bind()不调用函数,但是还想改变this指向,比如改变定时器内部this指向;

严格模式

1、什么是严格模式以及如何开启严格模式

  • JavaScript除了提供正常模式外,还提供了严格模式(strict mode)。ES5的严格模式是采用具有限制性JavaScript变体的一种方式,即在严格的条件下运行JS代码。
  • 严格模式在IE10以上版本的浏览器中才会被支持,旧版本浏览器中会被忽略。
  • 严格模式对正常的JavaScript语义做了一些更改;
    (1)、消除了JavaScript语法的一些不合理、不严谨之处,减少了一些怪异行为(例如变量不声明就不能使用);
    (2)、消除代码运行的一些不安全之处,保证代码运行的安全;
    (3)、提高编译器效率,增加运行速度;
    (4)、禁用了在ECMAScript的未来版本中可能会定义的一些语法,为未来新版本的JavaScript做好铺垫。比如一些保留字如:class enum export extends import super不能做变量名;

2、开启严格模式

  • 严格模式可以应用到【整个脚本】或【个别函数】中。因此在使用时,我们可以将严格模式分为【为脚本开启严格模式】和【为函数开启严格模式】两种情况;
  • 为脚本开启严格模式:为整个脚本文件开启严格模式,需要在所有语句之前放一个特定语句"use strict"(或'use strict');
<script>
	"use strict"
	console.log("这是严格模式")
</script>

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

<script>
	(function(){
		"use strict"
		var num = 10;
		function fn(){}
	})();
</script>
  • 为函数开启严格模式:要把某个函数开启严格模式,需要把"use strict";(或'use strict';)声明放在函数体所有语句之前;
<script>
	function fn(){
		//此时,只是给fn函数开启严格模式;
		'use strict';
		//下面的代码按照严格模式执行;但是fun里面的函数按照普通模式执行;
	}
	function fun(){}
</script>

3、严格模式中的变化

  • 严格模式对JavaScript的语法和行为,都做了一些变化;
  • 变量规定
    (1)、在正常模式中,如果一个变量没有声明就赋值,默认是全局变量。严格模式禁止这种用法,变量都必须先用var命令声明,然后再使用;
 <script>
      'use strict';
       // 1、严格模式下变量名必须先声明再使用;
      num = 10;
      console.log(num);// num is not defined
</script>

(2)、严禁删除已经申明的变量。例如:delete语法是错误的;

<script>
  'use strict';        
   // 2、严格模式下不能删除已经声明好的变量;
   var num = 10;
   console.log(num);
   delete num; //Uncaught SyntaxError: Delete of an unqualified identifier in strict mode. 
    </script>
  • 严格模式下this指向问题
    (1)、以前在全局作用域函数中this指向window对象;
    (2)、严格模式下全局作用域中函数的thisundefined
    (3)、以前构造函数时不加new也可以调用,当普通函数调用,里面的this指向全局对象;
    (4)、严格模式下,如果构造函数不加new调用,this会报错;
    (5)、new实例化的构造函数指向创建的对象实例;
<script>
        'use strict';        
        // 3、严格模式下,如果构造函数不加new调用,this会报错;
        function Star() {
            this.sex = '男';
        }
        Star();
        console.log(window.sex);//普通模式下可以打印出“男”;严格模式下报错07.开启严格模式.html:25 Uncaught TypeError: Cannot set properties of undefined (setting 'sex')
// 严格模式下全局作用域中函数的`this`是`undefined`;
</script>

(6)、定时器this还是指向window
(7)、事件、对象还是指向调用者;

  • 函数变化
    (1)、函数不能有重名的参数;
    (2)、函数必须声明在顶层、新版本的JavaScript会引入“块级作用域”(ES6中已引入)。为了与新版本接轨,不允许在非函数的代码块内声明函数;
  • 更多严格模式要求参考:https://developer.mozilla.org/zh-CN/docs/Glossary/strict_mode

高阶函数

  • 高阶函数是对其他函数进行操作的函数,它【接收函数作为参数】或【将函数作为返回值输出】;
  • 函数也是一种数据类型,同样可以作为参数,传递给另外一个参数使用。最典型的就是作为回调函数。
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="../../jQuery/jquery.min.js"></script>
    <style>
        div {
            width: 100px;
            height: 100px;
            background: pink;
            position: absolute;
        }
    </style>
</head>

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

        // 让div移动一段距离再变色;
        $("div").animate({ left: 500 }, function () { $("div").css("backgroundColor", "purple") })
    </script>
</body>

</html>

闭包

复习:变量作用域

  • 变量根据作用域的不同分为两种:全局变量和局部变量。
  • 函数内部可以使用全局变量;
  • 函数外部不可以使用局部变量;
  • 当函数执行完毕,本作用域内的局部变量会销毁;

什么是闭包?

  • 闭包(closure):指有权访问另一个函数作用域中变量的函数。—JavaScript高级程序设计
  • 简单理解就是,一个作用域可以访问另外一个函数内部的局部变量;
  • 闭包也是高阶函数;

闭包的作用

<script>
        // 闭包:指有权访问另一个函数作用域中变量的函数;
        // 一个作用域可以访问另外一个函数的局部变量;
        // 我们 fn 外面的作用域可以访问 fn 内部的局部变量;
        // 闭包的主要作用:延伸了变量的作用范围;
        function fn() {
            var num = 10;
            // function fun() {
            //     console.log(num);
            // }
            // fun();
            // return fun;
            return function () {
                console.log(num);
            }
        }
        var f = fn();
        f();
        // 类似于
        // var f = function fun() {
        //     console.log(num);
        // }
    </script>

闭包案例:点击li打印当前索引号

<body>
    <ul class="nav">
        <li>榴莲</li>
        <li>臭豆腐</li>
        <li>鲱鱼罐头</li>
        <li>大猪蹄子</li>
    </ul>
    <script>
        // 闭包案例:点击li输出当前li的索引号;

        // 1、我们可以利用动态添加属性的方法来做;
        var lis = document.querySelector('.nav').querySelectorAll('li');
        for (var i = 0; i < lis.length; i++) {//for循环是一个同步任务;(for循环里i++是最后一步)
            // 解决方法:利用动态添加属性的方法来做;
            lis[i].index = i;
            lis[i].onclick = function () {//function是一个异步任务,只有点击了才会去执行;而for循环是一个同步任务;
                console.log(i);//所以打印出来的全部都是4;
                console.log(this.index);//打印出来的是索引;
            }
        }

        // 2、利用闭包的方式得到当前小li的索引号;
        for (var i = 0; i < lis.length; i++) {
            // 利用for循环创建了4个立即执行函数;
            // 立即执行函数也称为小闭包,因为立即执行函数里面的任何一个函数都可以使用它的i这个变量;
            (function (i) {
                // console.log(i);
                lis[i].onclick = function () {
                    console.log(i);//缺点:特别占用内存,容易造成内存泄漏;好处:延长了变量的作用范围;
                }
            })(i)
        }
    </script>
</body>

闭包案例:3秒钟之后打印li内容

<body>
    <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 () {
                    // 异步任务的3种情况:定时器中的回调函数、事件(点击事件、鼠标事件等)中的回调函数、ajax中的回调函数;
                    console.log(lis[i].innerHTML);
                }, 3000)
            })(i)
        }
    </script>
</body>

闭包案例:计算打车价格

<body>
    <script>
        /*
        闭包应用:计算打车价格
        打车起步价13(3公里内),之后每多一公里增加5块钱,用户输入公里数就可以计算打车价格;
        如果有拥堵情况,总价格多收取10块钱拥堵费;
        */
        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));
        console.log(car.yd(true));
        console.log(car.price(1));
        console.log(car.yd(false));
    </script>
</body>

思考题

<script>
        // 1、思考题一:
        // var name = "The Window";//在全局作用域下定义的属性是挂在window下的;
        // var object = {
        //     name: "My Object",
        //     getNameFunc: function () {
        //         return function () {
        //             return this.name;//此处相当于一个立即执行函数,所以此处的this指向的是window;
        //         };
        //     }
        // }//此题中没有产生局部变量,所以没有闭包的产生;
        // console.log(object.getNameFunc()());//The Window
        // 分析
        // var f = object.getNameFunc();
        // 类似于
        // var f = function () {
        //     return this.name;
        // }
        // f();
        // function(){this}() // 立即执行函数里的this指向的是window;

        // 2、思考题二:
        var name = "The Window";
        var object = {
            name: "My Object",
            getNameFunc: function () {
                var that = this;//此处的this指向的是object;
                return function () {
                    return that.name;//
                };
            }
        }
        console.log(object.getNameFunc()())//My Object,有闭包的产生;
        // 分析
        // var f = object.getNameFunc();
        // var f = function () {
        //     return that.name;//
        // };
        // f();
    </script>

闭包总结

闭包是什么?

  • 闭包是一个函数(一个作用域可以访问另外一个函数的局部变量);(这个变量所在的函数就是一个闭包函数)
  • 简单理解:嵌套函数可以访问父级函数的变量,故return嵌套函数就可以在全局环境访问该变量;
  • 闭包的形成:函数嵌套+内函数调外函数的局部变量;

闭包的作用是什么?

  • 延伸变量的作用范围;

递归

什么是递归?

  • 如果一个函数在内部可以调用其本身,那么这个函数就是递归函数
  • 简单理解:函数内部自己调用自己,这个函数就是递归函数;
  • 递归函数的作用和循环效果一样;
  • 由于递归很容易发生“栈溢出”错误(stack overflow),所以必须要加退出条件return
<script>
        // 递归函数:函数内部自己调用自己,这个函数就是递归函数;
        var num = 1;
        function fn() {
            console.log('我要打印6句话');
            if (num === 6) {
                return;//递归里面必须加退出条件;
            }
            num++;
            fn();
        }
        fn()
    </script>

利用递归求阶乘

<script>
        // 1、求1*2*3...*n 阶乘
        function fn(n) {
            if (n === 1) {
                return 1;
            }
            return n * fn(n - 1);
        }
        console.log(fn(3));
    </script>

利用递归求斐波那契数列

<script>
        // 利用递归函数求斐波那契数列(兔子序列)1、1、2、3、5、8、13、21...
        // 用户输入一个数字n就可以求出这个数字对应的兔子序列值;
        // 我们只需要知道用户输入的n的前面两项(n-1 )就可以计算出n对应的序列值;
        // 这种方法效率较低,输入的数值较大时直接卡死;
        function fb(n) {
            if (n === 1 || n === 2) {// 用户输入1或2时,序列值都是1;
                return 1;
            }
            return fb(n - 1) + fb(n - 2);
        }
        console.log(fb(1));
        // console.log(fb(6));
    </script>

利用递归求:根据ID返回对应的数据对象

<body>
    <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); // 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>
</body>

浅拷贝和深拷贝

  • 浅拷贝只是拷贝一层,更深层次对象级别的只拷贝引用;
  • 深拷贝拷贝多层,每一级别的数据都会拷贝;
  • Object.assign(target,...sources) ES6新增方法可以浅拷贝;
<script>
        // 浅拷贝只是拷贝一层,更深层次对象级别的只拷贝引用;
        // 深拷贝拷贝多层,每一级别的数据都会拷贝;
        var obj = {
            id: 1,
            name: 'andy',
            msg: {
                age: 18
            }
        };
        var o = {};
        // for (var k in obj) {
        //     // k 是属性名;obj[k] 是属性值;
        //     o[k] = obj[k];
        // }
        // console.log(o);
        // o.msg.age = 90;
        // console.log(obj);
        console.log('--------------');
        Object.assign(o, obj);//浅拷贝的语法糖
        console.log(o);
        
		// 数组也属于Object
        var arr = [];
        console.log(arr instanceof Object); //true
    </script>

正则表达式

  • 正则表达式(Regular Expression)是用于匹配字符串中字符组合的模式。在javaScript中,正则表达式也是对象;
  • 正则表达式通常被用来检索、替换那些符合某个模式(规则)的文本,例如:验证表单:用户名表单只能输入英文字母、数字或者下划线,昵称输入框中可以输入中文(匹配)。此外,正则表达式还常用于过滤掉页面内容中的一些敏感词(替换),或从字符串中获取我们想要的特定部分(提取)等等;
  • 其他语言也会使用正则表达式,本阶段我们主要利用javaScript正则表达式完成表单验证;

创建正则表达式

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

1、通过调用RegExp对象的构造函数创建

var 变量名=new RegExp(/表达式/)

2、通过字面量创建

var 变量名=/表达式/
  • //注释中间放表达式就是正则字面量;

测试正则表达式test

  • test()正则对象方法,用于检测字符串是否符合该规则,该对象会返回truefalse,其参数是测试字符串;
  • regexObj.test(str)

正则表达式中的特殊字符

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

边界符

  • 正则表达式中的边界符(位置符)用来提示字符所处的位置,主要有两个字符;
    -
  • 如果^$在一起,表示必须是精确匹配;
<script>
        // 边界符 ^ $
        var rg = /abc/; //正则表达式里面不需要加引号,不管是数字型还是字符串型;
        // /abc/只要包含有abc这个字符串返回的都是true;
        console.log(rg.test("abc"));//true
        console.log(rg.test("abcd"));//true
        console.log(rg.test("aabcd"));//true

        console.log("------------");

        var reg = /^abc/;//以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字符串才符合规范;即以abc开头,又以abc结尾;
        
    </script>

字符类

  • 字符类表示有一系列字符可供选择,只要匹配其中一个就可以了。所有可供选择的字符都放在方括号内;
  • [-] 方括号内部范围符-
  • [^]方括号内部 取反符 ^
<script>
        // 字符类:[] 表示有一系列字符可供选择,只要匹配其中一个就可以了;
        var rg = /[abc]/;//只要包含有a 或者 包含有b 或者包含有c 都返回为true;
        console.log(rg.test('andy'));//字符串andy里面包含有a,所以打印结果为true;

        var reg = /^[abc]$/; //三选一 只有是a 或者是b 或者是c 这三个字母才返回true;

        var rg1 = /^[a-z]$/;//26个英文字母任何一个字母返回true;-表示的是a到z的范围;

        // 字符组合
        var reg2 = /^[a-zA-Z]$/;//在小写和大写的26个英文字母中任选一个都可以;
        var reg3 = /^[a-zA-Z0-9]$/;//小写和大写的26个英文字母、0-9的数字
        var reg4 = /^[a-zA-Z0-9_]$/;//小写和大写的26个英文字母、0-9的数字、_

        // 如果中括号里面有^,表示取反的意思,千万别和边界符^混淆;
        var reg5 = /^[^a-zA-Z0-9_-]$/;//第一个^是边界符,表示以它开头;第二个^表示取反,即不能包含26个大写和小写英文字母和_-,

    </script>

量词符

  • 量词符用来设定某个模式出现的次数;
    -
<script>
        // 量词符:用来设定某个模式出现的次数;
        // 简单理解:就是让下面的a这个字符重复多少次;
        var reg = /^a$/;

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

        // + 相当于 >= 1,可以出现1次或者很多次;

        // ? 相当于 1 || 0;

        // {3 } 就是重复3次;
        var reg2 = /^a{3}$/;

        // {3, } 大于等于3;

        // {3,16} 大于等于3 并且 小于等于16;

    </script>
  • 量词重复某个模式的次数;
//前面的模式可以重复6到16次;
var reg=/^[a-zA-Z0-9_-]{6,16}/

案例:用户名验证

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        span {
            color: #aaa;
            font-size: 14px;
        }

        .right {
            color: green;
        }

        .wrong {
            color: red;
        }
    </style>
</head>

<body>
    <!-- 
        功能需求:
          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>
</body>

</html>

括号总结

  • 大括号 量词符。 里面表示重复次数;
  • 中括号 字符集合。 匹配方括号中的任意字符;
  • 小括号 表示优先级;
<script>
        // 中括号 字符集合。 匹配方括号中的任意字符;
        var reg = /^[abc]$/; // a也可以,b也可以,c也可以;

        // 大括号 量词符。 里面表示重复次数;
        var reg1 = /^abc{3}$/; //它只是让c重复3次;abccc
        console.log(reg1.test(abc));//false
        console.log(reg1.test(abcabcabc));//false
        console.log(reg1.test(abccc));//true

        // 小括号 表示优先级;
        var reg2 = /^(abc){3}$/;//把abc重复3次;
    </script>

预定义类以及座机号码验证

  • 预定义类:指的是某些常见模式的简写方式;
    -
<script>
        // 座机号码验证:全国座机号码 两种格式:010-12345678 或者 0530-1234567
        // 正则里面的“或者”符号 |
        var reg = /^\d{3}-\d{8}|\d{4}-\d{7}$/;
        var reg1 = /^\d{3,4}-\d{7,8}$/;
    </script>

案例:表单验证

window.onload = function () {
    var regtel = /^1[3|4|5|7|8]\d{9}$/; // 手机号码的正则表达式;
    var regqq = /^[1-9]\d{4,}$/; // QQ号1000;
    var regnc = /^[u4e00-\u9fa5]{2,8}$/;//昵称
    var regmsg = /^\d{6}$/;//短信验证码,由6位数字组成;
    var regpwd = /^[a-zA-Z0-9]{6,16}$/;//密码

    var tel = document.querySelector('#tel');
    var qq = document.querySelector('#qq');
    var nc = document.querySelector('#nc');
    var msg = document.querySelector('#msg');
    var pwd = document.querySelector('#pwd');
    var surepwd = document.querySelector('#surepwd');

    regexp(tel, regtel);//手机号码验证
    regexp(qq, regqq);//QQ号码验证
    regexp(nc, regnc);//昵称验证
    regexp(msg, regmsg);//短信验证码
    regexp(pwd, regpwd);//密码框验证

    // 表单验证的函数封装
    function regexp(ele, reg) {
        ele.onblur = function () {
            if (reg.test(this.value)) {
                console.log('正确的');
                this.nextElementSibling.className = 'success';//nextElementSibling下一个兄弟
                this.nextElementSibling.innerHTML = '<i class="success_icon"></i>恭喜你输入正确';
            } else {
                console.log('错误的');
                this.nextElementSibling.className = 'error';//nextElementSibling下一个兄弟
                this.nextElementSibling.innerHTML = '<i class="error_icon"></i>输入错误';
            }
        }
    }
    // 确认密码
    surepwd.onblur = function () {
        if (this.value === pwd.value) {
            this.nextElementSibling.className = 'success';//nextElementSibling下一个兄弟
            this.nextElementSibling.innerHTML = '<i class="success_icon"></i>恭喜你输入正确';
        } else {
            this.nextElementSibling.className = 'success';//nextElementSibling下一个兄弟
            this.nextElementSibling.innerHTML = '<i class="error_icon"></i>两次输入密码不一致';
        }
    }
}

正则表达式中的替换 replace()

  • replace()方法可以实现替换字符串操作,用来替换的参数可以是一个字符串或是一个正则表达式;
  • stringObject.replace(regexp/substr,replacement)
  • 第一个参数:被替换的字符串 或者 正则表达式;
  • 第二个参数:替换为的字符串;
  • 返回值是一个替换完毕的新的字符串;

正则表达式参数

  • /表达式/[switch]
  • switch(也称为修饰符)按照什么样的模式来匹配,有三种值:
  • g:全局匹配;
  • i:忽略大小写;
  • gi:全局匹配 + 忽略大小写;
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        div {
            width: 100px;
            height: 50px;
            background: pink;
        }
    </style>
</head>

<body>
    <textarea name="" id="message" cols="30" rows="10"></textarea>
    <button>提交</button>
    <div></div>
    <script>
        // 替换 replace()
        var str = 'andy和red';
        //var newStr = str.replace('andy', 'bady');//第一个参数是被替换掉的,第二个参数是替换为的;
        var newStr = str.replace(/andy/, 'bady');
        console.log(newStr);//bady和red

        var text = document.querySelector('textarea');
        var btn = document.querySelector('button');
        var div = document.querySelector('div');
        btn.onclick = function () {
            // 注意:replace() 只能替换第一个满足条件的字符串,需要替换掉的字符串出现多次,它也只替换第一个;
            // div.innerHTML = text.value.replace(/激情/, '**');
            div.innerHTML = text.value.replace(/激情|gay/g, '**');//替换所有的“激情”和“gay”两个敏感词;
        }
    </script>
</body>

</html>

ES6

  • ES的全称是ECMAScript,它是由ECMA国际标准化组织,制定的一项脚本语言的标准化规范。

var let const关键字

面试题一

<script>
        var arr = [];
        for (var i = 0; i < 2; i++) {//for循环里用 var 申明变量,是一个全局变量;
            arr[i] = function () {
                console.log(i);
            }
        }
        // arr[0] 和 arr[1] 是两个函数,下面两句代码是在调用数组中的函数;
        arr[0]();//2 函数执行时在自己的作用域中找不到i值,根据作用域链会到上级作用域中去查找;
        arr[1]();//2
        // 当循环条件为 0 和 1 时,进入循环体;当循环条件为2时才跳出循环体,开始执行循环体后面的代码;
        // 注意:当i=0 和 i=1时,函数并没有执行;
        // 此题的关键点在于:变量i是全局的,函数执行时输出的都是全局作用域下的i值;
        // 当i=0 和i=1 时,arr数组中有两个值,这两个值都是 function () {console.log(i);}
    </script>

在这里插入图片描述

面试题二

<script>
        let arr = [];
        for (let i = 0; i < 2; i++) {
            arr[i] = function () {
                console.log(i);
            }
        }
        // arr[0] 和 arr[1] 是两个函数,下面两句代码是在调用数组中的函数;
        arr[0]();//0 
        arr[1]();//1
        // 此题的关键点在于:每次循环都会产生一个块级作用域,每个块级作用域中的变量都是不同的;
        // 函数执行时,输出的是自己上一级(循环产生的块级作用域)作用域下的i值;
    </script>

const关键字

  • 作用:声明常量,常量就是值(内存地址)不能变化的量;
  • 声明常量时必须赋初始值;
  • 常量赋值后,值(基本数据类型)不能更改;
  • 常量赋值后,复杂数据类型内部的值可以更改;
    -

解构赋值(分解数据结构,为变量赋值)

  • ES6中允许从数组中提取值,按照对应位置,对变量赋值。对象也可以实现解构;

数组解构

<script>
        // 数组解构允许我们按照一一对应的关系从数组中提取值,然后将值赋值给变量;
        let arr = [1, 2, 3]
        let [a, b, c, d, e] = arr;//a、b、c和arr中的1、2、3是一一对应的关系;
        console.log(a);//1
        console.log(b);//2
        console.log(c);//3

        // 如果解构不成功,变量的值为undefined;
        console.log(d);//undefined
        console.log(e);//undefined
    </script>

对象解构

<script>
        // 对象解构允许我们使用变量的名字匹配对象的属性;匹配成功,将对象的属性值赋值给变量;
        let person = { name: '张三', age: 20 };

        // let { name, age } = person;
        // console.log(name);//张三
        // console.log(age);//20

        let { name: myName } = person;//此处的name只是用来属性匹配;此处的myName才是真正的变量;
        console.log(myName);//张三
    </script>

箭头函数

  • ES6中新增的定义函数的方式;
  • 箭头函数是用来简化函数定义语法的;
()=>{}
const fn=()=>{} //通常将箭头函数赋值给一个变量;
fn()//调用箭头函数
  • 函数体中只有一句代码,且代码的执行结果就是返回值,可以省略大括号;
  • 如果形参只有一个,可以省略小括号;
<script>
        // 普通函数写法;
        function sum(num1, num2) {
            return num1 + num2;
        }
        // 箭头函数写法
        const sum = (num1, num2) => {
            return num1 + num2
        };
        // 1、在箭头函数中,如果函数体中只有一句代码,并且代码的执行结果就是函数的返回值,函数体 大括号 和 return 可以省略;
        const sum = (num1, num2) => num1 + num2;

        // 2、如果形参只有一个,可以省略小括号;
        function fn(v) {
            return v;
        }
        const fn = v => v;
    </script>
  • 箭头函数中的this关键字
  • 箭头函数不绑定this关键字,箭头函数中的this,指向的是函数定义位置的上下文this;即箭头函数被定义在哪,箭头函数中的this就指向哪;
  • 箭头函数经典面试题:
 <script>
        var obj = {
            age: 20,
            say: () => {
                alert(this.age);
            }
        }
        obj.say();//全局作用域下没有age属性,所以是undefined;
    </script>

剩余参数

  • 剩余参数语法允许我们将一个不定数量的参数表示为一个数组;
<script>
        function sum(first, ...args) {
            console.log(first);//10
            console.log(args);//[20,30]
        }
        sum(10, 20, 30);

        // 注意:在箭头函数中,使用不了arguments;
        const sum = (...args) => {
            let total = 0;
            args.forEach(item => total += item)
            return total;
        }
        console.log(sum(10, 20));//30
        console.log(sum(10, 20, 30));//60
</script>
  • 剩余参数和解构配合使用:
<script>
        let students = ['王五', '张三', '李四'];
        let [s1, ...s2] = students;// s1 接收'王五';...s2 接收'张三', '李四',注意 s2 是一个数组;
        console.log(s1);//'王五'
        console.log(s2);//['张三','李四']
</script>

Array的扩展方法

扩展运算符(展开语法)

  • 扩展运算符可以将数组或者对象转为用逗号分隔的参数序列;
  • console.log();打印结果时,逗号会解析为 console.log();里多个元素的分隔符;
<script>
        let arr = [1, 2, 3];
        // ...arr --> "a","b","c"
        console.log(...arr);//1 2 3
    </script>

扩展运算符应用

  • 用于【合并数组】

构造函数方法Array.from()

 <script>
        // 扩展运算符
        // let arr = [1, 2, 3];
        // ...arr --> "a","b","c"
        // console.log(...arr);//1 2 3

        // 扩展运算符应用

        // 1、用于 合并数组;
        // 合并数组方法1:
        let arr1 = [1, 2, 3];
        let arr2 = [4, 5, 6];
        // ...arr1 //1,2,3
        //...arr2  //4,5,6
        // let arr3 = [...arr1, ...arr2]
        // console.log(arr3);//[1, 2, 3, 4, 5, 6]

        // 合并数组方法2:
        arr1.push(...arr2);
        console.log(arr1); //[1, 2, 3, 4, 5, 6]

        // 2、将 类、数组 或者 可遍历对象转换为真正的数组;
        let oDivs = document.getElementsByTagName('div');
        oDivs = [...oDivs];
        // Array.from 方法(将伪数组转换为真正的数组);
        var arrayLike = {
            "0": "张三",
            "1": "李四",
            "2": "王五",
            "length": 3
        }
        var ary = Array.from(arrayLike);
        console.log(ary);//['张三', '李四', '王五']
    </script>
  • 该方法还可以接受第二个参数,作用类似于数组的map方法,用来对每个元素进行处理,将处理后的值放入返回的数组;
 // Array.from 还可以接收第二个参数,第二个参数是一个函数,对数组中的元素进行加工处理,数组中有多少个元素,该函数就会被调用多少次;形参item代表当前要处理的那个值;
        let arrayLike = {
            "0": 1,
            "1": 2,
            "length": 2
        }
        let newAry = Array.from(arrayLike, item => item * 2);
        console.log(newAry);//[2, 4]

Array.find()方法

  • 用于找出第一个符合条件的数组成员,如果没有找到返回undefined
 <script>
        let ary = [{
            id: 1,
            name: '张三'
        }, {
            id: 2,
            name: '李四'
        }];
        let target = ary.find((item, index) => item.id == 2);
        console.log(target);//{id: 2, name: '李四'}
    </script>

Array.findIndex()方法

  • 用于找出第一个符合条件的数组成员的位置,如果没有找到返回 -1;
<script>
        let arr = [1, 5, 10, 15];
        let index = arr.findIndex((value, index) => value > 9);
        console.log(index);//2
    </script>

Array.includes()方法

  • 表示某个数组是否包含给定的值,返回布尔值;
[1,2,3].includes(2);//true
[1,2,3].includes(5);//false

string的扩展方法

1、模版字符串

  • ES6新增的创建字符串的方式,使用反引号定义;
  • 模版字符串中可以解析变量;
  • 模版字符串中可以换行;
let result={
	name:'zhangsan',
	age:20,
	sex:'男'
};
let html=`<div>
	<span>${result.name}</span>
	<span>${result.age}</span>
	<span>${result.sex}</span>
</div>
console.log(html);
`
  • 在模版字符串中可以调用函数;
const sayHello = function () {
   return '哈哈哈哈哈哈';
};
let greet = `${sayHello()}aaaa`;
console.log(greet);

2、startsWith()endsWidth()

  • startsWith():表示参数字符串是否在原字符串的头部,返回布尔值;
  • endsWidth():表示参数字符串是否在原字符串的尾部,返回布尔值;

3、repeat()方法

  • repeat()方法表示将原字符串重复n次,返回一个新字符串;
'x'.repeat(3);//'xxx'
'hello'.repeat(2);//'hellohello'

Set数据结构

  • ES6提供了新的数据结构Set。它类似于数组,但是成员的值都是唯一的,没有重复的值;
  • 应用:用于网站存储用户搜索历史关键字;
  • Set本身是一个构造函数,用来生成Set数据结构;
  • const s = new Set();
  • Set函数可以接受一个数组作为参数,用来初始化;
  • const set = new Set([1,2,3,4,4])
  • 利用Set数据结构做数组去重:
<script>
        const s1 = new Set();
        console.log(s1.size);//0

        const s2 = new Set(['a', 'b']);
        console.log(s2.size);

        const s3 = new Set(['a', 'a', 'b', 'b']);
        console.log(s3);//Set(2) {'a', 'b'}
        console.log(s3.size);//2 Set会把重复的值给过滤掉;
        const arr = [...s3];
        console.log(arr);//['a', 'b']
    </script>

Set对象实例方法

  • add(value):添加某个值,返回Set结构本身;
  • delete(value):删除某个值,返回一个布尔值,表示删除是否成功;
  • has(value):返回一个布尔值,表示该值是否为Set的成员;
  • clear():清除所有成员,没有返回值;
const s = new Set();
s.add(1).add(2).add(3);

遍历Set

  • Set结构的实例与数组一样,也拥有forEach方法,用于对每个成员执行某种操作,没有返回值;
  • s.forEach(value=>console.log(value))
 const s = new Set(['a', 'b', 'c', 'a', 'a']);
        s.forEach(element => {
            console.log(element);//a b c
        });

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/579802.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【PowerQuery】M语言的使用产品和使用场景

当然PowerQuery的M语言应用场景不只是引用在PowerBI和Excel中,它具有广泛的应用场景。目前我们可以在以下产品的使用场景中应用到M语言。 Excel PowerQuery应用Excel通过M语言可以实现整体数据的清洗和重构。  PowerBI 的PowerQuery应用 PowerBI也是通过M语言来实现数据…

VSCODE 插件推荐

文章目录 项目管理Project Manager markdownmarkdown-pdfPaste ImageMarkdown Preview Enhanced 笔记Notes 思维导图vscode-mindmap 开发Visual Studio IntelliCode GitHub Repositories 项目管理 Project Manager 以下是项目管理器提供的一些功能&#xff1a; 将任何文件夹…

JS中this的指向

JS中this的指向 本文目录 JS中this的指向全局上下文&#xff08;Global Context&#xff09;函数上下文&#xff08;Function Context&#xff09;普通函数调用作为对象的方法调用构造函数调用箭头函数回调函数 事件处理器上下文&#xff08;Event Handler Context&#xff09;…

find_package深度解析及实例应用

1. 检索模式 1.1 module模式 在这个模式下会查找一个名为find.cmake的文件&#xff0c;首先去CMAKE_MODULE_PATH指定的路径下去查找&#xff0c;然后去cmake安装提供的查找模块中查找&#xff08;安装cmake时生成的一些cmake文件&#xff09;。找到之后会检查版本&#xff0c;…

大模型全情投入,低代码也越来越清晰

众所周知&#xff0c;不少互联网企业在大模型领域全情投入。那么在这阵阵浪潮中&#xff0c;我们可以观察到什么样的“众生相”&#xff1f; 今年3月以来&#xff0c;国内已有超过20家企业入局大模型赛道。从百度“文心一言”、阿里“通义千问”的发布&#xff0c;华为“盘古”…

解决Kali的Python版本切换问题以及pip2安装问题

问题背景 需要使用Python2版本运行脚本&#xff0c;但是Kali系统自从2021后Python2&#xff0c;3共存 解决方案 &#xff08;1&#xff09;打开终端输入以下命令&#xff0c;但是需要有root权限 update-alternatives --install /usr/bin/python python /usr/bin/python2 100…

k8s介绍

目录 1&#xff1a;k8s概念 2&#xff1a;为什么引入k8s和k8s特性 2.1 为什么要引入k8s&#xff1a; 2.2 k8s特性 3 K8S架构 1&#xff1a;k8s概念 k8s官方网站&#xff1a;Kubernetes Kubernetes 是一个可移植、可扩展的开源平台&#xff0c;用于管理容器化的工作负载和…

数据库基础——5.运算符

这篇文章我们来讲一下SQL语句中的运算符操作。 说点题外话&#xff1a;SQL本质上也是一种计算机语言&#xff0c;和C&#xff0c;java一样的&#xff0c;只不过SQL是用来操作数据库的。在C&#xff0c;java中也有运算符&#xff0c;这两种语言中的运算符和数学中的运算符差距不…

【形形色色的卷积】差分卷积

文章目录 0. 前言1. 中心差分卷积2. 像素差分卷积3. 参考 0. 前言 普通卷积不能显式地提取图像的梯度信息&#xff0c;因此不能较好地描述细粒度的纹理信息&#xff0c;在人脸活体检测、边缘检测等对细粒度纹理信息敏感的任务中难以取得理想的结果。针对上述问题&#xff0c;O…

基于STM32的定时器--定时中断(HAL库)

基于STM32的定时器--定时中断&#xff08;HAL库&#xff09; 介绍引言定时器介绍 实例项目介绍准备设计流程 介绍 引言 本文旨在介绍如何使用STM32CubeMX配置KEIL 5开发一个每10us定时器中断触发一次的项目。帮助初学者入门STM32的定时器使用。 定时器介绍 定时器是STM32微…

2.信息安全之常用黑客攻击手段

1.自己也可以建CA系统 winserver2003 https 准备2台机器xpsp3(证书,Web),在同一网段,https通讯 –>网络内部网络(同一网段) (iso镜像才有网络和服务…这个镜像已 经安装好了(不能乱删)) sp1 2 3 的网络 改 tcp/ip 属性 ip地址112.26.0.1 和子网掩码255.0.0.0(与ip地址对应,…

opencv_c++学习(二十七)

一、单目相机模型 上图为针孔相机成像原理&#xff0c;蓝色坐标中的O即为镜头光心。成像原理与小孔成像相同。 单目相机映射关系如下&#xff1a; 将上式进行变换&#xff0c;就可以从三位空间映射到2维平面的公式。 相机的畸变公式如下&#xff1a; 二、模型投影函数 vo…

数据结构基础内容-----第五章 串

文章目录 串串的比较串的抽象数据类型串的顺序存储结构朴素的额模式匹配算法kmp模式匹配算法 串 在计算机编程中&#xff0c;串&#xff08;String&#xff09;是指由零个或多个字符组成的有限序列。它是一种基本的数据类型&#xff0c;在许多编程语言中都得到了支持和广泛应用…

STM32之SPI和W25Q128

目录 SPI 介绍 SPI 物理架构 SPI 工作原理 SPI 工作模式 W25Q128 介绍 W25Q128 存储架构 W25Q128 常用指令 W25Q128 状态寄存器 W25Q128 常见操作流程 实验&#xff1a;使用 SPI 通讯读写 W25Q128 模块 硬件接线 cubeMX配置 w25q128_write_nocheck流程图 代码&a…

如何在华为OD机试中获得满分?Java实现【最长回文子串】一文详解!

✅创作者&#xff1a;陈书予 &#x1f389;个人主页&#xff1a;陈书予的个人主页 &#x1f341;陈书予的个人社区&#xff0c;欢迎你的加入: 陈书予的社区 &#x1f31f;专栏地址: Java华为OD机试真题&#xff08;2022&2023) 文章目录 1. 题目描述2. 输入描述3. 输出描述…

网络原理(八):HTTPS

目录 HTTP 基本工作流程 利用对称密钥进行加密 利用非对称密钥进行加密 引入了第三方权威机构加密 之前在http 协议中说到&#xff1a;我们现在很少有网站直接使用HTTP 协议的&#xff0c;而是使用HTTPS &#xff0c;至于什么原因&#xff0c;本篇会介绍清楚。 HTTPS 其实…

C++11 -- lambda表达式

文章目录 lamaba表达式的引入lambda表达式语法lamabda达式各部分说明捕获列表说明 lamaba表达式底层原理探索 lamaba表达式的引入 在C11之前,如果我们想对自定义类型Goods排序,可以根据姓名,价格,学号按照从大到小或者从小到大的方式排序,可是,这样我们要写额外写6个相关的仿函…

以太坊学习三: Merkle树和验证

Merkle tree简介 Merkle树又称为哈希树&#xff0c;是一种二叉树&#xff0c;由一个根节点、若干中间节点和一组叶节点组成。最底层的叶节点存储数据&#xff0c;在它之上的一层节点为它们对应的Hash值&#xff0c;中间节点是它下面两个子节点的Hash值&#xff0c;根节点是最后…

DAY 66 数据库缓存服务——NoSQL之Redis配置与优化

缓存概念 缓存是为了调节速度不一致的两个或多个不同的物质的速度&#xff0c;在中间对速度较慢的一方起到加速作用&#xff0c;比如CPU的一级、二级缓存是保存了CPU最近经常访问的数据&#xff0c;内存是保存CPU经常访问硬盘的数据&#xff0c;而且硬盘也有大小不一的缓存&am…

爆肝整理,最全单元测试-测试用例总结(全覆盖)及拿即用...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 Python自动化测试&…