鸡啄米 · 2024年1月8日

js基础

===

普通的相等性检查 == 存在一个问题,它不能区分出 0 和 false;
严格相等运算符 === 在进行比较时不会做任何的类型转换。
如果 a 和 b 属于不同的数据类型,那么 a === b 不会做任何的类型转换而立刻返回 false。

?? ||

|| 返回第一个 真 值。
?? 返回第一个 已定义的 值。

for switch

for (let i = 0; i < 3; i++) { // 结果为 0、1、2
  alert(i);
}
let a = 3;

switch (a) {
  case 4:
    alert('Right!');
    break;

  case 3: // (*) 下面这两个 case 被分在一组
  case 5:
    alert('Wrong!');
    alert("Why don't you take a math class?");
    break;

  default:
    alert('The result is strange. Really.');
}

函数

function ask(question, yes, no) {
  if (confirm(question)) yes()
  else no();
}

function showOk() {
  alert( "You agreed." );
}

function showCancel() {
  alert( "You canceled the execution." );
}

// 用法:函数 showOk 和 showCancel 被作为参数传入到 ask
ask("Do you agree?", showOk, showCancel);
// 函数声明
function sum(a, b) {
  return a + b;
}

// 函数表达式
let sum = function(a, b) {
  return a + b;
};

区别:
函数表达式,只能在创建后使用;
函数声明全局都可使用,可以在声明前使用

?. ?() ?[]

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

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

delete user?.name; // 如果 user 存在,则删除 user.name
  1. obj?.prop —— 如果 obj 存在则返回 obj.prop,否则返回 undefined。
  2. obj?.[prop] —— 如果 obj 存在则返回 obj[prop],否则返回 undefined。
  3. obj.method?.() —— 如果 obj.method 存在则调用 obj.method(),否则返回 undefined。

数字类型

  1. 初始化
let billion = 1000000000;
等价于
let billion = 1_000_000_000;
  1. 舍入
舍入(rounding)是使用数字时最常用的操作之一。

这里有几个对数字进行舍入的内建函数:

Math.floor
向下舍入:3.1 变成 3,-1.1 变成 -2。
Math.ceil
向上舍入:3.1 变成 4,-1.1 变成 -1。
Math.round
向最近的整数舍入:3.1 变成 3,3.6 变成 4,中间值 3.5 变成 4。
Math.trunc(IE 浏览器不支持这个方法)
移除小数点后的所有内容而没有舍入:3.1 变成 3,-1.1 变成 -1。
  1. parseInt parseFloat
这就是 parseInt 和 parseFloat 的作用。
它们可以从字符串中“读取”数字,直到无法读取为止。如果发生 error,则返回收集到的数字。
函数 parseInt 返回一个整数,而 parseFloat 返回一个浮点数:
  1. math.min math.max math.pow math.random
Math.random()
返回一个从 0 到 1 的随机数(不包括 1)。

Math.max(a, b, c...) 和 Math.min(a, b, c...)
从任意数量的参数中返回最大值和最小值。

Math.pow(n, power)
返回 n 的给定(power)次幂。

字符串类型

  1. 初始化
let single = 'single-quoted';
let double = "double-quoted";

let backticks = `backticks`;

单引号和双引号基本相同。但是,反引号允许我们通过 ${…} 将任何表达式嵌入到字符串中:
使用反引号的另一个优点是它们允许字符串跨行

function sum(a, b) {
  return a + b;
}

alert(`1 + 2 = ${sum(1, 2)}.`); // 1 + 2 = 3.
  1. 常用方法
.length 获取字符串长度

let str = `Hello`;

// 第一个字符
alert( str[0] ); // H
alert( str.charAt(0) ); // H

// 最后一个字符
alert( str[str.length - 1] ); // o

// 改变大小写
alert( 'Interface'.toUpperCase() ); // INTERFACE
alert( 'Interface'.toLowerCase() ); // interface

// 查找子字符串
str.indexOf(substr, pos)。
它从给定位置 pos 开始,在 str 中查找 substr,如果没有找到,则返回 -1,否则返回匹配成功的位置。
let str = 'Widget with id';

alert( str.indexOf('Widget') ); // 0,因为 'Widget' 一开始就被找到
alert( str.indexOf('widget') ); // -1,没有找到,检索是大小写敏感的

// includes
str.includes(substr, pos) 根据 str 中是否包含 substr 来返回 true/false。

//startsWith
alert( "Widget".startsWith("Wid") ); // true,"Widget" 以 "Wid" 开始

//endsWith
alert( "Widget".endsWith("get") ); // true,"Widget" 以 "get" 结束

// 获取子字符串
// str.slice(start [, end]):返回字符串从 start 到(但不包括)end 的部分
let str = "stringify";
alert( str.slice(0, 5) ); // 'strin',从 0 到 5 的子字符串(不包括 5)
alert( str.slice(0, 1) ); // 's',从 0 到 1,但不包括 1,所以只有在 0 处的字符

// str.substring(start [, end])
返回字符串从 start 到(但不包括)end 的部分。
这与 slice 几乎相同,但它允许 start 大于 end。

// str.substr(start [, length])
返回字符串从 start 开始的给定 length 的部分。

数组

  1. 初始化
let arr = new Array();
let arr = [];
  1. 获取元素
// 获取倒数元素,无法使用[-1]这种负索引的形式获取数组
let fruits = ["Apple", "Orange", "Plum"];

// 与 fruits[fruits.length-1] 相同
alert( fruits.at(-1) ); // Plum
  1. 类似队列方法
push(...items) 在末端添加 items 项。
pop() 从末端移除并返回该元素。
shift() 从首端移除并返回该元素。
unshift(...items) 从首端添加 items 项
  1. 遍历元素
for (let i=0; i<arr.length; i++)
  1. 元素操作

arr.splice(start[, deleteCount, elem1, ..., elemN])

从索引 start 开始修改 arr:删除 deleteCount 个元素并在当前位置插入 elem1, ..., elemN。最后返回被删除的元素所组成的数组。


let arr = ["I", "study", "JavaScript"];

arr.splice(1, 1); // 从索引 1 开始删除 1 个元素

alert( arr ); // ["I", "JavaScript"]
let arr = ["I", "study", "JavaScript", "right", "now"];

// 删除数组的前三项,并使用其他内容代替它们
arr.splice(0, 3, "Let's", "dance");

alert( arr ) // 现在 ["Let's", "dance", "right", "now"]
let arr = ["I", "study", "JavaScript", "right", "now"];

// 删除前两个元素
let removed = arr.splice(0, 2);

alert( removed ); // "I", "study" <-- 被从数组中删除了的元素
let arr = ["I", "study", "JavaScript"];

// 从索引 2 开始
// 删除 0 个元素
// 然后插入 "complex" 和 "language"
arr.splice(2, 0, "complex", "language");

alert( arr ); // "I", "study", "complex", "language", "JavaScript"
let arr = [1, 2, 5];

// 从索引 -1(尾端前一位)
// 删除 0 个元素,
// 然后插入 3 和 4
arr.splice(-1, 0, 3, 4);

alert( arr ); // 1,2,3,4,5

它会返回一个新数组,将所有从索引 start 到 end(不包括 end)的数组项复制到一个新的数组。start 和 end 都可以是负数,在这种情况下,从末尾计算索引。

它和字符串的 str.slice 方法有点像,就是把子字符串替换成子数组。

let arr = ["t", "e", "s", "t"];

alert( arr.slice(1, 3) ); // e,s(复制从位置 1 到位置 3 的元素)

alert( arr.slice(-2) ); // s,t(复制从位置 -2 到尾端的元素)

concat:组合数组

let arr = [1, 2];

// 从 arr 和 [3,4] 创建一个新数组
alert( arr.concat([3, 4]) ); // 1,2,3,4

// 从 arr、[3,4] 和 [5,6] 创建一个新数组
alert( arr.concat([3, 4], [5, 6]) ); // 1,2,3,4,5,6

// 从 arr、[3,4]、5 和 6 创建一个新数组
alert( arr.concat([3, 4], 5, 6) ); // 1,2,3,4,5,6

arr.forEach 方法允许为数组的每个元素都运行一个函数。

// 对每个元素调用 alert
["Bilbo", "Gandalf", "Nazgul"].forEach(alert);

解构

// 数组解构
// 解构赋值
// 设置 firstName = arr[0]
// 以及 surname = arr[1]
let [firstName, surname] = arr;

let [name1, name2, ...rest] = ["Julius", "Caesar", "Consul", "of the Roman Republic"];

// rest 是包含从第三项开始的其余数组项的数组
alert(rest[0]); // Consul
alert(rest[1]); // of the Roman Republic
alert(rest.length); // 2

// 对象结构
let options = {
  title: "Menu",
  width: 100,
  height: 200
};

let {title, width, height} = options;

alert(title);  // Menu
alert(width);  // 100
alert(height); // 200
// 对象结构的剩余模式 ...
let options = {
  title: "Menu",
  height: 200,
  width: 100
};

// title = 名为 title 的属性
// rest = 存有剩余属性的对象
let {title, ...rest} = options;

// 现在 title="Menu", rest={height: 200, width: 100}
alert(rest.height);  // 200
alert(rest.width);   // 100

// 嵌套结构
let options = {
  size: {
    width: 100,
    height: 200
  },
  items: ["Cake", "Donut"],
  extra: true
};

// 为了清晰起见,解构赋值语句被写成多行的形式
let {
  size: { // 把 size 赋值到这里
    width,
    height
  },
  items: [item1, item2], // 把 items 赋值到这里
  title = "Menu" // 在对象中不存在(使用默认值)
} = options;

alert(title);  // Menu
alert(width);  // 100
alert(height); // 200
alert(item1);  // Cake
alert(item2);  // Donut

// 智能参数结构:使用对象来替代变量
// 我们传递一个对象给函数
let options = {
  title: "My menu",
  items: ["Item1", "Item2"]
};

// ……然后函数马上把对象解构成变量
function showMenu({title = "Untitled", width = 200, height = 100, items = []}) {
  // title, items – 提取于 options,
  // width, height – 使用默认值
  alert( `${title} ${width} ${height}` ); // My Menu 200 100
  alert( items ); // Item1, Item2
}

showMenu(options);
当我们在代码中看到 "..." 时,它要么是 rest 参数,要么是 spread 语法。

有一个简单的方法可以区分它们:

若 ... 出现在函数参数列表的最后,那么它就是 rest 参数,它会把参数列表中剩余的参数收集到一个数组中。
若 ... 出现在函数调用或类似的表达式中,那它就是 spread 语法,它会把一个数组展开为列表。