目录
一.面向对象编程介绍
1.两大编程思想
(1)面向过程
POP(Process-oriented programming), 分析出解决问题所需要的步骤,然后用函数把这些步骤一步步实现,使用时再一个个依次调用
- 优点:性能比面向对象高,适合跟硬件联系很紧密的东西,例如单片机就采用的面向过程编程
- 缺点:没有面向对象易维护、易复用、易扩展
(2)面向对象
OOP (Object Oriented Programming), 把事务分解成为一个个对象,然后由对象之间分工与合作
- 优点:易维护、易复用、易扩展,由于面向对象有封装、继承、多态性的特性,可以设计出低耦合的系统,使系统 更加灵活、更加易于维护, 更适合多人合作的大型软件项目
- 缺点:性能比面向过程低
特性: 封装性, 继承性, 多态性
思维特点: (创建对象,使用对象,指挥对象做事情)
- 抽取(抽象)对象共用的属性和行为组织(封装)成一个类(模板)
- 对类进行实例化, 获取类的对象
ES6之前通过构造函数 + 原型实现面向对象编程,ES6通过类实现
2.ES6中的类和对象
(1)对象
万物皆对象, 在 JavaScript 中,对象是一组无序的相关属性和方法的集合,所有的事物都是对象,例如字符串、数值、数组、函数等
对象是由属性和方法组成的:
- 属性:事物的特征,在对象中用属性来表示(常用名词)
- 方法:事物的行为,在对象中用方法来表示(常用动词)
创建对象方式:
- 对象字面量 e.g: var obj = { }
- new Object() e.g: var obj = new Object();
- 自定义构造函数 e.g: function Stra( [ 形参 ] ){ }
(2)类class
在 ES6 中新增加了类的概念,可使用 class 关键字声明一个类,之后以这个类来实例化对象
- 类抽象了对象的公共部分,它泛指某一大类(class)
- 对象特指某一个, 通过类实例化一个具体的对象
(3)创建类
- class Name{ } class关键字创建类, 类名首字母大写
- 创建实例对象: var xx = new name(); 类必须使用new实例化对象, new不可省
(4)constructor构造函数
- constructor() 方法是类的构造函数(默认方法),用于传递参数,返回实例对象
- 通过 new 命令生成对象实例时,自动调用该方法
- 如果没有显示定义, 类内部会自动给我们创建一个constructor()
constructor(name,age) { // constructor 构造器或者构造函数
this.name = name;
this.age = age;
}
(5)类添加方法
类中的函数,不需写function, 多个函数方法之间,不需逗号分隔
(6)类的继承
extends关键字 子类可以继承父类的一些属性和方法
(7)super关键字
- super 关键字用于访问和调用对象父类上的函数, 可调用父类的构造函数, 也可调用父类的普通函数
- 子类在构造函数中使用super, 必须放到 this 前面(必须先调用父类的构造方法,在使用子类构造方法)
- 父类和子类的函数中this指向不同
- 调用父类构造函数: super(); 调用父类普通方法 e.g: super.say();
注意:
- ES6中没有变量提升, 所有必须先定义类,才能实例化对象
- 类里面的共有属性和方法一定要加this使用
- 类里面的this指向问题
- constructor 里面的this指向实例对象, 方法里面的this 指向这个方法的调用者
二.构造函数和原型
1.构造函数
(1)概述
- ES6,全称 ECMAScript 6.0 ,2015.06 发版。目前浏览器的 JavaScript 是ES5 版本,大多数高版本的浏览器也支持 ES6,不过只实现了 ES6 的部分特性和功能。 在 ES6之前 ,对象不是基于类创建的,而是用一种称为构建函数的特殊函数来定义对象和它们的特征
- 构造函数是一种特殊的函数,主要用来初始化对象,即为对象成员变量赋初始值, 总与 new 一起使用. 可以把对象中一些公共的属性和方法抽取出来,然后封装到这个函数里面
new 在执行时会做四件事情:
- 在内存中创建一个新的空对象
- 让 this 指向这个新的对象
- 执行构造函数里面的代码,给这个新对象添加属性和方法
- 返回这个新对象(所以构造函数里面不需要 return )
(2)成员
JavaScript的构造函数中可以添加一些成员,可在构造函数本身上/内部的 this 上添加.
- 静态成员:在构造函数本身上添加的成员 ,只能由构造函数本身来访问,不能通过对象来访问 e.g: Star.age=1
- 实例成员:在构造函数内部创建的对象成员称为实例成员,只能由实例化的对象来访问,不能通过构造函数来访问 e.g: ldh.sing();
(3)问题
存在浪费内存的问题, 创建一个实例,开辟一块空间,使用同一个函数存在内存浪费问题
解决: 原型分配
(4)特点
- 构造函数有原型对象prototype
- 构造函数原型对象prototype里面有constructor指向构造函数本身
- 构造函数可通过原型对象添加方法
- 构造函数的实例对象有__proto__对象原型指向构造函数的原型对象
2.原型
(1)原型对象prototype
- 构造函数通过原型分配的函数是所有对象所共享的 (共享资源,节约内存)
- JavaScript 规定, 每一个构造函数都有一个prototype属性,指向另一个对象. 注意prototype 就是一个对象,这个对象的所有属性和方法,都会被构造函数所拥有
- 把那些不变的方法,直接定义在 prototype对象上,这样所有对象的实例就可以共享这些方法
(2) 对象原型 __proto__
- 对象都会有一个属性 __proto__ 指向构造函数的 prototype 原型对象, 使得对象可以使用构造函数 prototype原型对象的属性和方法
- __proto__对象原型和原型对象 prototype 是等价的
- 意义:为对象的查找机制提供一个方向路线,但是它是一个非标准属性,不可使用这个属性(不能赋值等操作), 它只是内部指向原型对象 prototype
(3)构造函数constructor
- 对象原型和原型对象里都有一个constructor 属性,constructor称构造函数, 它指回构造函数本身
- 主要用于记录该对象引用于哪个构造函数,它可以让原型对象重新指向原来的构造函数
- 一般情况下,对象的方法都在构造函数的原型对象中设置. 如果有多个对象的方法,可以给原型对象采取对象形式赋值,但会覆盖构造函数原型对象原来的内容,修改后的原型对象就不再指向当前构造函数了. 此时,可以在修改后的原型对象中,添加一个constructor指向原来的构造函数
(4)原型链
(5)JavaScript 的成员查找机制(规则)
- 当访问一个对象的属性(包括方法)时,首先查找这个对象自身有没有该属性
- 如果没有就查找它的原型(也就是 __proto__指向的 prototype 原型对象),如果还没有就查找原型对象的原型(Object的原型对象),依此类推一直找到 Object 为止(null)。
- __proto__对象原型的意义就在于为对象成员查找机制提供一个方向路线(就近原则)
(6)this指向
- 构造函数中的this 指向实例对象.
- 原型对象里面放的是方法,this指向的是方法的调用者, 也就是这个实例对象.
(7)扩展内置对象
- 通过原型对象,对原来的内置对象进行扩展自定义的方法 e.g:给数组增加自定义求偶数和的功能Array.prototype.xxx = function(){}
- 不采用添加多种方法赋值对象形式Array.prototype = {},避免覆盖原有方法
<script>
function Star(uname, uage){ //构造函数都有一个prototype对象, 公共属性定义在构造函数中, 公共方法放在原型对象身上
this.uname = name; //构造函数中,this指向实例对象 zz,mm
this.age = age;
}
var that = this;
// 单个对象方法
Star.prototype.sing = function(){ //所有对象实例可共享
console.log('我是prototype原型对象');
that = this; //原型对象里面放的是方法,里面的this指向 这个方法的调用者 即实例对象 zz,mm
}
// 多个对象方法
Star.prototype = {
constructor: Star, //修改原来的原型对象,给其赋值的是一个对象,则必须手动利用constructor指向原来的构造函数
dance: function(){
console.log('我会dance');
},
movie: function(){
console.log('我会看movie');
}
}
var zz = new Star('zz',18);
var mm = new Star('mm',18);
zz.sing();
mm.sing();
</script>
3.类的本质
- class本质还是function, 类可认为是构造函数的另一种写法
- 类有原型对象prototype, 可通过原型对象添加方法, 所有方法都定义在类的prototype属性上
- 类的原型对象prototype里面有constructor指向类本身
- 类创建的实例,里面也有__proto__ 对象原型指向类的prototype原型对象
- ES6的类它的绝大部分功能,ES5都可以做到,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已,所以ES6的类其实就是语法糖
- 语法糖: 就是一种便捷写法, 简单理解:有两种方法可以实现同样的功能, 但是一种写法更加清晰、方便,那么这个方法就是语法糖
三.继承
ES6之前未提供 extends 继承,可通过构造函数+原型对象模拟实现, 被称为组合继承
1.call()
调用函数, 并且修改函数运行时的 this 指向
fun.call(thisArg, arg1, arg2, ...) thisArg:当前调用函数 this 的指向对象 arg1:传递的其他参数
2.借用构造函数继承父类型属性
核心原理: 通过 all()把父类型的 this 指向子类型的 this , 实现子类型继承父类型的属性
Father.call(this, uname, uage); //调用父构造函数,把父this,改为子this, 还可添加自身的属性
3.借用原型对象继承父类型方法
- 核心原理: 将子类所共享的方法提取出来,让子类的 prototype 原型对象 = new 父类()
- 本质: 子类原型对象等于是实例化父类,父类实例化另外开辟空间,就不会影响原来父类原型对象
- 将子类的 constructor 从新指向子类的构造函数
// 借用原型对象实现继承 父原型对象与实例对象地址不同
Son.prototype = new Father(); //创建Father的实例对象
Son.prototype.constructor = Son; //利用对象形式修改了原型对象,需利用constructor指回原来的构造函数
四.ES5中新增方法
1.概述
ES5新增了一些方法,可以很方便的操作数组或者字符串,主要包括:数组方法,字符串方法,对象方法
2.数组方法
迭代(遍历)方法: forEach()、map()、filter()、some()、every()
(1)forEach()
array.forEach(function(currentValue, index, arr)) 遍历
- currentValue:数组当前项的值
- index:数组当前项的索引
- arr:数组对象本身
(2)filter()
var newArr = array.filter(function(currentValue, index, arr){ 筛选条件 })
- 主要用于筛选数组
- 直接返回一个新数组,其中元素是通过检查指定数组中符合条件的所有元素
(3)some()
array.some(function(currentValue, index, arr))
- 用于检测数组中的元素是否满足指定条件(查找数组中是否有满足条件的元素)
- 返回值是布尔值, 查到返回true ,查不到返回false.
- 如果找到第一个满足条件的元素,则终止循环. 不在继续查找.
forEach()和some()区别:
- forEach(), filter() 中 return true; 不会终止循环
- some()中 return true; 终止循环, 迭代效率更高
3.字符串方法
str.trim(); 去除字符串两端的空白字符(空格)
trim() 方法 不影响原字符串本身,返回一个新的字符串
4.对象方法
(1)Object.keys()
用于获取对象自身所有的属性名, 返回一个由属性名组成的数组
效果类似 for…in
(2)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
// Object.defineProperty() 定义新属性或修改原有的属性
Object.defineProperty(obj,'sex',{ //有则修改,无则加, 可更改限定条件
value: 'nv'
});
console.log(obj); //{id: 1, uname: 'mm', uage: 21, sex: 'nv'}
Object.defineProperty(obj,'id',{
// false不允许修改属性值 true允许
writable: false
});
obj.id = 22;
console.log(obj); //{id: 1, uname: 'mm', uage: 21, sex: 'nv'}
五.函数
1.定义方式
- function 关键字 (命名函数) 自定义函数function fn(){ };
- 函数表达式 (匿名函数) var fun = function(){ }
- new Function('参数1', '参数2', '函数体') e.g: var f = new Function('a','b','console.log(a+b)');
- Function 里面参数都必须是字符串格式
- 所有函数都是 Function 的实例(对象)
- 函数也属于对象
- 第三种方式执行效率低,也不方便书写,因此较少使用
2.调用方式
- 普通函数
- 对象的方法
- 构造函数
- 绑定事件函数
- 定时器函数
- 立即执行函数
// 函数调用
// 1.普通函数 this指向window
function f1(){ };
f1(); //也可用 f1.call();
// 2.对象的方法 this指向函数调用者 f2
var f2 = {
sayHi: function(){ }
}
f2.sayHi();
// 3.构造函数 this指向实例对象mm, 原型对象prototype中this也指向实例对象
function f3(){ };
var mm = new f3();
// 4.绑定事件函数 this指向函数调用者btn
btn.onclick = function(){ }; //点击按钮调用
// 5.定时器函数 this指向函数调用者window(可省)
window.setInterval(function(){ },1000); //定时器自动一秒钟调用一次
// 6. 立即执行函数 this指向window,与普通函数同
(function(){ })(); //立即执行,自动调用
3.this
(1)函数内this指向
函数调用方式的不同决定了this 的指向不同, 一般指向函数调用者.
(2)改变this指向
JavaScript专门提供了一些函数方法来处理函数内部this的指向问题,常有bind(),call(),apply()
call() 方法 fun.call(thisArg, arg1, arg2, ...)
- 调用一个对象(可调用函数) , 可改变函数的 this 指向
- 应用: 实现继承
apply()方法 fun.apply(thisArg, [argsArray])
- 可调用函数, 可改变函数的 this 指向, 参数必须为数组形式 [argsArray]
- 应用: 常跟数组有关系. 比如借助于数学对象实现数组最大值最小值
bind()方法 fun.bind(thisArg, arg1, arg2, ...)
- 不会调用函数, 可以改变函数内部this指向
- 返回由指定的 this 值和初始化参数改造的原函数拷贝(返回原函数改变this之后产生的新函数)
- 应用: 比如改变定时器内部的this指向.
// 改变函数内this指向
// 1. call() 可调用函数,可改变函数内this指向. 主要作用: 实现继承
function Father(uname){
this.uname = uname;
}
function Son(uname, uage){
Father.call(this,uname,uage); //调用函数Father,将其this修改为Son中的this,并且添加uage属性 应用: 实现继承
}
// 2. apply() 可调用函数,可改变函数内this指向 参数必须是数组(伪数组) 应用: 利用apply借助数学内置对象求最大/小值
var o = { };
function fn(arr){
console.log(arr);
}
fn.apply(o,['pink']);
var arr1 = [1,3,5,2,4]; //求数组最大/小值
console.log(Math.max.apply(null, arr1));//不需改变this指向写null
// 3. bind() 绑定
var fn1 = fn.bind(o); //不调用原函数, 可改变原函数内部this指向
fn1; //返回原函数改变this之后产生的新函数
// 点击按钮后禁用,三秒后开启使用 定时器函数不需要立即执行,改变this指向用bind
var btn = document.querySelector('button');
btn.onclick = function(){
this.disabled = true; //this指向btn 按钮禁用
setInterval(function(){
this.disabled = false; //开启按钮 定时器中this指向window
}.bind(this),3000); //此处this指向btn对象
}
4.严格模式
(1)概述
JavaScript 除了提供正常模式外,还提供了严格模式(strict mode).ES5 的严格模式是采用具有限制性JavaScript 变体的一种方式,即在严格的条件下运行 JS 代码
严格模式在 IE10 以上版本的浏览器中才会被支持,旧版本浏览器中会被忽略
严格模式对正常的 JavaScript 语义做了一些更改:
- 消除了 Javascript 语法的一些不合理、不严谨之处,减少了一些怪异行为
- 消除代码运行的一些不安全之处,保证代码运行的安全
- 提高编译器效率,增加运行速度
- 禁用了在 ECMAScript 的未来版本中可能会定义的一些语法,为未来新版本的 Javascript 做好铺垫. 比如一些保留字如:class, enum, export, extends, import, super 不能做变量名
(2)开启严格模式
严格模式可以应用到整个脚本或个别函数中
严格模式分为: 脚本开启严格模式, 为函数开启严格模式
<!-- 为整个脚本(script标签) 开启严格模式 -->
<script>
// 1. 在所有语句之前放一个特定语句“use strict”;
'use strick'; //"use strict"加了引号,所以老版本的浏览器会把它当作一行普通字符串而忽略
// 下边的js代码会严格按照严格模式执行代码
num = 10;
console.log(num);
</script>
<script>
// 2. 立即执行的匿名函数之中 “use strict”;
(function(){ //防止脚本污染,独立创建一个作用域而不影响其他 script 脚本文件
'use strick';
})();
</script>
<!-- 为某个函数开启严格模式 -->
<script>
function fn(){ //按照严格模式执行
'use strick';
}
function fun(){ } //按照普通模式执行
</script>
(3)严格模式中的变化
严格模式对 Javascript 的语法和行为, 都做了一些改变
变量规定:
- 在正常模式中, 如果一个变量没有声明就赋值,默认是全局变量. 严格模式禁止这种用法,变量都必须先用var 命令声明, 然后再使用。
- 严禁删除已经声明变量。e.g: delete x; 语法是错误的
函数变化:
- 函数不能有重名的参数
- 函数必须声明在顶层. 新版本的 JavaScript 会引入“块级作用域”(ES6 中已引入). 为了与新版本接轨,不允许在非函数的代码块内声明函数
严格模式下 this 指向问题:
- 正常模式下,在全局作用域函数中this指向 window 对象
- 严格模式下全局作用域中函数中的this是 undefined
- 正常模式下,构造函数不加new也可以调用,当普通函数,this 指向全局对象
- 严格模式下,如果构造函数不加new调用, this指向undefined, 如果给他赋值则会报错
- new 实例化的构造函数指向创建的对象实例
- 定时器 this 还是指向window
- 事件、对象还是指向调用者
更多严格模式要求参考:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Strict_mode
5.高阶函数
高阶函数是对其他函数进行操作的函数, 它接收函数作为参数或将函数作为返回值输出
- 函数是一种数据类型,同样可以作为参数,传递给另外一个参数使用, 最典型的就是作为回调函数
- 同理函数也可以作为返回值传递回来
6.闭包
(1)变量作用域
变量根据作用域的不同分为两种:全局变量和局部变量
- 函数内部可以使用全局变量, 函数外部不可以使用局部变量
- 当函数执行完毕, 本作用域内的局部变量会销毁
(2)闭包概述
- 闭包(closure)指有权访问另一个函数作用域中变量的函数。 ----- JavaScript 高级程序设计
- 简单理解就是 ,一个作用域可以访问另外一个函数内部的局部变量
(3)作用
延伸变量的作用范围 e.g: 在函数外部访问内部局部变量
<script>
function fn(){ //fn闭包函数 闭包函数是典型的高阶函数
var num = 10;
function fun(){
console.log(num);
}
return fun;
}
var f = fn(); //类似 var f = function fun(){ console.log(num); }
f(); //相当于调用了fun(); 返回10,则说明访问了另外一个函数内部的局部变量, 产生闭包
</script>
(4)闭包的调试
chrome 调试工具-> Sources -> 设置断点-> 刷新 -> Scope 选项(Scope 作用域的意思) Scope 里面会有两个参数(global 全局作用域、local 局部作用域), 当执行到 fun() 时,Scope 里面会多一个 Closure 参数 ,这就表明产生了闭包
(5)应用
闭包函数是典型的高阶函数
立即执行函数也称小闭包函数, 因为立即函数里面的任何一个函数都可以使用他的i变量
7.递归
(1)概述
- 递归函数: 一个函数在内部可以调用其本身, 此函数就是~ (函数内部自己调用自己)
- 递归函数的作用和循环效果一样
- 由于递归很容易发生“栈溢出”错误(stack overflow),所以必须要加退出条件 return
(2)浅拷贝和深拷贝
- 浅拷贝只是拷贝一层, 更深层次对象级别的只拷贝引用.
- 深拷贝拷贝多层, 每一级别的数据都会拷贝.
- Object.assign(target, ...sources) es6 新增方法可以浅拷贝
六.正则表达式
1.概述
(1)概念
- 正则表达式(Regular Expression)是用于匹配字符串中字符组合的模式. JS中,它也是对象
- 常用来检索、替换那些符合某个模式(规则)的文本
- 常用于过滤掉页面内容中的一些敏感词(替换),或从字符串中获取我们想要的特定部分(提取)等
应用e.g: 验证表单(用户名表单只能输入英文字母,数字或者下划线, 昵称输入框中可输入中文(匹配))
(2)特点
- 灵活性、逻辑性和功能性非常的强
- 可以迅速地用极简单的方式达到字符串的复杂控制
- 对于刚接触的人,比较晦涩难懂. e.g:验证邮箱 ^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$
- 实际开发,一般都是直接复制写好的正则表达式. 但是要求会使用正则表达式并且根据实际情况修改正则表达式. e.g: 用户名 /^[a-z0-9_-]{3,16}$/
2.使用
(1)创建
- var 变量名 = new RegExp(/表达式/); 调用 RegExp 对象的构造函数创建
- var 变量名 = /表达式/; 字面量创建 // 注释中间放表达式就是正则字面量
(2)测试test()
- test() 正则对象方法, 用于检测字符串是否符合该规则,返回 true 或 false,其参数是测试字符串
- regexObj.test(str); //regexObj :写的正则表达式 str:要测试的文本 检测str文本是否符合规范
3. 特殊字符
(1)正则表达式的组成
一个正则表达式可以由简单的字符构成, 也可以是简单和特殊字符的组合
特殊字符也被称为元字符,在正则表达式中是具有特殊意义的专用符号,如 ^ 、$ 、+ 等
- 特殊字符非常多参考: MDN:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Regular_Expressions
- jQuery 手册:正则表达式部分
- 正则测试工具: http://tool.oschina.net/regex
(2)边界符
正则表达式中的边界符(位置符)用来提示字符所处的位置
- ^ 必须以该表达式开头,写在[ ]外 var o = /^123/;
- $ 必须以该表达式结尾 var o= /123$/;
- 如果 ^ 和 $ 在一起,表示必须是精确匹配, 只允许有一组该字符串
(3)字符类
字符类表示有一系列字符可供选择, 只要匹配其中一个即可. 所有可供选择的字符都放在方括号内
- [ ] 方括号 后面字符串只要包含表达式中任意一个字符,返回 true, 多选一即可,否则false
- [ - ] 方括号内部 范围符- e.g: a 到 z 26个英文字母中只要包含一个,就返回true
- [ ^ ] 方括号内部 取反符^ 只要包含方括号内的字符,都返回 false
- 字符组合 方括号内部可以使用字符组合
(4)量词符
用来设定某个模式出现的次数
(5)括号总结
- { } 量词符. 里面表示重复次数
- [ ] 字符集合, 匹配方括号中的任意字符
- ( ) 表示优先级
var o6 = /^abc{3}$/; //不加[] 只让c重复3次
var o7 = /^[abc]{3}$/; //abc任意组合长度为3则返回true
var o8 = /^(abc){3}$/; // () abc重复3次
(6)预定义类
某些常见模式的简写方式
4.替换
(1)replace()
实现替换字符串操作,用来替换的参数可以是一个字符串或是一个正则表达式
str.replace( regexp / subdtr , replacement);
- 第一个参数: 被替换的字符串 或者 正则表达式
- 第二个参数: 替换为的字符串
- 返回值是一个替换完毕的新字符串
(2)正则表达式参数
/表达式/[switch] switch(也称为修饰符) 按照什么样的模式来匹配. 三种值
- g:全局匹配
- i:忽略大小写
- gi:全局匹配 + 忽略大小写
//敏感词过滤
div.innerHTML = text.value.replace(/a|b/g,'**'); //将文本中所有a,b替换为**
七.ES6
1.简介
ES的全称是ECMAScript ,它是由ECMA国际标准化组织,制定的一项脚本语言的标准化规范
ES6实际上是一个泛指,ES2015 及后续的版本
2.ES6新增语法
(1)let关键字
- ES6中新增的用于声明变量的关键字
- let声明的变量只在所处的块级有效 ( 只能在所在{}中访问)
- 注意: 使用let关键字声明的变量才具有块级作用域, var声明的变量不具备块级作用域特性
特点:
- 声明循环变量 ,可防止循环变量变为全局变量
- let声明, 不存在变量提升(先声明,再使用,否则会报错)
- 暂时性死区 (块级中用let声明tmp,则tmp绑定为块级变量, 不会使用此区块外部的tmp)
经典面试题:
//let声明变量i: 每次循环都会产生一个块级作用域,每个块级作用域中的变量都是不同的,函数执行时输出的是自己上一级(循环产生的块级作用域)作用域下的i值.
//var声明变量i: 变量i是全局的,函数执行时输出的都是全局作用域下的i值,故输出不满足循环条件的i值
var arr = [];
for (let i = 0; i < 2; i++) {
arr[i] = function () {
console.log(i);
}
}
arr[0](); //0 ()调用函数
arr[1](); //1
(2)const关键字
声明常量, 常量就是值(内存地址)不能变化的量
- 具有块级作用域
- 声明常量时必须赋值
- 常量赋值后, 不能重新赋值
// 常量声明后值不可更改 存储的数据不需要变化,e.g:函数定义,数学公式中恒定不变的值, js解析引擎不需要时刻关注,效率高
const PI = 3.14; //基本数据类型值不可更改
// PI = 12; //报错
const arr = [100, 200]; //复杂数据类型:, 数据内部值可更改,但数据值不可更改
arr[0] = 111; //数组里的值可更改 未更改内存地址
//arr = [1,2]; //报错 数组不能重新赋值 更改常量值,更改内存地址报错
console.log(arr); // [111, 200]
let,const,var区别
- 使用 var 声明的变量,其作用域为该语句所在的函数内,且存在变量提升现象
- 使用 let 声明的变量,其作用域为该语句所在的代码块内,不存在变量提升
- 使用 const 声明的是常量,在后面出现的代码中不能再修改该常量的值
(3)解构赋值
- ES6中允许从数组中提取值, 按照对应位置, 对变量赋值. 对象也可以实现解构.
- 按照一定模式,从数组中或对象中提取值,将提取出来的值赋值给另外的变量。
①数组解构:
- let [a, b, c] = [1, 2, 3]; 左侧[ ]中的变量与右侧[ ]中的值一一对应
- 如果解构不成功,变量的值为undefined
②对象解构
使用变量的名字匹配对象的属性, 匹配成功将对象属性的值赋值给变量
// 对象解构
let son = {name: 'ff', age: 18}
let {name, age, sex} = son; //在son对象中,寻找let {}中变量对应的属性,有则将son中属性值赋值给该变量,无则返回undefined
console.log(name); //ff
console.log(sex); //undefined
let {name: myName} = son; //冒号左边只用于属性匹配,右边才是真正的变量 myName属于别名
console.log(myName); //ff
(4)箭头函数
ES6新增函数定义方式 ( ) => { }
const fn = (形参) => { 函数体 } 将箭头函数赋值给变量,调用变量即可
特点:
- 函数体中只有一句代码,且代码的执行结果就是返回值,可省略大括号 const sum = (num1, num2) => num1 + num2; //返回num1+num2的值
- 若形参只有一个,可省略小括号 const fn = v => v;
- 箭头函数不绑定this关键字,箭头函数中的this,指向的是函数定义位置的上下文this
(5)剩余参数
剩余参数语法允许将一个不定数量的参数表示为一个数组
function sum (first, ...args) {
console.log(first); // 10
console.log(args); // [20, 30]
}
sum(10, 20, 30)
剩余参数和解构配合使用:
// 剩余参数与结构配合使用
let array = ['mm','ff','zz'];
let [s1, ...s2] = array;
console.log(s1); //mm
console.log(s2); //['ff', 'zz'] 剩余的参数接收以数组形式返回
3.ES6的内置对象扩展
(1)Array的扩展方法
①扩展运算符(...) (展开语法)
- 可以将数组或者对象转为用逗号分隔的参数序列
- 可以用于合并数组 let ary3 = [...ary1, ...ary2];
- 可调用push()追加数组 arr1.push(...arr2)//将2中的元素追加到1中
- 将伪数组或可遍历对象转换为真正的数组, 转换后就可使用数组的方法
②构造函数方法Array.from()
- 将伪数组或可遍历对象转换为真正的数组
- 方法还可以接受第二个参数,作用类似于数组的map方法,用来对每个元素进行处理,将处理后的值放入返回的数组
③实例方法
- find() 用于找出第一个符合条件的数组成员,如果没有找到返回undefined
- findIndex() 用于找出第一个符合条件的数组成员的位置(索引号),如果没有找到返回-1
- includes() 某个数组是否包含给定的值,返回布尔值。
(3)String的扩展方法
①模板字符串``
反引号`` ES6新增的创建字符串的方式,使用反引号定义
- 模板字符串中可以解析变量 let sayHello = `我叫${name}`; //我叫mm
- 模板字符串中可以换行
- 在模板字符串中可以调用函数
②实例方法
- startsWith() 表示参数字符串是否在原字符串的头部,返回布尔值
- endsWith() 表示参数字符串是否在原字符串的尾部,返回布尔值
- repeat() 表示将原字符串重复n次,返回一个新字符串
(4)Set数据结构
ES6提供了新的数据结构Set, 类似于数组, 但是成员的值都是唯一的,没有重复的值
①创建
- Set本身是一个构造函数,用来生成Set数据结构 const s = new Set();
- Set函数可以接受一个数组作为参数, 用来初始化 const set = new Set([1, 2, 3, 4, 4]);
②实例方法
- add(value):添加某个值,返回本身
- delete(value):删除某个值,返回一个布尔值,表示删除是否成功
- has(value):返回一个布尔值,表示该值是否为 Set 的成员
- clear():清除所有成员,没有返回值
③遍历
- Set结构的实例与数组一样,也拥有forEach方法,用于对每个成员执行某种操作,没有返回值