Skip to content
On this page

JavaScript

ES6和ES5的区别

  • 箭头函数

  • let, const创建块级作用域,不会像var一样进行变量提升(先使用,后创建)

  • 模板字符串和字符串拼接的优劣

  • 类定义与继承

    ES6 引入了对类(class关键字)、构造函数(constructor关键字)和 extend 关键字(用于继承)的语言支持

  • for...of 语句用于可迭代对象的遍历

  • 展开操作符

  • Promise, async和await

  • 模块导出和导入

    • 默认导出,默认导入
    • 按需导出,按需导入
  • 解构赋值: 解构赋值的优点是可以让代码更简洁、清晰和易读,也可以避免一些临时变量的声明

    • 交换两个变量的值

    • 获取函数返回的多个值

    • 提取JSON数据

    • 函数参数定义

      js
      // 可以用解构赋值为函数参数定义默认值和别名
      
      function add([x=0,y=0]) {
          return x + y;
      }
      
      add([1]); // 返回1
      
      function print({name='Anonymous',age='Unknown'}) {
          console.log(`Name:${name},Age:${age}`);
      }
      
      print({name:'Tom'}); // 输出 Name:Tom,Age:Unknown

回调函数

js中回调函数的定义和作用如下:

  • 回调函数是一种函数表达式,它作为一个参数传递给另一个函数,在合适的时机被调用
  • 回调函数可以实现异步操作,即在主线程执行完同步代码后,再执行任务队列中的回调函数
  • 回调函数可以实现高阶函数,即一个函数可以接收另一个函数作为参数或返回值

例如,数组的 map 方法就是一个高阶函数,它接收一个回调函数作为参数,并对数组的每个元素执行该回调函数。代码如下:

javascript
let arr = [1, 2, 3];
let newArr = arr.map(function(x) {
  return x * 2;
});
console.log(newArr); // [2, 4, 6]

这是一个异步操作的回调函数例子:

javascript
// 定义一个模拟异步请求的函数
function request(url, callback) {
  // 使用 setTimeout 模拟网络延迟
  setTimeout(function() {
    // 使用 Math.random 模拟请求成功或失败
    let success = Math.random() > 0.5;
    if (success) {
      // 如果成功,调用回调函数并传入响应数据
      callback(null, "Response from " + url);
    } else {
      // 如果失败,调用回调函数并传入错误信息
      callback("Error: request failed for " + url);
    }
  }, 1000);
}

// 调用 request 函数,并传入一个匿名回调函数作为参数
request("https://example.com", function(err, data) {
  if (err) {
    // 如果有错误,打印错误信息
    console.log(err);
  } else {
    // 如果没有错误,打印响应数据
    console.log(data);
  }
});

异步编程

异步编程是一种让程序并行运行的手段,它可以让程序中的一个工作单元与主应用程序线程分开独立运行,并且在工作单元运行结束后,会通知主应用程序线程它的运行结果或者失败原因。异步编程可以提高程序的性能和用户体验,避免长时间的阻塞操作影响其他任务的执行

JavaScript中实现异步编程的方法有多种,常见的有以下几种

  • 回调函数:这是异步编程最基本的方法,就是将一个函数作为参数传递给另一个函数,在异步操作完成后调用该函数。
  • 事件监听:这是利用事件驱动模式,通过绑定事件处理器来响应异步操作的结果。
  • 发布/订阅:这是一种消息通信模式,通过一个中介对象来管理订阅者和发布者之间的消息传递,实现解耦和异步。
  • Promise对象:这是一种封装了异步操作状态和结果的对象,可以通过链式调用then方法来处理成功或失败的回调函数,避免了回调地狱
  • async/await:这是一种基于Promise和Generator的语法糖,可以让异步代码看起来像同步代码一样简洁清晰

箭头函数

为啥大多数情况都使用箭头函数

  • 作用域安全性:当箭头函数被一致使用时,所有东西都保证使用与根对象相同的thisObject。如果一个标准函数回调与一堆箭头函数混合在一起,那么作用域就有可能变得混乱。
  • 紧凑性:箭头函数更容易读写。
  • 清晰度:使用箭头函数可明确知道当前 this 指向。

箭头函数和普通函数的区别和使用场景

  • 箭头函数是一种简洁的函数表达式,使用 => 符号定义,普通函数使用 function 关键字定义
  • 箭头函数不能作为构造函数使用,也就不能使用 new 关键字
  • 箭头函数没有自己的 this、arguments、super 和 new.target,它们依赖于外部非箭头函数的值
  • 箭头函数不能用作生成器(generator),也就不能使用 yield 关键字

箭头函数适合用于不需要 this、arguments、super 和 new.target 的情况,例如回调函数、数组方法等。普通函数适合用于需要这些值的情况,例如构造函数、对象方法等。

Symbol

Symbol 是一种新的、特殊的对象,可以用作对象中惟一的属性名。使用 Symbol 替换string 可以避免不同的模块属性的冲突。还可以将Symbol设置为私有,以便尚无直接访问Symbol权限的任何人都不能访问它们的属性。

Symbol 是JS新的基本数据类型。与numberstringboolean 原始类型一样,Symbol 也有一个用于创建它们的函数。与其他原始类型不同,Symbol没有字面量语法。创建它们的唯一方法是使用以下方法中的Symbol构造函数

text
let symbol = Symbol();

ITFE

IIFE是一个立即调用的函数表达式,它在创建后立即执行, 使用此模式来避免污染全局命名空间,因为在IIFE中使用的所有变量(与任何其他普通函数一样)在其作用域之外都是不可见的。

模板字符串

  • 模板字符串使用反引号 (`) 来代替普通字符串中的用双引号和单引号。模板字符串可以包含特定语法(${expression})的占位符。占位符中的表达式和周围的文本会一起传递给一个默认函数,该函数负责将所有的部分连接起来¹。
  • 字符串拼接使用加号 (+) 或者 concat 方法来将多个字符串或变量连接起来,形成一个新的字符串。
  • 模板字符串相比于字符串拼接,有以下优点:
    • 可以直接在占位符中插入表达式,而不需要使用加号或者 concat 方法。
    • 可以保留换行和空格,而不需要使用转义字符 (\n) 或者连接多个字符串。
    • 可以支持嵌套模板,实现更复杂的逻辑。
    • 可以自定义标签函数,对模板进行处理或过滤
  • 字符串拼接相比于模板字符串,有以下优点:
    • 兼容性更好,可以在所有浏览器中运行,而模板字符串是 ES6 的新特性,在一些旧版本的浏览器中可能不支持
    • 性能稍微好一些,在一些测试中发现模板字符串比字符串拼接慢一点

Set、Map、WeakSet、WeakMap

Set用于数组去重,Map用于数据储存

Set:  (1)成员不能重复 (2)只有键值没有键名,类似数组 (3)可以遍历,方法有add, delete,has Map: (1)本质上是健值对的集合,类似集合, 但Map的键可以是非string, 这是它与对象最大的不同 (2)可以遍历,可以跟各种数据格式转换

Set去重:

js
let arr = [12,43,23,43,68,12];
let _arr = [...new Set(arr)];
console.log(_arr);//[12, 43, 23, 68]

Promise🔥

为了解决回调地狱的问题, 例如wx.request链式调用,代码层级深,不易于维护

使用Promise可以使代码结构更加清晰

js
const promise = new Promise((resolve, reject) => {
    console.log(1);
    resolve();
})
promise.then(res=>{
  console.log(2);
})

有三个状态:

pending、fulfilled、reject

有三个回调:

fulfilled->then

reject->catch

finally: 在fulfilled/reject之后执行, 可以在其中调用wx.stopLoading

reject和catch区别

reject 是用来抛出异常,catch 是用来处理异常 reject 是 Promise 的方法,而 catch 是 Promise 实例的方法 reject后的东西,一定会进入then中的第二个回调,如果then中没有写第二个回调,则进入catch

async/await以及对Generator的优势

async await 是用来解决异步的,async函数是Generator函数的语法糖 使用关键字async来表示,在函数内部使用 await 来表示异步 async函数返回一个 Promise 对象,可以使用then方法添加回调函数 当函数执行的时候,一旦遇到await就会先返回,等到异步操作完成,再接着执行函数体内后面的语句 async较Generator的优势: (1)内置执行器。Generator 函数的执行必须依靠执行器,而 Aysnc 函数自带执行器,调用方式跟普通函数的调用一样 (2)更好的语义。async 和 await 相较于 * 和 yield 更加语义化   (3)更广的适用性。yield命令后面只能是 Thunk 函数或 Promise对象,async函数的await后面可以是Promise也可以是原始类型的值 (4)返回值是 Promise。async 函数返回的是 Promise 对象,比Generator函数返回的Iterator对象方便,可以直接使用 then() 方法进行调用

forEach、for in、for of区别

forEach更多的用来遍历数组 for in 一般常用来遍历对象或json for of数组对象都可以遍历,遍历对象需要通过和Object.keys() for in循环出的是key,for of循环出的是value

js里forEach、for in、for of三者的区别是:

  • forEach是数组的一个方法,它可以遍历数组的每个元素,并且可以访问元素的值和索引
  • for in是一个语句,它可以遍历一个对象的可枚举的字符串属性
  • for of也是一个语句,它可以遍历一个可迭代对象定义的值

forEach的示例

  • 基本用法:forEach可以接受一个回调函数作为参数,这个回调函数会在数组的每个元素上执行。例如:
js
['a', 'b', 'c'].forEach(v => {
  console.log(v);
});
// 输出:
// a
// b
// c
  • 修改数组:一般来说,不建议使用forEach来修改数组。如果需要修改数组,可以使用map或filter方法。例如:
js
const numbers = [1, 2, 3];
numbers.forEach((v, i) => {
  numbers[i] = v * 2;
});
console.log(numbers);
// 输出:
// [2, 4, 6]
  • 稀疏数组:如果数组中有空元素,forEach会跳过它们。例如:
js
const arraySparse = [1, 3, /* empty */, 7];
let numCallbackRuns = 0;
arraySparse.forEach((element) => {
  console.log({ element });
  numCallbackRuns++;
});
console.log({ numCallbackRuns });
// 输出:
// { element: 1 }
// { element: 3 }
// { element: 7 }
// { numCallbackRuns: 3 }

for in和for of的示例

  • for in可以遍历一个对象的所有可枚举的字符串属性,返回的是键而不是值; 常用于对象的遍历, 例如:
js
const person = {name: 'Alice', age: 25, city: 'New York'};
for (let key in person) {
  console.log(key + ': ' + person[key]);
}
// 输出:
// name: Alice
// age: 25
// city: New York
  • for of可以遍历一个可迭代对象定义的值,并且可以访问元素的值。例如:

    js
    const fruits = ['apple', 'orange', 'cherry'];
    for (let fruit of fruits) {
      console.log(fruit);
    }
    // 输出:
    // apple
    // orange
    // cherry
  • 判断一个对象是否可迭代有两种方法

    • 使用for of遍历对象,如果可以用for of遍历, 则对象可迭代

    • 检查对象是否有@iterator方法,这个方法是一个特殊的符号属性,用来返回一个迭代器对象, 例如

      js
      const arr = [1, 2, 3];
      console.log(typeof arr[Symbol.iterator]); // function

for in和for of的优缺点

  • for in的优点是可以遍历对象的所有可枚举的字符串属性,包括原型链上的属性。这可以方便地获取对象的结构和信息。
  • for in的缺点是它不能直接遍历数组或类数组对象,因为它会返回索引而不是值。另外,它可能会遍历到一些不想要的属性,比如继承自Object.prototype的属性。
  • for of的优点是可以遍历任何可迭代对象定义的值,包括数组、字符串、Map、Set等。这可以方便地获取元素的值而不用关心索引或键。
  • for of的缺点是它不能直接遍历普通对象,因为普通对象不是可迭代对象。另外,它可能会跳过一些空元素或未定义的值。

遍历数组时使用foreach还是forof

这取决于你的需求和喜好

foreach是Array类型的一个方法,它可以对数组中的每一项执行一个回调函数,但是它不会改变原数组,也不能使用break或return中断循环

forof是ES6引入的一种新的循环语法,它可以遍历任何可迭代的对象,包括数组、字符串、Map、Set等,它比for循环更简洁,也没有forin那么多奇怪的特例

如果你只需要访问数组中的元素值,而不需要索引或其他属性,那么forof可能是最好的选择。如果你需要对数组进行链式操作或者异步处理,那么foreach可能更合适。

ES6的导入导出

导入通过import关键字

js
// 按需导入
import {sum} from "./example.js"
import {sum,multiply,time} from "./exportExample.js"
// 一整个模块全部导入,使用as重命名
import * as example from "./exportExample.js"

导出通过export关键字

js
//可以将export放在任何变量,函数或类声明的前面
export var firstName = 'Michael';
export var lastName = 'Jackson';
export var year = 1958;
//也可以使用大括号指定所要输出的一组变量
var firstName = 'Michael';
var lastName = 'Jackson';
var year = 1958;
export {firstName, lastName, year};
//使用export default时,对应的import语句不需要使用大括号
let bosh = function crs(){}
export default bosh;
import crc from 'crc';
//不使用export default时,对应的import语句需要使用大括号
let bosh = function crs(){}
export bosh;
import {crc} from 'crc';

前端SessionStorage,LocalStorage,Cookie 是三种不同的本地存储方式

区别:

  • 存储大小:Cookie 一般不超过4K,而 SessionStorage 和 LocalStorage 可以存储5Mb或更多²⁴。
  • 数据有效期:Cookie 由服务器生成,可以设置失效时间;如果没有设置时间,关闭浏览器 Cookie 失效;如果设置了时间,Cookie 就会存储在硬盘中,过期失效⁴。SessionStorage 只在当前会话有效,关闭浏览器或标签页就会清除。LocalStorage 持久化存储,除非手动删除。
  • 数据共享:Cookie 在同源的所有页面中都可以访问²。SessionStorage 只在同一个窗口(或标签页)中共享²。LocalStorage 在同源的所有窗口中都可以访问²。
  • 数据发送:Cookie 会参与服务端的通信,在 HTTP 请求的头部中携带¹²。SessionStorage 和 LocalStorage 是单纯的前端存储,不参与与服务端的通信¹²。
  • 设置方式:Cookie 可以由客户端( document.cookie )和服务端( Set-Cookie )都可以设置¹。SessionStorage 和 LocalStorage 只有客户端才能设置¹

应用场景

本地存储的应用场景主要取决于数据的有效期、共享范围和发送需求。一般来说:

  • Cookie 适合用于标记用户和跟踪用户行为,例如一些关键密匙验证等²。
  • LocalStorage 适合用于长期保存在本地的数据,例如令牌、用户偏好等²。
  • SessionStorage 适合用于敏感账号的一次性登录,或者临时保存一些表单数据等²。

微信小程序里的 storage 是属于 LocalStorage 的一种存储方式,它也是使用 key-value 的形式存储数据,但是它有以下几个特点:

  • 它是异步的,不会阻塞主线程。
  • 它可以设置过期时间,超过时间后会自动清除。
  • 它可以在同一个微信用户、同一个小程序下共享数据。

本地存储的安全性问题

  • 本地存储容易受到 XSS 攻击,导致数据被窃取或篡改¹。
  • 本地存储无法在服务器端控制或更新,需要用户手动删除或修改¹。
  • 本地存储可能会因为用户清除浏览器缓存而丢失数据¹。
  • 本地存储可能会因为设备被移除或转移而泄露数据²。

为了提高本地存储的安全性,可以采取以下一些措施:

  • 使用 HTTPS 协议和加密算法来保护数据传输和存储。
  • 使用服务器端验证和授权机制来限制对数据的访问。
  • 使用定期备份和恢复机制来防止数据丢失。
  • 使用物理锁或密码锁来保护设备不被盗用或转移。

typeof 和 instance

  • 作用
    • 检测数据类型
    • 检测对象之间的关联性
  • 返回
    • 小写字母字符串
    • 布尔值
  • 操作数
    • 简单数据类型、
    • 左边必须是引用类型,函数或者对象,右边必须是函数
  • 操作数数量
    • 1个
    • 2个

null和undefined

  • null是主动使用,undefined是被动的备选手段
  • 判断null和undefined时,应永远使用严格判断(===)
  • js中“没有传”、“没有给”和undefined基本等价;而null是有值的——例如:默认参数
js
// null可以理解为空指针,和数字在一起时会隐式转换为0,undefined本质是个特殊对象,是js的特有的机制
// null需要自己主动赋值,undefined是js运行机制自动返回的基本数据类型

// 只是个历史遗留问题而已,不足以说明null的本质
console.log(typeof null === 'object'); // true

// null和数字进行加减,大小比较的时候相当于0
console.log(null * 34); // 0
console.log(null + 34); // 34
console.log(null < 34); // 0<34 true

// ==比较的时候,除了undefined之外,和null比较返回false
console.log(null == undefined); // true
console.log(null == 0); // false
console.log(null == ''); // false

使用上

默认参数

js
//age参数有默认值——也就是说,不传就是18
function blue(age=18){
  console.log(age);
}

//传个undefined跟没传一样,系统认为“没有”和undefined等价
blue(undefined);  //18

//传null就是有了,不会触发默认值
blue(null);  //null

解构赋值

js
const [a=1,b=2]=[undefined, null];

//undefined就是没给——触发默认值
console.log(a);  //1

//null是给了,但是空——不触发默认值
console.log(b);  //null

上次更新于: