ZhangJie Software Development Engineer

web前端学习笔记

2023-10-18
ZhangJie

go学习笔记。

html

css

js

JavaScript 基础知识

空值合并运算符 ‘??’

空值合并运算符(nullish coalescing operator)的写法为两个问号 ??。

a ?? b 的结果是:

如果 a 是已定义的,则结果为 a, 如果 a 不是已定义的,则结果为 b。

let firstName = null;
let lastName = null;
let nickName = "Supercoder";

// 显示第一个已定义的值:
alert(firstName ?? lastName ?? nickName ?? "匿名"); // Supercoder
  、?? 之间重要的区别是:
  •   返回第一个 真 值。
  • ?? 返回第一个 已定义的 值。 换句话说,|| 无法区分 false、0、空字符串 “” 和 null/undefined。它们都一样 —— 假值(falsy values)。如果其中任何一个是 || 的第一个参数,那么我们将得到第二个参数作为结果。
let height = 0;

alert(height || 100); // 100
alert(height ?? 100); // 0

箭头函数,基础知识

let sum = (a, b) => a + b;

/* 这个箭头函数是下面这个函数的更短的版本:

let sum = function(a, b) {
  return a + b;
};
*/

alert( sum(1, 2) ); // 3
let double = n => n * 2;
// 差不多等同于:let double = function(n) { return n * 2 }

alert( double(3) ); // 6
let sayHi = () => alert("Hello!");

sayHi();
let sum = (a, b) => {  // 花括号表示开始一个多行函数
  let result = a + b;
  return result; // 如果我们使用了花括号,那么我们需要一个显式的 “return”
};

alert( sum(1, 2) ); // 3

Object(对象):基础知识

可选链 “?.”

如果可选链 ?. 前面的值为 undefined 或者 null,它会停止运算并返回 undefined。

let user = {}; // user 没有 address 属性

alert( user?.address?.street ); // undefined(不报错)

可选链 ?. 语法有三种形式:

  • obj?.prop —— 如果 obj 存在则返回 obj.prop,否则返回 undefined。
  • obj?.[prop] —— 如果 obj 存在则返回 obj[prop],否则返回 undefined。
  • obj.method?.() —— 如果 obj.method 存在则调用 obj.method(),否则返回 undefined。

函数进阶内容

Rest 参数与 Spread 语法

Rest 参数
function sumAll(...args) { // 数组名为 args
  let sum = 0;

  for (let arg of args) sum += arg;

  return sum;
}

alert( sumAll(1) ); // 1
alert( sumAll(1, 2) ); // 3
alert( sumAll(1, 2, 3) ); // 6
function showName(firstName, lastName, ...titles) {
  alert( firstName + ' ' + lastName ); // Julius Caesar

  // 剩余的参数被放入 titles 数组中
  // i.e. titles = ["Consul", "Imperator"]
  alert( titles[0] ); // Consul
  alert( titles[1] ); // Imperator
  alert( titles.length ); // 2
}

showName("Julius", "Caesar", "Consul", "Imperator");

有一个名为 arguments 的特殊类数组对象可以在函数中被访问,该对象以参数在参数列表中的索引作为键,存储所有参数。

function showName() {
  alert( arguments.length );
  alert( arguments[0] );
  alert( arguments[1] );

  // 它是可遍历的
  // for(let arg of arguments) alert(arg);
}

// 依次显示:2,Julius,Caesar
showName("Julius", "Caesar");

// 依次显示:1,Ilya,undefined(没有第二个参数)
showName("Ilya");

在过去,JavaScript 中不支持 rest 参数语法,而使用 arguments 是获取函数所有参数的唯一方法。现在它仍然有效,我们可以在一些老代码里找到它。

但缺点是,尽管 arguments 是一个类数组,也是可迭代对象,但它终究不是数组。它不支持数组方法,因此我们不能调用 arguments.map(…) 等方法。

此外,它始终包含所有参数,我们不能像使用 rest 参数那样只截取参数的一部分。

因此,当我们需要这些功能时,最好使用 rest 参数。

箭头函数没有自身的 this,也没有特殊的 arguments 对象。

Spread 语法
let arr = [3, 5, 1];

alert( Math.max(...arr) ); // 5(spread 语法把数组转换为参数列表)
let arr1 = [1, -2, 3, 4];
let arr2 = [8, 3, -8, 1];

alert( Math.max(1, ...arr1, 2, ...arr2, 25) ); // 25
let str = "Hello";

alert( [...str] ); // H,e,l,l,o
let str = "Hello";

// Array.from 将可迭代对象转换为数组
alert( Array.from(str) ); // H,e,l,l,o

运行结果与 […str] 相同。

不过 Array.from(obj) 和 […obj] 存在一个细微的差别:

Array.from 适用于类数组对象也适用于可迭代对象。 Spread 语法只适用于可迭代对象。 因此,对于将一些“东西”转换为数组的任务,Array.from 往往更通用。

老旧的 “var”

变量声明的三种方式:let, const, var

var 是一头非常不同的,源自远古时代的怪兽。在现代脚本中一般不再使用它,但它仍然潜伏在旧脚本中。

用 var 声明的变量,不是函数作用域就是全局作用域。它们在代码块外也是可见的(译注:也就是说,var 声明的变量只有函数作用域和全局作用域,没有块级作用域)。

使用 var,我们可以重复声明一个变量,不管多少次都行。如果我们对一个已经声明的变量使用 var,这条新的声明语句会被忽略。

“var” 声明的变量,可以在其声明语句前被使用。

全局对象

全局对象提供可在任何地方使用的变量和函数。默认情况下,这些全局变量内建于语言或环境中。

在浏览器中,它的名字是 “window”,对 Node.js 而言,它的名字是 “global”,其它环境可能用的是别的名字。

最近,globalThis 被作为全局对象的标准名称加入到了 JavaScript 中,所有环境都应该支持该名称。所有主流浏览器都支持它。

假设我们的环境是浏览器,我们将在这儿使用 “window”。如果你的脚本可能会用来在其他环境中运行,则最好使用 globalThis。

在浏览器中,使用 var(而不是 let/const!)声明的全局函数和变量会成为全局对象的属性。

如果一个值非常重要,以至于你想使它在全局范围内可用,那么可以直接将其作为属性写入:

// 将当前用户信息全局化,以允许所有脚本访问它
window.currentUser = {
  name: "John"
};

// 代码中的另一个位置
alert(currentUser.name);  // John

// 或者,如果我们有一个名为 "currentUser" 的局部变量
// 从 window 显式地获取它(这是安全的!)
alert(window.currentUser.name); // John

函数对象,NFE

一个函数的名字可以通过属性 “name” 来访问:

function sayHi() {
  alert("Hi");
}

alert(sayHi.name); // sayHi

还有另一个内建属性 “length”,它返回函数入参的个数,比如:

function f1(a) {}
function f2(a, b) {}
function many(a, b, ...more) {}

alert(f1.length); // 1
alert(f2.length); // 2
alert(many.length); // 2, rest 参数不参与计数

我们也可以添加我们自己的属性。这里我们添加了 counter 属性,用来跟踪总的调用次数:

function sayHi() {
  alert("Hi");

  // 计算调用次数
  sayHi.counter++;
}
sayHi.counter = 0; // 初始值

sayHi(); // Hi
sayHi(); // Hi

alert( `Called ${sayHi.counter} times` ); // Called 2 times

调度:setTimeout 和 setInterval

有时我们并不想立即执行一个函数,而是等待特定一段时间之后再执行。这就是所谓的“计划调用(scheduling a call)”。

目前有两种方式可以实现:

setTimeout 允许我们将函数推迟到一段时间间隔之后再执行。 setInterval 允许我们重复运行一个函数,从一段时间间隔之后开始运行,之后以该时间间隔连续重复运行该函数。

setTimeout
function sayHi() {
  alert('Hello');
}

setTimeout(sayHi, 1000);
function sayHi(phrase, who) {
  alert( phrase + ', ' + who );
}

setTimeout(sayHi, 1000, "Hello", "John"); // Hello, John
setTimeout(() => alert('Hello'), 1000);

取消调度的语法:

let timerId = setTimeout(...);
clearTimeout(timerId);
setInterval

与 setTimeout 只执行一次不同,setInterval 是每间隔给定的时间周期性执行。

想要阻止后续调用,我们需要调用 clearInterval(timerId)。

下面的例子将每间隔 2 秒就会输出一条消息。5 秒之后,输出停止:

// 每 2 秒重复一次
let timerId = setInterval(() => alert('tick'), 2000);

// 5 秒之后停止
setTimeout(() => { clearInterval(timerId); alert('stop'); }, 5000);

在大多数浏览器中,包括 Chrome 和 Firefox,在显示 alert/confirm/prompt 弹窗时,内部的定时器仍旧会继续“嘀嗒”。

所以,在运行上面的代码时,如果在一定时间内没有关掉 alert 弹窗,那么在你关闭弹窗后,下一个 alert 会立即显示。两次 alert 之间的时间间隔将小于 2 秒。

Promise,async/await

Promise

Promise 是将“生产者代码”和“消费者代码”连接在一起的一个特殊的 JavaScript 对象。

let promise = new Promise(function(resolve, reject) {
  // executor(生产者代码,“歌手”)
});

executor 会自动运行并尝试执行一项工作。尝试结束后,如果成功则调用 resolve,如果出现 error 则调用 reject。

消费者:then,catch

Promise 对象充当的是 executor(“生产者代码”或“歌手”)和消费函数(“粉丝”)之间的连接,后者将接收结果或 error。可以通过使用 .then 和 .catch 方法注册消费函数。

promise.then(
  function(result) { /* handle a successful result */ },
  function(error) { /* handle an error */ }
);
let promise = new Promise(function(resolve, reject) {
  setTimeout(() => resolve("done!"), 1000);
});

// resolve 运行 .then 中的第一个函数
promise.then(
  result => alert(result), // 1 秒后显示 "done!"
  error => alert(error) // 不运行
);
let promise = new Promise(function(resolve, reject) {
  setTimeout(() => reject(new Error("Whoops!")), 1000);
});

// reject 运行 .then 中的第二个函数
promise.then(
  result => alert(result), // 不运行
  error => alert(error) // 1 秒后显示 "Error: Whoops!"
);
new Promise((resolve, reject) => {
  throw new Error("error");
})
  .finally(() => alert("Promise ready")) // 先触发
  .catch(err => alert(err));  // <-- .catch 显示这个 error
fetch('https://no-such-server.blabla') // reject
  .then(response => response.json())
  .catch(err => alert(err)) // TypeError: Failed to fetch(这里的文字可能有所不同)

async/await

async/await 是以更舒适的方式使用 promise 的一种特殊语法,同时它也非常易于理解和使用。

async function f() {
  return 1;
}

f().then(alert); // 1

在函数前面的 “async” 这个单词表达了一个简单的事情:即这个函数总是返回一个 promise。其他值将自动被包装在一个 resolved 的 promise 中。

关键字 await 让 JavaScript 引擎等待直到 promise 完成(settle)并返回结果。

这里的例子就是一个 1 秒后 resolve 的 promise:

async function f() {

  let promise = new Promise((resolve, reject) => {
    setTimeout(() => resolve("done!"), 1000)
  });

  let result = await promise; // 等待,直到 promise resolve (*)

  alert(result); // "done!"
}

f();

模块

例如,我们有一个 sayHi.js 文件导出了一个函数:

// 📁 sayHi.js
export function sayHi(user) {
  alert(`Hello, ${user}!`);
}

……然后另一个文件可能导入并使用了这个函数:

// 📁 main.js
import { sayHi } from './sayHi.js';

alert(sayHi); // function...
sayHi('John'); // Hello, John!
  • 在声明前导出
// 导出数组
export let months = ['Jan', 'Feb', 'Mar','Apr', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];

// 导出 const 声明的变量
export const MODULES_BECAME_STANDARD_YEAR = 2015;

// 导出类
export class User {
  constructor(name) {
    this.name = name;
  }
}
  • 导出与声明分开
// 📁 say.js
function sayHi(user) {
  alert(`Hello, ${user}!`);
}

function sayBye(user) {
  alert(`Bye, ${user}!`);
}

export {sayHi, sayBye}; // 导出变量列表
  • Import *
// 📁 main.js
import {sayHi, sayBye} from './say.js';

sayHi('John'); // Hello, John!
sayBye('John'); // Bye, John!
// 📁 main.js
import * as say from './say.js';

say.sayHi('John');
say.sayBye('John');

vue

vue2

vue3


上一篇 学习资料

Comments

Content