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');