JavaScript笔记(五)| 青训营笔记

83 阅读9分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 11 天

简介

基础知识

image.png

  • string类型

    在 JavaScript 中,有三种包含字符串的方式。

    1. 双引号:"Hello".
    2. 单引号:'Hello'.
    3. 反引号: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

特殊值 undefinednull 一样自成类型。

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

这些方法都是模态的:它们暂停脚本的执行,并且不允许用户与该页面的其余部分进行交互,直到窗口被解除。

上述所有方法共有两个限制:

  1. 模态窗口的确切位置由浏览器决定。通常在页面中心。
  2. 窗口的确切外观也取决于浏览器。我们不能修改它。

+号运用于单个值,可以将其转化为数字

位运算符

严格相等和严格不相等

===

!==

与运算 && 在或运算 || 之前进行

与运算 && 的优先级比或运算 || 要高。

所以代码 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    //     这里也能运行(为什么不行呢)
  1. (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 的两个参数值 showOkshowCancel 可以被称为 回调函数 或简称 回调

我们可以使用函数表达式来编写一个等价的、更简洁的函数:

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

我们怎么才能让 welcomeif 外可见呢?

正确的做法是使用函数表达式,并将 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 种方式来暂停一个脚本:

  1. 断点。
  2. debugger 语句。
  3. error(如果开发者工具是打开状态,并且按钮 是开启的状态)。

developers.google.com/web/tools/c…