//声明变量
let a
//声明变量并赋值
let b = 10
//声明多个变量
let c = 5,d = 6,e = 7, arr = []
// 1.变量不能重复声明
let star = "ldh"
let star = "zxy"
console.log(star)
//2.块级作用域 (变量只在作用域内有效)
{
let boy = 'lihua'
}
console.log(boy)
与var不同的是, let不能在声明变量之前,使用该变量
//3.不存在变量提升
console.log(song)
let song = 'xin'
在下述的fn()函数中,打印school变量,在当前的函数作用域中并未找到,就会向上一层的作用域寻找。
{
let school = 'college'
function fn(){
console.log(school)
}
fn()
}
?
// 1. 声明常量 (一定要赋初始值)
const PI = 3.14
// 2. 一般常量使用大写(潜规则),不强制要求
const a = 100
// 3. 常量的值不能修改
// PI = 3
常量a只会在作用域内有效,外部打印找不到
{
const a = 100
}
console.log(a);
const ARR = [1, 2, 3, 4]
ARR.push(5) // 因为常量所指向的地址没有改变
console.log(ARR)
const F4 = ['xx', 'yy', 'zz', 'kk']
let [x, y, z, k] = F4
console.log(x)
console.log(y)
console.log(z)
console.log(k)
// 2. 对象
const zs = {
name: '张三',
age: 18,
sing: function() {
console.log('我会唱歌')
}
}
let {name, age, sing} = zs // 需要用大括号
console.log(name)
console.log(age)
console.log(sing)
sing()
let str = `hello`
console.log(str, typeof str)
let str2 = `<ul>
<li>1</li>
<li>2</li>
</ul>`
console.log(str2)
let lovestr = 'shenteng'
console.log(`${lovestr} is a`)
let a = 3
let b = 4
let str3 = `${a + b}`
console.log(str3)
function fun() {
return 'hello'
}
let str4 = `${fun()}`
console.log(str4)
之前的写法
let name = '张三'
let change = function(){
console.log('这是一个方法')
}
const sch = {
name: name,
change:change,
improve: function(){
console.log('这是一个方法')
}
}
console.log(sch)
ES6 允许在大括号里面直接写入变量和函数,作为对象的属性和方法
let name = '张三'
let change = function(){
console.log('这是一个方法')
}
const sch = {
// 1. 定义对象的简写形式
// 2. 省略了属性值和冒号
name,
change,
// 3.方法前面省略了function关键字
improve(){
console.log('这是一个方法')
}
}
console.log(sch)
// 使用之前方法声明一个函数
let fn = function(a,b){
return a + b
}
fn(1, 2)
// ES6 允许使用 => 定义函数
//声明函数
let fn = (a,b) => {
return a + b
}
//调用函数
console.log(fn(1,2))
首先定义两个函数,一个是正常的形式,另一个是箭头函数的形式定义的
function getName(){
console.log(this.name)
}
let getName2 = () => {
console.log(this.name)
}
设置 window对象 的 name 属性
// 设置 window对象 的 name 属性
window.name = 'xxyyzz'
const school = {
name: 'qinghua'
}
调用
getName() // this 指向 window 输出 xxyyzz
getName2() // this 指向 window 输出 xxyyzz
使用call方法调用
getName.call(school) // 结果是qinghua
getName2.call(school) // 结果还是 xxyyzz
let Person = (name, age) => {
this.name = name
this.age = age
}
let person = new Person('xxyyzz', 18)
console.log(person)
// 报错: Person is not a constructor
let fn1 = () => {
console.log(arguments)
}
fn1(1,2,3)
// 报错: arguments is not defined
let add = n => {
return n + n
}
let pow = n => n * n
需求1:点击div, 2s后颜色变成粉色
按照之前的做法?
let box = document.querySelector('.box');
box.addEventListener('click', function() {
// 定时器
setTimeout(function() {
console.log(this);
this.style.backgroundColor = 'pink';
}, 2000)
})
由上述的结果可以看出,此时的this指向window, 并不是指向当前的box?
方法1: 保存this的值
let box = document.querySelector('.box');
box.addEventListener('click', function() {
// 保存this的值
let _this = this
// 定时器
setTimeout(function() {
console.log(_this)
_this.style.backgroundColor = 'pink';
}, 2000)
})
方法2:使用箭头函数
?因为 箭头函数中 this 是静态的,?this 始终指向函数声明时所在作用域下的 this 的值
let box = document.querySelector('.box');
box.addEventListener('click', function() {
setTimeout(() => {
console.log(this);
this.style.backgroundColor = 'pink';
}, 2000)
})
需求2:从数组中返回偶数的元素
方法1:使用之前的方法?
// 需求2:从数组中返回偶数的元素
const arr = [1,2,6,8,9,100]
const result = arr.filter(function(item) {
if (item % 2 === 0) {
return true
} else {
return false
}
})
console.log(result)
方法2:使用箭头函数
// 需求2:从数组中返回偶数的元素
const arr = [1,2,6,8,9,100]
const result = arr.filter(item => item % 2 === 0)
console.log(result)
总结: 1. 箭头函数适合与this无关的回调,比如定时器、数组的方法回调
? ? ? ? ? ? 2. 箭头函数不适合于this有关的回调,比如DOM元素的事件回调、对象的方法
如果形参需要接收三个参数值,而实参只传了两个,比如下面的加法函数,那么结果就会是NAN,因为c没有值,就是undefined
function add(a, b, c) {
return a + b + c
}
let result = add(1, 2)
console.log(result)
此时就可以知道参数默认值的好处了
function add(a, b, c = 10) {
return a + b + c
}
let result = add(1, 2)
console.log(result)
但是这边有一个注意点,就是具有默认值的参数,一般位置放在最后面
// 2. 可以与结构赋值结合
function connect({host = '127.0.0.1', username, password, port}) {
console.log(host)
console.log(username)
console.log(password)
console.log(port)
}
connect({
host: 'localhost',
username: 'root',
password: 'root',
port: 3306
})
1. 先看下 ES5 获取实参的方式
// ES5 获取实参的方式
function date(){
console.log(arguments)
console.log(typeof arguments)
}
date('2019-10-10','2019-10-11','2019-10-12')
可以看出得出的结果是一个对象
2. 再看下ES6获取实参的方式? (rest 参数)
function date1(...args){
console.log(args)
}
date1('2021-10-10','2021-10-11','2021-10-12') // 得到的是数组
结果是数组,是数组的话就可以使用数组的一系列方法,提高了我们对参数的使用的灵活度,并且rest参数必须放到最后
...??表示扩展运算符,??扩展运算符可以将数组转化为逗号分隔的参数序列
1. 当不使用扩展运算符时
const boys = ['张三', '李四', '王五']
// 声明一个函数
function show() {
console.log(arguments)
}
show(boys)
2. 当使用扩展运算符时
const boys = ['张三', '李四', '王五']
// 声明一个函数
function show() {
console.log(arguments)
}
show(...boys)
3. 应用案例:
案例1:数组的合并?
首先看下ES5中的做法
//案例: 合并数组
const arr1 = [1, 2, 3]
const arr2 = [4, 5, 6]
const arr = arr1.concat(arr2)
console.log(arr)
ES6使用扩展运算符合并数组
const arr1 = [1, 2, 3]
const arr2 = [4, 5, 6]
const arr = [...arr1, ...arr2]
console.log(arr)
案例2:数组的克隆
// 2. 数组的克隆
const arr1 = [1, 2, 3]
const arr2 = [...arr1]
console.log(arr2)
案例3:将伪数组转换为真正的数组?
const divs = document.querySelectorAll('div')
console.log(divs)
// 3. 将伪数组转化为真正的数组
const divs = document.querySelectorAll('div')
// console.log(divs)
const divArr = [...divs]
console.log(divArr)
// 1. 创建Symbol
let s = Symbol()
console.log(s, typeof s)
Symbol()中也可以传入字符串参数,相当于一个标志,但是返回的结果是不同的
// 1. 创建Symbol
let s = Symbol()
console.log(s, typeof s)
let s2 = Symbol('张三')
let s3 = Symbol('张三')
console.log(s2, s3)
console.log(s2 === s3) // false
通过这种方式创建,可以通过描述的字符串得出唯一的 Symbol值
let s4 = Symbol.for('李四')
let s5 = Symbol.for('李四')
console.log(s4, s5)
console.log(s4 === s5) // true
方式1:
let game = {
name: '俄罗斯方块',
up: function() {
},
down: function() {
}
}
let methods = {
up: Symbol(),
down: Symbol(),
}
// 添加两个方法
game[methods.up] = function() {
console.log('我要上升了!')
}
game[methods.down] = function() {
console.log('我要下降了!')
}
console.log(game)
方式2:
let info = {
name: '张三',
[Symbol('age')]: 20,
[Symbol('say')]: function() {
console.log('我要说话了!')
}
}
console.log(info)
参考:Symbol.asyncIterator - JavaScript | MDN (mozilla.org)
// 声明一个数组
const arr = [1, 2, 3, 4, 5]
// 使用 for...of 遍历数组
for (let element of arr) {
console.log(element)
// 输出:1 2 3 4 5
}
工作原理:
1. 创建一个指针对象,指向当前数据结构的起始位置。也就是说,遍历器对象本质上,就是一个指针对象。
2. 第一次调用指针对象的next方法,可以将指针指向数据结构的第一个成员。
3. 第二次调用指针对象的next方法,指针就指向数据结构的第二个成员。
4. 不断调用指针对象的next方法,直到它指向数据结构的结束位置。
5.每一次调用next方法都会返回一个包含value和done两个属性的对象。其中,value属性是当前成员的值,done属性是一个布尔值,表示遍历是否结束。
const arr = [1, 2, 3, 4, 5]
let iterator = arr[Symbol.iterator]()
console.log(iterator.next()) // { value: 1, done: false }
console.log(iterator.next()) // { value: 2, done: false }
console.log(iterator.next()) // { value: 3, done: false }
console.log(iterator.next()) // { value: 4, done: false }
console.log(iterator.next()) // { value: 5, done: false }
console.log(iterator.next()) // { value: undefined, done: true }
案例:迭代器自定义遍历对象
1. 当我们直接遍历对象时
const banji = {
name:'终极一班',
stus:[
'xiaoming',
'xiaoning',
'xiaotian',
'knight'
],
}
for(let v of banji){
console.log(v)
}
无法遍历,因为对象没有iterable 这个属性
现在我们有个需求是:? 需要遍历该对象中stus:? 让其中的值逐条输出
因为对象没有iterable属性,所以需要加上这个接口,并让其返回一个对象,该对象中是一个next() 函数,用来遍历
// 声明一个对象
const banji = {
name:'终极一班',
stus:[
'xiaoming',
'xiaoning',
'xiaotian',
'knight'
],
[Symbol.iterator]() {
// 索引变量
let index = 0
return {
next: () => {
if (index < this.stus.length) {
// 构建一个yield返回值
const result = {value: this.stus[index], done: false}
// 下标自增
index++
return result
}
// 返回结束标志
return {value: undefined, done: true}
}
}
}
}
for(let v of banji){
console.log(v)
}
生成器其实就是一个特殊的函数,进行异步编程
写法的特殊地方就是需要加个 * 星号,靠左,靠右,中间都行?
function * gen() {
console.log("hello generator")
}
let iterator = gen()
console.log(iterator)
上述函数中的返回结果并没有输出,看它的返回结果实际上是一个迭代器对象,所以我们就可以调其中的next()方法,来运行这个函数中的console.log()语句
function * gen() {
console.log("hello generator")
}
let iterator = gen()
console.log(iterator)
iterator.next()
yield可以看成是函数代码的分隔符
function * gen() {
console.log("111")
yield "我是1" // yield为函数代码的分隔符
console.log("222")
yield "我是2"
console.log("333")
yield "我是3"
console.log("444")
}
let iterator = gen()
console.log(iterator)
iterator.next()
iterator.next()
iterator.next()
iterator.next()
这样子的话,就不像原来一下输出了执行的结果,而是通过next方法来控制执行
function * gen(arg) {
console.log(arg);
let re1 = yield 111
console.log(re1)
let re2 = yield 222
console.log(re2)
let re3 = yield 333
console.log(re3)
}
// 执行获取迭代器对象
let iterator = gen("AAA")
console.log(iterator.next())
// next方法可以传入实参
console.log(iterator.next("BBB")) // 第二次调用传入的参数,将作为第一个yield语句的返回结果
console.log(iterator.next("CCC")) // 第三次调用传入的参数,将作为第二个yield语句的返回结果
console.log(iterator.next("DDD")) // 第四次调用传入的参数,将作为第三个yield语句的返回结果
1s 后控制台输出 111,然后2s后输出 222, 然后3s后输出 333
按照之前的写法的话,当我们有很多个回调任务时,代码的缩进就会不断地向前推进,不利于阅读,这种现象就被称为 “回调地狱”
setTimeout(()=> {
console.log(111)
setTimeout(()=> {
console.log(222)
setTimeout(()=> {
console.log(333)
},3000)
},2000)
}, 1000)
解决回调地狱问题:使用生成器函数
// 定义三个任务函数
function one() {
setTimeout(() => {
console.log(111)
iterator.next() // 执行下一个任务
}, 1000)
}
function two() {
setTimeout(() => {
console.log(222)
iterator.next()
}, 2000)
}
function three() {
setTimeout(() => {
console.log(333)
iterator.next()
}, 3000)
}
// 定义生成器函数
function* gen() {
yield one()
yield two()
yield three()
}
// 调用生成器函数
let iterator = gen()
iterator.next()
模拟获取:用户数据、订单数据、商品数据
// 案例2: 模拟获取:用户数据、订单数据、商品数据
function getUsers() {
setTimeout(() => {
let data = '用户数据'
// 调用next方法,并将数据传入
iterator.next(data)
}, 1000)
}
function getOrders() {
setTimeout(() => {
let data = '订单数据'
iterator.next(data)
}, 1000)
}
function getGoods() {
setTimeout(() => {
let data = '商品数据'
iterator.next(data)
}, 1000)
}
function* gen() {
let users = yield getUsers()
console.log(users)
let orders = yield getOrders()
console.log(orders)
let goods = yield getGoods()
console.log(goods)
}
// 调用生成器函数
let iterator = gen()
iterator.next()
// 实例化 Promise 对象
const promise = new Promise((resolve, reject) => {
// 异步操作
setTimeout(() => {
const success = true
if (success) {
resolve(success)
} else {
reject(success)
}
}, 1000)
});
// 调用 Promise 对象 then 方法处理成功情况
promise.then((value) => {
console.log(value)
}, function(reason) {
console.log("操作失败!")
});
// 创建Promise对象
const promise = new Promise((resolve, reject) => {
setTimeout(() =>{
resolve('成功')
})
})
调用 then 方法,then方法的返回结果是 Promise 对象,对象状态由回调函数执行结果决定
1. 如果回调函数中返回的结果是 非Promise类型的属性,状态为成功,返回值为对象的成功的值
const result = promise.then((value) =>{
console.log(value)
return "I love you"
}, (reason) => {
console.error(reason)
})
console.log(result)
2.?如果回调函数中返回的结果是 Promise 对象,状态为返回结果的状态,返回值为对象的成功的值
const result = promise.then((value) =>{
console.log(value)
return new Promise((resolve, reject) =>{
resolve('ok')
})
}, (reason) => {
console.error(reason)
})
console.log(result)
3.?如果回调函数中返回的结果是 抛出异常,状态为失败,返回值为对象的失败的值
const result = promise.then((value) =>{
console.log(value)
throw '出错了'
}, (reason) => {
console.error(reason)
})
console.log(result)
const promise = new Promise((resolve,reject)=>{
setTimeout(()=>{
reject('出错啦!')
},1000)
})
promise.catch(reason => {
console.warn(reason)
})
let mySet = new Set()
console.log(mySet)
console.log(typeof mySet)
let mySet2 = new Set(["好事","坏事","大事","好事"]) // 可以去重
console.log(mySet2)
let mySet2 = new Set(["好事","坏事","大事","好事"]) // 可以去重
console.log(mySet2)
// 元素个数
console.log(mySet2.size)
// 添加元素
mySet2.add("小事")
console.log(mySet2)
// 删除元素
mySet2.delete("坏事")
console.log(mySet2)
// 检测是否有该元素
console.log(mySet2.has("大事")) // 返回结果是boolean类型
// 清空set
mySet2.clear()
console.log(mySet2)
// 遍历Set: 集合实现了iterator接口,所以可以使用for...of进行遍历
for(let item of mySet2){
console.log(item)
}
let myMap = new Map()
// 添加键值对(元素)
myMap.set('name', '张三')
myMap.set('age', 18)
let key = {
school: 'university'
}
myMap.set(key, ['清华大学','北京大学'])
console.log(myMap)
// 获取Map中的元素个数
console.log(myMap.size)
// 删除元素
myMap.delete('age')
console.log(myMap)
// 获取元素
console.log(myMap.get('name'))
console.log(myMap.get(key))
// 判断Map中是否存在某个元素
console.log(myMap.has('name'))
console.log(myMap.has('age'))
// 清空Map
// myMap.clear()
// console.log(myMap)
// 遍历Map
for (let v of myMap) {
console.log(v)
}
先看下ES5中用构造方法来实例化对象的做法
// 手机
function Phone(brand, price) {
this.brand = brand
this.price = price
}
// 添加方法
Phone.prototype.call = function() {
console.log("正在呼叫...")
}
// 实例化对象
let honor = new Phone('荣耀', 2499)
console.log(honor)
honor.call()
再看下ES6中通过 class类 来实现
class Phone {
// 构造方法
constructor(brand, price) { // constructor 名字不能修改,是固定的
this.brand = brand
this.price = price
}
// 添加方法,(方法必须使用该语法)
call() {
console.log("华为手机正在呼叫...")
}
}
// 实例化对象
let honor = new Phone('华为', 2499)
console.log(honor)
honor.call()
// 类的静态成员
class Phone {
static name = 'nojiya'
static change = function() {
console.log("我可以改变你们")
}
}
let phoneSHI = new Phone()
console.log(phoneSHI)
console.log(phoneSHI.name) // undefined
console.log(Phone.name) // nojiya
console.log(Phone.change)
也就是说static标注的属性和方法是属于类的,而不属于实例对象
ES5中使用构造函数实现类继承
//1. ES5中使用构造函数实现类继承
// 手机
function Phone(brand, price) {
this.brand = brand
this.price = price
}
// 添加方法
Phone.prototype.call = function() {
console.log("正在呼叫...")
}
// 智能手机
function SmartPhone(brand, price, color, size) {
Phone.call(this, brand, price) //call 方法是 JavaScript 中的函数方法,它可以在指定的作用域中调用函数,并将指定的参数传递给该函数。
this.color = color
this.size = size
}
// 设置子集构造函数的原型
SmartPhone.prototype = new Phone()
SmartPhone.prototype.constructor = SmartPhone
// 添加子类自己的方法
SmartPhone.prototype.photo = function() {
console.log("正在拍照...")
}
// 测试
let s = new SmartPhone("小米", 399, "黑色", "4.7inch")
console.log(s)
s.call()
s.photo()
ES6中使用class实现类继承
// 2. ES6中使用class实现类继承
// 手机
class Phone {
constructor(brand, price) {
this.brand = brand
this.price = price
}
call() {
console.log("正在呼叫...")
}
}
// 智能手机
// extends关键字
class SmartPhone extends Phone
{
constructor(brand, price, color, size) {
super(brand, price) //super 关键字用于调用父类的构造函数或方法。
this.color = color
this.size = size
}
photo() {
console.log("正在拍照...")
}
}
// 测试
let s = new SmartPhone("荣耀", 1399, "银色", "4.2inch")
console.log(s)
s.call()
s.photo()
1、get:
// get
class Phone {
get price() {
console.log('price属性被读取了')
return 'iloveyou'
}
}
// 实例化对象
let s = new Phone()
console.log(s.price)
2、set:
// set
class Phone {
set price(newVal) {
console.log('price属性被修改了')
}
}
// 实例化对象
let s = new Phone()
s.price = 'free'
Number.EPSILON 是JavaScript表示的最小精度,? EPSILON 属性的值接近于 2.2204460492503131e-16
// 1. Number.EPSILON 是JavaScript表示的最小精度
// EPSILON 属性的值接近于 2.2204460492503131e-16
function equal(a, b) {
if (Math.abs(a - b) < Number.EPSILON) {
return true
} else {
return false
}
}
console.log(0.1 + 0.2) // 0.30000000000004
console.log(0.1 + 0.2 === 0.3) // false
console.log(equal(0.1 + 0.2, 0.3)) // true
// 二进制、八进制、十进制、十六进制
let b = 0b10
let o = 0o10
let d = 10
let x = 0x10
console.log(b, o, d, x) // 2 8 10 16
判断一个数是否是有限大的数
function isFinite(num) {
return Number.isFinite(num)
}
console.log(isFinite(5)) // true
console.log(isFinite(5.0)) // true
console.log(isFinite(5.1)) // true
console.log(isFinite(Infinity)) // false
判断一个数是否是 NaN
function isNaN(num) {
return Number.isNaN(num)
}
console.log(isNaN(5)) // false
console.log(isNaN(NaN)) // true
将字符串转为整数
let num = "123.456";
console.log(Number.parseInt(num)); // 123
console.log(Number.parseFloat(num)); // 123.456
?判断一个数是否为整数
let num = 12.34
console.log(Number.isInteger(num)); // false
let num1 = 12
console.log(Number.isInteger(num1)); // true
17.7 Math.trunc()
将数字的小数部分抹掉
// 将数字的小数部分抹掉
// Math.trunc()
let num = 12.34
console.log(Math.trunc(num)); // 12
let num1 = -12.34
console.log(Math.trunc(num1)); // -12
判断一个数是正数、负数、还是零
// 判断一个数是正数、负数、还是零
// Math.sign()
let num = 12.34
console.log(Math.sign(num)); // 1
let num1 = -12.34
console.log(Math.sign(num1)); // -1
let num2 = 0
console.log(Math.sign(num2)); // 0
判断两个值是否完全相等
console.log(Object.is(120, 120)) // true
console.log(Object.is(120, 121)) // false;
对象的合并
const config1 = {
host: 'localhost',
port: 3306,
test: 'test'
}
const config2 = {
host: '192.168.0.100',
port: 8080,
}
console.log(Object.assign(config1, config2))
// Object.setPrototypeOf 设置原型对象
const school = {
name: 'qinghua',
}
const city = {
xiaoqu: ['北京', '上海']
}
console.log(Object.setPrototypeOf(school, city))
console.log(Object.getPrototypeOf(school))
1. 分别暴露
// 分别暴露
export let school = 'xx'
export function teach() {
console.log('teach')
}
2. 统一暴露?
// 统一暴露
let school = 'ty'
function sayHello() {
console.log('hello')
}
export { school, sayHello }
3. 默认暴露
// 默认暴露
export default {
name: '张三',
age: 18,
change: function() {
console.log('change')
}
}
方式1:通用的方式
方式二:解构赋值形式
方式三:简便形式,针对默认暴露
因为将所有的引入操作的代码都写在页面中,是非常占地方的,所以我们将它们都引入到一个入口文件app.js中,然后在页面中引入这个入口文件
// 入口文件
// 模块引入
import * as m1 from './m1.js'
import * as m2 from './m2.js'
import * as m3 from './m3.js'
<script src="./js/app.js" type="module">
</script>
并不是所有的浏览器都能向chrome一样都能识别ES6新特性,所以需要进行转换
1. 安装工具:?babel-cli babel-preset-env browserify?
npm i babel-cli babel-preset-env browserify -D
2. npx babel src/js -d dist/js --presets=babel-preset-env
3. npx browserify dist/js/app.js -o dist/bundle.js