现代 JavaScript 教程摘录

现代 JavaScript 教程

严格模式#

  1. "use strict" 指令将浏览器引擎转换为“现代”模式,改变一些内建特性的行为。我们会在之后的学习中了解这些细节。
  2. 严格模式通过将 "use strict" 放置在整个脚本或函数的顶部来启用。一些新语言特性诸如 “classes” 和 “modules” 也会自动开启严格模式。
  3. 所有的现代浏览器都支持严格模式。
  4. 我们建议始终使用 "use strict" 启动脚本。

代码规范,分号。#

alert("There will be an error")

[1, 2].forEach(alert)

现在,如果我们运行代码,只有第一个 alert 语句的内容被显示了出来,随后我们收到了一个错误!

但是,如果我们在第一个 alert 语句末尾加上一个分号,就工作正常了:

alert("All fine now");

[1, 2].forEach(alert)

JavaScript 并不会在方括号 [...] 前添加一个隐式的分号。

var、let、const#

  1. var 没有块级作用域
if (true) {
  var test = true; // 用 "var" 而不是 "let"
}

alert(test); // true,变量在 if 结束后仍存在
  1. var 在函数开头被处理,与它在代码里定义的位置无关(这里不考虑定义在嵌套函数里的场景)。
function sayHi() {
  phrase = "Hello";

  alert(phrase);

  var phrase;
}

// 等同于

function sayHi() {
  var phrase;

  phrase = "Hello";

  alert(phrase);
}

// 等同于

function sayHi() {
  phrase = "Hello"; // (*)

  if (false) {
    var phrase;
  }

  alert(phrase);
}

声明会被提升,但是赋值不会。

function sayHi() {
  alert(phrase);

  var phrase = "Hello";
}

sayHi();

// 等同于

function sayHi() {
  var phrase; // 声明在开头工作……

  alert(phrase); // undefined

  phrase = "Hello"; // ...赋值 — 当执行到这里时。
}

sayHi();

null、undefined#

JavaScript 中的 null 仅仅是一个代表“无”、“空”或“值未知”的特殊值。

undefined 的含义是 未被赋值

如果一个变量已被声明,但未被赋值,那么它的值就是 undefined

通常,使用 null 将一个“空”或者“未知”的值写入变量中,undefined 仅仅用于检验,例如查看变量是否被赋值或者其他类似的操作。

严格相等操作符:===#

alert( 0 == false ); // true
alert( '' == false ); // true
alert( 0 === false ); // false,因为被比较值的数据类型不同

// null 表示空值,undefined 表示未被赋值
alert( null == undefined ); // true
alert( null === undefined ); // false

函数#

  • 函数是值。它们可以在代码的任何地方被分配,复制或声明。
  • 如果函数在主代码流中被声明为单独的语句,则称为“函数声明”。
  • 如果该函数是作为表达式的一部分创建的,则称其“函数表达式”。
  • 在执行代码块之前,内部算法会先处理函数声明。所以函数声明在其被声明的代码块内的任何位置都是可见的。
  • 函数表达式在执行流程到达时创建。

在大多数情况下,当我们需要声明一个函数时,最好使用函数声明,因为函数在被声明之前也是可见的。这使我们在代码组织方面更具灵活性,通常也会使得代码可读性更高。

所以,仅当函数声明不适合对应的任务时,才应使用函数表达式。

函数声明与函数表达式#

// 函数声明
function sum(a, b) {
  return a + b;
}

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

注意函数表达式后跟了分号,因为这实际上是把一个函数赋值给变量 sum,属于赋值操作。

函数表达式代码块 做区分。

在函数声明被定义之前,它就可以被调用。

函数表达式是在代码执行到达时被创建,并且仅从那一刻起可用。

例如,一个全局函数声明对整个脚本来说都是可见的,无论它被写在这个脚本的哪个位置。

举个例:

// 函数声明
sayHi("John"); // Hello, John

function sayHi(name) {
  alert( `Hello, ${name}` );
}

// 函数表达式
sayHi("John"); // error!

let sayHi = function(name) {  // (*) no magic any more
  alert( `Hello, ${name}` );
};

例子:

// 函数声明
let age = 16; // 拿 16 作为例子

if (age < 18) {
  welcome();               // \   (运行)
                           //  |
  function welcome() {     //  |
    alert("Hello!");       //  |  函数声明在声明它的代码块内任意位置都可用
  }                        //  |
                           //  |
  welcome();               // /   (运行)

} else {

  function welcome() {
    alert("Greetings!");
  }
}

// 在这里,我们在花括号外部调用函数,我们看不到它们内部的函数声明。


welcome(); // Error: welcome is not defined


// 函数表达式
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 showMessage(from, text = "no text given") {
  alert( from + ": " + text );
}

showMessage("Ann"); // Ann: no text given

返回值#

空值的 return 或没有 return 的函数返回值为 undefined

回调函数#

把函数(的指针)作为参数传入一个函数。

  1. 属性
  2. 闭包
  3. 原型
export default {
  data() {
    return {
      testList: [],
      testStr: "",
    };
  },
  created: function() {

  },
  watch: {

  },
  methods: {
    testMethod: function(param) {

    },
  }
};

调试#

在 Chrome 中调试#

在 Chrome 中调试

Chrome DevTools

Debugger 命令#

function hello(name) {
  let phrase = `Hello, ${name}!`;

  debugger;  // <-- 调试器会在这停止

  say(phrase);
}

当我们在一个代码编辑器中并且不想切换到浏览器在开发者工具中查找脚本来设置断点时,这真的是非常方便。

代码风格#

没有什么规则是“必须”的

代码风格

image-20200205163625434

注释#

JSDoc#

说明——WIKI

例:

/**
 * 返回 x 的 n 次幂的值。
 *
 * @param {number} x 要改变的值。
 * @param {number} n 幂数,必须是一个自然数。
 * @return {number} x 的 n 次幂的值。
 */
function pow(x, n) {
  ...
}

总结#

一个好的开发者的标志之一就是他的注释:它们的存在甚至它们的缺席(译注:在该注释的地方注释,在不需要注释的地方则不注释,甚至写得好的自描述函数本身就是一种注释)。

好的注释可以使我们更好地维护代码,一段时间之后依然可以更高效地回到代码高效开发。

注释这些内容:

  • 整体架构,高层次的观点。
  • 函数的用法。
  • 重要的解决方案,特别是在不是很明显时。

避免注释:

  • 描述“代码如何工作”和“代码做了什么”。
  • 避免在代码已经足够简单或代码有很好的自描述性而不需要注释的情况下,还写些没必要的注释。

注释也被用于一些如 JSDoc3 等文档自动生成工具:他们读取注释然后生成 HTML 文档(或者其他格式的文档)。

数据类型#

JavaScript 中有七种基本的数据类型(译注:前六种为基本数据类型,也属于原生类型,而 object 为复杂数据类型)。

  • number 用于任何类型的数字:整数或浮点数。
  • string 用于字符串:一个字符串可以包含一个或多个字符,所以没有单独的单字符类型。
  • boolean 用于 truefalse
  • null 用于未知的值 —— 只有一个 null 值的独立类型。
  • undefined 用于未定义的值 —— 只有一个 undefined 值的独立类型。
  • symbol 用于唯一的标识符。
  • object 用于更复杂的数据结构。

Object(对象)#

JavaScript 中用 {...} 来创建对象,一个属性是一个键值对。

let user = new Object(); // “构造函数” 的语法
let user = {};  // “字面量” 的语法

列表中的最后一个属性应以逗号结尾:

let user = {
  name: "John",
  age: 30,
};

let john = {
  name: "John",
  sayHi: function() {
    alert("Hi buddy!");
  }
};

这叫做尾随(trailing)或悬挂(hanging)逗号。这样便于我们添加、删除和移动属性,因为所有的行都是相似的。

看到这就明白所谓的 JSON 到底是什么东西了——JavaScript Object Notation,JavaScript 对象表示法。

JSON 本身就是为 JavaScript 设计的。

// 读
alert( user.age ); // 30
// 写
user.isAdmin = true;
// 删
delete user.age;



// 读
alert(user["likes birds"]); // true
// 写
user["likes birds"] = true;
// 删
delete user["likes birds"];
let user = { name: "John", age: 30 };

alert( "age" in user ); // true,user.age 存在
alert( "blabla" in user ); // false,user.blabla 不存在。

有时候大家会说“数组类型”或“日期类型”,但其实它们并不是自身所属的类型,而是属于一个对象类型即 “object”。它们以不同的方式对 “object” 做了一些扩展。

垃圾回收#

可达性分析

标记清除(mark-and-sweep)

对象与方法#

// 这些对象作用一样

user = {
  sayHi: function() {
    alert("Hello");
  }
};

// 方法简写看起来更好,对吧?
let user = {
  sayHi() { // 与 "sayHi: function()" 一样
    alert("Hello");
  }
};

箭头函数没有自己的 this#

let user = {
  firstName: "Ilya",
  sayHi() {
    let arrow = () => alert(this.firstName);
    arrow();
  }
};

user.sayHi(); // Ilya

string#

let str = `Hello`;

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

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

方括号是获取字符的一种现代化方法,而 charAt 是历史原因才存在的。

它们之间的唯一区别是,如果没有找到字符,[] 返回 undefined,而 charAt 返回一个空字符串:

let str = `Hello`;

alert( str[1000] ); // undefined
alert( str.charAt(1000) ); // '' (空字符串)

总结#

  • 有 3 种类型的引号。反引号允许字符串跨越多行并可以在 ${…} 中嵌入表达式。
  • JavaScript 中的字符串使用 UTF-16 进行编码。
  • 我们可以使用像 \n 这样的特殊字符或通过使用 \u... 来操作它们的 unicode 进行字符插入。
  • 获取字符时,使用 []
  • 获取子字符串,使用 slicesubstring
  • 字符串的大/小写转换,使用:toLowerCase/toUpperCase
  • 查找子字符串时,使用 indexOfincludes/startsWith/endsWith 进行简单检查。
  • 根据语言比较字符串时使用 localeCompare,否则将按字符代码进行比较。

字符串还有其他几种有用的方法:

  • str.trim() —— 删除字符串前后的空格 (“trims”)。
  • str.repeat(n) —— 重复字符串 n 次。
  • ……更多内容细节参见手册

字符串还具有用正则表达式执行搜索/替换的方法。但这个话题很大,因此我们单独将它放在 正则表达式 章节讨论。