JavaScript总结

JavaScript简介

(1)定义

  • JavaScript 是脚本语言
  • JavaScript 是一种轻量级的编程语言。
  • JavaScript 是可插入 HTML 页面的编程代码。
  • JavaScript 插入 HTML 页面后,可由所有的现代浏览器执行。

(2)作用

javaScript能够改变 HTML 内容
  • getElementById() 是多个 JavaScript HTML 方法之一。
  • JavaScript 同时接受双引号和单引号:
JavaScript 能够改变 HTML 属性
  • JavaScript 能够改变任意 HTML 元素的大多数属性
<script>
function changeImage()
{
    element=document.getElementById('myimage')
    if (element.src.match("bulbon"))
    {
        element.src="/images/pic_bulboff.gif";
    }
    else
    {
        element.src="/images/pic_bulbon.gif";
    }
}
</script>
<img loading="lazy" id="myimage" onclick="changeImage()" src="/images/pic_bulboff.gif" width="100" height="180">
  • 本例会动态地改变 HTML 的来源(src),点亮灯泡。
JavaScript 能够改变 HTML 样式 (CSS)
  • 改变 HTML 元素的样式,是改变 HTML 属性的一种变种。
可通过改变 display 样式来隐藏 HTML 元素:
  • 可通过改变 display 样式来隐藏 HTML 元素。
JavaScript 能够显示 HTML 元素
  • 可通过改变 display 样式来显示隐藏 HTML 元素。

JavaScript的使用

(1)<script>标签

  • 在 HTML 中,JavaScript 代码必须位于<script></script>标签之间。
  • 旧的 JavaScript 例子也许会使用 type 属性:<script type="text/javascript">。type 属性不是必需的。JavaScript 是 HTML 中的默认脚本语言。

(2)<head> <body> 中的 JavaScript

  • 脚本可被放置与 HTML 页面的 <body> <head> 部分中,或兼而有之。
  • 把脚本置于<body> 元素的底部,可改善显示速度,因为脚本编译会拖慢显示。

(3)外部脚本

  • 脚本可放置与外部文件中,JavaScript 文件的文件扩展名是 .js。
  • 如需使用外部脚本,请在 <script> 标签的 src (source) 属性中设置脚本的名称:
<script src="myScript.js"></script>
  • 可以在 <head> <body>中放置外部脚本引用。该脚本的表现与它被置于<script> 标签中是一样的。
  • 外部脚本不能包含 <script>标签。

(4)外部 JavaScript 的优势

  • 在外部文件中放置脚本有如下优势:
  • 分离了 HTML 和代码
  • 使 HTML 和 JavaScript 更易于阅读和维护
  • 已缓存的 JavaScript 文件可加速页面加载

(5)外部引用

  • 可通过完整的 URL 或相对于当前网页的路径引用外部脚本:
<script src="https://www.w3school.com.cn/js/myScript1.js"></script>

JavaScript 输出

  • JavaScript 不提供任何内建的打印或显示函数。

(1)JavaScript 显示方案

  • JavaScript 能够以不同方式“显示”数据:
  • 使用window.alert() 写入警告框
  • 使用 document.write() 写入 HTML 输出
  • 使用 innerHTML 写入 HTML 元素
  • 使用 console.log()写入浏览器控制台
使用 innerHTML
  • 如需访问 HTML 元素,JavaScript 可使用 document.getElementById(id) 方法。
  • id 属性定义 HTML 元素。
使用 document.write()
  • 出于测试目的,使用 document.write() 比较方便。
  • 注意:在 HTML 文档完全加载后使用 document.write() 删除所有已有的 HTML 。
使用 window.alert()
  • 能够使用警告框来显示数据。
使用 console.log()
  • 在浏览器中,您可使用 console.log() 方法来显示数据。

JavaScript 语句

  • 在 HTML 中,JavaScript 语句是由 web 浏览器“执行”的“指令”。

(1)JavaScript 程序

  • 计算机程序是由计算机“执行”的一系列“指令”。在编程语言中,这些编程指令被称为语句。JavaScript 程序就是一系列的编程语句。
  • 在 HTML 中,JavaScript 程序由 web 浏览器执行

JavaScript 语句

  • JavaScript 语句由以下构成:运算符表达式关键词和注释
  • 分号分隔 JavaScript 语句。请在每条可执行的语句之后添加分号。
  • 如果有分号分隔,允许在同一行写多条语句。

JavaScript 空白字符

  • JavaScript 会忽略多个空格。您可以向脚本添加空格,以增强可读性。

JavaScript 行长度和折行

  • 如果 JavaScript 语句太长,对其进行折行的最佳位置是某个运算符。

JavaScript 代码块

  • JavaScript 语句可以用花括号({...})组合在代码块中。代码块的作用是定义一同执行的语句。

JavaScript 关键词

关键词

作用

break

终止 switch 或循环。

continue

跳出循环并在顶端开始。

debugger

停止执行 JavaScript,并调用调试函数(如果可用)。

do…while

执行语句块,并在条件为真时重复代码块。

for

标记需被执行的语句块,只要条件为真。

function

声明函数。

if…else

标记需被执行的语句块,根据某个条件。

return

退出函数。

switch

标记需被执行的语句块,根据不同的情况。

try…catch

对语句块实现错误处理。

var

声明变量。

JavaScript 语法

  • JavaScript 语法是一套规则,它定义了 JavaScript 的语言结构。
var x, y;	// 如何声明变量
x = 7; y = 8;	// 如何赋值
z = x + y;	// 如何计算值

(1)JavaScript 值

  • JavaScript 语句定义两种类型的值:混合值和变量值。混合值被称为字面量(literal)。变量值被称为变量。
JavaScript 字面量
  • 书写混合值最重要的规则是:书写混合值最重要的规则是。
  • 字符串是文本,由双引号或单引号包围。如果把数值放在引号中,会被视作文本字符串。

(2)JavaScript 变量

  • JavaScript 变量是存储数据值的容器。
  • 在编程语言中,变量用于存储数据值。JavaScript 使用 var 关键词来声明变量。= 号用于为变量赋值。
  • 您可以在一条语句中声明许多变量。以 var 作为语句的开头,并以逗号分隔变量。
  • 声明可横跨多行。
  • Value = undefined,在计算机程序中,被声明的变量经常是不带值的。值可以是需被计算的内容,或是之后被提供的数据,比如数据输入。不带有值的变量,它的值将是 undefined。
  • 如果再次声明某个 JavaScript 变量,将不会丢它的值。

(3)JavaScript 运算符

JavaScript 算数运算符
  • 算术运算符对数值(文字或变量)执行算术运算。

运算符

描述

+

加法,加法运算符加数。

-

减法,减法运算符减数。

*

乘法,乘法运算符乘数。

/

除法,除法运算符除数。

%

系数,系数运算符返回除法的余数。

**

幂,取幂运算符将第一个操作数提升到第二个操作数的幂。(x ** y 产生的结果与 Math.pow(x,y) 相同)

JavaScript 赋值运算符
  • JavaScript 使用赋值运算符向变量赋值。
    (1)= 赋值运算符向变量赋值。
    (2)+=赋值运算符向变量添加值。
    (3)-= 赋值运算符从变量中减去一个值。
    (4)*= 赋值运算符相乘变量。
    (5)/=赋值运算符对变量相除。
    (6)%=赋值运算符把余数赋值给变量。
JavaScript 字符串运算符:

(1) + 运算符也可用于对字符串进行相加(concatenate,级联)。

txt1 = "Bill";
txt2 = "Gates";
txt3 = txt1 + " " + txt2;
  • txt3 的结果将是:
Bill Gates

(2)字符串和数字的相加

  • 相加两个数字,将返回和,但对一个数字和一个字符串相加将返回一个字符串:
x = 7 + 8;
y = "7" + 8;
z = "Hello" + 7;
  • x、y 和 z 的结果将是:
15
78
Hello7
  • JavaScript 类型运算符

运算符

描述

typeof

返回变量的类型。

instanceof

返回 true,如果对象是对象类型的实例。

(4)JavaScript 关键词

  • JavaScript 关键词用于标识被执行的动作
  • ES2015 引入了两个重要的 JavaScript 新关键词:letconst,这两个关键字在 JavaScript 中提供了块作用域(Block Scope)变量(和常量)。在 ES2015 之前,JavaScript 只有两种类型的作用域:全局作用域和函数作用域。
全局作用域
  • 全局(在函数之外)声明的变量拥有全局作用域。
  • 全局变量可以在 JavaScript 程序中的任何位置访问。
  • 如果在块外声明声明,那么 var 和 let 也很相似。它们都拥有全局作用域:
var x = 10;       // 全局作用域
let y = 6;       // 全局作用域
函数作用域
  • 局部(函数内)声明的变量拥有函数作用域。
  • 局部变量只能在它们被声明的函数内访问。
  • 在函数内声明变量时,使用 varlet 很相似。它们都有函数作用域:
function myFunction() {
  var carName = "porsche";   // 函数作用域
}
function myFunction() {
  let carName = "porsche";   // 函数作用域
}
JavaScript 块作用域
  • 通过var 关键词声明的变量没有块作用域。在块 {} 内声明的变量可以从块之外进行访问:
{ 
  var x = 10; 
}
// 此处可以使用 x
  • 可以使用 let 关键词声明拥有块作用域的变量。在块 {} 内声明的变量无法从块外访问:
{ 
  let x = 10;
}
// 此处不可以使用 x
  • 使用 var 关键字重新声明变量会带来问题。在块中重新声明变量也将重新声明块外的变量:
var x = 10;
// 此处 x 为 10
{ 
  var x = 6;
  // 此处 x 为 6
}
// 此处 x 为 6
  • 使用let 关键字重新声明变量可以解决这个问题。在块中重新声明变量不会重新声明块外的变量:
var x = 10;
// 此处 x 为 10
{ 
  let x = 6;
  // 此处 x 为 6
}
// 此处 x 为 10
循环作用域
  • 在循环中使用var
var i = 7;
for (var i = 0; i < 10; i++) {
  // 一些语句
}
// 此处,i 为 10
  • 在循环中使用 let
let i = 7;
for (let i = 0; i < 10; i++) {
  // 一些语句
}
// 此处 i 为 7
  • 在第一个例子中,在循环中使用的变量使用 var 重新声明了循环之外的变量。
  • 在第二个例子中,在循环中使用的变量使用 let 并没有重新声明循环外的变量
  • 如果在循环中用 let 声明了变量 i,那么只有在循环内,变量 i 才是可见的
JavaScript Const
  • 在块作用域内使用 const 声明的变量与 let 变量相似:
var x = 10;
// 此处,x 为 10
{ 
  const x = 6;
  // 此处,x 为 6
}
// 此处,x 为 10
  • JavaScript const 变量必须在声明时赋值:
const PI = 3.14159265359;//正确
const PI;PI = 3.14159265359;//不正确
  • 关键字 const 有一定的误导性。它没有定义常量值。它定义了对值的常量引用。因此,我们不能更改常量原始值,但我们可以更改常量对象的属性。
原始值
  • 如果我们将一个原始值赋给常量,我们就不能改变原始值:
const PI = 3.141592653589793;
PI = 3.14;      // 会出错
PI = PI + 10;   // 也会出错
常量对象可以更改
  • 可以更改常量对象的属性:
// 您可以创建 const 对象:
const car = {type:"porsche", model:"911", color:"Black"};

// 您可以更改属性:
car.color = "White";

// 您可以添加属性:
car.owner = "Bill";
常量数组可以更改
  • 可以更改常量数组的元素:
// 您可以创建常量数组:
const cars = ["Audi", "BMW", "porsche"];

// 您可以更改元素:
cars[0] = "Honda";

// 您可以添加元素:
cars.push("Volvo");
重新声明
  • 在同一作用域或块中,不允许将已有的var let变量重新声明或重新赋值给 const
var x = 2;         // 允许
const x = 2;       // 不允许
{
  let x = 2;     // 允许
  const x = 2;   // 不允许
}
  • 在同一作用域或块中,为已有的 const 变量重新声明声明或赋值是不允许的:
const x = 2;       // 允许
const x = 3;       // 不允许
x = 3;             // 不允许
var x = 3;         // 不允许
let x = 3;         // 不允许

{
  const x = 2;   // 允许
  const x = 3;   // 不允许
  x = 3;         // 不允许
  var x = 3;     // 不允许
  let x = 3;     // 不允许
}
  • 在另外的作用域或块中重新声明 const 是允许的:
const x = 2;       // 允许

{
  const x = 3;   // 允许
}

{
  const x = 4;   // 允许
}

(5)JavaScript 注释

  • 并非所有 JavaScript 语句都被“执行”。双斜杠// /* */ 之间的代码被视为注释。
  • 注释会被忽略,不会被执行。
  • 使用注释来防止代码执行很适合代码测试。在代码行之前添加 // 会把可执行的代码行更改为注释。

(6)JavaScript 标识符

  • 标识符是名称。在 JavaScript 中,标识符用于命名变量(以及关键词、函数和标签)。
  • 在 JavaScript 中,首字符必须是字母、下划线(-)或美元符号($)。连串的字符可以是字母、数字、下划线或美元符号。
  • 数值不可以作为首字符。
  • 构造变量名称(唯一标识符)的通用规则是:
  • 名称可包含字母、数字、下划线和美元符号
  • 名称必须以字母开头
  • 名称也可以 $ 和 _ 开头(但是在本教程中我们不会这么做)
  • 名称对大小写敏感(y 和 Y 是不同的变量)
  • 保留字(比如 JavaScript 的关键词)无法用作变量名称

(7)JavaScript 对大小写敏感

  • 所有 JavaScript 标识符对大小写敏感。
  • JavaScript 不会把 VAR 或 Var 译作关键词 var。

(8)HTML 中的全局变量

  • 使用 JavaScript 的情况下,全局作用域是 JavaScript 环境。
  • 在 HTML 中,全局作用域是 window 对象。
  • 通过 var关键词定义的全局变量属于 window 对象:
var carName = "porsche";
// 此处的代码可使用 window.carName
  • 通过let 关键词定义的全局变量不属于 window 对象:
let carName = "porsche";
// 此处的代码不可使用 window.carName
  • 在相同的作用域,或在相同的块中,通过 let重新声明一个var 变量是不允许的:
var x = 10;       // 允许
let x = 6;       // 不允许

{
  var x = 10;   // 允许
  let x = 6;   // 不允许
}
  • 在相同的作用域,或在相同的块中,通过let 重新声明一个 let变量是不允许的:
let x = 10;       // 允许
let x = 6;       // 不允许

{
  let x = 10;   // 允许
  let x = 6;   // 不允许
}
  • 在相同的作用域,或在相同的块中,通过 var 重新声明一个let 变量是不允许的:
let x = 10;       // 允许
var x = 6;       // 不允许

{
  let x = 10;   // 允许
  var x = 6;   // 不允许
}
  • 在不同的作用域或块中,通过 let 重新声明变量是允许的:
let x = 6;       // 允许

{
  let x = 7;   // 允许
}

{
  let x = 8;   // 允许
}

3.JavaScript 数据类型

JavaScript 数据类型

  • JavaScript 变量能够保存多种数据类型:数值、字符串值、数组、对象等等:
var length = 7;                             // 数字
var lastName = "Gates";                      // 字符串
var cars = ["Porsche", "Volvo", "BMW"];         // 数组
var x = {firstName:"Bill", lastName:"Gates"};    // 对象

数据类型的概念

  • 当数值和字符串相加时,JavaScript 将把数值视作字符串。
var x = 911 + 7 + "Porsche";
  • 结果:
918Porsche
var x = "Porsche" + 911 + 7;

结果:

Porsche9117
  • 在第一个例子中,JavaScript 把 911 和 7 视作数值,直到遇见 “Porsche”。
  • 在第二个例子中,由于第一个操作数是字符串,因此所有操作数都被视为字符串。

动态类型

  • JavaScript 拥有动态类型。这意味着相同变量可用作不同类型:
var x;               // 现在 x 是 undefined
var x = 7;           // 现在 x 是数值
var x = "Bill";      // 现在 x 是字符串值

JavaScript 字符串值

  • 字符串(或文本字符串)是一串字符(比如 “Bill Gates”)。字符串被引号包围。可使用单引号或双引号
  • 可以在字符串内使用引号,只要这些引号与包围字符串的引号不匹配
var answer = "It's alright";             // 双引号内的单引号
var answer = "He is called 'Bill'";    // 双引号内的单引号
var answer = 'He is called "Bill"';    // 单引号内的双引号
  • 内建属性length 可返回字符串的长度。
  • 可以使用\ 转义字符避免JavaScript 误解字符串。
  • indexOf()方法返回字符串中指定文本首次出现的索引(位置)。
  • lastIndexOf()方法返回指定文本在字符串中最后一次出现的索引。
  • search() 方法搜索特定值的字符串,并返回匹配的位置。
  • slice()提取字符串的某个部分并在新字符串中返回被提取的部分。
  • replace() 方法用另一个值替换在字符串中指定的值。(只替换首个匹配,对大小写敏感)
  • 通过 toUpperCase() 把字符串转换为大写。通过 toLowerCase() 把字符串转换为小写。
  • concat() 连接两个或多个字符串。
  • trim() 方法删除字符串两端的空白符。
  • charAt() 方法返回字符串中指定下标(位置)的字符串。
  • charCodeAt() 方法返回字符串中指定索引的字符 unicode 编码。
  • 可以通过 split()将字符串转换为数组。

JavaScript 数值

  • JavaScript 只有一种数值类型。写数值时用不用小数点均可。
  • 超大或超小的数值可以用科学计数法来写

JavaScript 布尔值

  • 布尔值只有两个值:truefalse

JavaScript 数组

  • JavaScript 数组用方括号书写。数组的项目由逗号分隔。
  • 创建数组
var array-name = [item1, item2, ...];
访问数组
  • 访问数组元素
  • 通过引用索引号(下标号)来引用某个数组元素。
  • 访问完整数组
  • 可通过引用数组名来访问完整数组。
  • length 属性返回数组的长度(数组元素的数目)。length属性始终大于最高数组索引(下标)。
遍历数组
  • 遍历数组元素
  • 遍历数组的最安全方法是使用 “for” 循环:
var fruits, text, fLen, i;

fruits = ["Banana", "Orange", "Apple", "Mango"];
fLen = fruits.length;
text = "<ul>";
for (i = 0; i < fLen; i++) {
     text += "<li>" + fruits[i] + "</li>";
}
  • 可以使用 Array.foreach()函数:
var fruits, text;
fruits = ["Banana", "Orange", "Apple", "Mango"];

text = "<ul>";
fruits.forEach(myFunction);
text += "</ul>";

function myFunction(value) {
  text += "<li>" + value + "</li>";
}
添加数组元素
  • 向数组添加新元素的最佳方法是使用 ``push() ```方法。
var fruits = ["Banana", "Orange", "Apple", "Mango"];
fruits.push("Lemon");                // 向 fruits 添加一个新元素 (Lemon)
数组方法
  • 把数组转换为字符串
  • toString() 把数组转换为数组值(逗号分隔)的字符串。
  • join() 方法也可将所有数组元素结合为一个字符串。还可以规定分隔符:
var fruits = ["Banana", "Orange","Apple", "Mango"];
document.getElementById("demo").innerHTML = fruits.join(" * ");
  • pop()方法从数组中删除最后一个元素。
  • push() 方法(在数组结尾处)向数组添加一个新的元素。
  • shift() 方法会删除首个数组元素,并把所有其他元素“位移”到更低的索引。
  • unshift() 方法(在开头)向数组添加新元素,并“反向位移”旧元素。
  • concat()方法通过合并(连接)现有数组来创建一个新数组。它不会更改现有数组,总是返回一个新数组并且可以使用任意数量的数组参数。
  • slice() 方法用数组的某个片段切出新数组。

JavaScript 对象

  • JavaScript 对象用花括号来书写。
  • 对象属性是 name:value对,由逗号分隔。
  • 定义格式:
var person = {firstName:"Bill", lastName:"Gates", age:62, eyeColor:"blue"};
  • 访问方法:
objectName.propertyName
objectName["propertyName"]
  • 注意事项
  • 不要把字符串、数值和布尔值声明为对象,通过关键词 “new” 来声明 JavaScript 变量,则该变量会被创建为对象。

typeof 运算符

  • typeof 运算符可返回以下两种类型之一:
  • function
  • object
  • typeof 运算符把对象、数组或 null 返回 object。
  • typeof 运算符不会把函数返回 object。
  • 您可使用 JavaScript 的 typeof 来确定 JavaScript 变量的类型:
typeof ""                  // 返回 "string"
typeof "Bill"              // 返回 "string"
typeof "Bill Gates"          // 返回 "string"
typeof 0                   // 返回 "number"
typeof 314                 // 返回 "number"
typeof 3.14                // 返回 "number"
typeof (7)                 // 返回 "number"
typeof (7 + 8)             // 返回 "number"
  • typeof 运算符对数组返回 “object”,因为在 JavaScript 中数组属于对象。

Undefined

  • 在 JavaScript 中,没有值的变量,其值是 undefined。typeof 也返回 undefined.
  • 任何变量均可通过设置值为 undefined 进行清空。其类型也将是 undefined。

空值

  • 空的字符串变量既有值也有类型,空值与undefined不同.

NULL

  • 在 JavaScript 中,null 的数据类型是对象。
  • 可以通过设置值为 null 清空对象.
  • NULL与undefined的区别

NULL

undefined

类型为对象

类型为undefined

4.JavaScript 函数

  • JavaScript 函数是被设计为执行特定任务的代码块。
  • JavaScript 函数会在某代码调用它时被执行。

JavaScript 函数语法

  • JavaScript 函数通过 function 关键词进行定义,其后是函数名和括号 ()。
  • 函数名可包含字母、数字、下划线和美元符号(规则与变量名相同)。
  • 定义格式:
    function name(参数 1, 参数 2, 参数 3) { 要执行的代码 }
  • 参数是局部变量
  • 在 JavaScript 函数中声明的变量,会成为函数的局部变量

函数调用

  • 函数中的代码将在其他代码调用该函数时执行:
  • 当事件发生时(当用户点击按钮时)
  • 当 JavaScript 代码调用时
  • 自动的(自调用)

函数返回

  • 当 JavaScript 到达 return 语句,函数将停止执行。
  • 如果函数被某条语句调用,JavaScript 将在调用语句之后“返回”执行代码。

5.JavaScript 事件

  • 常见的HTML事件

事件

描述

onchange

HTML元素已经被改变

onclick

点击了HTML元素

onmouseover

把鼠标移动到 HTML 元素上

onmouseout

把鼠标移开 HTML 元素

onkeydown

用户按下键盘按键

onload

浏览器已经完成页面加载

Typescript总结

Typescript定义

定义

  • TypeScript 是一种由微软开发的自由和开源的编程语言。它是 JavaScript 的一个超集,而且本质上向这个语言添加了可选的静态类型和基于类的面向对象编程。

TypeScript 与 JavaScript 的区别

TypeScript

JavaScript

JavaScript 的超集用于解决大型项目的代码复杂性

一种脚本语言,用于创建动态网页

可以在编译期间发现并纠正错误

作为一种解释型语言,只能在运行时发现错误

强类型,支持静态和动态类型

弱类型,没有静态类型选项

最终被编译成 JavaScript 代码,使浏览器可以理解

可以直接在浏览器中使用

支持模块、泛型和接口

不支持模块,泛型或接口

社区的支持仍在增长,而且还不是很大

大量的社区支持以及大量文档和解决问题的支持

基础类型

布尔值

  • 最基本的数据类型就是简单的true/false值,称作boolean.
let isDone: boolean = false;

数字

  • TypeScript里的所有数字都是浮点数。 这些浮点数的类型是 number。 除了支持十进制和十六进制字面量,TypeScript还支持ECMAScript 2015中引入的二进制和八进制字面量。
let decLiteral: number = 6;
let hexLiteral: number = 0xf00d;
let binaryLiteral: number = 0b1010;
let octalLiteral: number = 0o744;

字符串

  • 1.string表示文本数据类型。 和JavaScript一样,可以使用双引号( ")或单引号(')表示字符串。
let name: string = "bob";
name = "smith";
  • 2.还可以使用模版字符串,它可以定义多行文本和内嵌表达式。 这种字符串是被反引号包围( `),并且以${ expr }这种形式嵌入表达式。
let name: string = `Gene`;
let age: number = 37;
let sentence: string = `Hello, my name is ${ name }.

I'll be ${ age + 1 } years old next month.`;

这与下面定义sentence的方式效果相同:

let sentence: string = "Hello, my name is " + name + ".\n\n" +
    "I'll be " + (age + 1) + " years old next month.";

数组

  • 有两种方式可以定义数组。 第一种,可以在元素类型后面接上 [],表示由此类型元素组成的一个数组:
let list: number[] = [1, 2, 3];

第二种方式是使用数组泛型,Array<元素类型>:

let list: Array<number> = [1, 2, 3];

元组

  • 元组类型允许表示一个已知元素数量和类型的数组,各元素的类型不必相同。 比如,你可以定义一对值分别为 string和number类型的元组。
// Declare a tuple type
let x: [string, number];
// Initialize it
x = ['hello', 10]; // OK
// Initialize it incorrectly
x = [10, 'hello']; // Error
  • 当访问一个已知索引的元素,会得到正确的类型:
console.log(x[0].substr(1)); // OK
console.log(x[1].substr(1)); // Error, 'number' does not have 'substr'
  • 当访问一个越界的元素,会使用联合类型替代:
x[3] = 'world'; // OK, 字符串可以赋值给(string | number)类型

console.log(x[5].toString()); // OK, 'string' 和 'number' 都有 toString

x[6] = true; // Error, 布尔不是(string | number)类型

枚举

  • enum类型是对JavaScript标准数据类型的一个补充。 像C#等其它语言一样,使用枚举类型可以为一组数值赋予友好的名字。
1.数字枚举
enum Direction {
  NORTH,
  SOUTH,
  EAST,
  WEST,
}

let dir: Direction = Direction.NORTH;
  • 默认情况下,NORTH 的初始值为 0,其余的成员会从 1 开始自动增长。换句话说,Direction.SOUTH 的值为 1,Direction.EAST 的值为 2,Direction.WEST 的值为 3。
  • 当然我们也可以设置 NORTH 的初始值,比如:
enum Direction {
  NORTH = 3,
  SOUTH,
  EAST,
  WEST,
}
2.字符串枚举
  • 在一个字符串枚举里,每个成员都必须用字符串字面量,或另外一个字符串枚举成员进行初始化。
enum Direction {
  NORTH = "NORTH",
  SOUTH = "SOUTH",
  EAST = "EAST",
  WEST = "WEST",
}
  • 通过观察数字枚举和字符串枚举的编译结果,我们可以知道数字枚举除了支持 从成员名称到成员值 的普通映射之外,它还支持 从成员值到成员名称 的反向映射:
enum Direction {
  NORTH,
  SOUTH,
  EAST,
  WEST,
}

let dirName = Direction[0]; // NORTH
let dirVal = Direction["NORTH"]; // 0
  • 对于纯字符串枚举,我们不能省略任何初始化程序。而数字枚举如果没有显式设置值时,则会使用默认规则进行初始化。
3.常量枚举

它是使用 const 关键字修饰的枚举,常量枚举会使用内联语法,不会为枚举类型编译生成任何 JavaScript。

const enum Direction {
  NORTH,
  SOUTH,
  EAST,
  WEST,
}

let dir: Direction = Direction.NORTH;

4.异构枚举

  • 异构枚举的成员值是数字和字符串的混合:
enum Enum {
  A,
  B,
  C = "C",
  D = "D",
  E = 8,
  F,
}
  • 数字枚举相对字符串枚举多了 “反向映射”:
console.log(Enum.A) //输出:0
console.log(Enum[0]) // 输出:A

Any类型

  • 在 TypeScript 中,任何类型都可以被归为 any 类型。这让 any 类型成为了类型系统的顶级类型(也被称作全局超级类型):
let notSure: any = 666;
notSure = "semlinker";
notSure = false;
  • 在对现有代码进行改写的时候,any类型是十分有用的,它允许你在编译时可选择地包含或移除类型检查。 你可能认为 Object有相似的作用,就像它在其它语言中那样。 但是 Object类型的变量只是允许你给它赋任意值 - 但是却不能够在它上面调用任意的方法,即便它真的有这些方法:
let notSure: any = 4;
notSure.ifItExists(); // okay, ifItExists might exist at runtime
notSure.toFixed(); // okay, toFixed exists (but the compiler doesn't check)

let prettySure: Object = 4;
prettySure.toFixed(); // Error: Property 'toFixed' doesn't exist on type 'Object'.

void类型

  • 某种程度上来说,void类型像是与any类型相反,它表示没有任何类型。 当一个函数没有返回值时,通常会发现其返回值类型是 void:
function warnUser(): void {
    console.log("This is my warning message");
}

Null 和 Undefined

  • TypeScript里,undefined和null两者各自有自己的类型分别叫做undefined和null。 和 void相似,它们的本身的类型用处不是很大:
// Not much else we can assign to these variables!
let u: undefined = undefined;
let n: null = null;
  • 默认情况下null和undefined是所有类型的子类型。 就是说可以把 null和undefined赋值给number类型的变量。然而,当你指定了–strictNullChecks标记,null和undefined只能赋值给void和它们各自。

Never

  • never类型表示的是那些永不存在的值的类型。 例如, never类型是那些总是会抛出异常或根本就不会有返回值的函数表达式或箭头函数表达式的返回值类型; 变量也可能是 never类型,当它们被永不为真的类型保护所约束时。
  • never类型是任何类型的子类型,也可以赋值给任何类型;然而,没有类型是never的子类型或可以赋值给never类型(除了never本身之外)。 即使 any也不可以赋值给never
// 返回never的函数必须存在无法达到的终点
function error(message: string): never {
    throw new Error(message);
}

// 推断的返回值类型为never
function fail() {
    return error("Something failed");
}

// 返回never的函数必须存在无法达到的终点
function infiniteLoop(): never {
    while (true) {
    }
}

Object

  • 使用object类型,就可以更好的表示像Object.create这样的API。例如:
declare function create(o: object | null): void;

create({ prop: 0 }); // OK
create(null); // OK

create(42); // Error
create("string"); // Error
create(false); // Error
create(undefined); // Error

常量声明

let声明

  • 当用let声明一个变量,它使用的是词法作用域或块作用域。 不同于使用 var声明的变量那样可以在包含它们的函数外访问,块作用域变量在包含它们的块或for循环之外是不能访问的。
function f(input: boolean) {
    let a = 100;

    if (input) {
        // Still okay to reference 'a'
        let b = a + 1;
        return b;
    }

    // Error: 'b' doesn't exist here
    return b;
}
  • 这里我们定义了2个变量a和b。 a的作用域是f函数体内,而b的作用域是if语句块里。
  • 在catch语句里声明的变量也具有同样的作用域规则。
try {
    throw "oh no!";
}
catch (e) {
    console.log("Oh well.");
}

// Error: 'e' doesn't exist here
console.log(e);
  • 拥有块级作用域的变量的另一个特点是,它们不能在被声明之前读或写。 虽然这些变量始终“存在”于它们的作用域里,但在直到声明它的代码之前的区域都属于暂时性死区。 它只是用来说明我们不能在 let语句之前访问它们,TypeScript可以告诉我们这些信息。
a++; // illegal to use 'a' before it's declared;
let a;
  • 注意一点,我们仍然可以在一个拥有块作用域变量被声明前获取它。 只是我们不能在变量声明前去调用那个函数。
function foo() {
    // okay to capture 'a'
    return a;
}

// 不能在'a'被声明前调用'foo'
// 运行时应该抛出错误
foo();

let a;

const 声明

  • const 声明是声明变量的另一种方式。
const numLivesForCat = 9;

它们与let声明相似,但是就像它的名字所表达的,它们被赋值后不能再改变。 换句话说,它们拥有与 let相同的作用域规则,但是不能对它们重新赋值。即引用的值是不可变的。

const numLivesForCat = 9;
const kitty = {
    name: "Aurora",
    numLives: numLivesForCat,
}

// Error
kitty = {
    name: "Danielle",
    numLives: numLivesForCat
};

// all "okay"
kitty.name = "Rory";
kitty.name = "Kitty";
kitty.name = "Cat";
kitty.numLives--;
  • 除非你使用特殊的方法去避免,实际上const变量的内部状态是可修改。但是TypeScript允许你将对象的成员设置成只读的。

解构

解构数组

  • 数组的解构赋值:
let input = [1, 2];
let [first, second] = input;
console.log(first); // outputs 1
console.log(second); // outputs 2

这创建了2个命名变量 first 和 second。 相当于使用了索引,但更为方便:

first = input[0];
second = input[1];
  • 解构作用于已声明的变量会更好:
// swap variables
[first, second] = [second, first];

作用于函数参数:

function f([first, second]: [number, number]) {
    console.log(first);
    console.log(second);
}
f(input);
  • 你可以在数组里使用...语法创建剩余变量:
let [first, ...rest] = [1, 2, 3, 4];
console.log(first); // outputs 1
console.log(rest); // outputs [ 2, 3, 4 ]

或其它元素:

let [, second, , fourth] = [1, 2, 3, 4];

对象解构

解构对象:

let o = {
    a: "foo",
    b: 12,
    c: "bar"
};
let { a, b } = o;

这通过 o.a and o.b 创建了 a 和 b 。 注意,如果你不需要 c 你可以忽略它。

  • 就像数组解构,你可以用没有声明的赋值:
({ a, b } = { a: "baz", b: 101 });

注意,我们需要用括号将它括起来,因为Javascript通常会将以 { 起始的语句解析为一个块。

  • 对象里使用...语法创建剩余变量:
let { a, ...passthrough } = o;
let total = passthrough.b + passthrough.c.length;

属性重命名

给属性以不同的名字:

let { a: newName1, b: newName2 } = o;

这里将 a: newName1 读做 “a 作为 newName1”。 方向是从左到右,好像写成了以下样子:

let newName1 = o.a;
let newName2 = o.b;

令人困惑的是,这里的冒号不是指示类型的。 如果你想指定它的类型, 仍然需要在其后写上完整的模式。

let {a, b}: {a: string, b: number} = o;

默认值

  • 默认值可以让你在属性为 undefined 时使用缺省值:
function keepWholeObject(wholeObject: { a: string, b?: number }) {
    let { a, b = 1001 } = wholeObject;
}

现在,即使 b 为 undefined , keepWholeObject 函数的变量 wholeObject 的属性 a 和 b 都会有值。

函数声明

  • 解构也能用于函数声明。 看以下简单的情况:
type C = { a: string, b?: number }
function f({ a, b }: C): void {
    // ...
}

但是,通常情况下更多的是指定默认值,解构默认值有些棘手。 首先,你需要在默认值之前设置其格式。

function f({ a="", b=0 } = {}): void {
    // ...
}
f();

其次,你需要知道在解构属性上给予一个默认或可选的属性用来替换主初始化列表。 要知道 C 的定义有一个 b 可选属性:

function f({ a, b = 0 } = { a: "" }): void {
    // ...
}
f({ a: "yes" }); // ok, default b = 0
f(); // ok, default to {a: ""}, which then defaults b = 0
f({}); // error, 'a' is required if you supply an argument

要小心使用解构。 从前面的例子可以看出,就算是最简单的解构表达式也是难以理解的。 尤其当存在深层嵌套解构的时候,就算这时没有堆叠在一起的重命名,默认值和类型注解,也是令人难以理解的。 解构表达式要尽量保持小而简单。也可以直接使用解构将会生成的赋值表达式。
函数
介绍
函数是JavaScript应用程序的基础。 它帮助你实现抽象层,模拟类,信息隐藏和模块。 在TypeScript里,虽然已经支持类,命名空间和模块,但函数仍然是主要的定义 行为的地方。 TypeScript为JavaScript函数添加了额外的功能,让我们可以更容易地使用。

函数

函数类型

  • 为函数定义类型:
function add(x: number, y: number): number {
    return x + y;
}

let myAdd = function(x: number, y: number): number { return x + y; };

我们可以给每个参数添加类型之后再为函数本身添加返回值类型。 TypeScript能够根据返回语句自动推断出返回值类型,因此我们通常省略它。

  • 书写完整函数类型
    函数的完整类型:
let myAdd: (x: number, y: number) => number =
    function(x: number, y: number): number { return x + y; };

函数类型包含两部分:参数类型返回值类型。 当写出完整函数类型的时候,这两部分都是需要的。 我们也可以这么写:

let myAdd: (baseValue: number, increment: number) => number =
    function(x: number, y: number): number { return x + y; };
  • 第二部分是返回值类型。 对于返回值,我们在函数和返回值类型之前使用( =>)符号。返回值类型是函数类型的必要部分,如果函数没有返回任何值,也必须指定返回值类型为 void而不能不写。

可选参数及默认参数

  • 可选参数
function createUserId(name: string, id: number, age?: number): string {
  return name + id;
}

// 默认参数
function createUserId(
  name: string = "semlinker",
  id: number,
  age?: number
): string {
  return name + id;
}

在声明函数时,可以通过 ?号来定义可选参数,比如 age?: number这种形式。在实际使用时,需要注意的是可选参数要放在普通参数的后面,不然会导致编译错误。

剩余参数

  • 剩余参数会被当做个数不限的可选参数。 可以一个都没有,同样也可以有任意个。
function push(array, ...items) {
  items.forEach(function (item) {
    array.push(item);
  });
}

let a = [];
push(a, 1, 2, 3);

箭头函数

常见语法
myBooks.forEach(() => console.log('reading'));

myBooks.forEach(title => console.log(title));

myBooks.forEach((title, idx, arr) =>
  console.log(idx + '-' + title);
);

myBooks.forEach((title, idx, arr) => {
  console.log(idx + '-' + title);
});
使用示例
  • 未使用箭头函数
function Book() {
 let self = this;
 self.publishDate = 2016;
 setInterval(function () {
   console.log(self.publishDate);
 }, 1000);
}
  • 使用箭头函数
function Book() {
 this.publishDate = 2016;
 setInterval(() => {
   console.log(this.publishDate);
 }, 1000);
}

TypeScript 类

类的属性与方法

  • 类是一种面向对象计算机编程语言的构造,描述了所创建的对象共同的属性和方法。
    在 TypeScript 中,我们可以通过 Class 关键字来定义一个类:
class Greeter {
 // 静态属性
 static cname: string = "Greeter";
 // 成员属性
 greeting: string;

 // 构造函数 - 执行初始化操作
 constructor(message: string) {
   this.greeting = message;
 }

 // 静态方法
 static getClassName() {
   return "Class name is Greeter";
 }

 // 成员方法
 greet() {
   return "Hello, " + this.greeting;
 }
}

let greeter = new Greeter("world");

类的访问权限

  • 在 TypeScript 中,我们可以通过 getter setter 方法来实现数据的封装和有效性校验,防止出现异常数据。
let passcode = "Hello TypeScript";

class Employee {
 private _fullName: string;

 get fullName(): string {
   return this._fullName;
 }

 set fullName(newName: string) {
   if (passcode && passcode == "Hello TypeScript") {
     this._fullName = newName;
   } else {
     console.log("Error: Unauthorized update of employee!");
   }
 }
}

let employee = new Employee();
employee.fullName = "Semlinker";
if (employee.fullName) {
 console.log(employee.fullName);
}

类的继承

  • 继承是一种联结类与类的层次模型。指的是一个类(称为子类、子接口)继承另外的一个类(称为父类、父接口)的功能,并可以增加它自己的新功能的能力,继承是类与类或者接口与接口之间最常见的关系。继承是一种 is-a 关系。
    在 TypeScript 中,我们可以通过 extends 关键字来实现继承:
class Animal {
 name: string;
 
 constructor(theName: string) {
   this.name = theName;
 }
 
 move(distanceInMeters: number = 0) {
   console.log(`${this.name} moved ${distanceInMeters}m.`);
 }
}

class Snake extends Animal {
 constructor(name: string) {
   super(name); // 调用父类的构造函数
 }
 
 move(distanceInMeters = 5) {
   console.log("Slithering...");
   super.move(distanceInMeters);
 }
}

let sam = new Snake("Sammy the Python");
sam.move();

抽象类

使用abstract关键字声明的类,我们称之为抽象类。抽象类不能被实例化,因为它里面包含一个或多个抽象方法。所谓的抽象方法,是指不包含具体实现的方法:

abstract class Person {
 constructor(public name: string){}

 abstract say(words: string) :void;
}

// Cannot create an instance of an abstract class.(2511)
const lolo = new Person(); // Error
  • 抽象类不能被直接实例化,只能实例化实现了所有抽象方法的子类。具体如下所示:
abstract class Person {
 constructor(public name: string){}

 // 抽象方法
 abstract say(words: string) :void;
}

class Developer extends Person {
 constructor(name: string) {
   super(name);
 }
 
 say(words: string): void {
   console.log(`${this.name} says ${words}`);
 }
}

const lolo = new Developer("lolo");
lolo.say("I love ts!"); // lolo says I love ts!