Day20:ES6语法

1.let和const

ES2015(ES6) 新增加了两个重要的 JavaScript 关键字: let 和 const。

let 声明的变量只在 let 命令所在的代码块内有效。

const 声明一个只读的常量,一旦声明,常量的值就不能改变。

let的特性
  1. let 声明的变量只在 let 命令所在的代码块内有效,声明的变量不在全局作用域中
  2. let声明的变量有块级作用域,在块级作用域中let的变量,出了{}就无法使用
  3. let声明的变量在同一个域中不能重复声明
  4. let可以防止循环变量变成全局变量

var得到全局变量,循环完之后还能使用。对后面的代码可能会产生影响

let有块级作用域,在{}外调用会报错,不会对后面的代码产生影响

  1. 使用let声明没有变量提升,var有变量提升

变量提升:在预编译时,js会优先处理变量和函数的声明,再执行变量和函数的赋值(仅限于var定义的变量)

// var有变量提升
console.log(d1) 
// 预编译:GO{d1:undefined} log出undefined,不会报错
var d1 = 20
console.log(d1) // 20

// let没有变量提升,必须先定义再使用
console.log(d2) 
// 直接报错Cannot access 'd2' before initialization
let d2 = 30
console.log(d2)

闭包出现时:

计时器for循环:计时器结束后统一调用,var = i是全局变量,执行定时器之前,i的值已经被循环修改为4,定时器启动时打印三个四,而不是123

let定义就不会出现这种情况

原理:每创建一个定时器,就会形成一个块级作用域,块级作用域内的i,互不影响,最后执行定时器的时候,打印的是各自块级作用域中的i。不会出现var变量i在全局作用域中,每次自加都重新赋值所有值的情况。

// 解决方式1:立即执行函数——每次拿到i的值,立即打印
for (var i = 1; i <= 3; i++) {
  (function (index) {
    setTimeout(function () {
      console.log(index)
    }, 1000)
  })(i)
}

// 解决方式2:使用let定义
for (let j = 1; j <= 3; j++) {
  setTimeout(function () {
    console.log(j)
  }, 1000) // 123 
}
暂时性死区

ES6 明确规定,代码块内如果存在 let 或者 const,代码块会对这些命令声明的变量从块的开始就形成一个封闭作用域。代码块内,在声明变量 PI 之前使用它会报错

var PI = "a";
if(true){
  console.log(PI);  // Cannot access 'PI' before initialization
  const PI = "3.1415926"; // 换成let也会报错
}

const 如何做到变量在声明初始化之后不允许改变的?

其实 const 其实保证的不是变量的值不变,而是保证变量指向的内存地址所保存的数据不允许改动。此时,你可能已经想到,简单类型和复合类型保存值的方式是不同的。

对于简单类型(数值 number、字符串 string 、布尔值 boolean),值就保存在变量指向的那个内存地址,因此 const 声明的简单类型变量等同于常量。

而复杂类型(对象 object,数组 array,函数 function),变量指向的内存地址其实是保存了一个指向实际数据的指针,所以 const 只能保证指针是固定的,至于指针指向的数据结构变不变就无法控制了,所以使用 const 声明复杂类型对象时要慎重。

const

与let相同

声明的变量有块级作用域,在块级作用域中声明的变量,出了{}就无法使用

声明的变量在同一个域中不能重复声明

声明没有变量提升

与let不同

const 声明一个只读的常量,一旦声明,常量的值就不能改变。会报错不能修改常量Assignment to constant variable.

const声明的变量必须赋初值,否则无法使用,会报错没有声明常量Missing initializer in const declaration

对于对象类型,const声明的变量,虽然值不能改变,但是里面的内容可以改变

const arr = [1,2,3]
arr = [4,5,6] // 相当于重新创建了一个数组,并把数组的地址赋给了arr
arr[1] = 10
console.log(arr) // 不能修改数组,但是可以修改数组中的内容
区别

var

let

const

块级作用域

没有

变量提升

没有

没有

能否被修改

不能(对象类型可以修改内容

使用

建议使用优先级 const > let > var

  1. const在编程语言中,一般表示常量,这样阅读代码的人不会随意修改,防止误操作
  2. js引擎对const做了优化,执行效率比较高

具体使用细节:

  1. 声明基本数据类型,如果确定变量后面会被修改,使用let(for循环中的循环变量)。如果确定不修改,使用const。如果不确定会不会修改,仍然先使用const,如果后期要改再改
  2. 声明对象类型,比如数组,对象,正则,一般很少修改地址,更多的是修改里面的内容。因此首选还是const

2.类和继承

在ES6中,class (类)作为对象的模板被引入,可以通过 class 关键字定义类。

class 的本质是 function。

它可以看作一个语法糖,让对象原型的写法更加清晰、更像面向对象编程的语法。

语法
class 类名 {
	// 定义构造函数
	constructor(参数1,参数2){
		this.属性名1 = 参数1
		this.属性名2 = 参数2
	}
	// 定义方法
	方法名(参数列表){
		方法体
	}
}
extends

通过 extends 实现类的继承。

classChildextendsFather{ ... }

// 定义一个父类
class Person {
  constructor(name,age){
    this.name = name
    this.age  = age
  }
  // 定义方法
  eat(){
    console.log('干饭')
  }
}
// 定义一个学生类继承人类(class A extends B
class Student extends Person{
  // 添加子类的特殊方法
  study(){
    console.log('学生要学习')
  }
}
// 创建一个学生对象
const stu = new Student('张三',23)
console.log(stu)
stu.eat()
stu.study()
super

子类 constructor 方法中必须有 super ,且必须出现在 this 之前

调用父类构造函数,只能出现在子类的构造函数

调用父类方法, super 作为对象,在普通方法中,指向父类的原型对象,在静态方法中,指向父类

不可继承常规对象。

// 定义一个父类
class Person {
  constructor(name,age){
    this.name = name
    this.age  = age
  }
  // 定义方法
  eat(){
    console.log('干饭')
  }
}
// 定义一个学生类继承人类
class Student extends Person{
  // 定义构造函数
  constructor(name,age,number){
    // this.name = name
    // this.age  = age
    super(name,age)
    this.number = number
  }
  // 添加子类的特殊方法
  study(){
    console.log('学生要学习')
  }
}
静态成员

静态属性

用static修饰的属性,只能通过类名调用

Person.country

静态方法

勇static修饰的方法,只能通过类名调用

Person.run()

3.解构赋值

解构赋值是对赋值运算符的扩展。

他是一种针对数组或者对象进行模式匹配,然后对其中的变量进行赋值。

在代码书写上简洁且易读,语义更加清晰明了;也方便了复杂对象中数据字段获取。

语法

数组模式匹配

const [变量列表] = 数组

对象模型的解构

const {变量列表} = 数组

const {name:myName,age:myAge,list:[a,b,c]} = {
    name:'张三',
    age:24,
    list:[99,80,100]
}

4.箭头函数

箭头函数提供了一种更加简洁的函数书写方式。基本语法是:

参数 => 函数体

完整格式

(参数列表) => {函数体}

// 优化,如果函数体只有一条语句,并且前面有return关键字,那么{}和return都可以省略
const max4 = (num1,num2) => num1 > num2 ? num1 : num2 
const res = max4(99,88)
console.log(res)

注意

箭头函数中this指向window对象而不是调用该方法的对象,window中没有name属性,获取不到name值

箭头函数绑定事件时,this也会丢失绑定到window上,获取不到调用者的属性

// 使用箭头函数书写字面量对象
var stu2  = {
  name:'张4',
  age:26,
  study:()=>{
    console.log(this) // this指向window对象
    console.log(this.name + '在学习') // window中没有name属性,获取不到name值
  }
}

// 使用箭头函数绑定事件
btn.addEventListener('click',()=>{
  console.log(this) /* window */
  console.log(this.innerHTML) // undefined
})

5.数组遍历

数组遍历的四种方法

const arr = [1,2,3]
// forEach方式
arr.forEach((item,index) => {
  console.log(item,index)
})
// for ... of 遍历(只能获取数组元素,获取不到数组元素的索引)
// 语法:for(const 元素名称 of 数组名){语句体}
for(const item of arr){
  console.log(item)
  console.log(arr.indexOf(item)) 
  // 只能通过这种方式获取数组元素的索引,但如果元素相同只会获取第一次的索引
}
// for...in遍历(获取的是索引
// 语法:for(const 索引 in 数组名){语句体}
for(const index in arr){
  console.log(index)
  console.log(arr[index]) //只能通过索引获取元素
}
// for...in主要用于遍历对象
const stu = {name:'张三',age:23}
for(const key in stu){
  console.log(key) // name age 对象的属性名
  console.log(stu.key) // undefined
  console.log(stu[key]) // 只有痛过这种方式才能获取到属性值
}
// for..of还可以使用解构的方式遍历(遍历的同时还可以解构)
var array = [
  {name:"河南省",cities:["郑州","洛阳","开封"]},
  {name:"辽宁省",cities:["沈阳","大连","鞍山"]},
  {name:"山东省",cities:["青岛","济南","烟台"]},
];
for(const {name,cities} of array){ 
    console.log(name)
    console.log(cities) //打印出了该省的城市
}

6.元素偏移量

概念

offset:偏移量,使用它的相关属性可以动态获取元素位置(偏移值),大小等。

作用

获取元素-距离-带定位父元素-的位置(获取的值是不带单位的)

获取自身元素的大小(宽高,不带单位)

属性

element.offsetParent

返回元素的偏移容器

返回带有定位的父级元素,如果父级元素都没有定位,则返回body

element.offsetTop

相对于垂直偏移位置的偏移容器

相当于上外边距

返回当前元素相对于定位父元素上方的偏移值

element.offsetLeft

相对于水平偏移位置的偏移容器

相当于左外边距

返回当前元素相对于定位父元素左方的偏移值

element.offsetWidth

返回元素的宽度,包含边框和内边距,但不含外边距

element.offsetHeight

返回元素的高度,包含边框和内边距,但不含外边距

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
可以使用 `useState` hook 来实现状态管理,以及 `useEffect` hook 来监听 `b.cMonth` 的变化并更新 `a.date`。代码如下: ```jsx import React, { useState, useEffect } from "react"; function App() { const [a, setA] = useState({ Term: "立夏", cYear: 2023, cMonth: 5, cDay: 6, date: "2023-5-6" }); const [b, setB] = useState({ Term: null, cYear: 2023, cMonth: 6, cDay: 2, date: "2023-6-2" }); useEffect(() => { const { cYear, cMonth, cDay } = a; if (b.cMonth === cMonth) { return; } const newDate = new Date(`${cYear}-${b.cMonth}-${cDay}`); setA({ ...a, cMonth: b.cMonth, date: newDate.toISOString().slice(0, 10) }); }, [b.cMonth]); const handleBCMonthChange = (event) => { const { value } = event.target; setB({ ...b, cMonth: parseInt(value, 10) }); }; return ( <div> <label> b.cMonth: <select value={b.cMonth} onChange={handleBCMonthChange}> <option value={1}>1</option> <option value={2}>2</option> <option value={3}>3</option> <option value={4}>4</option> <option value={5}>5</option> <option value={6}>6</option> <option value={7}>7</option> <option value={8}>8</option> <option value={9}>9</option> <option value={10}>10</option> <option value={11}>11</option> <option value={12}>12</option> </select> </label> <div>a.date: {a.date}</div> </div> ); } export default App; ``` 在 `useEffect` 中,我们监听了 `b.cMonth` 的变化,并通过 `new Date()` 构造了一个新的日期对象,然后更新了 `a.date`。注意,由于 `new Date()` 会默认使用当前时区,所以为了避免时区的影响,我们使用了 `toISOString()` 方法把日期格式化为 ISO 格式,并只取了前 10 个字符。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值