javascript高级 - 含ES6

文章目录

  • 面向对象编程
  • ES6中的类和对象
    • 面向对象
    • 对象
    • 创建类
    • 创建实例:
    • 类 constructor构造函数
    • 类的继承
      • super 关键字
      • 三个注意点
        • 1. 在ES6中类没有变量提升,所以必须先定义类,才能通过类实例化对象。
        • 2. 类里面的共有属性和方法一定要加this使用
        • 3.类里面的this指向问题
    • 面向对象案例- tab栏切换
  • 构造函数和原型
    • 构造函数
      • 原型对象 - prototype
      • 对象原型 `__proto__`
      • constructor 构造函数
      • 构造函数,实例,原型对象三者的关系
      • 原型链
    • 继承
      • call()
        • 借用构造函数继承父类型属性
        • 借用原型对象继承方法
  • ES5 中新增的方法
    • 数组方法
      • forEach()
      • filter()
      • some()
        • 商品查询案例
    • 字符串方法
      • trim()方法
    • 对象方法
      • Object.keys()
      • Object.defineProperty()
  • 函数进阶
    • 函数的定义和调用
      • 1.函数的定义方式
      • 2.函数的调用方式
    • this
      • 函数内部指向
      • 改变函数内部 this 指向
        • 1. call 方法
        • 2. apply 方法
        • 3.bind() 方法
        • call apply bind 总结
    • 严格模式
      • 认识严格模式
      • 开启严格模式
      • 严格模式的变化
    • 高阶函数
    • 闭包
      • 闭包案例
      • 闭包总结
    • 递归
      • 递归案例
      • 浅拷贝 和 深拷贝
  • 1.正则表达式
      • 1.1什么是正则表达式
      • 1.2 正则表达式的特点
    • 2.正则表达式在js中的使用
      • 2.1正则表达式的创建
      • 2.2测试正则表达式
    • 3.正则表达式中的特殊字符
      • 3.1正则表达式的组成
      • 3.2边界符
      • 3.3字符类
        • 3.3.1 [] 方括号
        • 3.3.2量词符
        • 3.3.3用户名表单验证
        • 3.3.4 括号总结
      • 3.4预定义类
      • 3.5正则替换replace
  • ES6语法
    • 目标
    • ES6相关概念(★★)
      • 什么是ES6
      • 为什么使用 ES6 ?
    • ES6新增语法
      • let(★★★)
        • let声明的变量只在所处于的块级有效
        • 不存在变量提升
        • 暂时性死区
        • 经典面试题
          • 使用 var
          • 使用let
        • 小结
      • const(★★★)
        • 具有块级作用域
        • 声明常量时必须赋值
        • 常量赋值后,值不能修改
        • 小结
      • let、const、var 的区别
      • 解构赋值(★★★)
        • 数组解构
        • 对象解构
        • 小结
      • 箭头函数(★★★)
        • 小结
        • 面试题
      • 剩余参数(★★)
        • 剩余参数和解构配合使用
    • ES6 的内置对象扩展
      • Array 的扩展方法(★★)
        • 扩展运算符(展开语法)
          • 扩展运算符可以应用于合并数组
          • 将类数组或可遍历对象转换为真正的数组
        • 构造函数方法:Array.from()
        • 实例方法:find()
        • 实例方法:findIndex()
        • 实例方法:includes()
      • String 的扩展方法
        • 模板字符串(★★★)
          • 模板字符串中可以解析变量
          • 模板字符串中可以换行
          • 在模板字符串中可以调用函数
        • 实例方法:startsWith() 和 endsWith()
        • 实例方法:repeat()
      • Set 数据结构(★★)
        • 实例方法
        • 遍历

面向对象编程

  1. 面向过程编程POP(Process-oriented programming)
    面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步- 步实现,使用的时候再一个一个的依次调用就可以了。
  • 举个栗子:将大象装进冰箱,面向过程做法。
    javascript高级 - 含ES6_第1张图片

面向过程,就是按照我们分析好了的步骤,按照步骤解决问题。

  1. 面向对象编程OOP (Object Oriented Programming)
    面向对象是把事务分解成为一个个对象,然后由对象之间分工与合作。
    举个栗子:将大象装进冰箱,面向对象做法。
    先找出对象,并写出这些对象的功能:
  2. 大象对象
    • 进去
  3. 冰箱对象
    • 打开
    • 关闭
  4. 使用大象和冰箱的功能

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

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

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

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

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

用面向过程的方法写出来的程序是一份蛋炒饭,
而用面向对象写出来的程序是一份盖浇饭。

ES6中的类和对象

面向对象

面向对象更贴近我们的实际生活,可以使用面向对象描述现实世界事物.但是事物分为具体的事物和抽象的事物
javascript高级 - 含ES6_第2张图片
面向对象的思维特点:

  1. 抽取(抽象)对象共用的属性和行为组织(封装)成-个类(模板
  2. 对类进行实例化,获取类的对象

面向对象编程我们考虑的是有哪些对象,按照面向对象的思维特点,不断的创建对象使用对象指挥对象做事情.

对象

现实生活中:万物皆对象,对象是一个具体的事物,看得见摸得着的实物。例如,-本书-辆汽车、一个人可以是"对象”, 一个数据库、-张网页、 一个远程服务器的连接也可以是"对象”。
在JavaScript中,对象是一组无序的相关属性和方法的集合 ,所有的事物都是对象,例如字符串、数值、数组、函数等。

对象是由属性方法组成的:

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

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

对象特指某一个,通过类实例化一个具体的对象
面向对象的思维特点:

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

创建类

语法:

class name:{
     
		//class body
	}

创建实例:

var xx = new name();

注意类必须new 实例化对象

类 constructor构造函数

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

类的继承

现实中的继承:子承父业,比如我们都继承了父亲的姓。
程序中的继承:子类可以继承父类的一些属性和方法。

语法:

class Father {
        //父类
}
class Son extends Father {
      
	// 子类继承父类
}

super 关键字

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

class Father {
     
		say (){
     
			return '我是爸爸';
		}
}
class son extends Father {
      //这样子类就继承了父类的属性和方法
	say() {
     
		// super.say()   super调用父类的方法
		return super.say() + '的儿子' ;
	}
var damao = new Son() ;
console. log (damao. say()) ;

语法:

class Person {
      // 父类
	constructor (surname) {
     
		this. surname = surname;
	}
}
class Student extends Person {
       //子类继承父类
	const ructor (surname, firstname) {
     
		super (surname);//调用父类的cons tructor (surname)
		this. firstname = firstname; //定义子类独有的属性
	}
}

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

三个注意点

1. 在ES6中类没有变量提升,所以必须先定义类,才能通过类实例化对象。

2. 类里面的共有属性和方法一定要加this使用

javascript高级 - 含ES6_第5张图片

3.类里面的this指向问题

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

<button>点击</button>
<script>
var that;
var _that;
	class Star {
     
		constructor(uname,age){
     
		//constructor 里面的this 指向的是  创建的实例对象
			this.uname = uname;
			this.age = age;
			this.btn = document.querySelector('button');
			this.btn.onclick = this.sing;
		}
		sing() {
     
			//这个 sing方法里面的this 指向的是 btn 这个按钮,因为这个按钮调用了这个函数
			console.log(this);
			//that 里面存储的是 constructor 里面的this
			console.log(that.uname)
		}
		dance(){
     
		//这个dance 里面的this 指向的是实例对象 ldh 因为ldh 调用了这个函数
			_that = this;
			console.log(this);
		}
	}

var ldh = new Star('刘德华');
console.log(that === ldh);
ldh.dance();
console.log(_that === ldh);
</script>

面向对象案例- tab栏切换

javascript高级 - 含ES6_第6张图片
javascript高级 - 含ES6_第7张图片
javascript高级 - 含ES6_第8张图片

javascript高级 - 含ES6_第9张图片
javascript高级 - 含ES6_第10张图片

构造函数和原型

构造函数

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

new 在执行会做四件事情:

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

javascript的构造函数中可以添加一些成员,可以在构造函数本身上添加,也可以在构造函数内部的 this 上添加。通过这两种方式添加的成员,就分别称为 静态成员实例成员

  • 静态成员:在构造函数上添加的成员称为静态成员,只能由构造函数本身来访问
  • 实例成员: 构造函数内部创建的对象成员称为实例成员,只能由实例化的对象来访问
 <script>
        // 构造函数中的属性和方法我们称为成员, 成员可以添加
        function Star(uname, age) {
     
            this.uname = uname;
            this.age = age;
            this.sing = function() {
     
                console.log('我会唱歌');

            }
        }
        var ldh = new Star('刘德华', 18);
        // 1.实例成员就是构造函数内部通过上面this添加的成员 uname age sing 就是实例成员
        // 实例成员只能通过实例化的对象来访问
        console.log(ldh.uname);
        ldh.sing();
        // console.log(Star.uname); // 不可以通过构造函数来访问实例成员
        
        
        // 2. 静态成员 在构造函数本身上添加的成员  sex 就是静态成员
        Star.sex = '男';
        // 静态成员只能通过构造函数来访问
        console.log(Star.sex);
        console.log(ldh.sex); // 不能通过对象来访问
    </script>

原型对象 - prototype

一般情况下,公共属性可以定义到构造函数里面,但是公共的方法(函数) 需要放到 构造函数里的 prototype 对象中

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

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

 <script>
        // 1. 构造函数的问题. 
        function Star(uname, age) {
     
            this.uname = uname;
            this.age = age;
            // this.sing = function() {
     
            //     console.log('我会唱歌');

            // }
        }
        // 把方法写到 prototype 中  实现共享
        Star.prototype.sing = function() {
     
            console.log('我会唱歌');
        }
        var ldh = new Star('刘德华', 18);
        var zxy = new Star('张学友', 19);

        // console.dir(Star);
        ldh.sing();
        zxy.sing();
        // 2. 一般情况下,我们的公共属性定义到构造函数里面, 公共的方法我们放到原型对象身上

        console.log(ldh.sing === zxy.sing);
    </script>

对象原型 __proto__

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

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

javascript高级 - 含ES6_第11张图片

constructor 构造函数

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

<script>
        function Star(uname, age) {
     
            this.uname = uname;
            this.age = age;
        }
        // 很多情况下,我们需要手动的利用constructor 这个属性指回 原来的构造函数
        // Star.prototype.sing = function() {
     
        //     console.log('我会唱歌');
        // };
        // Star.prototype.movie = function() {
     
        //     console.log('我会演电影');
        // }
        Star.prototype = {
     
            // 如果我们修改了原来的原型对象,给原型对象赋值的是一个对象,则必须手动的利用constructor指回原来的构造函数
            constructor: Star,
            sing: function() {
     
                console.log('我会唱歌');
            },
            movie: function() {
     
                console.log('我会演电影');
            }
        }
        var ldh = new Star('刘德华', 18);
        var zxy = new Star('张学友', 19);
        console.log(Star.prototype);
        console.log(ldh.__proto__);
        console.log(Star.prototype.constructor);
        console.log(ldh.__proto__.constructor);
    </script>

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

javascript高级 - 含ES6_第12张图片

原型链

javascript高级 - 含ES6_第13张图片

继承

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

call()

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

function fun(){
     
	console.log(this)
}
fun.call(thisArg,arg1,arg2)
  • thisArg: 当前调用函数this指向对象
  • arg1,arg2:传递的其他参数

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

 <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.score = score;
        }
        var son = new Son('刘德华', 18, 100);
        console.log(son);
    </script>

借用原型对象继承方法

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

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

        }
        var son = new Son('刘德华', 18, 100);
        console.log(son);
        console.log(Father.prototype);
        console.log(Son.prototype.constructor);
    </script>

ES5 中新增的方法

数组方法

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

forEach()

语法:

arr.forEach(funtion(currentAalue,index,arr){})

  • currentAalue:数组当前项的值
  • 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);
    </script>

filter()

语法:

array.filter(function(currentAalue,index,arr){})

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

some()

语法:

array.some(function(currentAalue,index,arr){})

  • some() 方法用于检测数组中的元素是否满足指定条件,查找数组中是否有满足条件的元素
  • 注意它返回的是 布尔值,如果查找到这个元素,就返回true,如果查找不到就返回false
  • 如果找到第一个满足条件的元素,则终止循环,不在继续查找
  • currentAalue:数组当前项的值
  • index:数组当前项的索引
  • arr:数组对象本身
<script>
        // some 查找数组中是否有满足条件的元素 
        // var arr = [10, 30, 4];
        // var flag = arr.some(function(value) {
     
        //     // return value >= 20;
        //     return value < 3;
        // });
        // console.log(flag);
        var arr1 = ['red', 'pink', 'blue'];
        var flag1 = arr1.some(function(value) {
     
            return value == 'pink';
        });
        console.log(flag1);
        // 1. filter 也是查找满足条件的元素 返回的是一个数组 而且是把所有满足条件的元素返回回来
        // 2. some 也是查找满足条件的元素是否存在  返回的是一个布尔值 如果查找到第一个满足条件的元素就终止循环
    </script>

商品查询案例

点击查看代码

字符串方法

trim()方法

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

srt.trim()

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

<script>
        // trim 方法去除字符串两侧空格
        var str = '   an  dy   ';
        console.log(str);
        var str1 = str.trim();
        console.log(str1);
        var input = document.querySelector('input');
        var btn = document.querySelector('button');
        var div = document.querySelector('div');
        btn.onclick = function() {
     
            var str = input.value.trim();
            if (str === '') {
     
                alert('请输入内容');
            } else {
     
                console.log(str);
                console.log(str.length);
                div.innerHTML = str;
            }
        }
    </script>

对象方法

Object.keys()

Object.keys() 用于获取对象自身属性所有的属性

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

        })
    </script>

Object.defineProperty()

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

Object.defineProperty(obj,prop,descriptor)
  • obj:必需。目标对象
  • prop:必需。需定义或修改的属性的名字
  • descriptor:必需。目标属性所拥有的特性

descriptor说明:以对象形式{} 书写

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

函数进阶

函数的定义和调用

1.函数的定义方式

  1. 函数是声明方式 function 关键字(命名函数)
  2. 函数表达式(匿名函数)
  3. new 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('a', 'b', 'console.log(a + b)');
        f(1, 2);
        // 4. 所有函数都是 Function 的实例(对象)
        console.dir(f);
        // 5. 函数也属于对象
        console.log(f instanceof Object);
    </script>

javascript高级 - 含ES6_第14张图片

2.函数的调用方式

  1. 普通函数
  2. 对象的方法
  3. 构造函数
  4. 绑定事件函数
  5. 定时器函数
  6. 立即执行函数
 <script>
        // 函数的调用方式

        // 1. 普通函数
        function fn() {
     
            console.log('人生的巅峰');

        }
        // fn();   fn.call()
        // 2. 对象的方法
        var o = {
     
            sayHi: function() {
     
                console.log('人生的巅峰');

            }
        }
        o.sayHi();
        // 3. 构造函数
        function Star() {
     };
        new Star();
        // 4. 绑定事件函数
        // btn.onclick = function() {};   // 点击了按钮就可以调用这个函数
        // 5. 定时器函数
        // setInterval(function() {}, 1000);  这个函数是定时器自动1秒钟调用一次
        // 6. 立即执行函数
        (function() {
     
            console.log('人生的巅峰');
        })();
        // 立即执行函数是自动调用
    </script>

this

函数内部指向

调用的方式不同决定了this的指向不同
一般指向我们的调用者。

javascript高级 - 含ES6_第15张图片

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

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

        }, 1000);
        // 6. 立即执行函数 this还是指向window
        (function() {
     
            console.log('立即执行函数的this' + this);
        })();
    </script>
</body>

改变函数内部 this 指向

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

1. call 方法

<script>   
        // 1. call()
        var o = {
     
            name: 'andy'
        }
        function fn(a, b) {
     
            console.log(this);
            console.log(a + b);
        };
        
        fn.call(o, 1, 2);
        // call 第一个可以调用函数 第二个可以改变函数内的this 指向
        // 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);
        }
        var son = new Son('刘德华', 18, '男');
        console.log(son);
    </script>

2. apply 方法

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

fun.apply(thisArg,[argsArray])
  • thisArg: 在fun函数运行时,指定的函数
  • argsArray:传递的值,必须包含在数组里面
  • 返回值就是函数的返回值,因为它就是调用函数
<script>
        // 2. apply()  应用 运用的意思
        var o = {
     
            name: 'andy'
        };

        function fn(arr) {
     
            console.log(this);
            console.log(arr); // 'pink'

        };
        fn.apply(o, ['pink']);
        // 1. 也是调用函数 第二个可以改变函数内部的this指向
        // 2. 但是他的参数必须是数组(伪数组)
        // 3. apply 的主要应用 比如说我们可以利用 apply 借助于数学内置对象求数组最大值 
        // Math.max();
        var arr = [1, 66, 3, 99, 4];
        var arr1 = ['red', 'pink'];
        // var max = Math.max.apply(null, arr);
        var max = Math.max.apply(Math, arr);
        var min = Math.min.apply(Math, arr);
        console.log(max, min);
    </script>

3.bind() 方法

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

fun.bind(thisArg,arg1,arg2,...)
  • thisArg: 在fun函数运行时,指定的函数
  • arg1,arg2:传递的其他参数
  • 返回值由指定的this值和初始化参数改造的原函数拷贝
<script>
        // 3. bind()  绑定 捆绑的意思
        var o = {
     
            name: 'andy'
        };

        function fn(a, b) {
     
            console.log(this);
            console.log(a + b);
        };
        var f = fn.bind(o, 1, 2);
        f();
        // 1. 不会调用原来的函数   可以改变原来函数内部的this 指向
        // 2. 返回的是原函数改变this之后产生的新函数
        // 3. 如果有的函数我们不需要立即调用,但是又想改变这个函数内部的this指向此时用bind
        // 4. 我们有一个按钮,当我们点击了之后,就禁用这个按钮,3秒钟之后开启这个按钮
        // var btn1 = document.querySelector('button');
        // btn1.onclick = function() {
     
        //     this.disabled = true; // 这个this 指向的是 btn 这个按钮
        //     // var that = this;
        //     setTimeout(function() {
     
        //         // that.disabled = false; // 定时器函数里面的this 指向的是window
        //         this.disabled = false; // 此时定时器函数里面的this 指向的是btn
        //     }.bind(this), 3000); // 这个this 指向的是btn 这个对象
        // }
        var btns = document.querySelectorAll('button');
        for (var i = 0; i < btns.length; i++) {
     
            btns[i].onclick = function() {
     
                this.disabled = true;
                setTimeout(function() {
     
                    this.disabled = false;
                }.bind(this), 2000);
            }
        }
    </script>

call apply bind 总结

相同点

都可以改变函数内部的this指向

区别点:

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

主要应用场景:

  1. call经常做传承
  2. apply 经常跟数组有关系,比如借助于数学对象实现数组最大值最小值
  3. bind 不调用函数,但是还想改变this指向,比如改变定时器内部的this指向。(重要)

严格模式

认识严格模式

javascript高级 - 含ES6_第16张图片

开启严格模式

  1. 为脚本开启严格模式

需要在 所有语句之间放一个特定语句 "use strict"; (或 'use strict');

// 第一种方式
<script>
	"use strict"
	console.log("这是严格模式")
</script>


//第二种方式
<script>
	(function(){
     
	"use strict"
	var nm = 10;
	function fn() {
     }
	})()
</script>


因为"use strict"加了引号,所以老版本的浏览器会把它当作一行普通字符串而忽略。

  1. 为函数开启严格模式

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

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

    function fun() {
     
        // 里面的还是按照普通模式执行
       }
    </script>

严格模式的变化

  1. 变量规定
    ① 在正常模式中,如果一 个变量没有声明就赋值,默认是全局变量。严格模式禁止这种用法,变量都必须先用
    var命令声明,然后再使用。
    ②严禁删除已经声明变量。例如, delete x;语法是错误的。
  1. 严格模式下this指向问题
    ①以前在全局作用域函数中的this指向window对象。
    ②严格模式下全局作用域中函数中的this是undefined.
    ③以前构造函数时不加new也可以调用,当普通函数, this 指向全局对象
    ④严格模式下,如果构造函数不加new调用, this会报错.
    ⑤new实例化的构造函数指向创建的对象实例。
    ⑥定时器this还是指向window。
    ⑦事件、对象还是指向调用者。
  1. 函数变化
    ①函数不能有重名的参数
    ②函数必须声明在顶层新版本的JavaScript 会引入“块级作用域”( ES6中已引入)。为了与新版本接轨,
    不允许在非函数的代码块内声明函数。
<script>
        'use strict';
        // 1. 我们的变量名必须先声明再使用
        // num = 10;
        // console.log(num);
        var num = 10;
        console.log(num);
        // 2.我们不能随意删除已经声明好的变量
        // delete num;
        // 3. 严格模式下全局作用域中函数中的 this 是 undefined。
        // function fn() {
     
        //     console.log(this); // undefined。

        // }
        // fn();
        // 4. 严格模式下,如果 构造函数不加new调用, this 指向的是undefined 如果给他赋值则 会报错.
        // function Star() {
     
        //     this.sex = '男';
        // }
        // // Star();
        // var ldh = new Star();
        // console.log(ldh.sex);
        // 5. 定时器 this 还是指向 window 
        // setTimeout(function() {
     
        //     console.log(this);

        // }, 2000);
        // a = 1;
        // a = 2;
        // 6. 严格模式下函数里面的参数不允许有重名
        // function fn(a, a) {
     
        //     console.log(a + a);

        // };
        // fn(1, 2);
        function fn() {
     }
    </script>

高阶函数

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

//第一种情况
funtion fn(callback){
     
	callback && callback()
}

fu(function(){
     alert('hi')}
//第二种情况
function fn(){
     
	return function() {
     }	
}

fn();
  • 此时fn ,就是一个高阶函数

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

闭包

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

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

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

        //     }
    </script>

闭包案例

  1. 循环注册点击事件
<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++) {
     
            lis[i].index = i;
            lis[i].onclick = function() {
     
                // console.log(i);
                console.log(this.index);

            }
        }
        // 2. 利用闭包的方式得到当前小li 的索引号
        for (var i = 0; i < lis.length; i++) {
     
            // 利用for循环创建了4个立即执行函数
            // 立即执行函数也成为小闭包因为立即执行函数里面的任何一个函数都可以使用它的i这变量
            (function(i) {
     
                // console.log(i);
                lis[i].onclick = function() {
     
                    console.log(i);

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

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

闭包总结

  1. 闭包是什么?

闭包是一个函数(一个作用域可以访问另一个函数的局部变量)

  1. 闭包的作用是什么吗?

延伸变量的作用范围

<script>
        // 思考题 1:

        var name = "The Window";
        var object = {
     
            name: "My Object",
            getNameFunc: function() {
     
                return function() {
     
                    return this.name;
                };
            }
        };

        console.log(object.getNameFunc()())
        var f = object.getNameFunc();
        // 类似于
        var f = function() {
     
            return this.name;
        }
        f();

        // 思考题 2:

        var name = "The Window";  
        var object = {
         
            name: "My Object",
            getNameFunc: function() {
     
                var that = this;
                return function() {
     
                    return that.name;
                };
            }
        };
        console.log(object.getNameFunc()())
    </script>

递归

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

  • 简单理解: 函数内部自己调用自己。这个函数就是递归函数
  • 递归函数的作用和循环效果一样
<script>
        // 递归函数 : 函数内部自己调用自己, 这个函数就是递归函数
        var num = 1;

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

            if (num == 6) {
     
                return; // 递归里面必须加退出条件
            }
            num++;
            fn();
        }
        fn();
    </script>

递归案例

  1. 利用递归函数求1~n的阶乘 1 * 2 * 3 * 4 * …n
<script>
        // 利用递归函数求1~n的阶乘 1 * 2 * 3 * 4 * ..n
        function fn(n) {
     
            if (n == 1) {
     
                return 1;
            }
            return n * fn(n - 1);
        }
        console.log(fn(3));
        console.log(fn(4));
        // 详细思路 假如用户输入的是3
        //return  3 * fn(2)
        //return  3 * (2 * fn(1))
        //return  3 * (2 * 1)
        //return  3 * (2)
        //return  6
    </script>
  1. 利用递归函数求斐波那契数列(兔子序列) 1、1、2、3、5、8、13、21…
<script>
        // 利用递归函数求斐波那契数列(兔子序列)  1、1、2、3、5、8、13、21...
        // 用户输入一个数字 n 就可以求出 这个数字对应的兔子序列值
        // 我们只需要知道用户输入的n 的前面两项(n-1 n-2)就可以计算出n 对应的序列值
        function fb(n) {
     
            if (n === 1 || n === 2) {
     
                return 1;
            }
            return fb(n - 1) + fb(n - 2);
        }
        console.log(fb(3));
        console.log(fb(6));
    </script>
  1. 根据id返回对应的数据对象
 <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>

浅拷贝 和 深拷贝

  1. 浅拷贝只是拷贝一层,更深层次对象级别的只拷贝引用。
  2. 深拷贝拷贝多层,每一级别的数据都会拷贝。
  3. 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 = 20;
        // console.log(obj);

        console.log('--------------');
        Object.assign(o, obj);
        console.log(o);
        o.msg.age = 20;
        console.log(obj);
    </script>

javascript高级 - 含ES6_第17张图片

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

            }
        }
        deepCopy(o, obj);
        console.log(o);

        var arr = [];
        console.log(arr instanceof Object);
        o.msg.age = 20;
        console.log(obj);
    </script>

javascript高级 - 含ES6_第18张图片

1.正则表达式

1.1什么是正则表达式

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

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

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

1.2 正则表达式的特点

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

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

2.1正则表达式的创建

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

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

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

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

 var rg = /123/;

2.2测试正则表达式

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

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

在这里插入图片描述

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

3.1正则表达式的组成

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

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

MDN

jQuery 手册:正则表达式部分

[正则测试工具](

3.2边界符

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

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

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

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

3.3字符类

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

3.3.1 [] 方括号

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

var rg = /[abc]/; // 只要包含有a 或者 包含有b 或者包含有c 都返回为true
console.log(rg.test('andy'));//true
console.log(rg.test('baby'));//true
console.log(rg.test('color'));//true
console.log(rg.test('red'));//false
var rg1 = /^[abc]$/; // 三选一 只有是a 或者是 b  或者是c 这三个字母才返回 true
console.log(rg1.test('aa'));//false
console.log(rg1.test('a'));//true
console.log(rg1.test('b'));//true
console.log(rg1.test('c'));//true
console.log(rg1.test('abc'));//true
----------------------------------------------------------------------------------
var reg = /^[a-z]$/ //26个英文字母任何一个字母返回 true  - 表示的是a 到z 的范围  
console.log(reg.test('a'));//true
console.log(reg.test('z'));//true
console.log(reg.test('A'));//false
-----------------------------------------------------------------------------------
//字符组合
var reg1 = /^[a-zA-Z0-9]$/; // 26个英文字母(大写和小写都可以)任何一个字母返回 true  
------------------------------------------------------------------------------------
//取反 方括号内部加上 ^ 表示取反,只要包含方括号内的字符,都返回 false 。
var reg2 = /^[^a-zA-Z0-9]$/;
console.log(reg2.test('a'));//false
console.log(reg2.test('B'));//false
console.log(reg2.test(8));//false
console.log(reg2.test('!'));//true

3.3.2量词符

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

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

3.3.3用户名表单验证

功能需求:

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

在这里插入图片描述

在这里插入图片描述

分析:

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

3.3.4 括号总结

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

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

3.小括号表示优先级

正则表达式在线测试

3.4预定义类

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

在这里插入图片描述

案例:验证座机号码

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

表单验证案例

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

3.5正则替换replace

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

var str = 'andy和red';
var newStr = str.replace('andy', 'baby');
console.log(newStr)//baby和red
//等同于 此处的andy可以写在正则表达式内
var newStr2 = str.replace(/andy/, 'baby');
console.log(newStr2)//baby和red
//全部替换
var str = 'abcabc'
var nStr = str.replace(/a/,'哈哈')
console.log(nStr) //哈哈bcabc
//全部替换g
var nStr = str.replace(/a/a,'哈哈')
console.log(nStr) //哈哈bc哈哈bc
//忽略大小写i
var str = 'aAbcAba';
var newStr = str.replace(/a/gi,'哈哈')//"哈哈哈哈bc哈哈b哈哈"

案例:过滤敏感词汇

<textarea name="" id="message"></textarea> <button>提交</button>
<div></div>
<script>
    var text = document.querySelector('textarea');
    var btn = document.querySelector('button');
    var div = document.querySelector('div');
    btn.onclick = function() {
     
    	div.innerHTML = text.value.replace(/激情|gay/g, '**');
    }
</script>

ES6语法

目标

  • 能够说出使用let关键字声明变量的特点
  • 能够使用解构赋值从数组中提取值
  • 能够说出箭头函数拥有的特性
  • 能够使用剩余参数接收剩余的函数参数
  • 能够使用拓展运算符拆分数组
  • 能够说出模板字符串拥有的特性

ES6相关概念(★★)

什么是ES6

ES 的全称是 ECMAScript , 它是由 ECMA 国际标准化组织,制定的一项脚本语言的标准化规范。
javascript高级 - 含ES6_第19张图片

为什么使用 ES6 ?

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

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

ES6新增语法

let(★★★)

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

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

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

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

不存在变量提升

console.log(a); // a is not defined 
let a = 20;

暂时性死区

利用let声明的变量会绑定在这个块级作用域,不会受外界的影响

 var tmp = 123;
 if (true) {
      
     tmp = 'abc';
     let tmp; 
 } 

经典面试题

使用 var
 var arr = [];
 for (var i = 0; i < 2; i++) {
     
     arr[i] = function () {
     
         console.log(i); 
     }
 }
 arr[0]();
 arr[1]();

javascript高级 - 含ES6_第20张图片

使用let

经典面试题图解: 此题的关键点在于变量i是全局的,函数执行时输出的都是全局作用域下的i值。

 let arr = [];
 for (let i = 0; i < 2; i++) {
     
     arr[i] = function () {
     
         console.log(i); 
     }
 }
 arr[0]();
 arr[1]();

javascript高级 - 含ES6_第21张图片

经典面试题图解: 此题的关键点在于每次循环都会产生一个块级作用域,每个块级作用域中的变量都是不同的,函数执行时输出的是自己上一级(循环产生的块级作用域)作用域下的i值.

小结

  • let关键字就是用来声明变量的
  • 使用let关键字声明的变量具有块级作用域
  • 在一个大括号中 使用let关键字声明的变量才具有块级作用域 var关键字是不具备这个特点的
  • 防止循环变量变成全局变量
  • 使用let关键字声明的变量没有变量提升
  • 使用let关键字声明的变量具有暂时性死区特性

const(★★★)

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

let 用于保存变量 cosnt 用来保存 对象和数组

具有块级作用域

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

声明常量时必须赋值

const PI; // Missing initializer in const declaration

常量赋值后,值不能修改

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

const ary = [100, 200];
ary[0] = 'a';
ary[1] = 'b';
console.log(ary); // ['a', 'b']; 
ary = ['a', 'b']; // Assignment to constant variable.

小结

  • const声明的变量是一个常量
  • 既然是常量不能重新进行赋值,如果是基本数据类型,不能更改值,如果是复杂数据类型,不能更改地址值
  • 声明 const时候必须要给定值

let、const、var 的区别

  • 使用 var 声明的变量,其作用域为该语句所在的函数内,且存在变量提升现象
  • 使用 let 声明的变量,其作用域为该语句所在的代码块内,不存在变量提升
  • 使用 const 声明的是常量,在后面出现的代码中不能再修改该常量的值

javascript高级 - 含ES6_第22张图片

解构赋值(★★★)

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

数组解构

 let [a, b, c] = [1, 2, 3];
 console.log(a)//1
 console.log(b)//2
 console.log(c)//3
//如果解构不成功,变量的值为undefined

对象解构

 let person = {
      name: 'zhangsan', age: 20 }; 
 let {
      name, age } = person;
 console.log(name); // 'zhangsan' 
 console.log(age); // 20

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

小结

  • 解构赋值就是把数据结构分解,然后给变量进行赋值
  • 如果结构不成功,变量跟数值个数不匹配的时候,变量的值为undefined
  • 数组解构用中括号包裹,多个变量用逗号隔开,对象解构用花括号包裹,多个变量用逗号隔开
  • 利用解构赋值能够让我们方便的去取对象中的属性跟方法

箭头函数(★★★)

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

() => {
     } //():代表是函数; =>:必须要的符号,指向哪一个代码块;{}:函数体
const fn = () => {
     }//代表把一个函数赋值给fn

函数体中只有一句代码,且代码的执行结果就是返回值,可以省略大括号

 function sum(num1, num2) {
      
     return num1 + num2; 
 }
 //es6写法
 const sum = (num1, num2) => num1 + num2; 

如果形参只有一个,可以省略小括号

 function fn (v) {
     
     return v;
 } 
//es6写法
 const fn = v => v;

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

const obj = {
      name: '张三'} 
 function fn () {
      
     console.log(this);//this 指向 是obj对象
     return () => {
      
         console.log(this);//this 指向 的是箭头函数定义的位置,那么这个箭头函数定义在fn里面,而这个fn指向是的obj对象,所以这个this也指向是obj对象
     } 
 } 
 const resFn = fn.call(obj); 
 resFn();

小结

  • 箭头函数中不绑定this,箭头函数中的this指向是它所定义的位置,可以简单理解成,定义箭头函数中的作用域的this指向谁,它就指向谁
  • 箭头函数的优点在于解决了this执行环境所造成的一些问题。比如:解决了匿名函数this指向的问题(匿名函数的执行环境具有全局性),包括setTimeout和setInterval中使用this所造成的问题

面试题

var age = 100;

var obj = {
     
	age: 20,
	say: () => {
     
		alert(this.age)
	}
}

obj.say();//箭头函数this指向的是被声明的作用域里面,而对象没有作用域的,所以箭头函数虽然在对象中被定义,但是this指向的是全局作用域

剩余参数(★★)

剩余参数语法允许我们将一个不定数量的参数表示为一个数组,不定参数定义方式,这种方式很方便的去声明不知道参数情况下的一个函数

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

剩余参数和解构配合使用

let students = ['wangwu', 'zhangsan', 'lisi'];
let [s1, ...s2] = students; 
console.log(s1);  // 'wangwu' 
console.log(s2);  // ['zhangsan', 'lisi']

ES6 的内置对象扩展

Array 的扩展方法(★★)

扩展运算符(展开语法)

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

 let ary = [1, 2, 3];
 ...ary  // 1, 2, 3
 console.log(...ary);    // 1 2 3,相当于下面的代码
 console.log(1,2,3);
扩展运算符可以应用于合并数组
// 方法一 
 let ary1 = [1, 2, 3];
 let ary2 = [3, 4, 5];
 let ary3 = [...ary1, ...ary2];
 // 方法二 
 ary1.push(...ary2);
将类数组或可遍历对象转换为真正的数组
let oDivs = document.getElementsByTagName('div'); 
oDivs = [...oDivs];

构造函数方法:Array.from()

将伪数组或可遍历对象转换为真正的数组

//定义一个集合
let arrayLike = {
     
    '0': 'a',
    '1': 'b',
    '2': 'c',
    length: 3
}; 
//转成数组
let arr2 = Array.from(arrayLike); // ['a', 'b', 'c']

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

 let arrayLike = {
      
     "0": 1,
     "1": 2,
     "length": 2
 }
 let newAry = Array.from(arrayLike, item => item *2)//[2,4]

注意:如果是对象,那么属性需要写对应的索引

实例方法:find()

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

let ary = [{
     
     id: 1,
     name: '张三'
 }, {
      
     id: 2,
     name: '李四'
 }]; 
 let target = ary.find((item, index) => item.id == 2);//找数组里面符合条件的值,当数组中元素id等于2的查找出来,注意,只会匹配第一个

实例方法:findIndex()

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

let ary = [1, 5, 10, 15];
let index = ary.findIndex((value, index) => value > 9); 
console.log(index); // 2

实例方法:includes()

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

[1, 2, 3].includes(2) // true 
[1, 2, 3].includes(4) // false

String 的扩展方法

模板字符串(★★★)

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

let name = `zhangsan`;

模板字符串中可以解析变量
let name = '张三'; 
let sayHello = `hello,my name is ${
       name}`; // hello, my name is zhangsan
模板字符串中可以换行
 let result = {
      
     name: 'zhangsan', 
     age: 20,
     sex: '男' 
 } 
 let html = ` 
${ result.name} ${ result.age} ${ result.sex}
`
;
在模板字符串中可以调用函数
const sayHello = function () {
      
    return '哈哈哈哈 追不到我吧 我就是这么强大';
 }; 
 let greet = `${
       sayHello()} 哈哈哈哈`;
 console.log(greet); // 哈哈哈哈 追不到我吧 我就是这么强大 哈哈哈哈

实例方法:startsWith() 和 endsWith()

  • startsWith():表示参数字符串是否在原字符串的头部,返回布尔值
  • endsWith():表示参数字符串是否在原字符串的尾部,返回布尔值
let str = 'Hello world!';
str.startsWith('Hello') // true 
str.endsWith('!')       // true

实例方法: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]);//{1, 2, 3, 4}

实例方法

  • add(value):添加某个值,返回 Set 结构本身
  • delete(value):删除某个值,返回一个布尔值,表示删除是否成功
  • has(value):返回一个布尔值,表示该值是否为 Set 的成员
  • clear():清除所有成员,没有返回值
 const s = new Set();
 s.add(1).add(2).add(3); // 向 set 结构中添加值 
 s.delete(2)             // 删除 set 结构中的2值   
 s.has(1)                // 表示 set 结构中是否有1这个值 返回布尔值 
 s.clear()               // 清除 set 结构中的所有值
 //注意:删除的是元素的值,不是代表的索引

遍历

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

s.forEach(value => console.log(value))

你可能感兴趣的:(javascript)