es6深入浅出学习笔记

面向对象编程介绍

两大编程思想

面向过程编程 POP(process-oriented programming)

  • 面向过程就是分析解决问题所需要的步骤,然后用函数把这些步骤一步步的实现,使用的时候在一个个的依次调用就可以了。
  • 例子:把大象装进冰箱
    • 打开冰箱门
    • 把大象放进冰箱
    • 关上冰箱门
  • 面向对象的特性:
    • 封装性(盒子)
    • 继承性(子承父业)
    • 多态性(同一个对象在不同的时刻可以体现出不同的状态)
  • 优点:
    • 面向对象编程具有灵活,代码可复用,容易维护和开发的优点,更适合多人合作的大型软件项目

面向对象 OOP(Object Oriented Programming)

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

  • 例子:把大象装进冰箱

    • 找出对象,并写出这些对象的功能(大象,冰箱)
    • 使用大象和冰箱的功能
      • 面向对象是以对象的功能来划分问题,而不是步骤
  • 区别

    • 面向过程
      • 优点:性能比面向对象高,适合跟硬件联系很紧密的东西,例如单片机就采用面向过程编程
      • 缺点:没有面向对象容易维护,易复用,易拓展
    • 面向对象
      • 优点:易维护,易复用,由于面向对象有封装,继承,多态的特性,可以设计出低耦合的系统,使系统更加灵活,更加易于维护
        • 内聚就是指程序内的各个模块之间的关系紧密程度,耦合就是各个外部程序(子程序)之间的关系紧密程度.
      • 缺点:性能比面向过程低
    • 面向过程制作出来的程序就是一份蛋炒饭,面向对象写出来的程序就是一份盖浇饭

Es6的类和对象

面向对象的思维特点:

  • 抽取(抽象)对象公共的属性和行为组织(封装)成一个类(模板)
  • 对类进行实例化,获取类的对象

对象:

  • 对象是一个具体的事物。在JavaScript中,对象是一组无序的相关属性和方法的集合,所有的事物都是对象。

  • 对象是由属性和方法组成的

  • 属性:事物的特征,在对象中用属性来表示

  • 方法:事物的行为,在对象中用方法来表示

类:在es6中增加了类的概念,可以使用class关键字声明一个类。

  • 类抽象了对象的公共部分,它泛指一个大类
  • 对象特指某一个,通过类来实例化一个具体的对象

创建一个类和对象:

class gender{
    
}
  • 对象
new gender();

类中constructor构造函数

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

//创建类
class Start{
    //构造函数
    constructor(name){
        // this就是创建的实例
        this.name = name;
    }
}
// 利用类创建对象
var people = new Start('卑微小陈');	//调用类时,只要加了参数,将自动调用构造函数方法
//使用
console.log(people.name);	//在浏览器中打印输出 卑微小陈

注意:

  • 通过class挂机案子创建类。类名首字母大写
  • 类里面的构造函数,可以接受传递过来的参数,同时返回实例对象
  • 构造函数 只要 new 生成实例时,就会自动调用这个函数,如果不写这个函数,类也会自动生成这个函数
  • 生成实例 new 关键字不能省略
  • 创建类后面不要加小括号,生成实例,类名后面加小括号,构造函数不需要加function

类中添加方法

//创建类
class Start{
    //构造函数
    constructor(name){
        // this就是创建的实例
        this.name = name;
    }
    //方法
    // 类里面的所有的函数不需要写关键字function
    // 多个函数方法之间不需要添加逗号分隔
    taking(content){
        console.log(this.name + content);
    }
}
// 利用类创建对象
var people = new Start('卑微小陈说:'); 
people.taking('上帝撒了一把智慧,而我却打了一把伞~')
console.log(people);

类的继承

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

// 父类
class Father{
    money(){
        console.log(10000000)
    }
}
// 子类	继承(extend) 父类
class Son extend Father{
    
}
var son = new Son();
son.money();	//打印输出10000000

super关键字

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

calss 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(2,4);
son.sum();	//打印输出6

注意:

  • 继承中,如果实例化字类输出一个方法,先看子类有没有这个方法,如果有就执行字类的
  • 继承中,如果子类里面没有,就去查找父类有没有这个方法,如果有,就执行父类的这个方法(就近原则)
  • 继承中的属性和方法的查找原则:就近原则

字类类可以调用父类的方法:

class Father{
    say(){
        return '我是爸爸';
    }
}
class Son extends Father{
    say(){
        // 打印输出 字类 的say方法
        // console.log('我是儿子');
        //子类调用父类里面的方法
        // super.say()就是调用父类中的普通函数say这个函数
        console.log(super.say() + '的儿子');
    }
}
var son = new Son();
son.say();

子类继承父类加法方法 同时 扩展减法方法

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;
    }
    substract(){
        console.log(this.x - this.y);
    }
}
var son = new Son(5,2);
//调用子类中的substract方法
son.substract();	//打印输出3
//调用子类从父类中继承的sum方法
son.sum();	//打印输出7

注意

  • 在Es6中类没有变量提升,所以必须先定义类,才能通过类实例化对象
  • 类里面的共有的属性和方法一定要加this使用
  • 类里面的this的指向的问题
  • constructor里面的this指向实例对象,方法里面的this指向这个方法的调用者
<body>
    <button>点我</button>
	<script>
    	var that;
		var _that;
    	class Strat{
            // construcror 里面的this指向的是创建的实例对象
            constructor(name,age){
                that = this;
                console.log(that);	//打印输出实例对象	‘华晨宇’
                
                this.name = name;
                this.age = age;
                //调用Sing方法
                //this.sing();
                //通过按钮点击事件来调用sing方法
                this.btn = document.queryString('button');	//通过标签选择器选中按钮
                this.btn.onclick = sing;	//注意sing方法后面不要加小括号哟,加小括号后立马调用sing方法
            }
            sing(){
                //sing方法里面的this指向的是btn这个按钮,因为btn调用了sing这个方法
                //console.log(this.name);	//输出undefined
                
                console.log(that.name);	//输出华晨宇
            }
            dance(){
                //这个dance里面的this指向的是实例对象 people,因为people调用了这个函数
                _that = this;
                console.log(_that);	//打印输出华晨宇
            }
        }
		// 实例化Strat方法
		var people = new Strat('华晨宇');
		people.dance();
    </script>    
</body>

构造函数和原型

在典型的OOP(面向对象)的语言中(如java),都存在类的概念,类就是对象的模板,对象就是类的实例,但是在ES6之前,js并没有引入类的概念

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

创建对象的方式:

  1. 对象字面量
  2. new Object()
  3. 自定义构造函数
// 对象字面量
var obj1 = {};

// new Object()
var onj2 = new Object();

//自定义构造函数
function Start(name,age){
    this.name = name;
    this.age = age;
}
//通过构造函数生成不同的对象
var sin = new Start('易烊千玺',20);

构造函数

构造函数是一种特殊的函数,主要用来出啊实话对象,即对象成员变量赋初始值,它总是与new一起使用。我们可以把对象中一些公共的属性和方法抽取出来,然后封装到这个函数里面。

在js中,使用构造函数时要注意以下两点:

  1. 构造含糊用于创建某一类对象,其首字母大写
  2. 构造函数要和new一起使用才有意义。

new在执行时会做的四件事情:

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

构造函数中的属性和方法我们成为成员,成员可以添加

成员可以分为:实例成员和静态成员

  • 实例成员

    • 就是构造函数内部通过this添加的成员
    • 实例成员只能通过实例化对象来访问(sin.name)
  • 静态成员

    • 在构造函数本身添加的成员(Start.sex = ‘女’)
    • sex就是静态成员
    • 静态成员只能通过构造函数来访问(console.log(Start.sex))

构造函数的问题

构造函数的方法很好用,但是存在浪费内存的问题。[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qymgR9lF-1585145600549)(C:\Users\A\AppData\Roaming\Typora\typora-user-images\image-20200321115420071.png)]

构造函数原型 prototype

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

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

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

  • 原型是什么?
    • 一个对象,我们也称为prototype为原型对象
  • 原型的主要作用是什么?
    • 共享方法
  • 一般情况下。我们的公共属性定义到构造(constructor)函数里面,公共的方法我们放到原型(prototype )对象上面

对象原型__proto__

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

  • __proto__对象原型和原型对象prototype是等价的

constructor构造函数

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

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

function Start(name,age){
    this.name = name;
    this.age = age;
}
//Start.prototype.sing = function(){
//    console.log('唱歌');
//}
//很多情况下,我们需要手动的利用constructor这个属性 指回 原来的构造函数
Start.prototype = {
    //让构造函数指回原来的构造函数
    //如果我们修改了原来的原型对象,给原型对象赋值的是一个对象,则必须手动的利用constructor指回原来的构造函数
    constructor:Start,
    sing:function(){
        
    }
    movie:function(){
        
    }
}
var l = new Start('卑微小陈',20);

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

image-20200322111839139

原型链

image-20200322112908327

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

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

原型对象this指向

  • 在构造函数中,里面的this指向的是对象实例
  • 原型对象函数里面的this指向的是 实例对象

扩展内置对象

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

数组的原型对象:

image-20200322114417121

注意:数组和字符串内置对象不能给原型对象覆盖操作Array.prototype = {},只能是Array.prototype.xx = function(){}的方式

console.log(Array.prototype);
// 原型对象的应用 扩展内置对象方法
// 在原来的方法里面追加一个方法
Array.prototype.sum = function() {
	var sum = 0;
	for(var i = 0; i < this.length; i++){
		sum += this[i];
	}
	return sum;
}
// 错误 
// 数组和字符串内置对象不能给原型对象覆盖操作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());
console.log(Array.prototype);

继承

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

call()

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

fun.call(thisArg,arg1,arg2,...)
  • thisArg:当前调用函数this的指向对象
  • arg1,arg2:传递的其他参数
function like(x, y) {
	console.log('想要自由');
	console.log(this); //当前this指向window
	console.log(x + y);
}
var o = {
	name: 'Harty'
};
//调用函数
// like();

// 1 call()可以调用函数
like.call();
// 2 call()可以修改like方法里面的this指向
like.call(o,1,2); // 此时函数的对象就指向了o这个对象 打印输出3

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

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

// 1 父构造函数
			function Father(name,age){
				// this指向父构造函数的对象实例
				this.name = name;
				this.age = age;
			}
			// 2 子构造函数
			function Son(name, age, score){
				// this指向子构造函数的对象实例
				Father.call(this,name,age);	//调用父构造函数,将父构造函数的this指向变成子构造函数的this指向
				// 添加子构造函数自身的数据
				this.score = score;	//score既继承了父构造函数的数据,也有自身的数据
			}
			var son = new Son('卑微小陈', 20, 59);
			console.log(son);

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

// 1 父构造函数
			function Father(name,age){
				// this指向父构造函数的对象实例
				this.name = name;
				this.age = age;
			}
			// 给父构造函数添加方法
			Father.prototype.money = function(){
				console.log(100000);
			}
			// 2 子构造函数
			function Son(name, age, score){
				// this指向子构造函数的对象实例
				Father.call(this,name,age);	//调用父构造函数,将父构造函数的this指向变成子构造函数的this指向
				// 添加子构造函数自身的数据
				this.score = score;	//score既继承了父构造函数的数据,也有自身的数据
			}
			// 使用添加的父构造函数的方法
			// 这样直接赋值会有问题,如果修改了子原型对象,父原型对象也会跟着一起修改
			// Son.prototype = Father.prototype;
			Son.prototype = new Father();	//实例父原型对象,然后给子原型对象
			//如果利用对象的形式修改了原型对象,别忘了利用constructor指回原来的构造函数
			Son.prototype.constructor = Son;
			
			// 给子构造函数添加方法(子构造函数专门的方法)
			Son.prototype.worker = function(){
				console.log('程序媛');
			}
			var son = new Son('卑微小陈', 20, 59);
			console.log(son);

类(es6通过类实现面向对象编程)

类的本质

类的本质还是一个函数,我们也可以简单的认为类就是构造函数的另外一种写法。

构造函数的特点:

  • 构造函数有原型对象prototype

  • 构造函数原型对象prototype里面有constructor指向构造函数本身

  • 构造函数可以通过原型对象添加方法

  • 构造函数创建的实例对象有__proto__原型指向构造函数的原型对象

class Start{
    
}
console.log(typeof Start);	//function
// 1)构造函数有原型对象prototype
console.log(Start.prototype);
// 2)构造函数原型对象prototype里面有constructor指向构造函数本身
console.log(Start.prototype.constructor);
// 3)构造函数可以通过原型对象添加方法
Start.prototype.sing = function(){
    console.log('唱歌');
}
// 4)构造函数创建的实例对象有`__proto__`原型指向构造函数的原型对象
var lyf = new Start();
console.dir(lyf);
console.log(lyf.__proto__ === Start.prototype);

类的所有方法都定义在类的prototype属性上

所以es6的类它的绝大部分功能,es5都能做到,新的class写法只是让对象原型的写法更加清晰,更像面型对象的语言

所以es6的类就是语法糖

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

es5新增的方法

  • 数组方法

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

    • forEach()

      array.forEach(function(currentValue,index,arr));
      

      currentVaule:数组当前项的值

      index:数组当前项的索引

      arr:数组对象本身[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xBpYnt9i-1585145600550)(C:\Users\A\AppData\Roaming\Typora\typora-user-images\image-20200322135502793.png)]

    • filter()—过滤器

      array.filter(function(currentValue,index,arr))
      

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

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

      currentValue:数组当前项的值

      index:数组当前项的索引

      arr:数组对象本身[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zpNS9dCT-1585145600551)(C:\Users\A\AppData\Roaming\Typora\typora-user-images\image-20200322140323797.png)]

    • some()

      array.some(function(currentValue,index,arr))
      

      some()方法用于检测数组中的元素是否满足指定条件,通俗的讲就是数组中是否有满足条件的元素

      注意它的返回值是布尔值,如果查找到这个元素,就返回true,如果查找不到,就返回false

      如果找到第一个满足条件的元素,则终止循环,不再继续查找

      currentValue:数组当前项的值

      index:数组当前项的索引

      arr:数组对象本身[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gqfWggoF-1585145600552)(C:\Users\A\AppData\Roaming\Typora\typora-user-images\image-20200322141030520.png)]

  • 字符串方法

    • trim()方法

      • trim()方法会从一个字符喜欢的两端删除空白字符

        str.trim()
        

        trim()方法本身并不影响原字符串本身,它返回的是一个新的字符串

        var str = '    卑微小陈   在线找工作     ';
        console.log(str);
        console.log(str.trim());
        
  • 对象方法

    • Object.defineProperty()定义对象中新属性或修改原有的属性

      Object.defineProperty(obj,prop,descriptor)
      

      obj:必需。目标对象

      prop:必需。需定义或修改的属性的名字

      descriptor:必需。目标属性所拥有的特性,以对象{}的形式书写

      value:设置属性的值。默认undefined

      writeable:值是否可以重写。true|false,默认为false

      enumerable:目标属性是否可以被枚举。true|false,默认false

      configurable:目标属性是否可以被删除或是否可以再次修改特性true|false,默认false

      <!DOCTYPE html>
      <html lang="zh">
      <head>
      	<meta charset="UTF-8">
      	<meta name="viewport" content="width=device-width, initial-scale=1.0">
      	<meta http-equiv="X-UA-Compatible" content="ie=edge">
      	<title></title>
      </head>
      <body>
      	<script type="text/javascript">
      		var obj = {
      			id: 1,
      			name: '小陈',
      			hobby: '代码编程'
      		};
      		// 1 以前的调价和修改属性的方式
      		// obj.age = 20;
      		// console.log(obj);
      		
      		// 2 Object.defineProperty()定义新属性或修改原有的属性
      		Object.defineProperty(obj, 'age', {
      			// 如果里面有age属性就修改操作,没有就做添加操作
      			value: 20	//给obj数组里面,添加一个age属性,并且age属性的属性值为20
      		});
      		console.log(obj);
      		
      		Object.defineProperty(obj, 'id', {
      			writable: false,//值为false,不允许修改这个属性值,默认为false
      		});
      		obj.id = 2;
      		console.log(obj);
      		
      		Object.defineProperty(obj, 'address', {
      			value: '湖南长沙',
      			// enumberable如果为false,则不允许遍历
      			enumberable: false,
      			// configurable如果为false,则不允许被删除和再次修改这个特性,默认为false
      			configurable: false
      		});
      		console.log(obj);
      		console.log(Object.keys(obj));
      		delete obj.address;	//删除address信息
      		console.log(obj);
      		delete obj.hobby;	//删除hobby信息
      		console.log(obj);
      	</script>
      </body>
      </html>
      

小试牛刀之数组方法

根据数组方法来查询数据:

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
		<style type="text/css">
			.content {
				width: 800px;
				height: auto;
				margin: 50px auto;
			}
			.search input{
				margin-left: 10px;
			}
			input[type = 'text']{
				width: 50px;
			}
			table{
				margin-top:30px;
			}
			tbody{
				text-align: center;
			}
		</style>
	</head>
	<body>
		<div class="content">
			<div class="search">
				按照条件查询:<input type="text" class="start"/> - <input type="text" class="end"/> <input type="button" class="search_price" value="搜索" />
				按照商品名称查询:<input type="text" class="select_name"/><input type="button" class="select_btn" value="查询" />
			</div>
			<table border="1px" cellspacing="0" cellpadding="0" width="500px">
				<thead>
					<tr>
						<th>id</th>
						<th>产品名称</th>
						<th>价格</th>
					</tr>
				</thead>
				<tbody>
					
				</tbody>
			</table>
		</div>
		<script type="text/javascript">
			var data = [{
				id: 1,
				name: '华为',
				pice: 10000
			},{
				id: 2,
				name: 'opp',
				pice: 7000
			},
			{
				id: 3,
				name: 'vivo',
				pice: 6666
			},
			{
				id: 4,
				name: 'iPhone',
				pice: 15000
			},
			{
				id: 5,
				name: '小米',
				pice: 7000
			},
			{
				id: 6,
				name: '三星',
				pice: 6500
			}];
			// 获取相应的元素
			var tbody = document.querySelector('tbody');
			// 根据价格查询按钮
			var search_price = document.querySelector('.search_price');
			// 起始价
			var start = document.querySelector('.start');
			// 结束价格
			var end = document.querySelector('.end');
			// 商品名称
			var select_name = document.querySelector('.select_name');
			var select_btn = document.querySelector('.select_btn');
			// 页面加载将所有的数据渲染到页面上面
			setData(data);
			// 渲染页面的方法
			function setData(mydata){
				// 先清空原来tbody的数据
				tbody.innerHTML = '';
				// 把数据渲染到页面中
				mydata.forEach(function(value){
					// console.log(value);	value的值是一个Object
					// 创建一个行
					var tr = document.createElement('tr');
					// 创建单元格
					tr.innerHTML = '' + value.id + '' + value.name + '' + value.pice + '';
					// 将创建的单元格放到tbody中
					tbody.appendChild(tr);
				});
			}
			
			// 根据价格查询商品
			// 当我们点击了按钮,就可以更具我们的商品价格去筛选数组里面的对象
			search_price.addEventListener('click',function(){
				// alert(11)
				var newData = data.filter(function(value){
					// value指的是每个数组元素
					return value.pice >= start.value && value.pice <= end.value;
				})
				// console.log(newData);
				// 将筛选完成之后的数据渲染到页面上面
				setData(newData);
			});
			
			// 根据商品名称来查找数据
			select_btn.addEventListener('click',function(){
				// 因为拿到的数据是一个对象,所以我们要声明一个数组来接收
				var arr = [];
				// 如果查询数组中唯一的元素,用some方法,因为它找到这个元素,就不再进行循环,效率更高
				data.some(function(value){
					if(value.name === select_name.value){
						// console.log(value);
						// 找到对象之后,就将对象添加到数组里面去
						arr.push(value);
						// 如果返回的是true,则证明找到了这个对象
						return true;	// 注意return后面必须写 true,some方法返回的是一个布尔类型
					}
				});
				// 把拿到的数据渲染到页面上面
				setData(arr);
			});
		</script>
	</body>
</html>

forEach,filter和some的区别

<!DOCTYPE html>
<html lang="zh">
<head>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<meta http-equiv="X-UA-Compatible" content="ie=edge">
	<title></title>
</head>
<body>
	<script type="text/javascript">
		var arr = ['red','yellow','blue','pink'];
		<!-- 1.forEach迭代遍历 -->
		// forEach去遍历所有的元素
		arr.forEach(function(value){
			if(value === 'pink'){
				console.log('找到了该元素');
				return true;	//forEach里面的return不会终止迭代
			}
			console.log('啦啦啦');
		});
		
		// 2.some遍历
		// 如果查询数组中唯一的元素,用some方法更合适
		arr.some(function(value){
			if(value === 'yellow'){
				console.log('找到了该元素yellow');
				return true; //在some里面遇到return true就是终止遍历,迭代效率更高	
			}
			console.log(11);
		});
		
		// 3.filter遍历
		arr.filter(function(value){
			if(value === 'blue'){
				console.log('找到了该元素blue');
				return true; //filter里面的return不会终止迭代
			}
			console.log(11);
		});
	</script>
</body>
</html>

函数的进阶(重点,面试必问)

函数的定义和调用

函数的定义

  • 函数声明的方式function关键字(命名函数)

    function fn(){};
    
  • 函数表达式(匿名函数)

    var fn = function(){};
    
  • 利用new Function(‘参数1’,‘参数2’,‘函数体’)

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

    注意:

    ​ Function里面参数都必须是字符串格式

    ​ 这种方式执行效率低,也不方便书写,因此比较少使用

    ​ 所有的函数都是Function的实例(对象)

    ​ 函数也属于对象

    // 1 自定义函数(命名函数)
    			function fnn() {};
    			// 2 函数表达式(匿名函数)
    			var fnn = function(){};
    			// 3 利用new Function('参数1','参数2','函数体')
    			var fn = new Function('a','b','console.log(a + b)');
    			fn(1,2);
    			// 4 所有的函数都是Function的实例对象
    			console.dir(fn);
    			console.log(fn instanceof Object);	//判断fn函数是否是Object类型,返回的是布尔类型的值
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yvU8E8Bk-1585145600553)(C:\Users\A\AppData\Roaming\Typora\typora-user-images\image-20200323133814635.png)]

函数的调用

// 函数的调用方式
			// 1 普通函数
			function fn(){
				console.log('卑微小陈在线学习');
			}
			fn();	//或者fn.call();
			
			// 2 对象的方法
			var  o = {
				sayHi: function(){
					console.log('加油~');
				}
			}
			o.sayHi();
			
			// 3 构造函数
			function dream(){};
			new dream();
			
			// 4 绑定事件函数
			btn.onclick = function(){};	//点击按钮就可以调用这个函数
			
			// 5 定时器函数
			setInterval(function(){},1000);	//这个函数是定时器自动1秒调用一次
			
			// 6 立即执行函数
			(function(){ 
				console.log('学习');
			})();	// 立即执行函数是自动调用函数

this

函数内的this指向

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

一般指向我们的调用者

调用方式 this指向
普通函数的调用 window
构造函数的调用 实例对象 原型对象里面的方法也指向实例对象
对象方法的调用 该方法所属对象
事件绑定方法 绑定事件对象
定时器函数 window
立即执行函数 window

改变函数内的this指向

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

  • call方法

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

    fun.call(thisArg,arg1,arg2,...)
    
    var o = { name: 'Heart' };
    function fn(){
        console.log(this)
    }
    fn.call(o);	//将fn函数this指向o
    

    call的主要作用可以实现继承

    function Father(name,age,sex){
        this.name = name;
        this.age = age;
        this.sex = sex;
    }
    function Son(name,age,sex){
        // 调用Father函数,然后将this指向为Son函数,并使用Father里面的内容
        Father.call(this,name,age,sex);
    }
    var son = new Son('千玺',19,'男');
    console.log(son);
    
  • apply方法

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

    fun.apply(thisArg,[argsArray])
    

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

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

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

    var o = { name: 'Heart' };
    function fn(arr){
        // 也是调用函数 第二个可以改变函数内部的this指向
        console.log(this);
        // 但是她的参数必须是数组(伪数组)
        console.log(arr);	//打印出来的不是数组,是一个字符串
    }
    fn.apply(o,['smHeart']);
    // apply 的主要应用 比如说我们可以利用apply借助数学内置对象求最大值
    var arr = [1,16,9,4,8];
    // 存储的就是数组中的最大值
    //var max = Math.max.apply(null,arr);
    var max = Math.max.apply(Math,arr);
    // 打印输出数组中的最大值
    console.log(max);
    
  • bind()方法不会调用函数,但能改变函数内部的this指向

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

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

    arg1,arg2:传递其他参数

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

    var o = { name: 'Heart' };
    function fn(arr){
        console.log(this);
    };
    var f = fn.bind(o);
    f();	//调用新函数f()
    // 1 不会调用原来的函数  可以改变原来的函数内部的this指向
    // 2 返回的是原函数改变this之后产生的新函数
    // 3 如果有的函数不需要立即调用,但是又不想改变这个函数内部的this指向此时用bind
    
    // 当我们有一个按钮,我们点击之后就禁用这个按钮,3秒钟之后开启这个按钮
    <button type="button" class="btn">点我</button>
    		<script type="text/javascript">
    			var btn = document.querySelector('.btn');
    			btn.onclick = function(){
    				this.disabled = true;	//这个this指向的是btn这个按钮
    				// var that = this;
    				setTimeout(function(){
    					// 1 给this赋值在使用改变this指向
    					// that.disabled = false; // 定时器里面的函数this指向的是window
    					// 2 bind
    					this.disabled = false;//这个this也是指向的btn对象
    				}.bind(this),3000)	//这个this也是指向的btn对象
    			}
    		</script>
    
  • call,apply,bind总结

    • 相同点:
      • 都可以改变函数内部的this指向
    • 异同点
      • call和apply会调用函数,并且改变函数内部的this指向
      • call和apply传递的参数不一样,call传递参数aru1,aru2…形式,apply必须以数组形式传递[arg]
      • bind不会调用函数,可以改变函数内部的this指向
    • 主要应用场景
      • call主要应用于继承
      • apply经常跟数组有关系,比如借助于数学对象实现数组最大值最小值
      • bind不调用函数,但是还想改变this指向,比如改变定时器内部的this指向

严格模式

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

严格模式在IE10以上版本的浏览器中才会被支持,旧版本浏览器会被忽略。

严格模式对正常的JavaScript语义做了一些更改:

  • 消除了JavaScript语法的不合理,不严谨之处,减少了一些怪异的行为。
  • 消除代码运行的一些不安全之处,保证代码运行的安全。
  • 提高编译效率,增加运行速度。
  • 禁用了在ECMAScript的未来版本中可能会定义的一些语法,为未来新版本的JavaScript做好铺垫。比如一些保留字,如class,enum,extends,import,super不能做变量名。

开启严格模式

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

为脚本开启严格模式

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

<script>
	'use strict';
	 // 下面的js代码就会按照严格模式执行代码
</script>
<script>
    (function(){
    	'use strict';
	})();
</script>

为函数开启严格模式

<script>
	function fn(){
    	// 给fn函数开启严格模式
    	'use strict';
    	// fn函数内部的下面的代码按照严格模式执行
	}    
</script>

严格模式中的变化

严格模式对javascript的语法和行为,都做了一些改变。

  • 变量规定

    • 在严格模式下,如果一个变量没有声明就赋值,默认是全局变量,严格模式禁止这种用法,变量都必须先用var声明,然后再使用。

    • 严禁删除已声明的变量。例如,delete x;语法是错误的。

  • this的指向问题

    • 以前在全局作用域函数中this指向window对象

    • 严格模式下全局作用域中函数的this是undefined

    • 以前构造函数时不加new也是可以调用函数,当普通函数,this指向全局对象

    • 严格模式下,如果构造函数不加new调用,this会报错

    • new实例化的构造函数指向创建的对象实例

    • 定时器this指向的还是window对象

    • 事件,对象还是指向调用者

  • 函数变化

    • 函数不能有重名的参数
    • 函数必须声明在顶层新版本的JavaScript会引入“块级作用域”(ES6中已经引入)。为了与新版本接轨,不允许在非函数的代码块内声明函数。

高阶函数

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

把一个函数作为参数进行传递:

<script>
	function fn(callback){
    	callback&& callback();
	}
fn(function(){alert('hi')})
</script>

把一个函数作为返回值传递:

<script>
	function fn(){
    	return function(){}
	}   
	fn();
</script>

此时fn就是一个高阶函数

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

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

闭包

变量的作用域

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

  • 函数内部可以使用全局变量
  • 函数外部不可以使用全局变量
  • 当函数执行完毕,本作用域内的局部变量会销毁

什么是闭包

闭包(closure)指有权访问另一个函数作用域中变量函数。 —JavaScript高级程序设计

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

function fn(){
    var num = 10;
    // 闭包:我们fun 这个函数作用域 访问了另外一个函数 fn 里面的局部变量 num
    function fun(){
        //只要是函数就会创建一个函数的作用域
        console.log(num);
    }
    fun();
}
fn();

闭包的作用

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

// 我们fn外面的作用域可以访问fn内部的局部变量
function fn(){    
	var num = 10;    
	 
	//function fun(){        
   		//只要是函数就会创建一个函数的作用域        
    //	console.log(num);    
	//}    
	//return fun;
    
    return function(){
        console.log(num);
    }
}
var f = fn();
// 类似于
//var f = function fun(){        
   		//只要是函数就会创建一个函数的作用域        
//    	console.log(num);    
//	} 

// 调用f
f();

闭包案例

  • 闭包应用—点击li输出当前li的索引号

  • 利用动态添加属性的方式

    <ul class="nav">
    			<li>榴莲</li>
    			<li>臭豆腐</li>
    			<li>鲱鱼罐头</li>
    			<li>蛇果</li>
    		</ul>
    		<script type="text/javascript">
    var lis = document.querySelector('.nav').querySelectorAll('li');
    			for (var i = 0; i < lis.length; i++) {
    				// 动态的给li添加索引
    				lis[i].index = i;
    				lis[i].onclick = function() {
    					// 拿到当前索引
    					console.log(this.index);
    				}
    			}
    </script>
    
  • 利用闭包的方式得到当前小li的索引号

    for (var i = 0; i < lis.length; i++) {
    				// 利用for循环创建了4个立即执行函数
    				// 立即执行函数也称为小闭包,因为立即执行函数里面的任意一个函数副都可以使用它的i变量
    				(function(i) {
    					lis[i].onclick = function() {
    						// 拿到当前索引
    						console.log(i);
    					}
    				})(i);
    			}
    
  • 循环中的setTimeout()–3秒钟hi后,打印所有li元素的内容

    <ul class="nav">
    			<li>榴莲</li>
    			<li>臭豆腐</li>
    			<li>鲱鱼罐头</li>
    			<li>蛇果</li>
    		</ul>
    		<script type="text/javascript">
    			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>
    
  • 计算打车的价格

    • 打车起步价13(3公里内),之后没多一公里加5块钱,用户输入公里数就可以计算打车价格

      如果有拥堵情况,总价格多收取10块钱

      <script type="text/javascript">
      			var car = (function(){
      				// 起步价
      				var start = 13;
      				// 总价
      				var total = 0;
      				return {
      					// 正常的总价
      					price: function(n) {
      						if(n <= 3){
      							total = start;
      						} else{
      							total = (n - 3) * 5 + start;
      						}
      						return total;
      					},
      					// 拥堵之后的价格
      					yd: function(flag) {
      						return flag ? total + 10 : total;
      					}
      				}
      			})();
      			console.log(car.price(5));	// 23
      			console.log(car.yd(true));	// 33
      		</script>
      

闭包总结

  • 什么是闭包?
    • 闭包是一个函数(一个作用域可以访问另外一个作用域的局部变量)
  • 闭包的作用
    • 延伸了我们变量的作用范围

递归

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

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

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

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

function fn() {
    fn();
    // 不加退出条件就是死递归
}
fn();
var num = 1;
function fn(){
    console.log('2');
    if (num == 6) {
        return;	// 递归里面必须加退出条件
    }
    num++;
    fn();
}
fn();

利用递归求数学题

  • 1*2*3...*n的阶乘。
function fn(n){
    if(n == 1){
        return 1;
    }
    return n * fn(n - 1);
}
console.log(fn(3));

详细思路:

return 3 * f(2)

return 3 * (2 * f(1))

retutn 3 * (2 * 1)

retutn 3 * (2 )

retutn 6

  • 求斐波那契数列(兔子序列) 1,1,2,3,5,8,13,21…

    用户输入一个数字n可以求出 这个数字对应的兔子序列值

    我们只需要知道用户输入的n的前面两项【(n-1)(n-2)】就可以计算出n对应序列值

    function fn(n){
        if(n === 1 || n === 2){
            return 1;
        }
        return fn(n-1) + f(n-2);
    }
    
  • 根据用户输入的id号,就可以返回数据的对象

    var data = [{
        id: 1,
        name: '小陈',
        hobby: [{
            id: 11,
            name: '弹钢琴'
        },{
            id: 12,
            name: '指弹'
        }]
    },{
       id: 2,
       name: '美美',
       hobby: [{
            id: 21,
            name: '看小说'
       },{
            id: 22,
            name: '海贼控'
       }]
    },{
       id: 3,
       name: '娟娟',
       hobby: [{
            id: 13,
            name: '看小说'
       },{
            id: 14,
            name: '海贼控'
       }]  
    },{
       id: 4,
       name: '胖虎',
       hobby: [{
            id: 15,
            name: '王者荣耀'
       },{
            id: 16,
            name: '撩哥能妹'
       }]  
    },{
       id: 5,
       name: '漫漫',
       hobby: [{
            id: 17,
            name: '蹦迪一枝花'
       },{
            id: 18,
            name: '桃花满怀'
       }] 
    },{
       id: 6,
       name: '一菲',
       hobby: [{
            id: 19,
            name: 'LOL控'
       },{
            id: 20,
            name: '湖科一姐'
       }] 
    }];
    //我们想要输入id号,就可以返回数组对象
    // 1 利用 forEach  去遍历里面的每一个对象
    function getId(json,id){
        var o = {};
        json.forEach(function(item){
            // console.log(item); 打印输出每个对象
            // 得到数组外层的数据
            if(item.id == id){
               o = item;
               // console.log(item);
               // 得到数组里层的数据 11 12可以利用递归执行函数
               // 里面应该有hobby这个数组并且数组的长度不为0
                
            }else if(item.hobby && item.hobby.length > 0){
              o = getId(item.hobby,id);       
            }
        });
        return o;
    }
    console.log(getId(data,21));
    

浅拷贝和深拷贝

  • 浅拷贝只是拷贝一层,更深层次的对象级别的只拷贝引用(修改浅拷贝后的值,拷贝前的值也会改变)

    通过js中的for循环实现浅拷贝:

    var obj = {
        id: 1,
        name: 'herat',
        msg: {
            age: 20,
            hobby: '指弹'
        }
    };
    var o = {};
    // 通过for循环实现浅拷贝
    for(var k in obj){
        // k 是属性名 obj[k] 属性值
        o[k] = obj[k];
    }
    console.log(o);
    

    通过es6新增的方法实现浅拷贝:

    Object.assign(target,…sources) es6新增的实现浅拷贝的方法

    var obj = {
        id: 1,
        name: 'herat',
        msg: {
            age: 20,
            hobby: '指弹'
        }
    };
    var o = {};
    Object.assign(o,obj);	//把obj拷贝给o
    console.log(o);
    
  • 深拷贝拷贝多层,每一级别的数据都会拷贝(修改浅拷贝后的值,拷贝前的值也不会改变)

    通过函数递归实现深拷贝数据:

    var obj = {
        id: 1,
        name: 'herat',
        msg: {
            age: 20,
            hobby: '指弹'
        }
    };
    var o = {};
    // 封装函数
    function deepCopy(newobj,oldobj){
        for(var k in obj){
            // 判断我们的属性值属于那种数据类型
            // 1 获取属性值 obj[k]
            var item = obj[k];
            // 2 判断这个值是否是数组(数组也属于对象类型)
            if(item instanceof Array){
                newobj[k] = [];
                // 递归函数是把值(item)给属性(newobj[k])
                deepCopy(newobj[k],item);
            }
            // 3 判断这个值是否是对象
            else if (item instanceof Object){
                newobj[k] = {};
                deepCopy(newobj[k],item);
            }
            // 4 属于简单数据类型
            else{
               newobj[k] = item; 
            }
        }
    };
    deepCopy(o,obj);
    console.log(o);
    

正则表达式

正则表达式的概述

什么是正则表达式?

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

正则表达式通常被用来检索,替换那些符合某个模式(规则)的文本。例如表单验证(匹配,替换,提取)

正则表达式的特点:

  • 灵活性,逻辑性和功能性非常的强。
  • 可以迅速地用简单的方式达到字符串的复杂控制。

正则表达式在JavaScript中的使用

创建正则表达式

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

  • 通过RegExp对象的构造函数来创建

    var 变量名 = new RegExp(/表达式/);
    
    var regexp = new RegExp(/123/);
    
  • 利用字面量创建

    var 变量名 = /表达式/;
    
    var rg = /123/;
    

检测正则表达式test

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

regexObj.test(str);

regexObj 是写的正则表达式

str 我们要检测的文本

就是检测str文本是否符合我们写的正则表达式规范

正则表达式中的特殊字符

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

  • 边界符 ^ $

    // 只要包含abc这个字符串都会返回true
    var reg = /abc/;	//正则表达式里面不需要加引号 不管是数字型还是字符型
    
    var reg = /^abc/;	//匹配以abc开头的字符
    
    var reg = /^abc$/;	//匹配以abc开头,以abc结尾的字符
    
边界符 说明
^ 表示匹配行首的文本(以谁开始)
$ 表示匹配行尾的文本(以谁结束)
如果^和$在一起,表示必须是精确匹配。
  • 字符类:[] 表示有一系列字符可供选择,只要匹配其中一个就可以了

    var reg = /[abc]/;	//只要包含有a 或者  包含有b  或者  包含有c的字符,都返回为true
    
    var reg = /^[abc]$/;	//三选一,只有是a或者b或者c,就返回true
    
    var reg = /^[a-z]$/;	//26个英文字母任何一个字母都返回true
    
    var reg = /^[a-zA-Z0-9_-]{3,16}$/	//3到16位(用户名)
    
    var reg = /^[a-zA-Z0-9_-]{6,18}$/	//密码
    
    var reg = /^[^a-zA-Z0-9_-]{6,18}$/	//如果中括号里面有^,表示取反的意思
    
  • 量词符

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

    量词 说明
    * 重复零次或更多次(>=0)
    + 重复一次或更多次(>=1)
    重复零次或一次 [0,1]
    {n} 重复n次 (=n)
    {n,} 重复n次或更多次 (>=n)
    {n,m} 重复n到m次 [n,m]
  • 预定义类

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

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

案例

  • 验证用户名

功能需求:

  1. 如果用户输入合法,则后面提示信息为:用户名合法,并且颜色为绿色
  2. 如果用户输入不合法,则后面提示信息为:用户名不符合规范,并且颜色为红色
<!DOCTYPE html>
<html lang="zh">
	<head>
		<meta charset="UTF-8">
		<meta name="viewport" content="width=device-width, initial-scale=1.0">
		<meta http-equiv="X-UA-Compatible" content="ie=edge">
		<title></title>
		<style type="text/css">
			span {
				color: gray;
				font-size: 14px;
			}

			.right {
				color: green;
			}

			.wrong {
				color: red;
			}
		</style>
	</head>
	<body>
		<input type="text" class="username"><span>请输入用户名</span>
		<script type="text/javascript">
			var reg = /^[a-zA-Z0-9_-]{6,16}$/;
			var username = document.querySelector('.username');
			var span = document.querySelector('span');
			// 失去焦点事件
			username.onblur = function() {
				if (reg.test(this.value)) {
					// 输入的值符合正则规范
					// console.log('输入正确');
					// 给提示的span添加样式
					span.className = 'right';
					span.innerHTML = '用户名输入格式正确';
				} else {
					// console.log('输入错误');
					// 给提示的span添加样式
					span.className = 'wrong';
					span.innerHTML = '用户名输入格式错误';
				}
			}
		</script>
	</body>
</html>
  • 验证座机号码

    座机号码验证:全国座机号码 两种格式:010-12345678 或者 0530-1234567

    <!DOCTYPE html>
    <html lang="zh">
    <head>
    	<meta charset="UTF-8">
    	<meta name="viewport" content="width=device-width, initial-scale=1.0">
    	<meta http-equiv="X-UA-Compatible" content="ie=edge">
    	<title></title>
    </head>
    <body>
    	<script type="text/javascript">
    		// 注意:
    			// 正则里面的 或者 用 | 表示
    			// 正则表达式里面不能存在空格
    		var reg = /^\d{3}-\d{8}|^\d{4}-\d{7}$/;
    		// 简写
    		var reg = /^\d{3,4}-\d{8,7}$/;
    	</script>
    </body>
    </html>
    

括号总结

// 中括号 字符集合,匹配方括号中的任意字符
var reg = /^[abc]$/;	//类似于 a || b || c

//大括号 量词符,里面表示重复数
var reg = /^a{3}$/; 	//a重复了3次
var reg = /^abc{3}$/;	//abccc  c重复了3次

//小括号  表示优先级
var reg = /^(abc){3}$/;	//abcabcabc  abc重复3次	

正则表达式中的替换

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

stringObject.replace(regxp/substr,replacement);
  • 第一个参数:被替换的字符 或者 正则表达式

  • 第二个参数:替换的字符串

  • 返回的是一个替换完毕的新字符串

    var str = 'amy and mike';
    //通过字符串替换
    var newStr = str.replece('amy','joo');
    //通过正则表达式替换
    var newStr = str.replece(/amy/,'joo');
    

console.log(str); // 打印输出的str为 joo and mike


```javascript



	
	
	
	


	
	

正则表达式参数

/表达式/[switch]

switch(也称为修饰符),按照什么样的模式来匹配,有三种值:

  • g:全局匹配
  • i:忽略大小写
  • gi:全局匹配 + 忽略大小写

ES6

什么是ES6?

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

年份 版本
2015年6月 ES2015
2016年6月 ES2016
2017年6月 ES2017
2018年6月 ES2018
…… ……

ES6实际上是一个泛指,泛指2015及后续版本。

为什么要使用ES6?

每次标准的诞生都意味着语言的完善,功能的强大。JavaScript语言本身也有一些令人不满意的地方。

  • 变量提升特性增加了程序运行时的不可预测性
  • 语法过于松散,实现相同的功能,不同的人可能会写出不同的代码

let(变量)

ES6中新增的用于声明变量的关键字。

  • let声明的变量只在所处于的块级有效

    使用let关键字声明的变量具有块级作用域(就是一队大括号产生的作用域)

    if (true) {
        let a = 10;
    }
    console.log(a);	//a is not defined
    

    块级作用域的好处是:在业务逻辑比较复杂的时候,避免内层变量覆盖外层变量,防止了循环变量,变成全局变量。

    注意:使用let关键字声明的变量才具有块级作用域,使用var声明的变量不具有块级作用域。

  • 不存在变量的提升

    console.log(a);	// a is not defined
    let a = 10;
    
  • 暂时性死区

    var tmp = 123;
    if (true) {
        console.log(tmp);	//tmp is not defined
        let tmp = 456;
    }
    
    image-20200324184913060 image-20200324190440644

const(常量)

作用:声明常量,常量就是值(内存地址)不能变化的量。

  • 具有块级作用域

    if (true) {
        const a = 10;
    }
    console.log(a); 	// a is not defined
    
  • 使用const关键字声明常量时必须赋值

    const PI; 	//Missing initializer in const declaration
    
  • 常量赋值后,值不能修改

    基本数据类型:值不能更改

    const PI;
    PI = 3.14;	//Assignment to constant variable.
    

    复杂数据类型:数据结构内部的值可以更改,但是数据本身不能被更改

    const ary = [100,200];
    ary[0] = 20;
    // 对于复杂类型来说  里面的值是可以更改的
    console.log(ary);	//打印输出[20,200]
    ary = [1,3];
    // 但是不能 重新赋值(更改了常量值的内存地址)
    console.log(ary);	// Assignment to constant variable.
    

let,const的优点

优点:防止重复声明,限制修改

let:不能重复声明,变量,块级

const:不能重复声明,常量,块级

let,const,var的区别

  • 使用var声明的变量,其作用域为 该语句所在的函数内,且存在变量提升现象

    <button>1</button>
    <button>2</button>
    <button>3</button>
    <script type="text/javascript">
    	window.onload = function() {
    		let btn = document.getElementsByTagName('button');
    		for (var i = 0; i < btn.length; i++) {
    			btn[i].onclick = function() {
    				alert(i); // 使用var关键字,点击每个按钮都会弹出3
    			}
    		}
        	// 弹出3的原因是,循环结束的条件是 3
        	// 事件是异步的,所以只能的到最后的索引值
    	}
    </script>
    

    通过函数发的方式修改:

    for (var i = 0; i < btn.length; i++) {
    	(function(){
    		btn[i].onclick = function() {
    			alert(i); 
    		}
    	})(i);
    }
    
  • 使用let声明的变量,其作用域为 该语句所在的代码块内,不存在变量的提升

    <button>1</button>
    <button>2</button>
    <button>3</button>
    <script type="text/javascript">
    	window.onload = function() {
        	// 2 将var  改为  let
    				for (let i = 0; i < btn.length; i++) {
    					btn[i].onclick = function() {
    						alert(i); // 使用let关键字,点击那个按钮就弹出那个按钮的索引
    					}
    				}
    	}
    </script>
    
    image-20200325132156725
  • 使用const声明的变量,在后面出现的代码中 不能在修改该常量的值

    var let const
    函数级作用域 块级作用域 块级作用域
    变量提升 不存在变量提升 不存在变量提升
    值可更改 值可更改 值不可更改

解构赋值

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

数组解构

变量的值和数组的值数量是一样:

let [a,b,c] = [1,2,3];
console.log(a);	// 1
console.log(b);	// 2
console.log(c);	// 3

不一样:

如果解析不成功,变量的值为undefined

let foo = [];
let [a,b] = [1];
console.log(a);		// 1
console.log(b);		// undefined
arr = [1, 2, 3];
let [a, b, c] = arr;
console.log(arr);	//打印输出 [1, 2, 3]

对象解构

解构赋值

按照一定模式,从数组中或对象中提取值,将提取出来的值赋值给另外的变量。

使用变量去匹配属性:

let person = { name: 'zhangsan', age: 20};
let { name, age } = person;
console.log(name);	// zahngsan
console.log(age);	// 20
let person = { name: 'zhangsan', age: 20};
let { name: myName, age: myAge } = person;	//myName,myAge属于别名
console.log(myName);	//zhangsan
console.log(myAge);		//20

注意

  1. 两边的结构必须一样

    let {a, b, c} = [1, 2, 3];	//错误
    //必须为
    let {a, b, c} = {x, x, x}
    
  2. 右边必须是合法

    arr = [1, 2, 3];
    let [a, b, c] = arr;
    console.log(arr);	//打印输出 [1, 2, 3]
    
  3. 赋值和解构必须同时完成

    let {a, b};	
    console.log({a, b} = {a: 12,b: 15});
    // 错误  Missing initializer in destructuring declaration
    
    //正确写法
    let {a, b} = {a: 12,b: 15};
    console.log({a, b}); // 打印输出 {a: 12, b: 15}
    

箭头函数

ES6中新增的定义函数的方式。

  • 箭头函数是用来简化函数定义语法。
() => {}

// 箭头函数的调用
// 将函数赋值给一个变量,然后通过变量名来调用
const fn = () => {
    console.log(123)
}
fn();	 //在控制台打印输出 123
  • 函数中只有一句代码,且代码的执行结果就是返回值,可以省略大括号。
// 普通函数
function sum(num1, num2){
    return num1 + num2;
}

// 箭头函数
const sum = (num1, num2) => num1 + num2;
const result = sum(2, 3);
console.log(result);	// 浏览器控制台输出 5
  • 如果形参只有一个,可以省略小括号。
// 普通函数
function fn(v){
    return v;
}

//箭头函数
const fn = v => { alert(v); };
fn(20);

排序:

let arr = [1,4,55,9,2,0];
console.log(arr.sort());	// 打印输出 [0, 1, 2, 4, 55, 9]  他是把数值当成字符来排序的

// 普通函数
arr.sort(function(n1,n2){
	return n1 - n2;
});
console.log(arr);	//打印输出  [0, 1, 2, 4, 9, 55]

// 箭头函数	() => {}
arr.sort((n1,n2) => { n1 - n2; });
console.log(arr);	//打印输出  [0, 1, 2, 4, 9, 55]

方法

map 映射 【一一对应】

根据分数判断评语:

let arr = [45,60,90,100];
// 通过if来判断
let arr2 = arr.map(function(item){
    if (item >= 60) {
        return '及格';
    } else {
        return '不及格';
    }
});
// 通过三元表达式来判断
let arr2 = arr.map(item){
    return item >= 60 ? '及格':'不及格';
}
// 箭头函数
 let arr2 = arr.map(item >= item >= 60 ? '及格':'不及格');

console.log(arr);	//  [45, 60, 90, 100]
console.log(arr2);	// 	["不及格", "及格", "及格", "及格"]

reduce 缩减 【只有一个】

求总和和平均数:

let arr = [45, 60, 90, 100, 75, 67];
let result = arr.reduce(function (tmp, item, index) {
	// console.log('第'+ index + ':' + tmp + ',' +item);	
	// 第1:45,60,第2:undefined,90,第3:undefined,100,第4:undefined,75,第5:undefined,67
	return tmp + item;
});
console.log('和为:' + result);	// 打印输出437
console.log('平均数为:' + result / arr.length);	//平均数为:72.83333333333333

平均数:

let arr = [45, 60, 90, 100, 75, 67];
let result = arr.reduce(function (tmp, item, index) {
    if(index == arr.length -1){	//最后一次
		return (tmp + item)/arr.length;
	} else {
		return tmp + item;
	}
});
console.log(result);	// 打印输出72.83333333333333

filter 过滤

筛选偶数:

let arr = [45, 60, 90, 100, 75, 67];
// 1 通过if判断来筛选
let newArry = arr.filter(item => {
	if(item % 2 === 1){
		return false;
	} else {
		return true;
	}
});
// 2 通过三元表达式来筛选
let newArry = arr.filter(item => item % 2 === 0);
console.log(newArry); //[60, 90, 100]

forEach 遍历

let arr = [45, 60, 90, 100, 75, 67];
// 第0个值是45,第1个值是60,第2个值是90,第3个值是100,第4个值是75,第5个值是67
arr.forEach((item, index) => console.log('第' + index + '个值是' + item));
// 通过字符串模板(反单引号)来遍历
arr.forEach((item, index) => console.log(`第${index}个值是${item}`));

箭头函数中的this指向

箭头函数不绑定 this 关键字,箭头函数中的this,指向的是 函数定义位置的上下文this

箭头函数被定义在哪儿,this关键字就指向哪儿。

箭头函数不绑定this,箭头函数没有自己的this关键字,如果在箭头函数中使用this,this关键字将指向箭头珊瑚定义位置中的this。

const obj = { name: '小陈' };
function fn () {
    console.log(this); // 	Object
    // 匿名函数
    return () => {
        console.log(this);// 	Object
    }
}
// call方法可以改变函数内部的this指向
const resFn = fn.call(obj);	//使用call方法调用fn函数,将函数的this指向了obj对象
resFn();
// Identifier 'json' has already been declared
// 注意: class 命名必须为以大写字母开头
class Json {
	constructor(){
		console.log(this);	// 打印输出 Json {} 对象
		
		this.a = 12;
		this.b = 8;
		this.fn = function() {
			console.log(this.a); // 打印输出 12
		}
		this.fnn = function() {
			console.log(this.b); // 打印输出 8
		}
	}
}
// 实例化Json类
let json = new  Json();
// 调用Json里面的fn方法
json.fn();
// 调用Json里面的fnn方法
json.fnn();

箭头函数面试题

对象是不能产生作用域的

var obj = {
    age: 20,
    // 箭头函数没有this
    say: () => {
        console.log(this.age);	//打印输出undefined
    }
}
// obj是一个对象,不能产生作用域,所以箭头函数实际上被定义在全局作用域下,所以调用时,this指向的window,window没有age属性,所以打印输出的是undefined
obj.say();

剩余参数(参数展开/扩展运算符)

参数展开:收集剩余参数,展开信息。

剩余参数语法允许我们将一个不定量的参数表示为一个数组。

当函数实参大于新参时,可以把大于的实参放在数组中。

function sum(first, ...args){
    console.log(first);	// 10
    console.log(args);	// [20,30]
}
sum(10, 20, 30);

计算所传值的和:

const sum = (...args) => {
    let total = 0;
    //args.forEach(item => {
    //    total += item;
    //});
    args.forEach(item => total += item);
    //将计算的值返回回去
    return total;
};
sum(10, 20);
sum(10, 20, 30);
let are = [14, 12, 11];
function show(a, b, c){
    console.log(a + b + c);
}
show(are);	// 打印输出  14,12,11undefinedundefined ,相当于只传了一个参数
show(...are);	// 打印输出 37

json的合并:

let json = {a: 12, b: 15};
let json2 = {
	...json,
	c: 20
}
console.log(json2);

剩余参数和解构配合使用

剩余参数和解构配合使用:

let students = ['yanyan' , 'meimei', 'juanjuan'];
let [s1,...s2] = students;
console.log(s1);	// 'yanyan'
console.log(s2);`	// ['meimei', 'juanjuan']

JSON

JSON的标准写法:{“a”:2,“b”:3},在js中可以简写{a: 2, b:3}。

  • JSON.stringify()

    • 传入json值,返回来的是字符串

      var js = JSON.stringify({a: 2, b:3});
      console.log(js);	// 打印输出 {"a":2,"b":3}
      
  • JSON.parse()

    • 将字符串解析为json

      var js = JSON.parse('{"a":2,"b":3}');
      console.log(js);	//打印输出{a: 2, b: 3}
      

ES6的内置对象扩展

Array的扩展方法

扩展运算符(展开语法)

扩展运算符可以将数组或者对象转为用逗号分隔的参数序列。

let ary = [1, 2, 3];
//...ary 	//1 2 3 也就是将ary数组的中括号去掉了
console.log(...ary);

扩展运算符可以应用与 合并数组

// 方法一
let ary1 = [1, 2, 3];
let ary2 = [4, 5, 6];
let ary3 = [...ary1, ...ary2];
console.log(ary3);	// 打印输出[1, 2, 3, 4, 5, 6]

// 方法二
//通过push方法,将ary2中的元素追加到ary1中
ary1.push(...ary2);
console.log(ary1);	// 打印输出[1, 2, 3, 4, 5, 6]

扩展运算符可以将类数组或可遍历的对象转换为真正的数组

let d = document.getElementsByTageName('div');
d = [...d];
<!DOCTYPE html>
<html lang="zh">
	<head>
		<meta charset="UTF-8">
		<meta name="viewport" content="width=device-width, initial-scale=1.0">
		<meta http-equiv="X-UA-Compatible" content="ie=edge">
		<title></title>
	</head>
	<body>
		<div>1</div>
		<div>2</div>
		<div>3</div>
		<div>4</div>
		<div>5</div>
		<script type="text/javascript">
			var d = document.getElementsByTagName('div');
			// 现在的d是一个伪数组
			// console.log(d);
			
			//将伪数组转换成一个真正的数组
			const ds = [...d];
			console.log(ds);
			
			// 将伪数组转换为真正的数组后,就可以使用数组的方法
			ds.push('a');
			console.log(ds);
		</script>				
	</body>
</html>

构造函数方法Array.from()

将类数组或可遍历的对象转换成真正的数组。

let array = {
    '0':'a',
    '1':'b',
    '2':'c',
    '3':'d',
    '4':'e',
    length:5	//注意:在伪数组中一定要写length,不然遍历出来的值为[]
};
let arry2 = Array.from(array);
console.log(arry2);	//["a", "b", "c", "d", "e"]

方法还可以接受第二个参数,作用类似于数组的map方法,用来对每个元素进行处理,将处理后的值返回数组。

let array = {
    '0':'1',
    '1':'2',
    '2':'3',
    '3':'4',
    '4':'5',
    length:5	//注意:在伪数组中一定要写length,不然遍历出来的值为[]
}
let arry2 = Array.from(array, item => item * 2);
console.log(arry2);	// [2, 4, 6, 8, 10]

Array.find()

用于找出第一个符合条件的数组成员,如果没有找到返回undefined。

let array = [{
    id: 1,
    name: '小陈'
},{
    id: 2,
    name: '小王'
},{
    id: 3,
    name: '小姜'
},{
    id: 4,
    name: '小唐'
},{
    id: 5,
    name: '小黎'
},{
    id: 6,
    name: '小肖'
}];
// item 代表的是当前循环的值,index代表的是当前循环的索引
// let target = array.find((item, index) => item.id ==2);
let target = array.find((item) => item.id ==2);
console.log(target);	//打印输出  {id: 2,name: '小王'}

Array.findIndex()

用于找出第一个符合条件的数组成员的位置,如果没有找到返回-1。

var ary = [1, 3, 5, 7, 9];
let index = ary.findIndex((value) => value > 5);
console.log(index);	//打印输出   3

Array.includes()–检测是否存在

表示某个数组是否包含给定的值,返回的是布尔值。

//js
console.log([1, 2, 3].indexOf(3) != -1);	// 返回true
//es7中includes方法
[1, 2, 3].includes(2);	//true

String的扩展方法

模板字符串

模板字符串

ES6新增的创建字符串的方式,使用反引号定义。

let name = `xiaochen`;

特点:

  • 模板字符串可以 解析变量

    let name = `xiaochen`;
    let sayHello = `hello,my name is ${name}`;
    console.log(sayHello);	// 打印输出  hello,my name is xiaochen
    
  • 模板字符串可以 换行

    let result = {
    	name: 'xiaochen',
    	age: 20,
    	sex: '女'
    }
    let html = `
    
    ${result.name} ${result.age} ${result.sex}
    `
    ; console.log(html);
  • 模板字符串可以 调用函数

    const sayHello = function(){
    	return 'hello world';
    }
    // 在变量中调用函数
    let get = `${sayHello()}`;
    console.log(get);	//打印输出 hello world
    

startsWith和endsWith

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

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

    let str = 'hello world';
    let r1 =str.startsWith('hello');	
    console.log(r1);	// true
    let r2 = str.endsWith('world');		
    console.log(r2);	// true
    

repeat()

repeat方法表示将原字符串重复n次,返回一个新的字符串

let x = 'x'.repeat(3);
console.log(x);	//打印输出 xxx

Set数据结构

ES6提供了新的数据结构Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。

Set本身是一个构造函数,用来生成Set数据结构。

const s = new Set();
console.log(s.size);	//打印输出0,证明数据结构为空

Set构造函数可以接受一个数组作为参数,用来初始化。(数组中的值会被自动存储在Set数据结构中)

const set = new Set([1, 2, 3, 4]);
console.log(set.size);	//打印输出4,证明在当前数据结构中存储了4个值

当我们使用Set,向里面传值时,会把重复的值过滤掉。

const s = new Set([1, 1, 2]);
console.log(s.size);	//打印输出2

可以利用Set的这个属性做数组去重。

const s = new Set([1, 1, 2]);
console.log(s.size);	//打印输出2
const ary = [...s];	// 通过扩展运算符得到新的数组
console.log(ary);	//打印输出 [1, 2]

Set数据结构实例方法

  • add(value):添加某个值,返回Set结构本身

  • delete(value):删除某个值,返回一个布尔值,表示是否删除成功

  • has(value):返回一个布尔值,表示该值是否为Set的成员

  • clear():清除所有成员,没有返回值

    const s = new Set();
    // 向Set结构中添加值
    s.add(1).add(2).add(3);
    console.log(s);	//打印输出 {1, 2, 3}
    
    // 删除Set结构中的2
    s.delete(2);
    console.log(s);	//打印输出 {1, 3}
    
    // 判断Set结构中有没有1   返回的是布尔值
    console.log(s.has(1));	// 打印输出 true
    
    // 清除Set结构中所有的值
    s.clear();
    console.log(s);	// 打印输出 {}
    
    

从Set结构中取值(遍历)

Set结构的实例与数组一样,也拥有forEach方法,用于每个成员执行某种操作,没有返回值。

s.forEach(value => console.log(value));
const s5 = new Set(['小小','大大','跳跳','悄悄']);
s5.forEach((value) => {
	console.log(value);	//打印输出小小 大大 跳跳 悄悄
});

babel.js编译

用法:解决低版本浏览器不兼容es6的问题

方法1:引入js文件,在客户端编译。

缺点:会延迟,会卡,浏览器低版本不认识(babel本身就不兼容低版本浏览器)

<!-- 引入browser.min.js文件 -->
<script src="browser.min.js" charset="utf-8"></script>
<!-- 在script标签上面添加 type="text/babel" -->
<script type="text/babel">
	let a = 12;
	let show = n => alert(n);
	show(a);
</script>

方法2:编译js文件

  1. 安装Node.js,初始化项目

    创建package.json文件

    npm init -y

    2.安装babel-cli

    npm i @babel/core @babel/cli @babel/preset-env -D

    npm i @babel/polyfill -S

    1. 添加执行脚本(package.json中)

      "scripts": {
          "test": "echo \"Error: no test specified\" && exit 1",
          "build": "babel src -d dest"	//通过babel来编译src这个目录,输出dest
        }
      
    2. 添加.babelrc配置文件

      {
      	"presets": [
        		// 声明预设      
      		"@babel/preset-env"
      	]
      }
      
    3. 执行编译

      npm run build

异步操作

异步操作(ajax):

​ 优点:可以同时进行多个操作,用户体验良好

​ 缺点:代码混乱,编写麻烦

ajax('www.baidu.com',function (data) {
	ajax('www.baidu.com',function (data) {
		...
	},function (data) {
		console.log("编译失败");
	})
},function () {
	console.log("编译失败");
});

同步操作:

​ 优点:代码清晰,编写简单

​ 缺点:一次只能进行一个操作,用户体验不好

let data1 = ajax('http://baidu.com');
...

融合异步操作和同步操作的优势:Promise,async/await

Promise–封装

注意:jquery本身就是一个Promise,有返回值then

Promise

<!DOCTYPE html>
<html lang="zh">
	<head>
		<meta charset="UTF-8">
		<meta name="viewport" content="width=device-width, initial-scale=1.0">
		<meta http-equiv="X-UA-Compatible" content="ie=edge">
		<title></title>
		<script src="styles/jquery.js" type="text/javascript" charset="utf-8"></script>
	</head>
	<body>
		<script type="text/javascript">
			// 对异步操作做一个统一封装
			let p = new Promise(function(resolve, reject) {
				// resolve 解决/成功	
				// reject 拒绝/失败
				$.ajax({
					url: 'data/1.txt',
					dataType: 'json',
					success(arr) {
						resolve(arr);
					},
					error(res) {
						reject(res);
					}
				});
			});
			// 调用
			p.then(function(arr) {
				// 成功
				console.log('成功');
				console.log(arr);
			},function(res) {
				console.log('失败');
				console.log(res);
			});
		</script>
	</body>
</html>

Promise.all()方法

Promise.all([
	$.ajax(url: 'data/1.txt', dataType: 'json');
	$.ajax(url: 'data/2.txt', dataType: 'json');
	$.ajax(url: 'data/3.txt', dataType: 'json');
]).then(arr => {
	// 输出的是上面三个文件里面的所有内容
	// 需要上面三个文件都成功,然后就执行成功操作
	console.log(arr);
    //可以通过解构解析,取出某一个文件中的值
    [data1,data2,data3] = arr;
    console.log(data2);	//打印输出data2中的数据
},res => {
	console.log('失败')
});

Promise.race()–竞速–谁先读取完就先用哪个

除非都失败了才走error

async/await

通俗的说async和await是函数的一种形式。

async是一种语法,使用时直接加载function的前面。

async function show(){
    ...
    
    // 等待操作结束后再继续往下执行
    let data = await $.ajax();
}

普通函数 和 async函数区别:

​ 普通函数————一直执行,直到结束

​ async函数————能够"暂停"-“await”

// 实际上async的执行过程(解析成单个来解析的)
// 写起来更方便,但是执行还是异步的
// function show1() {
// 	let a = 12;
// 	let b = 5;
// }
// let data = await $.ajax({url: 'data/1.txt', dataType: 'json'}).then(function(){
// 	show2();
// });
// function show2() {
// 	console.log(a + b + data[0]); //打印输出 18
// }

async function show() {
	let a = 12;
	let b = 5;
	
    //使用try catch来捕获错误
    try{
        let data = await $.ajax({url: 'data/1.txt', dataType: 'json'});
	
		console.log(a + b + data[0]); //打印输出 18
    } catch(e) {
    	console.log(e);
    }
}
show();

ES6面向对象

语言发展历史

机器语言 -> 汇编语言 -> 低级语言(面向过程)-> 高级语言(面向对象)-> 模块系统 -> 框架 -> 系统接口(API)

ES5中的面向对象

  1. es5中的面向对象是一个假的面向对象
  2. 没有统一的写法(处于一个自我摸索的阶段)
// 没有专门的类声明方法,类是通过函数实现的
// 1.既是构造函数,又是类
// 是以函数的方式来写对象
function Person(name, age){
	this.name = name;
	this.age = age;
}
// 2.方法独立在类之外
// 给类添加方法
Person.prototype.show = function (){
	alert(this.name);
} 
// 3.没有专门的继承方法
function worker(name, age, job) {
	Parson.call(this, name, age);	// 4.从父类继承数据
	this.job = job;
}
// 5.没有专门继承父类的方式
// 创建类的实例
worker.prototype = new Person();
worker.prototype.constructor = worker;
worker.prototype.Job = function (){
    console.log(this.job);
};
let w = new worker('xiaochen', 18, '程序员');

ES6中的面向对象

统一了写法,出现了类class,构造函数constructor,继承extends,超类/父类super的声明。

继承的优点:省事儿(父类有的直接用),便于扩展(出现错误只需要修改一处)。

class Person{
    constructor(name, age){
        this.name = name;
        this.age = age;
    }
    show(){
        console.log(this.name);
    }
}
class work extends Person{
    constructor(name, age, job){
        super(name,age);
        this.job = job;
    }
    showjob(){
        console.log(this.job);
    }
}

let w = new work('xiaochen', 12, '程序员');
w.showjob();

ES6模块系统

JS模块系统演化简史

没有模块 -> CMD(Common Model Definition)[民间模块系统,但不受官方支持,模块必须加载完后再执行] -> AMD(Asynchronous Module Definitious) [异步模块,模块加载自由,不受官方支持] -> 语言提供模块支持(ES6)

模块的定义和使用

浏览器现在不支持ES6的模块系统,但是为了使用模块系统,就出现了webpack

webpack.config.js中:

// 引入path包
const path = require('path');

module.exports = {
    //	告诉浏览器编译的模式是什么
    mode: 'production',
    // 入口
    entry: './index.js',
    // 输出 or 编译结果
    output: {
        // 放在哪儿去
        path: path.resolve(__dirname,'build');
        // 文件名
        filename: 'build.js';
    }
};

使用

  1. 导出(export)

    // 导出一个变量
    export let a = 12;
    // 导出多个变量
    let a,b,c = ...;
    export {a, b, c};
    // 导出函数
    export function show(){
        
    }
    // 导出class
    export class Promise{}
    
    // 导出默认成员
    export default a...
    
  2. 导入(import)

    import * as mod from 'XXX';	// 引入所有成员
    import mod from 'XXX';	// 引入default成员
    import {a,b} from 'XXX';	// 引入a,b变量
    import 'XXX';	//引入整个文件
    let p = import('./mode');	//异步引入,使用什么就引入什么(当函数使用的)
    
  3. webpack编译

总结

  1. es7中的 幂操作 和 includes方法

    // 求三的五次方
    console.log(Math.pow(3, 5));	// 打印输出243
    
    // 在es7中求三的五次方
    console.log(3 ** 5);	// 打印输出243
    

    es7中的includes方法

  2. es8中 async/await

,但是执行还是异步的
// function show1() {
// let a = 12;
// let b = 5;
// }
// let data = await $.ajax({url: ‘data/1.txt’, dataType: ‘json’}).then(function(){
// show2();
// });
// function show2() {
// console.log(a + b + data[0]); //打印输出 18
// }

async function show() {
let a = 12;
let b = 5;

//使用try catch来捕获错误
try{
    let data = await $.ajax({url: 'data/1.txt', dataType: 'json'});

	console.log(a + b + data[0]); //打印输出 18
} catch(e) {
	console.log(e);
}

}
show();


## ES6面向对象

### 语言发展历史

机器语言 -> 汇编语言  ->  低级语言(面向过程)-> 高级语言(面向对象)-> 模块系统  ->  框架   -> 系统接口(API)

### ES5中的面向对象

1. es5中的面向对象是一个假的面向对象
2. 没有统一的写法(处于一个自我摸索的阶段)

```javascript
// 没有专门的类声明方法,类是通过函数实现的
// 1.既是构造函数,又是类
// 是以函数的方式来写对象
function Person(name, age){
	this.name = name;
	this.age = age;
}
// 2.方法独立在类之外
// 给类添加方法
Person.prototype.show = function (){
	alert(this.name);
} 
// 3.没有专门的继承方法
function worker(name, age, job) {
	Parson.call(this, name, age);	// 4.从父类继承数据
	this.job = job;
}
// 5.没有专门继承父类的方式
// 创建类的实例
worker.prototype = new Person();
worker.prototype.constructor = worker;
worker.prototype.Job = function (){
    console.log(this.job);
};
let w = new worker('xiaochen', 18, '程序员');

ES6中的面向对象

统一了写法,出现了类class,构造函数constructor,继承extends,超类/父类super的声明。

继承的优点:省事儿(父类有的直接用),便于扩展(出现错误只需要修改一处)。

class Person{
    constructor(name, age){
        this.name = name;
        this.age = age;
    }
    show(){
        console.log(this.name);
    }
}
class work extends Person{
    constructor(name, age, job){
        super(name,age);
        this.job = job;
    }
    showjob(){
        console.log(this.job);
    }
}

let w = new work('xiaochen', 12, '程序员');
w.showjob();

ES6模块系统

JS模块系统演化简史

没有模块 -> CMD(Common Model Definition)[民间模块系统,但不受官方支持,模块必须加载完后再执行] -> AMD(Asynchronous Module Definitious) [异步模块,模块加载自由,不受官方支持] -> 语言提供模块支持(ES6)

模块的定义和使用

浏览器现在不支持ES6的模块系统,但是为了使用模块系统,就出现了webpack

webpack.config.js中:

// 引入path包
const path = require('path');

module.exports = {
    //	告诉浏览器编译的模式是什么
    mode: 'production',
    // 入口
    entry: './index.js',
    // 输出 or 编译结果
    output: {
        // 放在哪儿去
        path: path.resolve(__dirname,'build');
        // 文件名
        filename: 'build.js';
    }
};

使用

  1. 导出(export)

    // 导出一个变量
    export let a = 12;
    // 导出多个变量
    let a,b,c = ...;
    export {a, b, c};
    // 导出函数
    export function show(){
        
    }
    // 导出class
    export class Promise{}
    
    // 导出默认成员
    export default a...
    
  2. 导入(import)

    import * as mod from 'XXX';	// 引入所有成员
    import mod from 'XXX';	// 引入default成员
    import {a,b} from 'XXX';	// 引入a,b变量
    import 'XXX';	//引入整个文件
    let p = import('./mode');	//异步引入,使用什么就引入什么(当函数使用的)
    
  3. webpack编译

总结

  1. es7中的 幂操作 和 includes方法

    // 求三的五次方
    console.log(Math.pow(3, 5));	// 打印输出243
    
    // 在es7中求三的五次方
    console.log(3 ** 5);	// 打印输出243
    

    es7中的includes方法

  2. es8中 async/await

  3. es9中 rest/spread消除异步操作,及 异步迭代,Promise.all/race/finally

学习资料推荐:https://es6.ruanyifeng.com/

你可能感兴趣的:(笔记)