var、let、const区别和优先级

  • 优先级:const > let > var
    • 三种声明方式中,第一优先使用的是const,如果希望变量被改变则用let,至于var最好不在代码出现
  • var声明的变量没有块作用域

for (var i = 0; i < 5; i++) {  
    console.log("变量i循环内部->>",i)  
}  
console.log("变量i循环外部->>",i)

for (let j = 0; j < 5; j++) {  
    console.log("变量j循环内部->>",j)  
}  
console.log("变量j循环外部->>",j)

此处如果使用var,控制台依然会打印出i。原因是var没有块作用域

  • let的出现有效的隔离了代码块内外的变量,使得代码的结构更清晰,维护起来更加容易。
  • const在JS中用来声明常量,所谓常量就是只能赋值一次的变量。在JS中对const的使用是非常频繁的,当一个变量用来保存一个对象时(函数或其他对象),为了避免变量被修改通常会使用const来声明
const obj = {name:"张三"}  
obj = {name:"李四"} // ❌ 对象不可改变  
obj.name = "李四" // √ 属性可以改变  
console.log(obj)

const fn = function () {  
    
}
  • const使用场景:
    • 对于一些常量使用const
    • 对于对象和函数也可以使用const

解构和展开

解构赋值

  • 数组解构

const [d, e, f] = [40, 50, 60] // 可以在赋值时直接声明变量  
console.log(d); // 40  
console.log(e); // 50  
console.log(f); // 60

[a=5, b=7] = [1]; // 赋值是可以指定默认值  
console.log(a); // 1  
console.log(b); // 7

let arr = ['孙悟空','猪八戒','沙和尚','唐僧']
const [a,b, ,d] = arr // 解构跳过元素
console.log(a); // 孙悟空
console.log(b); // 猪八戒
console.log(d); // 唐僧

const [a,b,...c] = arr // ...变量,会接收后面所有元素
console.log(a); // 孙悟空
console.log(b); // 猪八戒
console.log(c); // ['沙和尚','唐僧']
  • 解构函数返回值
function f() {  
  return [1, 2];  
}  
  
let [a, b] = f();  
console.log(a); // 1  
console.log(b); // 2
  • 对象解构

const obj = {  
  name:'孙悟空',  
  age:18,  
  gender:'男'  
}; 

({name:a,age:b,gender:c} = obj); // 将name赋值给a,age赋值给b,gender赋值给c
console.log(a)  // 孙悟空
console.log(b)  // 18
console.log(c) // 男

const {name,gender,age} = obj; // 如果变量名和属性名一致,可以省略

// 对象的嵌套解构
const {inner:{size}} = {a: 10, b: 20, inner:{size: 5}}; // 将对象中inner.size赋值给变量size  
console.log(size) // 5
  • 利用数组的解构交换变量位置
let a, b;  
a=10;  
b=20;  
// 常规交换  
let tmp = a;  
a = b;  
b = tmp;  
// 解构交换  
[a, b] = [b ,a]
//解构   创建数组

let arr = [1, 3, 2];  
[arr[1], arr[2]] = [arr[2], arr[1]];  // 交换数组中两个元素的位置
console.log(arr)

展开

  • 函数展开:通过… 展开一个数组
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 arr = [1,2,3]  
const arr2 = [...arr]  // 相当于将arr浅复制给arr2
const arr2 = [0,...arr,4,5,6]  
console.log(arr2)
  • 对象展开

const obj = {  
  name:'孙悟空',  
  age:18,  
  gender:'男'  
};  
const obj2 = {...obj}; // 将obj在新的对象中展开,相当于浅拷贝  
console.log(obj2);  
  
const obj3 = {...obj, address: '花果山'};  // 添加元素
console.log(obj3);

箭头函数

使用

  • 只有一个参数的箭头函数
    • 参数 => 返回值
  • 如果没有参数或多个参数,参数需要使用()括起来
    • () => 返回值
    • (a,b,c) => 返回值
  • 箭头后面的值就是函数的返回值
    • 返回值必须是一个表达式(有返回值的语句)
    • 如果返回值是对象 必须加()
  • 如果需要在箭头函数中定义复杂逻辑,可以直接在箭头后跟一个代码块
 const fn = function (a){  
   return 'hello';  
 };  
 const fn2 = a => 'hello';  
 console.log(fn2(123));  
  
const sum = (a, b) => a + b;  
let result = sum(123, 345);  
  
const sum = (a, b) => ({name:'孙悟空'});  
  
const fn4 = (a, b) => {  
  if (a === 10){  
    a += 5;  
  }else {  
    a += 10;  
  }  
  return a+b;  
};

区别

  • 箭头函数没有arguments
function fn() {  
  console.log(arguments.length); // arguments保存当前函数的实参  
}  
fn('hello');  
const fn2 = () => {  
  console.log(arguments)  
}  
fn2('hello') // arguments is not defined
  • 剩余参数
function fn(a, b, ...args) {  
  console.log(args);  
}  
fn("1",2,3,5)
  • 没有自己的this,他的this总是外层作用域的this
const fn3 = () => {  
  console.log(this) // Window{}  
}  
fn3()  
  
const obj = {  
  hello:() => {  
    console.log(this) // 还是Window{}  
  }  
}  
obj.hello()  
  
const obj2 = {  
  hello:function (){  
    const test = () => {  
      console.log(this) // obj2  
    }  
    test()  
  }  
}  
obj2.hello()
  • 箭头函数中的this无法通过call()、apply()、bind()
  • 箭头函数无法作为构造函数使用

模块化

初期的JavaScript项目是非常小的,并不需要引入模块化来对其进行处理。但是随着前端项目复杂度的提高,项目中的代码越来越多,引入模块化成为一个迫在眉睫的问题。

所谓的模块化指将一个大的项目拆分成一个一个小的模块。拆分模块的好处有很多,比如使代码变得结构清晰、易于维护、提高复用度等。传统的JS中引入外部的脚本也可以当成是一种初级的模块化方式,但那种方式存在着很多不足,对我们开发人员来说并不十分友好。所以有一些前辈开始设计新的模块化方法来对JS进行扩展,像CommonJS、AMD、ReauireJS等模块系统都是很好的尝试。CommonJS现在依然是Node.js中默认的模块化方式。

第三方的模块化进行的如火如荼,ECMA官方当然不能袖手旁观,于是就有了ES6的模块化方案。ES6的模块化分成两个部分:export和import。

Export(导出)

在创建JS模块时,我们通过export向模块外部暴露内容(函数、对象、原始值)。在其他模块中可以通过import引入这些内容。使用了export的模块会自动开启严格模式。

export导出的方式有两种:

  1. 默认导出
  2. 命名导出
// 导出变量(命名导出)  
export let name1, name2, , nameN;   
export let name1 = , name2 = , , nameN;   
  
// 导出函数(命名导出)  
export function functionName(){...}  
  
// 导出类(命名导出)  
export class ClassName {...}  
  
// 导出一组  
export { name1, name2, , nameN };  
  
// 重命名导出  
export { variable1 as name1, variable2 as name2, , nameN };  
  
// 解构赋值后导出  
export const { name1, name2: bar } = o;  
  
// 默认导出  
export default expression;  
export default function () {  } // also class, function*  
export default function name1() {  } // also class, function*  
export { name1 as default,  };  
  
// 聚合模块  
export * from ; // 将其他模块中的全部内容导出(除了default)  
export * as name1 from ; // ECMAScript® 2O20 将其他模块中的全部内容以指定别名导出  
export { name1, name2, , nameN } from ; // 将其他模块中的指定内容导出  
export { import1 as name1, import2 as name2, , nameN } from ; // 将其他模块中的指定内容重命名导出  
export { default,  } from ;   

Import(引入)

import用来引入其他模块中导出的内容,注意!只有通过export导出的内容才能够通过import引入。和export一样,使用了import的模块会自动启用严格模式。

// 引入默认导出  
import defaultExport from "module-name";  
  
// 将所有模块导入到指定命名空间中  
import * as name from "module-name";  
  
// 引入模块中的指定内容  
import { export1 } from "module-name";  
import { export1 , export2 } from "module-name";  
  
// 以指定别名引入模块中的指定内容  
import { export1 as alias1 } from "module-name";  
import { export1 , export2 as alias2 , [...] } from "module-name";  
  
// 引入默认和其他内容  
import defaultExport, { export1 [ , [...] ] } from "module-name";  
import defaultExport, * as name from "module-name";  
  
// 引入模块  
import "module-name";

类(Class)

类是对象的模板,类中定义了对象中包含了哪些属性和方法。也可以直接通过function来定义类,但这两种定义方式并不是完全通用。

class Person {  
    //属性  
    name = "孙悟空";  
    // 属性  
    age = 18;   
    // 方法  
    sayHello() {   
        console.log(`大家好,我是${this.name}`);  
    }  
}  
const p = new Person(); // 创建对象  
p.sayHello(); // 调用方法