一.ES5严格模式
使用:在全局或函数的第一条语句定义为’use strict’
严格模式下的约束:
- 只能用var定义变量:未定义为严格模式时,没有使用var直接定义的变量会被默认当成全局变量,但是在严格模式下会报错。
- 禁止自定义函数中的this指向window。比如说构造函数中的this,如果不是new一个实例对象(此时this指向该实例对象),自调用时this是指向window的,此时在严格模式下会报错 。
- 创建eval()作用域。eval()作用我们之前讲过,能解析括号内的字符串,转换为代码。我们不在严格模式下使用eval()是window作用域下,但是在严格模式下,有eval()自己的作用域,使得变量更加安全和私有化。
- 对象不能有同名的属性。
二.ES6变量的解构赋值
let obj = {username:'ame',age:18}
let {username,age} = obj
console.log(username) //'ame'
我们想要将一个对象中的属性值提取出来,一般的做法是
var username = obj.username
但是属性很多的话,我们用解构赋值的方法简单许多,需要注意的是解构赋值的对象里面的属性名必须和原来对象的属性名一致。否则无法解构赋值。
三.彻底弄清let const var区别
let
-
作用:与var类似,用来声明一个变量。
-
特点
①在块级作用域生效
②不能重复声明
③不会预处理,不存在提升//关于第三点,我们使用var定义时,未定义先打印,会返回值undefined console.log(username) //undefined var username = 'ame' console.log(username) //报错 let username = 'ame'
-
应用:循环遍历加监听
//假设我们有3个按钮 let btns = doucument.getElementsByTagName('button') for(var i=0;i<btns.length;i++){ var btn = btns[i] btn.onclick = function(){ alert(i) } }
在这个例子中,我们无论点击哪个按钮,最后都会弹出3,因为回调函数会被放到事件队列里面,当主线程上代码被执行完了,回调函数会像钩子一样去执行。
我们有两种解决办法:
①使用之前js高级程序设计第七章学的闭包的内容,创造立即执行函数,因为立即执行函数是私有作用域,相当于构建闭包。
//假设我们有3个按钮
let btns = doucument.getElementsByTagName('button')
for(var i=0;i<btns.length;i++){
var btn = btns[i]
(function(i){
btn.onclick = function(){
alert(i)
}
})(i)
}
②就是将var替换成let,因为let在块级作用域中生效,且不会声明提升,所以相当于也是自己的作用域。
//假设我们有3个按钮
let btns = doucument.getElementsByTagName('button')
for(let i=0;i<btns.length;i++){
var btn = btns[i]
btn.onclick = function(){
alert(i)
}
}
const
- 作用:定义一个常量
- 特点:
①不能修改
②其他特点同let - 应用:用来保存不会改变的数据
四.模板字符串
let str = '我的名字是:'+obj.username //我们拼接字符串常用这样拼接
let str = `我的名字是:${obj.username}` //es6模板字符串
最外层不是单引号,看清楚哦,是esc下面的一个键。
五.箭头函数详解
箭头函数的特点:
1.箭头函数没有自己的this,箭头函数的this不是在调用的时候决定的,而是在定义的时候处在的对象就是他的this。
//形参的情况
//1.没有形参的时候
let fun1 = () => console.log('666')
fun1() //666
//2.只有一个形参的时候
let fun2 = (a) => console.log(a)
fun2(aaa) //'aaa'
//3.有两个以上的参数
let fun3 = (x,y) => console.log(x,y)
fun2(20,30) //20,30
//函数体的情况
//1.函数体只有一条表达式或者语句的时候,大括号可以省略 -------会自动返回语句执行的结果或者## 标题表达式的结果
let fun4 = () => console.log('666')
fun4() //666
//2.函数不只一条语句或者表达式的情况
let fun5 = (x,y) => {
console.log(x,y)
return x + y
}
console.log(fun5(10,20)) //10,20 返回30
六.三点运算符
- 替代arguments
之前我们了解到arguments,是函数生成时自带的属性,是个类数组,里面存放的是这个函数的实参,但是因为是类数组,所以我们不能对其进行forEach,map等操作,三点运算符可以做到。
function fate(...value){
console.log(value) //[10,20,30]
value.forEach(function(item,index){
console.log(item,index) //10 0 20 1 30 2
})
}
fate(10,20,30)
需要注意的是:三点运算符只能是最后部分参数,如果…value后面还有参数,会报错,前面有一个参数,则value数组里面的项从前往后少一个。
- 扩展运算符
let arr = [2,3,4,5]
let arr1 = [1,6]
arr1 = [1,...arr,6] //[1,2,3,4,5,6]
console.log(arr) //[2,3,4,5]
console.log(...arr) //2,3,4,5
七.形参默认值
我们使用构造函数创建实例对象时,如果实例对象没有传入参数,那么会默认为undefined,但是我们需要当不传入参数时,有默认的形参。
function Point(x=0,y=0){
this.x = x
this.y = y
}
let point = new Point(10,20)
console.log(point) //point{x:10,y:20}
let point1 = new Point1()
console.log() //point1{x:0,y:0}
八.promise对象原理
1.作用:有了promise对象,可以将异步的操作以同步的流程表现出来,避免的层层嵌套的回调函数。ES6的promise是一个构造函数,用来实现promise实例。
2. 状态:①pending 初始化状态 ②fullfilled 成功状态 ③rejected 失败状态。
let promise = new Promise((resolve,reject)=>{
//执行异步操作,通常是发送ajax请求,开启定时器
setTimeout(()=>{
//根据异步任务的返回结果去修改promise的状态(一般根据状态码判断)
if(state == 200){
//异步任务执行成功
resolve(666) //修改promise的状态为 fullfilled:成功的状态 这里可以传递参数,then()里面第一个函数接收
}
else{
//异步任务执行失败
reject(555) //修改promise的状态为 rejected:失败的状态 这里可以传递参数,then()里面第二个函数接收
}
},500)
})
promise.then((success)=>{
//成功的回调
console.log(success,'成功了') //666,成功了
},(error)=>{
//失败的回调
console.log(erroe.'失败了') //555,失败了
})
九.Symbol属性介绍
Symbol概念:ES6新添加了一种原始数据类型Symbol。
特点:
- Symbol属性对应的值是唯一的,解决命名冲突问题。
- Sumbol的值不能与其他数据进行计算,包括同字符串拼串。
- for in,for of遍历时不会遍历Symbol属性。
let symbol = Symbol() //Symbol是自带的函数,但是不是构造函数
let obj = {username:'ame',age:18}
obj[symbol] = 'hello'
console.log(obj) //{username:'ame',age:18,symbol():'hello'}
let symbol2 = Symbol()
let symbol3 = Symbol()
//symbol2和symbol3打印虽然结果相同,但是本身并不相等,想进行区分的话可以symbol(a),symbol(b)加以区分,并不影响属性值。
十.iterator接口机制
作用:
- 为各种数据结构提供一种统一的,简便的访问接口。
- 使得数据结构的成员能够按照某种次序进行排列。
- ES6创建了一种新的遍历命令for…of循环。Iterator接口主要供for…of消费。
工作原理:
- 创建一个指针对象(遍历器对象),指向数据结构的起始位置。
- 接下来不断调用next方法,指针会一直往后移动,直到指向最后一个成员。
- 每调用玩next方法返回的是一个包含value和done的对象。{value:当前成员的值,done:布尔值} done为false表示未遍历完当前数据,done为true表示已经完成遍历。
自定义iterator接口:
function myIterator(arr){
let nextIndex = 0 //记录指针的位置
return{
next:function(){
return nextIndex < arr.length ? {value:arr[nextIndex++],done:false} : {undefined,done:true}
}
}
}
//准备一个数据
let arr = [1,2,3,4,5,'hi']
let interatorObj = myIterator(arr)
console.log(interatorObj.next())
console.log(interatorObj.next())
console.log(interatorObj.next())
console.log(interatorObj.next())
console.log(interatorObj.next())
console.log(interatorObj.next())
console.log(interatorObj.next())
将interaotr接口部署到指定的数据类型上,可以使用for of进行循环遍历。
指定的数据类型:数组,字符串,arguments,set容器,map容器。
比如字符串的for of遍历
let str = 'abcd'
for(let i of str){
console.log(i) //a b c d
}
但是对一个对象进行for of遍历会报错。
十一.Generator函数简介
概念:
- ES6提供用来解决异步编程的方案之一。
- 用来生成遍历器对象。
- 可暂停函数,yield可暂停,next方法可启动。每次返回的是next后的返回值的结果。
特点:
- function与函数名之间有一个星号。
- 内部用yield定义不同的状态。
function* myGenerator(){
console.log('开始执行')
yield 'hello' //yield后面可以跟字符串或者执行语句,执行语句可以执行,但会返回一个undefined。这里返回的是{value:'hello',done:false}
console.log('暂停后,再次执行')
yield 'generator'
}
let MG = myGenerator()
console.log(MG.next()) //{value:'hello',done:false}
console.log(MG.next()) //{value:'generator',done:false}
因为最后暂停了,虽然后面没有语句,但是仍然返回的遍历器对象中done为false。什么时候返回true呢。
function* myGenerator(){
console.log('开始执行')
yield 'hello' //yield后面可以跟字符串或者执行语句,执行语句可以执行,但会返回一个undefined。这里返回的是{value:'hello',done:false}
console.log('暂停后,再次执行')
yield 'generator'
console.log('遍历结束')
return '返回'
}
let MG = myGenerator()
console.log(MG.next()) //{value:'hello',done:false}
console.log(MG.next()) //{value:'generator',done:false}
console.log(MG.next()) //{value:'返回',done:true}
那么Generator函数有什么作用呢?我们可以在yield后写ajax接口,只有在成功后执行next语句,保证ajax接口执行的顺序,就可以实现异步操作的同步执行。
我们在iterator接口中知道,数组,字符串可以用for of进行遍历,因为内部封装了Symbol.iterator属性,但是对象没有封装,所以对象用for of语句会报错。我们这一章学习到Generator函数返回的是一个遍历器对象,我们可以借用Generator函数实现对象的for of遍历。
let obj = {username:'ame',age:18}
obj[Symbol.iterator] = function* myTest(){
yield 1
yield 2
yield 3
}
for(let i of obj){
console.log(i) //1 2 3
}
我们使用Generator函数来具体实现一下传递参数的异步操作。
这次我们实现的是:先获取新闻,然后根据新闻里面的数据获取评论的url,然后再获取评论。
function getNews(url){
$.get(url,function(data){
let url = 'http://localhost:8080' + data.commentsUrl
sx.next(url) //next中传递的参数,可以使用一个变量值获取到yiled返回的值中
})
}
function* sendXml(){
let url = yield getNews(http://localhost:8080/news?id=3)
yield getNews(url)
}
//获取遍历器对象
let sx = sendXml()
sx.next()
十二.async函数
概念:真正意义上去解决异步回调的问题。同步流程解决异步操作。来源与ES7。本质是Generator的语法糖。
特点:
- 不需要像Generator去调用next方法,遇到await等待,当前的异步操作完成就往下执行。
- 返回的总是promise对象,可以使用then进行下一步操作。
- async取代Generator函数的*,await取代Generator的yield。
- 语义更为明确,使用简单。
//下面我们演示一下用async配合promise对象进行传递参数。
async function getNews(){
return new Promise((resolve,reject) => {
$.ajax({
method:'Get',
url,
succees:data => resolve(data) //箭头函数获取参数
error:error => resolve(false)
})
})
}
async function sendXml(){
let result = await getNews('http://localhost:8080/news?id=7')
if(!result){
console.log('暂时没有评论!')
return;
}
await getNews('http://localhost:8080') + result.commentsUrl
}
sendXml()
十三.Class类使用详解
- 通过class定义类/实现类的继承
- 在类中通过constructor定义构造方法
- 通过new创建类的实例
- 通过extends来实现类的继承
- 通过super来调用父类的构造方法
class Person{
//类的构造方法
constructor(name,age){
this.name = name
this.age = age
}
//类的一般方法,放在实例对象的原型上,实例对象是通过原型继承的
showname(){
console.log(this.name)
}
}
let person = new Person('ame',18)
console.log(perosn)
person.showname() //ame
//子类
class starPerson extends Person{
constructor(name,age,salary){
super(name,age)//调用父类的构造方法(相当于继承了父类的那些方法)
this.salary = salary
}
//如果想要实例对象可以访问子类的属性,可以重写父类的方法
showname(){
console.log(this.salary)
}
}
let p1 = new starPerson('maybe',25,10000)
console.log(p1) //{name:'maybe',age:25,salary:10000}
p1.showname() //10000(重写父类的方法后,不会再调用父类的方法)
十四.字符串,数值,数组,对象的扩展
字符串的扩展:
- includes(str):判断是否包含指定的字符串
- startsWith(str):判断是否以指定字符串开头
- endsWith(str):判断是否以指定字符串结尾
- repeat(count):重复指定次数
let str = 'adasdasd'
console.log(str.includes(a)) //true
//其余同理
数值的扩展:
- 二进制与八进制数值表示法: 二进制用0b, 八进制用0o
- Number.isFinite(i) : 判断是否是有限大的数
- Number.isNaN(i) : 判断是否是NaN
- Number.isInteger(i) : 判断是否是整数
- Number.parseInt(str) : 将字符串转换为对应的数值
- Math.trunc(i) : 直接去除小数部分
数组的扩展:
- Array.from(v) : 将伪数组对象或可遍历对象转换为真数组
- Array.of(v1, v2, v3) : 将一系列值转换成数组
- find(function(value, index, arr){return true}) : 找出第一个满足条件返回true的元素
- findIndex(function(value, index, arr){return true}) : 找出第一个满足条件返回true的元素下标
//本身这样获取的按钮对象是伪数组,不能使用forEach遍历,但是我们使用数组的from方法就可以变成真数组进行遍历。
let btns = document.getElementsByTagName('button')
Array.from(btns).forEach(function(item,index){
console.log(item) //打印出按钮的表示代码
})
let arr = Array.of(1,2,3,4,5)
console.log(arr) //[1,2,3,4,5]
对象的扩展:
- Object.is(v1, v2)
- 判断2个数据是否完全相等
- Object.assign(target, source1, source2…)
- 将源对象的属性复制到目标对象上
- 直接操作 proto 属性
let obj2 = {};
obj2.proto = obj1;
//他是以字符串的形式来判断的,这个对于NAN来说就很特殊
console.log(Object.is(NaN, NaN)) //true
let obj = {}
let obj1 = {
name:'ame',
age:18
}
Object.assign(obj,obj1)
console.log(obj) //{name:'ame',age:18}
_ proto_属性指向的是原型对象,隐式原型,不能直接操作
protoType可以直接操作,显式原型。
这里使用ES6新方法可以直接操作隐式原型。
let obj3 = {}
let obj4 = {money:2000000}
obj3._proto_ = obj4
console.log(obj3) //为空对象,但是原型上有money属性
console.log(obj3.money) //2000000
十五.深度克隆
1、数据类型:
* 数据分为基本的数据类型(String, Number, boolean, Null, Undefined)和对象数据类型
- 基本数据类型:
特点: 存储的是该对象的实际数据
- 对象数据类型:
特点: 存储的是该对象在栈中引用,真实的数据存放在堆内存里
2、复制数据
- 基本数据类型存放的就是实际的数据,可直接复制
let number2 = 2;
let number1 = number2;
- 克隆数据:对象/数组
1、区别: 浅拷贝/深度拷贝
浅拷贝:拷贝的引用,修改拷贝以后的数据会影响原数据。
深拷贝:拷贝的时候生成新数据,修改拷贝以后的数据不会影响原数据。
知识点:对象数据存放的是对象在栈内存的引用,直接复制的是对象的引用。
2、常用的拷贝技术
1). Object.assign() //浅拷贝
2). Array.prototype.concat() //浅拷贝
3). Array.prototype.slice() //浅拷贝
4). JSON.parse(JSON.stringify(arr/obj)) //深拷贝(深度克隆), 因为数据类型发生了转换,先转换为JSON字符串,然后再转换为JS类型,但不能处理函数数据(因为JSON.stringify()中放入的只能是原生的JS对象或者数组)
如何实现深度克隆:
知识点储备:
如何判断数据类型:
1.typeof返回的数据类型:String,Number,,boolean,Null,Undefined,Object,Function。
2.Object.prototype.toString.call(obj)
let result = [1,2,3,4,5]
console.log(Object.prototype.toString.call(result)) //[object Array]
console.log(typeof Object.prototype.toString.call(result)) //string
//[object Array]我们只想要后面的标识类,不想要前面的object
console.log(Object.prototype.toString.call(result).slice(8,-1))
3.for in循环 对象(属性名) 数组(下标)
//定义监测数据类型的功能函数
function checkType(target){
return Object.prototype.toString.call(result).slice(8,-1))
}
//实现深度克隆 --->针对的是对象和数组
functio clone(target){
//先判断拷贝的数据类型
//初始化变量result 成为最终克隆的数据
let result,targetType = checkType(target)
if(targetType == 'Array'){
result = []
}else if(targetType == 'object'){
result = {}
}else{
return target //表示target为基本数据类型,不需要深度克隆
}
//遍历目标数据
for(let i in target){
//获取遍历数据结构的每一项值
let value = target[i]
//判断目标结构里每一项是否存在数组/对象(存在就重新执行一次该函数)
if(checkType(value) === 'object' || checkType(value) === 'Array'){
result[i] = clone(value)
}else{ //value的值是基本的数据类型或者是函数
result[i] = value
}
}
}
十六.set,map容器详解
- Set容器 : 无序不可重复的多个value的集合体
- Set()
- Set(array)
- add(value)
- delete(value)
- has(value)
- clear()
- size
- Map容器 : 无序的 key不重复的多个key-value的集合体
- Map()
- Map(array)
- set(key, value)//添加
- get(key)
- delete(key)
- has(key)
- clear()
- size
let set = new set([1,2,4,5,3,2,1])
console.log(set) //{1,2,4,5,3} //重复的会自动删除掉
let map = new map([['a''username']])
console.log(map) //{'a'=>'username'}
可以利用set无序不可重复的特点实现数组去重。