这是我参与「第五届青训营 」伴学笔记创作活动的第 11 天
简介
基础知识
-
string类型
在 JavaScript 中,有三种包含字符串的方式。
- 双引号:
"Hello". - 单引号:
'Hello'. - 反引号:
Hello.
双引号和单引号都是“简单”引用,在 JavaScript 中两者几乎没有什么差别。
反引号是 功能扩展 引号。它们允许我们通过将变量和表达式包装在
${…}中,来将它们嵌入到字符串中。例如:let name = "John"; // 嵌入一个变量 alert( `Hello, ${name}!` ); // Hello, John! // 嵌入一个表达式 alert( `the result is ${1 + 2}` ); // the result is 3${…}内的表达式会被计算,计算结果会成为字符串的一部分。可以在${…}内放置任何东西:诸如名为name的变量,或者诸如1 + 2的算数表达式,或者其他一些更复杂的。需要注意的是,这仅仅在反引号内有效,其他引号不允许这种嵌入。
- 双引号:
- null 和 undefined
特殊值 undefined 和 null 一样自成类型。
undefined 的含义是 未被赋值。
如果一个变量已被声明,但未被赋值,那么它的值就是 undefined:
let age;
alert(age); // 弹出 "undefined"
-
object类型
其他所有的数据类型都被称为“原始类型”,因为它们的值只包含一个单独的内容(字符串、数字或者其他)。相反,
object则用于储存数据集合和更复杂的实体。
-
typeof 运算符
typeof运算符返回参数的类型。当我们想要分别处理不同类型值的时候,或者想快速进行数据类型检验时,非常有用。对
typeof x的调用会以字符串的形式返回数据类型:typeof undefined // "undefined" typeof 0 // "number" typeof 10n // "bigint" typeof true // "boolean" typeof "foo" // "string" typeof Symbol("id") // "symbol" typeof Math // "object" (1) typeof null // "object" (2) typeof alert // "function" (3)
交互
alert、prompt和confirm
这个我们前面已经看到过了。它会显示一条信息,并等待用户按下 “OK”。
例如:
alert("Hello");
弹出的这个带有信息的小窗口被称为 模态窗。“modal” 意味着用户不能与页面的其他部分(例如点击其他按钮等)进行交互,直到他们处理完窗口。在上面示例这种情况下 —— 直到用户点击“确定”按钮。
prompt 函数接收两个参数:
result = prompt(title, [default]);
浏览器会显示一个带有文本消息的模态窗口,还有 input 框和确定/取消按钮。
-
title显示给用户的文本
-
default可选的第二个参数,指定 input 框的初始值,可选可不选
语法:
result = confirm(question);
confirm 函数显示一个带有 question 以及确定和取消两个按钮的模态窗口。
点击确定返回 true,点击取消返回 false。
例如:
let isBoss = confirm("Are you the boss?");
alert( isBoss ); // 如果“确定”按钮被按下,则显示 true
这些方法都是模态的:它们暂停脚本的执行,并且不允许用户与该页面的其余部分进行交互,直到窗口被解除。
上述所有方法共有两个限制:
- 模态窗口的确切位置由浏览器决定。通常在页面中心。
- 窗口的确切外观也取决于浏览器。我们不能修改它。
+号运用于单个值,可以将其转化为数字
位运算符
严格相等和严格不相等
===
!==
与运算 && 在或运算 || 之前进行
与运算 && 的优先级比或运算 || 要高。
所以代码 a && b || c && d 跟 && 表达式加了括号完全一样:(a && b) || (c && d)。
空值合并运算符
当一个值既不是null也不是underfined时,我们将其称为“已定义的”
- 如果
a是已定义的,则结果为a, - 如果
a不是已定义的,则结果为b。
换句话说,如果第一个参数不是 null/undefined,则 ?? 返回第一个参数。否则,返回第二个参数。
使用场景:
例如,在这里,如果 user 的值不为 null/undefined 则显示 user,否则显示 匿名:
let user;
alert(user ?? "匿名"); // 匿名(user 未定义)
在下面这个例子中,我们将一个名字赋值给了 user:
let user = "John";
alert(user ?? "匿名"); // John(user 已定义)
case
共享一段代码
case 3:
case 5:......
函数
如果在函数内部声明了同名变量,那么函数会 遮蔽 外部变量。例如,在下面的代码中,函数使用局部的 userName,而外部变量被忽略
函数表达式
函数是一个值,
我们在前面章节使用的语法称为 函数声明:
function sayHi() {
alert( "Hello" );
}
另一种创建函数的语法称为 函数表达式。
它允许我们在任何表达式的中间创建一个新函数。
例如:
let sayHi = function() {
alert( "Hello" );
};
上面的两个代码示例的含义是一样的:“创建一个函数并将其放入变量 sayHi 中”。
我们可以复制函数到其他变量:
function sayHi() { // (1) 创建
alert( "Hello" );
}
let func = sayHi; // (2) 复制
func(); // Hello // (3) 运行复制的值(正常运行)!
sayHi(); // Hello // 这里也能运行(为什么不行呢)
(2)行将sayHi复制到了变量func。请注意:sayHi后面没有括号。如果有括号,func = sayHi()会把sayHi()的调用结果写进func,而不是sayHi函数 本身。
回调函数
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);
ask 的两个参数值 showOk 和 showCancel 可以被称为 回调函数 或简称 回调。
我们可以使用函数表达式来编写一个等价的、更简洁的函数:
function ask(question, yes, no) {
if (confirm(question)) yes()
else no();
}
ask(
"Do you agree?",
function() { alert("You agreed."); },
function() { alert("You canceled the execution."); }
);
这里直接在 ask(...) 调用内进行函数声明。这两个函数没有名字,所以叫 匿名函数。这样的函数在 ask 外无法访问(因为没有对它们分配变量),不过这正是我们想要的。
不用多余的变量,很好地完成这个基本功能...
函数表达式和函数声明之间的区别:
函数声明:在主代码流中声明为单独的语句的函数
函数表达式:在一个表达式或另一个语法结构中创建的函数
细微差别: JavaScript引擎会在不同时候创建函数,函数表达式是在代码执行到达的时候被创建,并且从那一刻起可用,函数声明则是在被定义前就可以调用这是内部算法的原故。当 JavaScript 准备 运行脚本时,首先会在脚本中寻找全局函数声明,并创建这些函数。我们可以将其视为“初始化阶段”。
在处理完所有函数声明后,代码才被执行。所以运行时能够使用这些函数。
如果我们使用函数声明,则以下代码无法像预期那样工作:
let age = prompt("What is your age?", 18);
// 有条件地声明一个函数
if (age < 18) {
function welcome() {
alert("Hello!");
}
} else {
function welcome() {
alert("Greetings!");
}
}
// ……稍后使用
welcome(); // Error: welcome is not defined
这是因为函数声明只在它所在的代码块中可见。
下面是另一个例子:
let age = 16; // 拿 16 作为例子
if (age < 18) {
welcome(); // \ (运行)
// |
function welcome() { // |
alert("Hello!"); // | 函数声明在声明它的代码块内任意位置都可用
} // |
// |
welcome(); // / (运行)
} else {
function welcome() {
alert("Greetings!");
}
}
// 在这里,我们在花括号外部调用函数,我们看不到它们内部的函数声明。
welcome(); // Error: welcome is not defined
我们怎么才能让 welcome 在 if 外可见呢?
正确的做法是使用函数表达式,并将 welcome 赋值给在 if 外声明的变量,并具有正确的可见性。
下面的代码可以如愿运行:
let age = prompt("What is your age?", 18);
let welcome;
if (age < 18) {
welcome = function() {
alert("Hello!");
};
} else {
welcome = function() {
alert("Greetings!");
};
}
welcome(); // 现在可以了
可以使用问号运算符 ? 来进一步对代码进行简化:
let age = prompt("What is your age?", 18);
let welcome = (age < 18) ?
function() { alert("Hello!"); } :
function() { alert("Greetings!"); };
welcome(); // 现在可以了
什么时候选择函数声明与函数表达式?
根据经验,当我们需要声明一个函数时,首先考虑函数声明语法。它能够为组织代码提供更多的灵活性。因为我们可以在声明这些函数之前调用这些函数。
这对代码可读性也更好,因为在代码中查找 function f(…) {…} 比 let f = function(…) {…} 更容易。函数声明更“醒目”。
……但是,如果由于某种原因而导致函数声明不适合我们(我们刚刚看过上面的例子),那么应该使用函数表达式。
箭头函数
一个具体的例子:
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 sayHi = () => alert("Hello!");
一旦我们看习惯了之后,这种情况很快就会改变。
箭头函数对于简单的单行行为(action)来说非常方便
有时我们需要更复杂一点的函数,比如带有多行的表达式或语句。在这种情况下,我们可以使用花括号将它们括起来。主要区别在于,用花括号括起来之后,需要包含 return 才能返回值(就像常规函数一样)。
就像这样:
let sum = (a, b) => { // 花括号表示开始一个多行函数
let result = a + b;
return result; // 如果我们使用了花括号,那么我们需要一个显式的 “return”
};
alert( sum(1, 2) ); // 3
回顾JavaScript特性
switch结构内部使用===(严格相等)进行比较
代码质量
调试器
—— “下一步(Step) ”:运行下一条(即当前行)指令,快捷键 F9。
运行下一条语句。如果我们现在点击它,alert 会被显示出来。
一次接一次地点击此按钮,整个脚本的所有语句会被逐个执行。
—— “跨步(Step over)”:运行下一条(即当前行)指令,但 不会进入到一个函数中,快捷键 F10。
跟上一条命令“下一步(Step)”类似,但如果下一条语句是函数调用则表现不同。这里的函数指的是:不是内建的如 alert 函数等,而是我们自己写的函数。
如果我们对比一下,“下一步(Step)”命令会进入嵌套函数调用并在其第一行暂停执行,而“跨步(Step over)”对我们不可见地执行嵌套函数调用,跳过了函数内部。
—— “步入(Step into) ”,快捷键 F11。
和“下一步(Step)”类似,但在异步函数调用情况下表现不同。如果你刚刚才开始学 JavaScript,那么你可以先忽略此差异,因为我们还没有用到异步调用。
至于之后,只需要记住“下一步(Step)”命令会忽略异步行为,例如 setTimeout(计划的函数调用),它会过一段时间再执行。而“步入(Step into)”会进入到代码中并等待(如果需要)。详见 DevTools 手册。
—— “步出(Step out)” :继续执行到当前函数的末尾,快捷键 Shift+F11。
继续执行当前函数内的剩余代码,并暂停在调用当前函数的下一行代码处。当我们使用 偶然地进入到一个嵌套调用,但是我们又对这个函数不感兴趣时,我们想要尽可能的继续执行到最后的时候是非常方便的。
—— 启用/禁用所有的断点。
这个按钮不会影响程序的执行。只是一个批量操作断点的开/关。
—— 启用/禁用出现错误时自动暂停脚本执行。
当启动此功能,如果开发者工具是打开着的时候,任何脚本执行错误都会导致该脚本执行自动暂停。然后我们可以在调试器中分析变量来看一下什么出错了。因此如果我们的脚本因为错误挂掉的时候,我们可以打开调试器,启用这个选项然后重载页面,查看一下哪里导致它挂掉了和当时的上下文是什么。
在代码中的某一行上右键,在显示的关联菜单(context menu)中点击一个非常有用的名为 “Continue to here” 的选项。
当你想要向前移动很多步到某一行为止,但是又懒得设置一个断点时非常的方便。
有 3 种方式来暂停一个脚本:
- 断点。
debugger语句。- error(如果开发者工具是打开状态,并且按钮 是开启的状态)。