Part1 · JavaScript【深度剖析】
TypeScript语言
文章说明:本专栏内容为本人参加【拉钩大前端高新训练营】的学习笔记以及思考总结,学徒之心,仅为分享。如若有误,请在评论区支出,如果您觉得专栏内容还不错,请点赞、关注、评论。共同进步!
上一篇:【ECMAScript模板字符串】、【ES6参数】、【展开数组、对象】、【箭头函数】、【对象】、【代理Proxy】、【class类】、【set、map数据结构】
本篇开始,梳理一些TypeScript语言的知识。首先看一下关于强、弱类型的语言方面的知识。
一、强类型与弱类型、静态类型与动态类型
1.强类型与弱类型(类型安全)
①、强类型语言:强类型语言也称为强类型定义语言。是一种总是强制类型定义的语言,要求变量的使用要严格符合定义,所有变量都必须先定义后使用。java、.NET、C++等都是强制类型定义的。也就是说,一旦一个变量被指定了某个数据类型,如果不经过强制转换,那么它就永远是这个数据类型了。
1974年美国两个专家做出解释:语言层面限制函数的实参类型必须与形参类型相同
个人理解:强类型语言不允许有各种方式的隐式转换
例如你有一个整数,如果不显式地进行转换,你不能将其视为一个字符串。
class Main {
// 接受一个整形的参数
static void foo(int num) {
System.out.println(num);
}
public static void main(String[] args) {
Main.foo(100); // ok
Main.foo("100"); // error "100" is a string,不允许传入其他类型的值
Main.foo(Interger.parseInt("100")) // ok,强制类型转换
}
}
D:\DeskTop\lagou\Flow>python
Python 3.7.0 (v3.7.0:1bf9cc5093, Jun 27 2018, 04:59:51) [MSC v.1914 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> '100' - 50
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for -: 'str' and 'int'
>>> abs('foo')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: bad operand type for abs(): 'str'
>>>
与其相对应的是弱类型语言:数据类型可以被忽略的语言。它与强类型定义语言相反, 一个变量可以赋不同数据类型的值。
**②、弱类型语言:**弱类型语言也称为弱类型定义语言。与强类型定义相反。像vb、php、js等就属于弱类型语言,语言层面不会限制实参的类型。
个人理解:弱类型语言则允许任意的数据隐式类型转换
function (num) {
console.log(num);
}
foo(100); // ok
foo('100'); // ok
foo(parseInt('100')); // ok
Microsoft Windows [版本 10.0.18363.1139]
(c) 2019 Microsoft Corporation。保留所有权利。
D:\DeskTop\lagou\Flow>node
Welcome to Node.js v12.19.0.
Type ".help" for more information.
> '100' - 50
50
> Math.floor('foo')
NaN
> Math.floor(true)
1
JavaScript中所有的类型错误都是在代码执行(逻辑判断)的过程中抛出的,并不是在语法层面进行抛出。
2.静态类型与动态类型(类型检查)
-
静态类型语言
一种在编译时,数据类型是固定的语言。大多数静态类型定义语言强制这一点,它要求你在使用所有变量之前要声明它们的数据类型。Java和C是静态类型定义语言。
-
动态类型语言
一种在执行期间才去发现数据类型的语言,与静态类型定义相反。VBScript和Python是动态类型定义的,因为它们是在第一次给一个变量赋值的时候找出它的类型的。
var foo = 100; foo = 'bar'; console.log(foo); // 在JavaScript中,变量是没有类型的。而变量中存放的值是有类型的
3.JavaScript类型系统特征
JavaScript为弱类型且动态类型,语言本身的系统非常薄弱,甚至说JavaScript根本没有类型系统----【任性】,其缺失类型系统的可靠性【不靠谱】。
早期JavaScript的目的并没有想到去处理很多程序,很多项目只有几十行上百行代码。
3.弱类型的问题及强类型的优势
- 弱类型的问题,运行阶段才能发现类型异常问题。例如在setTimeout中,当时间结束时才会发现错误,如果调试过程中没有等到足够的时间,那么这个类型错误就会形成隐患。
示例一:运行时才能发现错误
const obj = {};
obj.foo();
// obj.foo();
// ^
//
// TypeError: obj.foo is not a function
// at Object.<anonymous> (D:\DeskTop\lagou\Flow\01\01-getting-started.js:2:5)
// at Module._compile (internal/modules/cjs/loader.js:1015:30)
// at Object.Module._extensions..js (internal/modules/cjs/loader.js:1035:10)
// at Module.load (internal/modules/cjs/loader.js:879:32)
// at Function.Module._load (internal/modules/cjs/loader.js:724:14)
// at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:60:12)
// at internal/main/run_main_module.js:17:47
示例二:非预期结果
function sun(a, b) {
return a + b;
}
console.log(sum(100, 100));
console.log(sum('100', '100'));
// 200
// 100100
示例三:数据错误
const obj = {};
obj[true] = 100
console.log(obj['true']); // 100
- 强类型的优势:错误更早的暴露、代码更智能,编码更准确、重构更方便、减少不必要的类型判断
二、Flow概述及方法
1.概述
flow是 facebook 出品的 JavaScript **静态类型检查工具,https://flow.org/en/docs/usage/**这是其官方文档链接。Vue.js 的源码利用了Flow 做了静态类型检查。
JavaScript 是动态类型语言,它的灵活性有目共睹,但是过于灵活的副作用是很容易就写出非常隐蔽的隐患代码,在编译期甚至看上去都不会报错,但在运行阶段就可能出现各种奇怪的 bug
类型检查的定义:类型检查,就是在编译期尽早发现(由类型错误引起的)bug,又不影响代码运行(不需要运行时动态检查类型),使编写 JavaScript 具有和编写 Java 等强类型语言相近的体验
在vue中使用Flow做静态类型检查,是因为 Babel 和 ESLint 都有对应的 Flow 插件以支持语法,可以完全沿用现有的构建配置,非常小成本的改动就可以拥有静态类型检查的能力
类型注解
function add(x: number, y: number): number {
return x + y //x的类型是number,y的类型是number,函数的返回值类型是number
}
add('Hello', 11)
2.快速上手
-
初始化模块
- yarn init -yes
-
安装flow
- yarn add flow-bin -dev
-
使用flow类型注解
-
必须在文件文件开始处标记:@flow
-
关闭VS Code语法校验:setting,搜索JavaScript validate,找到enable,取消勾选
-
// @flow function sum(a:number, b:number) { return a + b; } sum(100, 100) sum('100', '100') let num:number = 100 num = '100'
-
-
使用
-
yarn flow init yarn flow # 第一次会很慢,后续会很快 # yarn flow stop # 结束flow #Error ---------------------------------------------------------------------------------------------------- 01/01.js:8:12 #Cannot call `sum` with `'100'` bound to `b` because string [1] is incompatible with number [2]. [incompatible-call] # 01/01.js:8:12 # 8| sum('100', '100') # ^^^^^ [1] #References: # 01/01.js:3:26 # 3| function sum(a:number, b:number) { # ^^^^^^ [2]
-
3.编译移除注解
- 方案一:自动移除类型注解,官方提供的模块:flow-remove-types
yarn add flow-remove-types --dev
yarn flow-remove-types . -d dist
# yarn flow-remove-types [需要移除注解的文件,一般为src] -d [输出目录,如果不存在则新建,一般为dist]
- 方案二:babel
yarn add @babel/core @babel/cli @babel/preset-flow --dev
然后在项目中添加项目文件-【.babelrc】,然后在文件中输入:
{
"presets":["@babel/preset-flow"]
}
随后使用命令行
yarn babel src -d dist
4.开发者工具插件
**VS Code搜索插件【flow language support】,flow官方提供的插件,可以实时显示类型异常。**默认情况下,修改完代码,需要重新保存后才会检测类型异常。
flow官网:https://flow.org/en/docs/editors
5.类型推断
flow支持在代码编写过程中就进行类型推断,例如下面代码中,需要算一个数的平方,当传入非数字类型时,flow会进行代码提示,抛出类型错误。
// @flow
function square(n: number) {
return n * n;
}
square('100')
6.类型注解
在绝大多数情况下一样,它可以帮我们推断出来变量,或者是参数的具体类型,但是没有必要给所有的成员都去添加,它可以更明确的去限制类型注解,而且对我们后期去理解,也是有帮助的可能去使用。
let num:number = 100;
// num = 'string',此时只能赋值数字类型
function foo():number {
return 100
}
// 此时函数只能返回数字类型,如果函数没有返回值,默认返回undefined,那么也会提醒报错。没有返回值的函数,我们需要将函数返回值类型标注为void
7.原始类型
在用法上flow,几乎没有任何的难度,无外乎就是使用flow命令去根据我们代码当中添加的类型注解,去检测我们代码当中的那些类型使用上的异常。值得我们再去了解的无外乎就是flow当中,具体支持哪些类型,以及,我们在类型注解上有没有一些更高级的用法,这里呢,我们具体来看。
flow中能使用的类型有很多,最简单的就是JavaScript中的原始类型,目前原始类型共有6中,number、Boole、string、null、undefined、symbol。以下进行快速尝试:
/**
* 原始类型
*
* @flow
*/
const a: string = 'foobar'
const b: number = Infinity // NaN // 100
const c: boolean = false // true
const d: null = null
const e: void = undefined
const f: symbol = Symbol()
8.数组类型
/**
* 数组类型
*
* @flow
*/
const arr1: Array<number> = [1, 2, 3]
const arr2: number[] = [1, 2, 3]
// 元组
const foo: [string, number] = ['foo', 100]
9.对象类型
/**
* 对象类型
*
* @flow
*/
const obj1: { foo: string, bar: number } = { foo: 'string', bar: 100 }
const obj2: { foo?: string, bar: number } = { bar: 100 }
const obj3: { [string]: string } = {}
obj3.key1 = 'value1'
obj3.key2 = 'value2'
10.函数类型
/**
* 函数类型
*
* @flow
*/
function foo (callback: (string, number) => void) {
callback('string', 100)
}
foo(function (str, n) {
// str => string
// n => number
})
11.特殊类型
/**
* 特殊类型
*
* @flow
*/
// 字面量类型
const a: 'foo' = 'foo'
const type: 'success' | 'warning' | 'danger' = 'success'
// ------------------------
// 声明类型
type StringOrNumber = string | number
const b: StringOrNumber = 'string' // 100
// ------------------------
// Maybe 类型
const gender: ?number = undefined
// 相当于
// const gender: number | null | void = undefined
12.Mixed与Any
/**
* Mixed Any
*
* @flow
*/
// string | number | boolean | ....
function passMixed (value: mixed) {
if (typeof value === 'string') {
value.substr(1)
}
if (typeof value === 'number') {
value * value
}
}
passMixed('string')
passMixed(100)
// ---------------------------------
function passAny (value: any) {
value.substr(1)
value * value
}
passAny('string')
passAny(100)
今日分享截止到这里,明天更新:TypeScript部分。
记录:2020/11/10