1.变量声明
首先js变量声明有三种,var,const,let,这三种变量声明中我们第一优先使用const,需要改变这个值的时候我们用ley,var是尽量不去使用。
那么我们现在来总结一下三种声明变量的区别。首先是var let 都是定义变量。
{var a = 20}console.log(a);
首先是var声明变量这里我们输出控制台看到了20。因为我们var没有块级作用域,使用var定义的变量会直接作为全局变量。
{let b =20}console.log(b);
这里的输出结果是会报错, b是局部变量,只能在{}中使用。也就是说let声明的变量有块级作用域,而var没有。那么作用域有什么用呢?
for (var i = 0; i < 5; i++) {console.log('循环内部', i);//能访问到i}console.log('循环内部', i);
这里还是拿到了i 因为var让i变成了全局变量,这很明显不符合常理,因为我们循环体中我们仅仅希望i去控制循环,并不希望去设置一个全局变量i。
for (let i = 0; i < 5; i++) {console.log('循环内部', i);//能访问到i}console.log('循环内部', i);
使用let就只能在作用域里面访问到i,在全局是访问不到的。
我们在去尝试下面的代码。
if (true) {var b = 33}console.log(b);
控制台输出了33,很正常因为全局变量b=33.我们改一下。
if (true) {let b = 33}console.log(b);
这里我们就无法访问到b,因为只在{}这个块级作用域里面才能访问到b,我们在来看一种情况。
if (false) {/ var b = 33}console.log(b);
这里没有报错,甚至输出的是undefined,这里是因为var会引发 变量提升,就是当我们用var去声明一个变量的时候,浏览器在解析代码的时候,会优先把var声明的变量在一开始就var声明,所以尽管没有赋值成功,也会默认声明,这显然非常不符合逻辑。所以我们开发时不去使用var。
const obj = {name: '随你'}
obj.age = 18
obj.name = "www"console.log(obj.name);console.log('obj.age', obj.age)
这里我们用const去定义一个obj对象,我们不可以直接去更改obj对象,// obj = { name: '猴子石' }不可以这样直接对obj进行赋值,但是我们可以改变对象的内容,去添加新的对象或者覆盖掉已经有的属性。
总结1.var 没有块级作用域2.let 有块级作用域3.const 和let类似 具有块级作用域,但是只能赋值一次4.const 使用场景 1.对一些常量可以使用const 一般大写字母 const PI = 3.15
5.对一些对象函数也可以使用const 可以避免对象和函数被意外修改 可以让obj永远指向后面的对象。
2.解构赋值
我们从数组里面获取里面的元素的时候,我们最开始是用变量赋值的方式。
let a, blet arr = ['w', 'n']a = arr[0]b = arr[1]console.log(a, b);
好像很简单的逻辑就很麻烦写起来。我们可以用解构赋值的语法去获取。
let a, blet arr = ['w', 'n'][a, b] = arr //解构赋值console.log(a, b);
这样就非常简便,我们甚至可以在解构赋值的时候去声明变量。
const [a, b] = arr
这就是数组的解构赋值,但是如果有很多数组,我们希望去用数组解构的时候,就需要一一对应,或者用...b这种去让b去接住后面所有的数组元素。
const wrr = ['wwwww', '傻孩子', 'bailong', 'aaaa', 'ppp']// const [a, b, , c] = wrr// console.log(a, b, c);//多个解构就是一一对应,多余的就不显示想跳过一个就,就可以// const [a, b, ...c] = wrr// console.log(a, b, c);//也可以直接...c(数组)去拿取后面所有的元素 而且这里的...c会接所有元素所以只能放最后
这就是数组的解构赋值现在我们来看对象的解构。
const obj = {name: 'wwww',age: 12,gender: '难'}let a, b, c;({ name: a, age: b, gender: c } = obj) //这里用括号因为不()直接等号左边{}不合理//要注意的是被赋值的变量在右边,对象的属性在左边 比如第一个将name赋值被aconsole.log(a, b, c);//通常我们把变量名和对象的属性名一致// const { name: name, gender: gender, age: age } = obj// console.log(name, gender, age);//然后对象可以简写因为左右一致,const { name, age, gender } = objconsole.log(name, age, gender);// 解构赋值还可以交换数组中变量的位置arr = [1, 2, 3]console.log(arr);[arr[1], arr[2]] = [arr[2], arr[1]]console.log(arr);// 这里把下标1,2换了位置
和数组的解构赋值思路一样,只不过对象需要用{}包裹,然后赋值的时候,左边是我们对象里面的属性,右边是我们想要进行赋值的变量!!!,然后我们可以简写(往往解构的时候变量名和对象的属性名一致),就可以直接。
这样去解构赋值,也可以用解构赋值来换取位置,方法如图。
3.展开
function fn(a, b, c) {return (a + b + c)}const arr = [1, 2, 3]//计算三个数字的和// let result = fn(arr[0], arr[1], arr[2])let result = fn(...arr)//...展开数组 ...可以展开数组console.log(result);const arr2 = [7, 8, ...arr, 4, 5, 6]//将arr浅复制给arr2console.log(arr2);//数组可以展开对象也可以const obj = {name: 'www',age: 12,gender: 'nan'}const obj2 = { gogog: 'www', ...obj, address: 'wopa', name: 'zzzz' } //把obj在新对象中展开相当于浅复制//对象是无序的如果重复了后来的覆盖前面的console.log(obj2);
首先我们在前面的数组结构用到了...a去表示一个数组变量,那么我们就可以用...arr去表示一个数组展开后的内容,也就是...展开数组。我们可以用...arr去传参,相当于传过去展开的数组,对象也可以这样,相当于展开了对象。然后我们这里用...obj去给obj2这个对象赋值,展开之后,这里name重复后来的会覆盖掉前面的。
4.箭头函数
const fn = function () {return 'hello'}const fn2 = a => aconsole.log(fn2(123));箭头函数只有一个参数的函数,参数等于返回值如果没有参数或者多个参数用()括起来()=>返回值 (a,b,c)=>{}const sum = (a, b, c) => a + b + cconst sum = (a, b, c) => ({ name: 'sun' })let result = sum(1, 2, 3)console.log(result);箭头后面的叫做返回值 返回值必须是表达式?表达式(有返回值的语句)如果返回的是对象需要加()如果需要在箭头函数定义逻辑,在箭头后跟一个代码块代码块语法和普通函数一致const fn4 = (a, b) => {if (a === 10) {a += 5} else if (a === 20) {a += 10}return a + b}console.log('fn4', fn4(1, 2))
箭头函数是我们定义函数的另一种简写方式,当只有一个参数的时候,而且返回值等于参数的时候可以这样去书写,但是如果有多个参数的话,我们就需要给参数加一个()
而且返回值必须是表达式,也就是必须要有值作为返回值,如果我们希望通过一个逻辑去返回值的话,我们就在箭头后面跟一个代码块。这就是箭头函数的使用方法。
5.箭头函数和普通函数的区别
//箭头函数是传统函数表达式的简写方式,但是也有一些限制导致一些场景无法使用//没有自己的this//没有arguments//不能作为构造函数调用//无法通过call apply bind指定函数的thisfunction fn(a, b, ...args) {//用来保存函数实参arguments 类数组不能用操作数组的方法// console.log(arguments.length);//输出的2// console.log(...arguments);//输出的1,2console.log(...args);//用...args代替arguments更好用是真正的数组}fn(1, 2, 3, 4, 5)const fn2 = (...argus) => {// console.log(arguments);//报错argument is undefinedconsole.log(...argus);}fn2(1, 2, 3)//箭头函数没有arguments(类数组)但是也可以用...argus去保存实参//箭头函数没有自己的thisconst fn3 = () => {console.log(this);//这里的this是window}fn3()//函数中的this是指向外层作用域(运行环境中的this)。这里fn3的作用域是全局 所以说windowconst obj = {fn4: () => {console.log(this);}}obj.fn4()//这里仍然是window因为箭头函数没有自己的this,它的this总是外层作用域的thisconst obj2 = {ss: function () {console.log(this);}}obj2.ss()//这里的this指向的就是obj2因为普通函数调用时看“谁点的它”(谁调用)谁调用就指向谁const obj3 = {ss: function () {const test = () => {console.log(this);}test()}}obj3.ss()//这里指向的是obj3,因为这个this就是外层作用域也就是普通函数的this,普通函数的this指向obj3所以这里指向obj3
首先箭头函数虽然是传统函数的简写方式,但是也是有一些场景无法使用的,比如没有自己的this没有argument保存实参,也不能作为构造函数调用,因为不能用this,也无法通过call,bind方法去指定this。如图fn4箭头函数中的this是外层作用域也就是obj的this,而obj是在全局中定义的,所有this指向的window。而ss这个普通函数obj2.ss输出的this指向的obj2因为普通函数的this是说调用指向谁。