let与var的区别:
其实这两个的功能都是差不多的,都是声明变量用的。
但是,let 声明的变量只在其代码块内有效,比如说:
for (let i = 0; i <= 10; i++) {
console.log(i);
}
console.log(i);
这段代码在for循环里面声明的 i 里面的会正常打印,外面的这个打印就会报错
就会是这样,但是用var声明就不会有这样的问题,就可以理解成,let 声明的变量只在那个大括号内有效,出了那个大括号就无效了(所以就是let声明的变量只在块级作用域内有效)
什么是块级作用域:
有一段代码,是被大括号包起来的,那么大括号里面就是一个块级作用域
为什么需要块级作用域:
(1)内层变量可能会覆盖外层变量
var temp = new Date();
function show() {
console.log("temp=", temp)
if (false) {
var temp = "hello world";
}
}
show();
执行上面的代码,输出的结果为 temp=undefined ,原因就是变量由于提升导致内层的temp变量覆盖了外层的temp变量
(2)计数的循环变量可能会变成全局变量
我们不希望计数的那个作为全局变量,希望它计数完成之后就消失,所以就可以用let
由上面这两种例子可以看出来,let 不仅是在当前声明的代码块里面有效,而且还不存在变量提升的问题
let使用的时候的注意事项:
(1)不存在变量提升的问题
(2)存在暂时性死区(在代码块内,使用let命令声明变量之前,该变量都是不可用的)
(3)不允许重复声明,使用var声明变量是没有限制的,还有一点要注意的是,函数的形参与内部声明的变量也不能重名,否则会报错
const命令注意事项:
(1)const 声明的是一个只读的常量,而且声明的时候就要赋好值,
对于简单数据类型,const在栈中存储的是变量的值,一旦修改就会报错
对于复杂数据类型,const在栈中保存的是一个指针,指向堆内存中的某个空间(变量的值保存在这里)指针不可以改变,但是指针指向堆内存中的值可以改变(经常考)
(2)不存在常量提升的问题
(3)只在声明的代码块内有效
(4)不允许重复声明
(5)存在暂时性死区
数组的解构赋值:
var arr = [1, 2, 3]
let [num1, num2, num3] = arr
console.log(num1, num2, num3);
就可以取出相应的值
对于更复杂一点的:
var arr = [{
name: "zahngsan",
age: 18
},
[1, 2],
3
]
let [{ name, age }, [num1, num2], num3] = arr
console.log(name, age, num1, num2, num3);
// 把每一个值都结构出来
// 如果只是想解构对象或者是数组
// 就可以
let [obj, array1, num3] = arr
console.log(obj, array1, num3);
下面的结果就是
还有几种情况就是部分结构
// 只拿第一个值
let [num1] = [1, 2, 3]
console.log(num1);
// 只拿第二个值
let [, num] = [1, 2, 3]
console.log(num);
// 只拿第三个值
let [, , num3] = [1, 2, 3]
console.log(num3);
对象的解构赋值(变量名必须与属性的名称一致,才能够取到正确的值 )
let { username, userage } = {
username: 'zhangsan',
userage: 18
}
console.log(username, userage);
如果真的想要修改变量的名字
let { username: name, userage: age } = {
username: 'zhangsan',
userage: 18
}
console.log(name, age);
解构赋值的好处:
(1)可以交换两个变量的值
(2)函数可以返回多个值
(3)函数返回一个对象
扩展运算符的基本使用:
//扩展运算符的基本使用
// 合并数组
// 正常情况下
let arr1 = [1, 2, 3]
let arr2 = [4, 5, 6]
let arr3 = [].concat(arr1, arr2)
console.log(arr3);
// 使用扩展运算符就会方便很多
let arr4 = [...arr1, ...arr2]
rest 参数的使用,优势和注意事项:
rest 可以说是和扩展运算符是相反的(互为逆运算),就是这个可以把一串字符转换成数组
// rest 参数的使用
// 第一个优点就是可以让函数的参数不再使用arguments了
function add(...values) {
console.log(values); // 打印结果(3) [1, 2, 3]
var sum = 0
values.forEach((item) => {
sum += item
})
console.log(sum);
}
add(1, 2, 3)
rest和扩展运算符的区分:
(1)当... 出现在函数的形参上或者是赋值号的左侧,属于rest运算符
(2)当... 出现在函数的实参上或者是赋值号的右侧,属于扩展运算符
箭头函数的使用:
(x,y) => {return x+y}
箭头函数this指向的问题:
箭头函数没有真正属于自己的this,它是捕获其上下文中的this作为自己的this,所以它不能被new调用
下面这个代码this指向的是window,因为这个this指向的是其上一层的this就是window,window里面没有定义username,所以输出的值为undefined
因为箭头函数没有真正的this指向,所以不能调用apply,call,bind这三个函数
箭头函数不适用的场景:
(1)箭头函数不能作为构造函数来使用,因为构造函数都是通过new操作符来生成对应的对象实例,而生成实例的过程就是通过构造函数给实例绑定this的过程,但是箭头函数没有this,因此箭头函数不能作为构造函数
(2)箭头函数没有prototype属性
(3)不适合将原型函数写成箭头函数
Object.assign()方法的应用:
这个方法可以实现浅拷贝:
正常情况下的浅拷贝:(这种循环的方式比较麻烦)
var obj1 = {
name: "zahngsan",
age: 12
}
var obj = {}
for (let key in obj1) {
obj[key] = obj1[key]
}
console.log(obj);
Object.assign(target,source) // 第一个参数是目标对象,第二个参数就是原来的对象
这样写就很方便
var obj1 = {
name: "zahngsan",
age: 12
}
var obj = {}
Object.assign(obj, obj1)
console.log(obj);
Object.assign()这个方法有多个参数,第一个参数是目标对象,后面剩下的所有对象都是原对象
Object.assign()注意事项:
(1)如果目标对象与源对象有同名的属性,那么后面的属性会覆盖前面的属性
(2)不可枚举的属性不会被复制