let
1. 作用:
* 与var类似, 用于声明一个变量
2. 特点:
* 在块作用域内有效
* 不能重复声明
* 不会预处理, 不存在变量提升
之前用var时有预处理。还记得undefined吗
3. 应用:
* 循环遍历加监听
* 使用let取代var是趋势
例子:点击按钮,正确显示它的索引值
<button>测试1</button>
<br>
<button>测试2</button>
<br>
<button>测试3</button>
<br>
<script type="text/javascript">
let btns= document.getElementsByTagName('button')
for (var i=0;i<btns.length;i++)
{
var btn=btns[i];
//这里返回的都是3,因为点击事件是回调函数,回调函数是会放到事件队列里面,当主线程的代码执行完后,再来执行,这个时候i为3了
// btn.onclick=function(){
// alert(i)
// }
//可以用立即执行函数解决,闭包的原理。因为立即执行函数也是一个函数,它有一个作用域。也就是每次
//传入的i是函数的私有数据,所以可以解决
(function(i){
btn.onclick=function(){
alert(i)
}
})(i)//后面这个i是实参,前面那个i是形参
}
//不过用let就更方便了,因为let有块级作用域,所以每次执行一个i的时候,就是该板块,就代表此时的私有数据
</script>
不过用let就更方便了,因为let有块级作用域,所以每次执行一个i的时候,就是该板块,就代表此时的私有数据
for(let i=0;i<btns.length;i++)
{
var btn=btns[i];
btn.onclick=function(){
console.log(i)
}
}
const
1. 作用:
* 定义一个常量
2. 特点:
* 不能修改
* 其它特点同let
3. 应用:
* 保存不用改变的数据
const KEY = 'NBA'
//KEY ='CBA'
console.log(KEY)
变量的解构赋值
1. 理解:
* 从对象或数组中提取数据, 并赋值给变量(多个)
2. 对象的解构赋值
let {n, a} = {n:'tom', a:12}
注意一定要与属性值一致
let obj = {username:'pp',age:18}
// let {age} = obj
// console.log(age)//18
let {username,age} = obj
console.log(username,age)
3. 数组的解构赋值
let [a,b] = [1, 'atguigu'];
默认按照顺序取值
let arr = ['abc',3,'dd',66,'bf']
let [a,b,c,d] = arr;
console.log(a,b,c,d)//abc 3 dd 66 bf
如果只想取中间的部分值,则可以加逗号代替不取的位置
let arr = ['abc',3,'dd',66,'bf']
let [,c,d] = arr;
console.log(c,d)// 3 "dd"
4. 用途
* 给多个形参赋值
function foo({username,age}){//{username,age}=obj
console.log(username,age)
}
foo(obj)
模板字符串
1. 模板字符串 : 简化字符串的拼接
* 模板字符串必须用 `` 包含
* 变化的部分使用${xxx}定义
<script type="text/javascript">
let obj = {username:'苹苹',age:20}
let str = '我是可爱的'+obj.username+','+'我今年'+obj.age+'了'
console.log(str)
let ss=`我是可爱的${obj.username},我今年${obj.age}了`
console.log(ss)
</script>
简化的对象写法
* 省略同名的属性值
* 省略方法的function
* 例如:
<script type="text/javascript">
let username = 'ppqq'
let age = '100'
let obj = {
username,//同名的属性可以不写
age,
getName() {//可以省略函数的function
return this.username
}
}
console.log(obj)
console.log(obj.getName())
箭头函数
* 作用: 定义匿名函数
* 基本语法:
* 没有参数: () => console.log('xxxx')
* 一个参数: i => i+2
* 大于一个参数: (i,j) => i+j
* 函数体不用大括号: 默认返回结果
* 函数体如果有多个语句, 需要用{}包围,若有需要返回的内容,需要手动返回
* 使用场景: 多用来定义回调函数
* 箭头函数的特点:
1、简洁
2、箭头函数没有自己的this,箭头函数的this不是调用的时候决定的,而是在定义的时候处在的对象就是它的this
3、扩展理解: 箭头函数的this看外层的是否有函数,
如果有,外层函数的this就是内部箭头函数的this,
如果没有,则this是window。
eg:返回window
eg:返回obj
帮助理解,getName()也就相当于 () => {},所以是window
三点运算符
. rest(可变)参数
* 用来取代arguments 但比 arguments 灵活,只能是最后部分形参参数
function foo (...value){
console.log(arguments)
console.log(value)
value.forEach(function(item,index){
console.log(item,index)
})
}
foo(2,38,11)
2. 扩展运算符
let arr = [1,6]
let arr1 = [2,3,4,5]
arr = [1,...arr1,6]
console.log(arr)
console.log(...arr)
结果为:
形参默认值
* 形参的默认值----当不传入参数的时候默认使用形参里的默认值
function Point(x = 1,y = 2) {
this.x = x;
this.y = y;
}
promise对象
1. 理解:
* Promise对象: 代表了未来某个将要发生的事件(通常是一个异步操作)
* 有了promise对象, 可以将异步操作以同步的流程表达出来, 避免了层层嵌套的回调函数(俗称'回调地狱')
* ES6的Promise是一个构造函数, 用来生成promise实例
2. 使用promise基本步骤(2步):
let promise = new Promise((resolve,reject) => {
console.log('111')
setTimeout(() => {
console.log('333')
resolve();
//reject()
},2000)
})
console.log('222')
promise
.then(() =>{
console.log('成功啦')
},() => {
console.log('失败啦')
})
结果:
还可以传参数,data,error是对应的
let promise = new Promise((resolve,reject) => {
console.log('111')
setTimeout(() => {
console.log('333')
//resolve('哈哈哈');
reject('呜呜呜')
},2000)
})
console.log('222')
promise
.then((data) =>{
console.log(data,'成功啦')
},(error) => {
console.log(error,'失败啦')
})
3. promise对象的3个状态
* pending: 初始化状态
* fullfilled: 成功状态
* rejected: 失败状态
4. 应用:
* 使用promise实现超时处理
* 使用promise封装处理ajax请求
let request = new XMLHttpRequest();
request.onreadystatechange = function () {
}
request.responseType = 'json';
request.open("GET", url);
request.send();
//定义获取新闻的功能函数
function getNews(url){
let promise = new Promise((resolve,reject) => {
//状态:初始化
//执行异步任务
//创建xmlHttp实例对象
let xmlHttp = new XMLHttpRequest()
//绑定监听 readyState
xmlHttp.onreadystatechange = function(){
if(xmlHttp.readyState === 4 ){
if( xmlHttp.status === 200){
console.log(xmlHttp.responseText)
resolve(xmlHttp.responseText) //修改promise的状态为成功的状态
}else{ //请求失败
reject('暂时没有新闻内容')
}
}
};
//open 设置请求的方式以及url
xmlHttp.open('GET',url);
//send
xmlHttp.send()
})
return promise;
}
getNews('http://127.0.0.1:3000/news?id=2')
.then((data) => {
console.log(data)
let commentsUrl = JSON.parse(data).commentsUrl
let Url ='http://127.0.0.1:3000'+commentsUrl
//发送请求
return getNews(Url)
},(error) => {
console.log(error)
})
.then((data) => {
console.log(data)
},() => {
})
Promise过程分析
Symbol
前言:ES5中对象的属性名都是字符串,容易造成重名,污染环境
Symbol:
概念:ES6中的添加了一种原始数据类型symbol(已有的原始数据类型:String, Number, boolean, null, undefined, 对象)
特点:
1、Symbol属性对应的值是唯一的,解决命名冲突问题
2、Symbol值不能与其他数据进行计算,包括同字符串拼串
3、for in, for of遍历时不会遍历symbol属性。
使用:
1、调用Symbol函数得到symbol值
let symbol = Symbol();
let obj = {};
obj[symbol] = 'hello';
2、传参标识
let symbol = Symbol('one');
let symbol2 = Symbol('two');
console.log(symbol);// Symbol('one')
console.log(symbol2);// Symbol('two')
3、内置Symbol值
* 除了定义自己使用的Symbol值以外,ES6还提供了11个内置的Symbol值,指向语言内部使用的方法。
- Symbol.iterator
* 对象的Symbol.iterator属性,指向该对象的默认遍历器方法
//等同于在指定的数据结构内部上部署了iterator接口
//当使用for of 去遍历某一个数据结构的时候,首先去找Symbol.iterator,找到了就去遍历,没有找到的话就不能遍历
let targetData = {
[Symbol.iterator]:function(){
let nextIndex = 0;
return {
next : function(){
return nextIndex<this.length?{value:this[nextIndex++],done:false}:{value:undefined,done:true}
//在这里实现了闭包,首先有函数嵌套,其次,内部函数引用了外部函数变量,并且内部的方法在外部去调用
}
}
}
}
Iterator接口
概念: iterator是一种接口机制,为各种不同的数据结构提供统一的访问机制
作用:
1、为各种数据结构,提供一个统一的、简便的访问接口;
2、使得数据结构的成员能够按某种次序排列
3、ES6创造了一种新的遍历命令for...of循环,Iterator接口主要供for...of消费。
工作原理:
- 创建一个指针对象(遍历器对象),指向数据结构的起始位置。
- 第一次调用next方法,指针自动指向数据结构的第一个成员
- 接下来不断调用next方法,指针会一直往后移动,直到指向最后一个成员
- 每调用next方法返回的是一个包含value和done的对象,{value: 当前成员的值,done: 布尔值}
* value表示当前成员的值,done对应的布尔值表示当前的数据的结构是否遍历结束。
* 当遍历结束的时候返回的value值是undefined,done值为true
原生具备iterator接口的数据(可用for of遍历)
1、Array
2、arguments
3、set容器
4、map容器
5、String
注意对象没有iterator
//模拟指针对象(遍历器对象)
function myIterator(arr){
let nextIndex = 0
return {
next : function(){
return nextIndex<arr.length?{value:arr[nextIndex++],done:false}:{value:undefined,done:true}
//在这里实现了闭包,首先有函数嵌套,其次,内部函数引用了外部函数变量,并且内部的方法在外部去调用
}
}
}
//准备一个数据
let arr = [1,4,2,'abd']
let iteratorObj = myIterator(arr)
console.log(iteratorObj.next())
console.log(iteratorObj.next())
console.log(iteratorObj.next())
console.log(iteratorObj.next())
console.log(iteratorObj.next())
//将iterator接口部署到指定的数据类型上,可以使用for of 去遍历循环
//数组、字符串、arguments、set容器、map容器
for (let i of arr){
console.log(i)
}
let str = 'ppqq will be forever'
for (let i of str){
console.log(i)
}
function fun(){
for(let i of arguments){
console.log(i)
}
}
fun(1,3,4,'ppqq')
使用三点运算符,解构赋值,默认去调用iterator接口
Generator函数
概念:
1、ES6提供的解决异步编程的方案之一
2、Generator函数是一个状态机,内部封装了不同状态的数据,
3、用来生成遍历器对象
4、可暂停函数(惰性求值), yield可暂停,next方法可启动。每次返回的是yield后的表达式结果
特点:
1、function 与函数名之间有一个星号
2、内部用yield表达式来定义不同的状态
例如:
function* generatorExample(){
let result = yield 'hello'; // 状态值为hello
yield 'generator'; // 状态值为generator
}
3、generator函数返回的是指针对象(接11章节里iterator),而不会执行函数内部逻辑
4、调用next方法函数内部逻辑开始执行,遇到yield表达式停止,返回{value: yield后的表达式结果/undefined, done: false/true}
5、再次调用next方法会从上一次停止时的yield处开始,直到最后
6、yield语句返回结果通常为undefined, 当调用next方法时传参内容会作为启动时yield语句的返回值。
function* myGenerator(){
console.log('开始执行')
//yield 'hello'
let result = yield 'hello'
console.log(result)
// yield console.log('aha')
console.log('暂停后,再次执行')
yield 'generator'
console.log('遍历完毕')
return '返回的结果' //没有这句的话,返回的是undefined,因为consolelog返回的是undefined
}
let MG = myGenerator()
console.log(MG.next())
console.log(MG.next('aaa可以指定参数作为返回值'))
console.log(MG.next())
结果:
对象的symbol.iterator属性 指向遍历器对象。输出结果为1 2 3
let obj = {username :'pppp',age:19}
obj[Symbol.iterator] = function* myTest(){
yield 1
yield 2
yield 3
}
for(let i of obj){
console.log(i)
}
// 案例练习
/*
* 需求:
* 1、发送ajax请求获取新闻内容
* 2、新闻内容获取成功后再次发送请求,获取对应的新闻评论内容
* 3、新闻内容获取失败则不需要再次发送请求。
*
* */
function getNews(url){
$.get(url,function(data){
console.log(data)
let commentsUrl = data.commentsUrl;
let url = 'http://127.0.0.1:3000' + commentsUrl;
// 当获取新闻内容成功,发送请求获取对应的评论内容
// 调用next传参会作为上次暂停是yield的返回值
SX.next(url);
})
}
function* sendxml(){
let url=yield getNews('http://127.0.0.1:3000/news?id=3')
yield getNews(url)
}
//获取遍历器对象
let SX = sendxml()
SX.next()
async函数
async函数(源自ES2017)
概念: 真正意义上去解决异步回调的问题,同步流程表达异步操作
本质: Generator的语法糖
语法:
async function foo(){
await 异步操作;
await 异步操作;
}
特点:
1、不需要像Generator去调用next方法,遇到await等待,当前的异步操作完成就往下执行
2、返回的总是Promise对象,可以用then方法进行下一步操作
3、async取代Generator函数的星号*,await取代Generator的yield
4、语意上更为明确,使用简单,经临床验证,暂时没有任何副作用
async function foo(){
return new Promise(resolve => {
// setTimeout(function(){
// resolve()
// },2000)
setTimeout(resolve,2000)//resolve就是一个函数
})
}
async function test(){
console.log('开始执行',new Date().toTimeString())
await foo()
console.log('执行完毕',new Date().toTimeString())
}
test()
执行结果:可以发现时间相差两秒
async 的await返回值问题
function test(){
return 'hehehe'
}
async function asyncPrint(){
// let result = await test()
// console.log(result)//打印hehehe
// let result = await Promise.resolve('promise')
// console.log(result)//打印promise
let result = await Promise.reject('失败')
console.log(result)//报错,并打印 失败
}
asyncPrint()
获取新闻内容
//获取新闻内容
async function getNews(url){
return new Promise((resolve,reject) => {
$.ajax({
method:'GET',
url,//同名属性可以不写后面的
success:data => resolve(data),
error: error=> reject()
})
})
}
async function sendXml(){
let result=await getNews('http://127.0.0.1:3000/news?id=6')
console.log(result)
result=await getNews('http://127.0.0.1:3000'+result.commentsUrl)
console.log(result)
}
sendXml()
class
1. 通过class定义类/实现类的继承
2. 在类中通过constructor定义构造方法
3. 通过new来创建类的实例
4. 通过extends来实现类的继承
5. 通过super调用父类的构造方法
6. 重写从父类中继承的一般方法
class Person {
//调用类的构造方法
constructor(name, age){
this.name = name;
this.age = age;
}
//定义一般的方法
showName(){
console.log(this.name, this.age);
}
}
let person = new Person('kobe', 39);
console.log(person, person.showName());
//定义一个子类
class StrPerson extends Person{
constructor(name, age, salary){
super(name, age);//调用父类的构造方法
this.salary = salary;
}
showName(){//在子类自身定义方法
console.log(this.name, this.age, this.salary);
}
}
let str = new StrPerson('weide', 38, 1000000000);
console.log(str);
str.showName();
字符串扩展
1. includes(str) : 判断是否包含指定的字符串
2. startsWith(str) : 判断是否以指定字符串开头
3. endsWith(str) : 判断是否以指定字符串结尾
4. repeat(count) : 重复指定次数
let str = 'abcdefg';
console.log(str.includes('a'));//true
console.log(str.includes('h'));//false
//startsWith(str) : 判断是否以指定字符串开头
console.log(str.startsWith('a'));//true
console.log(str.startsWith('d'));//false
//endsWith(str) : 判断是否以指定字符串结尾
console.log(str.endsWith('g'));//true
console.log(str.endsWith('d'));//false
//repeat(count) : 重复指定次数a
console.log(str.repeat(5));
数值扩展
1. 二进制与八进制数值表示法: 二进制用0b, 八进制用0o
2. Number.isFinite(i) : 判断是否是有限大的数
3. Number.isNaN(i) : 判断是否是NaN
4. Number.isInteger(i) : 判断是否是整数
5. Number.parseInt(str) : 将字符串转换为对应的数值
6. Math.trunc(i) : 直接去除小数部分
console.log(0b1010);//10
console.log(0o56);//46
//Number.isFinite(i) : 判断是否是有限大的数
console.log(Number.isFinite(NaN));//false
console.log(Number.isFinite(5));//true
//Number.isNaN(i) : 判断是否是NaN
console.log(Number.isNaN(NaN));//true
console.log(Number.isNaN(5));//falsse
//Number.isInteger(i) : 判断是否是整数
console.log(Number.isInteger(5.23));//false
console.log(Number.isInteger(5.0));//true
console.log(Number.isInteger(5));//true
//Number.parseInt(str) : 将字符串转换为对应的数值
console.log(Number.parseInt('123abc'));//123
console.log(Number.parseInt('a123abc'));//NaN
// Math.trunc(i) : 直接去除小数部分
console.log(Math.trunc(13.123));//13
数组扩展
1. Array.from(v) : 将伪数组对象或可遍历对象转换为真数组
2. Array.of(v1, v2, v3) : 将一系列值转换成数组
3. find(function(value, index, arr){return true}) : 找出第一个满足条件返回true的元素
4. findIndex(function(value, index, arr){return true}) : 找出第一个满足条件返回true的元素下标
//Array.from(v) : 将伪数组对象或可遍历对象转换为真数组
let btns = document.getElementsByTagName('button');
console.log(btns.length);//3
Array.from(btns).forEach(function (item, index) {
console.log(item, index);
});
//Array.of(v1, v2, v3) : 将一系列值转换成数组
let arr = Array.of(1, 'abc', true);
console.log(arr);
//find(function(value, index, arr){return true}) : 找出第一个满足条件返回true的元素
let arr1 = [1,3,5,2,6,7,3];
let result = arr1.find(function (item, index) {
return item >3
});
console.log(result);//5
//findIndex(function(value, index, arr){return true}) : 找出第一个满足条件返回true的元素下标
let result1 = arr1.findIndex(function (item, index) {
return item >3
});
console.log(result1);//2
对象扩展
1. Object.is(v1, v2)
* 判断2个数据是否完全相等
2. Object.assign(target, source1, source2..)
* 将源对象的属性复制到目标对象上
3. 直接操作 __proto__ 属性
let obj2 = {};
obj2.__proto__ = obj1;
console.log(Object.is('abc', 'abc'));//true
console.log(NaN == NaN);//false
console.log(Object.is(NaN, NaN));//true
console.log(0 == -0);//true
console.log(Object.is(0, -0));//false
//Object.assign(target, source1, source2..)
let obj = {name : 'kobe', age : 39, c: {d: 2}};
let obj1 = {};
Object.assign(obj1, obj);
console.log(obj1, obj1.name);
//直接操作 __proto__ 属性
let obj3 = {name : 'anverson', age : 41};
let obj4 = {};
obj4.__proto__ = obj3;
console.log(obj4, obj4.name, obj4.age);
深度复制
1、数据类型:
* 数据分为基本的数据类型(String, Number, boolean, Null, Undefined)和对象数据类型
- 基本数据类型:
特点: 存储的是该对象的实际数据
- 对象数据类型:
特点: 存储的是该对象在栈中引用,真实的数据存放在堆内存里
2、复制数据
- 基本数据类型存放的就是实际的数据,可直接复制
let number2 = 2;
let number1 = number2;
- 克隆数据:对象/数组
1、区别: 浅拷贝/深度拷贝
判断: 拷贝是否产生了新的数据还是拷贝的是数据的引用
知识点:对象数据存放的是对象在栈内存的引用,直接复制的是对象的引用
let obj = {username: 'kobe'}
let obj1 = obj; // obj1 复制了obj在栈内存的引用
2、常用的拷贝技术
1). arr.concat(): 数组浅拷贝
2). arr.slice(): 数组浅拷贝
3). JSON.parse(JSON.stringify(arr/obj)): 数组或对象深拷贝, 但不能处理函数数据
4). 浅拷贝包含函数数据的对象/数组
特点:拷贝的引用,修改拷贝以后的数据会修改原数据,使得原数据不安全
5). 深拷贝包含函数数据的对象/数组
特点:拷贝的时候会生成新数据,修改拷贝以后的数据不会改变原数据
思考:如何实现深度拷贝
拷贝的数据里有对象和数组
拷贝的数据里不能有对象/数组,即使有对象/数组可以继续遍历对象、数组,拿到里边每一项值,一直到拿到是基本数据类型,然后再去复制,就是深度拷贝
注意:
1、typeof 返回的数据类型: String,Number,Boolean,Undefined,Object,Function
2、Object.prototype.toString.call(obj) 这个可以返回 [object Array]等,这是个例子。所以用这个可以很好的判断类型
3、for in 遍历数组的时候遍历的是下标,遍历对象的时候遍历的是属性名
深度克隆的实现
// 深度克隆(复制)
function getObjClass(obj) {
let result = Object.prototype.toString.call(obj).slice(8, -1);
if(result === 'Null'){
return 'Null';
}else if(result === 'Undefined'){
return 'Undefined';
}else {
return result;
}
}
// for in 遍历数组的时候遍历的是下标
let testArr = [1,2,3,4];
for(let i in testArr){
console.log(i); // 对应的下标索引
}
// 深度克隆
function deepClone(obj) {
let result, objClass = getObjClass(obj);
if(objClass === 'Object'){
result = {};
}else if(objClass === 'Array'){
result = [];
}else {
return obj; // 如果是其他数据类型不复制,直接将数据返回
}
// 遍历目标对象
for(let key in obj){
let value = obj[key];
if(getObjClass(value) === "Object" || 'Array'){
result[key] = deepClone(value);
}else {
result[key] = obj[key];
}
}
return result;
}
let obj3 = {username: 'kobe',age: 39, sex: {option1: '男', option2: '女'}};
let obj4 = deepClone(obj3);
console.log(obj4);
obj4.sex.option1 = '不男不女'; // 修改复制后的对象不会影响原对象
console.log(obj4, obj3);
Set容器 和 Map容器
1. Set容器 : 无序不可重复的多个value的集合体
* Set()
* Set(array)
* add(value)
* delete(value)
* has(value)
* clear()
* size
let set = new Set([1,2,3,4,3,2,1,6]);
console.log(set);
set.add('abc');
console.log(set, set.size);
//delete(value)
set.delete(2);
console.log(set);
//has(value)
console.log(set.has(2));//false
console.log(set.has(1));//true
//clear()
set.clear();
console.log(set);
2. Map容器 : 无序的 key不重复的多个key-value的集合体
* Map()
* Map(array)
* set(key, value)//添加
* get(key)
* delete(key)
* has(key)
* clear()
* size
let map = new Map([['abc', 12],[25, 'age']]);
console.log(map);
map.set('男', '性别');
console.log(map);
console.log(map.get(25));//age
//delete(key)
map.delete('男');
console.log(map);
console.log(map.has('男'));//false
console.log(map.has('abc'));//true
map.clear();
console.log(map);
for of 循环遍历
for(let value of target){}循环遍历
1. 遍历数组
2. 遍历Set
3. 遍历Map
4. 遍历字符串
5. 遍历伪数组
let arr = [1,2,3,4,5];
for(let num of arr){
console.log(num);
}
let set = new Set([1,2,3,4,5]);
for(let num of set){
console.log(num);
}
let str = 'abcdefg';
for(let num of str){
console.log(num);
}
let btns = document.getElementsByTagName('button');
for(let btn of btns){
console.log(btn.innerHTML);
}
ES7
1. 指数运算符(幂): **
2. Array.prototype.includes(value) : 判断数组中是否包含指定value