顽皮的雪狐七七

顽皮的雪狐七七 查看完整档案

北京编辑  |  填写毕业院校  |  填写所在公司/组织填写个人主网站
编辑

学习探索使我内心平静,
头脑风暴使我激动兴奋,
我努力向前,
在看不到终点的路上,欣赏风景~

个人动态

顽皮的雪狐七七 发布了文章 · 12月1日

TypeScript(二) —— 配置文件注解

目录

  • 配置文件注解
  • 使用说明
之前我们讲了 TypeScript(一) —— 了解并快速入门 ,现在展开说明一下配置文件里面选项的含义。

编译项目的时候,可以生成一个配置文件tsconfig.json

# yarn
yarn tsc --init
# npm
tsc --init

里面属性是typescript编译器配置的一些选项,下面是一些常用的配置及其含义,之后用到什么就进行补充。

配置文件注解

{
  "compilerOptions": {
    // 设置编译后的javascript采用的标准
    "target": "es5", 
    /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */
    
    // 可以指定引用的标准库,默认是[],下面引用的是ES2015的标准库,避免Symbol和Promise的报错
    // 第二个DOM是DOM+BOM,使用console之类的用的,如果是空数组不需要写,默认就有,如果自己修改了这个数组,就要手动加上
    "lib": ["ES2015","DOM"],
    /* Specify library files to be included in the compilation. */
    
    // 输出的代码使用什么方式进行模块化,这里用的是commonJS,会把输入输出弄成require和module.export的方式
    "module": "commonjs",
    /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
    
    // 开启源代码映射,我们在调试的时候可以使用sourceMap文件去调试typescript源代码
    "sourceMap": true,
    /* Generates corresponding '.map' file. */
    
    // 设置编译结果输出的文件夹
    "outDir": "dist",
    /* Redirect output structure to the directory. */
    
    // 源代码ts文件所在的文件夹
    "rootDir": "src",
    /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
    
    /*类型检查相关 Strict Type-Checking Options */
    // 开启严格模式,对类型检查十分严格
    // 例如:any类型,也要严格写出来
    "strict": true,
    /* Enable all strict type-checking options. */
    
    // 检查变量不能为空null,可单独开启
    "strictNullChecks": true,              /* Enable strict null checks. */
  }
}

使用说明

有了配置文件之后,我们使用tsc命令编译整个项目的时候,配置文件才会生效,如果是单个文件,则不会起作用。

# yarn
yarn tsc
# npm
tsc
查看原文

赞 0 收藏 0 评论 0

顽皮的雪狐七七 发布了文章 · 12月1日

TypeScript(一) —— 了解并快速入门

之前ES6合集已经更新完毕,趁热打铁今天来更新一下TypeScript,虽然已经有很多的文档和pdf了,但是自己总结的就是自己的。

目录

  • 前言
  • 概述
  • TypeScript优缺点

    • 优势
    • 缺点
  • 起步

    • 安装
    • 创建文件
    • 执行编译命令
  • 配置文件
  • 类型标准库
  • 设置错误消息显示为中文

    • 使用VSCode设置
    • 使用命令行设置
  • 作用域问题

前言

我们之前已经讨论过了JavaScript自身类型系统的问题,如果对强弱类型以及强类型的好处有不太懂的,回顾看 JavaScript类型系统 , 也介绍了Vue2.0源码中使用过的JavaScript类型检查器Flow,回顾看 Flow(一)—— JavaScript静态类型检查器,在Vue3.0的源码中已经使用了TypeScript进行类型检查,很多的类库也使用了TypeScript,所以接下来简单的了解一下它。

概述

TypeScript是一个基于JavaScript之上的编程语言,是JavaScript的超集(superset)。和Flow一样,也是旨在解决JavaScript类型系统的问题。下图我们可以清楚的看出:JavaScriptES6TypeScript的关系

TypeScript静态类型的语言,在开发的时候使用TypeScript,但是浏览器环境是不支持TypeScript运行的,必须在生产环境进行编译成JavaScript才支持,所以需要提前进行编译。同时,TypeScript不是强类型语言,因为需要兼容JavaScript的隐式类型转换,它只是提前了类型检查的时机,并没有让类型系统本身变得更严格。

TypeScript优缺点

优势

  • 解决JavaScript类型系统不足,在开发阶段就进行类型检查,可以大大提高代码的可靠程度。
  • 渐进式开发,如果不会TypeScript,一开始使用JavaScript也是支持的,可以学一个特性用一个特性。
  • TypeScript相比较Flow的类型检查,功能更加强大,生态也更加健全、完善。
  • 逐渐应用广泛,AngularVue3.0都已经开始使用了,TypeScript已经成为前端领域的第二语言。

缺点

  • 语言本身多了很多概念,提高了学习成本
  • 项目初期,引入TypeScript会增加一些成本,还需要进行编译处理

起步

安装

npm-typescript

# yarn
yarn add typescript --dev

# npm
npm install -g typescript

安装完成后,在node-Modules/bin 目录下有tsc的文件,我们可以使用tsc去将ts文件转化成js文件

创建文件

src文件夹中添加一个后缀是.ts 的文件

// 随便写写,先按照js原生写,使用ES6新特性
const hello = (name: string) => {
  console.log(name)
}

hello('TypeScript')

执行编译命令

# yarn
yarn tsc hello-TypeScript.ts

# npm
tsc .\src\hello-TypeScript.ts

会在同名目录下添加一个编译后的js文件

// 已经全部转换成es3语法(默认是es3)
var hello = function (name) {
    console.log(name);
};
hello('TypeScript');

配置文件

编译项目的时候,可以生成一个配置文件tsconfig.json

# yarn
yarn tsc --init
# npm
tsc --init

里面属性是typescript编译器配置的一些选项,一般我们需要什么改什么即可,下面是一些常用的配置及其含义 TypeScript(二) —— 配置文件注解

有了配置文件之后,我们使用tsc命令编译整个项目的时候,配置文件才会生效,如果是单个文件,则不会起作用。

# yarn
yarn tsc
# npm
tsc

可以看到dist目录下有对应的js文件和js.map文件

类型标准库

标准库是内置文件对应的声明,配置文件中默认的版本是es3,所以类型声明类似SymbolPromise会报错,在某种情况下Arrayconsole也可能会报错,这个时候要让程序认识那些个类型,在VSCode中,类型右键跳转定义可以看到lib文件夹里面有很多内置对象的定义,这就是TypeScript标准库,以Symbol为例:

例如:Symbol,是ES6的语法才支持的,这个时候有两种解决方案

  • 需要将配置文件中的target改为es2015
  • 不改target,将lib选项改为["ES2015"]
单独设置这个,console的定义会报错,默认引用的DOM类库被覆盖,需要加上"DOM",这里的DOM是包含了DOM+BOM

如果下次有遇到类似的错误,可以找到其引用的标准库,然后在配置文件中引用。

设置错误消息显示为中文

开发小技巧,可以让TypeScript显示错误消息为中文。

使用VSCode设置

设置 -> 输入typescript locale -> TypeScript:Locale -> zh-CN

使用命令行设置

# yarn
yarn tsc --locale zh-CN
# npm
tsc --locale zh-CN

# 那反设置成英文模式
tsc --locale en

作用域问题

如果在项目中执行ts文件,那么不同文件中的相同变量是会报错的,为了避免这种问题,要进行作用域的处理

// 解决方法一:每个文件使用立即执行函数包裹
(function () {
   const a = 123 
})()

// 解决方法二:使用export导出

const a = 123
export {}
查看原文

赞 0 收藏 0 评论 0

顽皮的雪狐七七 发布了文章 · 11月30日

Flow(二)—— 简单语法使用

目录

  • Flow类型推断
  • Flow类型注解使用的地方
  • Flow Type Annotations

    • 类型参考网址
    • 原始类型
    • 数组类型

      • 元组 —— Tuple Types
    • 对象类型

      • 通用写法
      • 添加可选属性
      • Map类
      • 混合使用
    • 函数类型

      • 函数参数
      • 可选函数参数
      • Rest参数
      • 函数返回
    • 特殊类型

      • 字面量类型
      • maybe类型
    • Mixed 与 Any

      • Mixed
      • Any
      • 两者的区别
  • Flow运行环境API
之前写了Flow的相关介绍,如果想回顾的参考 Flow(一)—— JavaScript静态类型检查器 ,这里简单的介绍Flow的语法,了解Flow的意义是在第三方源码中可以看到Flow的使用情况,可以帮助我们更好的了解源码。

Flow类型推断

没有执行变量类型,但是可以根据代码的使用情况推断类型,就叫类型推断

// @flow
// 因为字符串不能进行乘法运算,所以也会报错
 function square (n) {
   return n * n
 }

 square('100')

不过虽然有类型推断,但是建议开发者每个都添加类型,这样有可读性。

Flow类型注解使用的地方

绝大多数flow都可以推断出变量类型,但是并不意味着我们不需要给每个变量添加类型注解。添加类型注解可以明确去限制类型,而且对我们之后理解这里的代码很有帮助,建议尽可能的去使用类型注解

  • 类型可以标注的地方

    • 函数参数
    • 变量
    • 函数返回值
// @flow
// 函数参数标注类型注解
function square (n: number) {
   return n * n
}

// 变量标注类型注解
let num: number = 100

// 函数返回值标注类型注解
function foo (): number {
   return 100
}
// 上面那种情况,如果没有返回值,默认返回undefind,上面也会报错
// 所以如果没有返回值,需要将返回类型标记为void
function foo1 (): void {
   
}

Flow Type Annotations

类型参考网址

原始类型

// @flow

 // 字符串
 const a: string = 'foobar'

 // 数字
 const b1: number = 100
 const b2: number = NaN
 const b3: number = Infinity // 无穷大

 // 布尔
 const c1: boolean = true
 const c2: boolean = false

 // null
 const d: null = null

 // undefined
 const e: void = undefined

 // symbol
 const f: symbol = Symbol()

数组类型

// @flow
// 写法一:Array后面要添加泛型参数,number指定全部由数字组成的数组,如果有其他类型就会报错
 const arr1: Array<number> = [1, 2, 3]
 const arr2: Array<mixed> = [1, true, "three",{a:'1',b:2}]
// 写法二:
 const arr3: number[] = [1, 2, 3]

除了这种数组写法,还有一种特殊的固定长度的数组,我们称为 —— 元组

元组 —— Tuple Types

  • 固定长度数组,如果改变长度会报错
  • 下标对应的元素必须是规定类型,设置新值得时候也必须匹配
  • 元组不匹配Array类型,因为数组类型长度不确定
  • 不能再元组上使用变异数组的方法,例如:copyWithinfillpoppushreverseshiftsortspliceunshift
// @flow
// 元组 —— 固定长度的数组
 // 下面的数组规定了两个元素,如果改变长度就会报错,而且下标对应的元素必须是规定的类型
 const arr4: [string, number] = ['foo', 100]
 arr4[2] = 1 // Cannot assign `1` to `arr3[2]` because  tuple type [1] only has 2 elements, so index 2 is out of bounds.
 const item0: string = arr4[0] // Works!
 const item1: number = arr4[0] // Cannot assign `arr3[0]` to `item1` because  string [1] is incompatible with  number [2]

对象类型

通用写法

确定一个对象中键值有哪些,并且每个是什么类型

// @flow
 const obj1: { foo: string, bar: number} = { foo: 'string', bar: 100}
 
// 如果访问了obj1中没有的属性,原来会返回undefined,现在直接当做类型报错
 obj1.baz // Cannot get `obj1.baz` because property `baz` (did you mean `bar`?) is missing in  object type [1]

添加可选属性

可选属性可以是undefined或者省略,但是不能是null

 // foo如果可有可无,那么在foo后面加一个问号
 // 可选属性可以是undefined或者省略,但是不能是null
 const obj2: { foo?: string, bar: number} = { bar: 100}
 obj2.foo = undefined // Works!
 obj2.foo = null // Cannot assign `null` to `obj2.foo` because  null [1] is incompatible with  string [2]

Map类

键的类型用方括号

 // 初始化为空,可以自己添加键值对,规定键是string类型,值也是string类型
 const obj3: { [string] : string } = {}

 obj3.key1 = 'value1'
 obj3.key2 = 100 // annot assign `100` to `obj3.key2` because  number [1] is incompatible with  string [2]

混合使用

Map类和普通可以混合使用

// @flow
var obj: {
  size: number,
  [id: number]: string
} = {
  size: 0
};

function add(id: number, name: string) {
  obj[id] = name;
  obj.size++;
}

函数类型

一般是指参数类型和返回值类型进行类型注解

函数参数

// @flow

// 参数输入确定类型
 function square (n: number) {
   return n * n
 }

可选函数参数

function func1 (num?: number) {
    const n = num ? num : 1
    console.log(n)
 }

 func1()  // 1  可以接受undefined,不能接受null
 func1(2) // 2
 func1(null) // Error!

Rest参数

// @flow
function method(...args: Array<number>) {
  // ...
}

method();        // Works.
method(1);       // Works.
method(1, 2);    // Works.
method(1, 2, 3); // Works.

函数返回

// 返回值确定类型
 // 有返回值
 function foo (): number {
   return 100
 }
 // 无返回值
 function foo1 (): void {
   
 }

// 回调函数参数和返回值类型 
 function func (callback: (string, number) => void) {
   callback('string', 100)
 }

 func(function (str, n) {
   // str => string
   // n => number
   // 无返回值
 })

特殊类型

字面量类型

与传统类型不同,这种字面量类型必须限制变量必须是某个值,一般不会单独使用,会配合 联合类型 去组合几个特性的值

// @flow
// 下面定义了n字面量,值只能是存放foo字符串,不能换成别的字符串和别的类型
const n: 'foo' = 'foo'
// 只能是下面三个字符串类型中的一种(下面的就是联合类型,也成或类型)
const type : 'success' | 'warning' | 'danger' = 'success'
// b变量既可以是string也可以是number,可以是字符串也可以是数字
const b: string | number = 'string' // 100 


// 也可以自己定义一个类型,StringOrNumber是一个类型的别名
type StringOrNumber = string | number
const test: StringOrNumber = 'string' // 100

maybe类型

有可能,在基本类型的基础上扩展了nullundefined的类型

// @flow
// 添加?可以使用null和undefined
const gender: ?number = null
const gender1: ?number = undefined
const gender2: ?number = 100

// 相等于下面的number或null或undefined
const gender: number | null | void = undefined

Mixed 与 Any

Mixed

Mixed可以接收任意类型的值,是所有类型的联合类型string | number | boolean | ...

// 参数是mixed类型
function passMixed (value: mixed) {
  console.log(value)
}

passMixed('string') // string
passMixed(100) // 100

Any

Mixed一样,可以接收任意类型的值

function passAny (value: any) {
    console.log(value)
}

passAny('string') // string

passAny(100) // 100

两者的区别

  • Mixed是一个强类型,如果有使用隐患的话就会报错,只能用typeof进行类型判断
  • Any是一个弱类型,如果有使用隐患,语法上不会报错。
  • Mixed是安全的(推荐使用),Any是不安全的,存在的意义是为了兼容老代码
// Mixed
// 如果没有明确这个变量是字符串还是数字,那么不能直接进行使用的,会报错
function passMixed (value: mixed) {
  console.log(value)
  value = value ** 2 // Cannot perform arithmetic operation because  mixed [1] is not a number.
}

// 如果想要 解决上面的问题,需要使用typeof进行类型判断
function passMixed (value: mixed) {
  if (typeof value === 'string') {
    value.substr(1)
  }
  if (typeof value === 'number') {
    value ** 2
  }
}
// Any
// 下面语法上是不会报错的, 运行阶段不确定
function passAny ( value: any) {
  value = value ** 2
}

Flow运行环境API

JavaScript需要运行在某个环境中,例如:浏览器环境或者node环境。
他们有本身自己的API,如浏览器中的DOMBOMnode中的path等,我们在flow中也会使用到这些对象。

那么这些有特殊的类型限制,例如:

document.getElementById() //里面参数传字符串,数字会报错
// 这是浏览器环境内置的API的一些限制
document.getElementById('app') //返回对应的类型是HTMLElement
// 如果没有找到对应元素,也返回null类型,那么接收的时候可以这么写
const element: HTMLElement | null = document.getElementById('app')

右键跳到定义可以看到,原生里面有定义

官网仓库给出了一些类型声明,开发的时候可以参考使用

查看原文

赞 0 收藏 0 评论 0

顽皮的雪狐七七 发布了文章 · 11月30日

Flow(一)—— JavaScript静态类型检查器

目录

  • Flow概述
  • Flow VS. TypeScript
  • Flow安装
  • Flow使用

    • 1.在文件开头要使用注释
    • 2.在代码中使用类型注解
    • 3.在package.json中添加,
    • 4.初始化flow
    • 5.执行flow命令进行检查
    • 6.关闭flow命令
  • Flow编译

    • 1.官网提供flow-remove-types

      • npm环境
      • yarn环境
    • 2.Babel
  • Flow开发工具插件

    • VSCode —— Flow Language Support(Flow 官方提供的)
    • 其他的编辑器
  • Flow语法使用

Flow概述

w3cschool-flow官方参考文档

FlowJavaScript的静态类型检查器,是通过静态类型推断实现类型检查的。是2014年由FaceBook推出的一款工具,使用它就可以弥补JavaScript弱类型带来的一些弊端。可以说它为JavaScript提供了更完善的类型系统。在ReactVue的项目中,都看到Flow的使用。

JavaScript本身是动态类型检查的语言,代码在执行的过程中检查类型是否正确,C#Java都是静态类型检查,在代码编译的时候检查类型是否正确。使用flow就可以让JavaScript有像C#Java一样的开发体验。

通过在代码中添加注解的方式,标记每个变量或者参数是什么类型的,然后Flow根据这些注解就可以检查代码异常,从而实现在开发阶段类型异常的检查。

  • 参数的冒号称为类型注解,在发布的时候会通过Babel将注解消除,所以生产环境不会产生影响。
  • 不要求所有的变量和参数都加注解,不加的为any类型。
function sum (a: number, b: number) {
    return n * n
}

sum(100, 50)
sum('100', 50) // 可以检测异常

Flow VS. TypeScript

FlowTypeScript都是静态类型检查器,TypeScript的功能更加强大,但他们都是JavaScript的超集,基于JavaScript,最终都要编译成JS运行。一般大型项目都需要静态类型检查来确保代码的可维护性和可读性,Vue2.0的源码引入了flowflow可以让代码在最小改动的情况下使用动态类型检查,而在Vue3.0中已经使用TypeScript开发了。

Flow安装

npm flow-bin

npm init -y

npm i flow-bin --dev

可以在node_modules.bin目录中看到flow,我们可以在命令行中执行flow,作用就是项目中检查代码中类型异常。

Flow使用

1. 在文件开头要使用注释

@flow的标记,这样flow才会检查这个文件。

2. 在代码中使用类型注解

PS: 这里看到VSCode中会有js的语法校验,需要手动关闭

设置 -> javascript valida -> 关闭JavaScript验证

2. 在package.json中添加,

"scripts": {
    "flow": "flow"
}

如果是yarn直接运行yarn flow即可,也可以不设置scripts直接npx flow运行

3. 初始化flow

运行下面的语句,会再同级目录下生成一个.flowconfig的配置文件

npm run flow init

# > TypeScript@1.0.0 flow E:\professer\TypeScript
# > flow "init"

4. 执行flow命令进行检查

npm run flow

# > TypeScript@1.0.0 flow E:\professer\TypeScript
# > flow

# Launching Flow server for E:\professer\TypeScript
# Spawned flow server (pid=5816)
# Logs will go to C:\Users\AppData\Local\Temp\flow\EzCzBprofesserzBlagouzBTypeScript.log
# Monitor logs will go to C:\Users\AppData\Local\Temp\flow\EzCzBprofesserzBlagouzBTypeScript.monitor_logNo errors!

5. 关闭flow命令

npm run flow stop

# > TypeScript@1.0.0 flow E:\professer\TypeScript
# > flow "stop"

# Trying to connect to server for `E:\professer\TypeScript`
# Told server for `E:\professer\TypeScript` to die. Waiting for confirmation...
# Successfully killed server for `E:\professer\TypeScript`

Flow编译

因为flow的类型注解不是javascript的标准语法,所以不能直接运行。我们需要将代码转换成浏览器可以执行的原始的JavaScript语言运行。所以我们可以在完成编码过后,移除掉我们添加的注解。

1. 官网提供flow-remove-types

npm flow-remove-types

npm环境

1) 安装

npm i flow-remove-types --dev

2) package.json中修改配置

# 把src目录下的编译之后转到dist目录下
"scripts": {
    "flow": "flow",
    "flowRemove": "flow-remove-types src/ -d dist/"
}

3) 运行

npm run flowRemove

可以看到在dist目录下有了编译之后的js文件

yarn环境

1) 安装

yarn add flow-remove-types

2) 运行

# 第一个是指定目录,-d 之后是输出目录
yarn flow-remove-types src -d dist/

2. Babel

1) 安装

yarn环境

# @babel/cli我们可以直接使用babel命令完成编译
# @babel/perset-flow包含了我们转换flow类型检查的插件
yarn add @babel/core @babel/cli @babel/perset-flow

npm环境

npm i @babel/core @babel/cli @babel/perset-flow --dev

2) 添加配置文件

同级目录添加.babelrc文件,添加配置

{
  "presets": ["@babel/preset-flow"]
}

3) 使用

yarn环境

yarn babel src -d dist

npm环境

package.json中添加

"scripts": {
    "babel": "babel src/ -d dist/"
}

运行babel

npm run babel

Flow开发工具插件

异常不在终端显示,开发工具中直接显示。

VSCode —— Flow Language Support(Flow 官方提供的)

可以在代码中直接看到并修改

其他的编辑器

flow-editors

Flow语法使用

Flow(二)—— 简单语法使用

查看原文

赞 0 收藏 0 评论 0

顽皮的雪狐七七 发布了文章 · 11月30日

JavaScript类型系统

目录

  • 类型系统

    • 类型安全 —— 强类型 VS. 弱类型

      • 两者之间的区别
    • 类型检查 —— 静态类型 VS. 动态类型

      • 两者之间的区别
  • JavaScript类型系统的特征
  • 为什么JS是弱类型且动态类型呢?

    • 为什么需要类型检查呢?
    • 弱类型的不足
    • 强类型的优势
  • JavaScript自有类型系统问题的解决方案
  • 关于查资料对于编译型语言和解释型语言的知识点补充

    • 编译型语言
    • 解释型语言

这篇文章要先讨论一些概念,这些概念我们在开始学习JavaScript的时候就知道了,JavaScript是一个弱类型且动态类型的语言,那么这些概念具体这里做了整理。之后还要重点讨论的是 JavaScript自有类型系统的问题,以及如何借助一些优秀的技术方案,解决这些问题。

类型系统

我们经常用两个维度去描述一个编程语言的特性,这两个维度不要混淆,强类型并不是动态类型...

  • 强类型与弱类型,这是从类型安全的维度分类
  • 静态类型与动态类型,这是从类型检查的维度分类

类型安全 —— 强类型 VS. 弱类型

强类型 :要求语言层面限制函数的实参类型必须与形参类型相同。

弱类型 : 语言层面不会限制实参的类型。

下面举个例子:

// Java
class Main {
    // 这里定义了传入的参数是int类型,那么实际的时候也应该是int类型
    static void foo(int num) {
        System.out.printIn(num);
    }
    
    public static void main(Sting[] args) {
        // 下面的如果int类型就通过,如果不是int类型就会报错
        Main.foo(100); // ok
        Main.foo('100'); // error "100" is a string
        Main.foo(Integer.parseInt("100")); // ok
    }
}
// ---------------------------------------------------
// JavaScript
// 传的时候没有规定是什么类型,那么实参是什么类型都不会报错
function foo(num) {
    console.log(num)
}

foo(100) // ok
foo('100') // ok
foo(parseInt('100')) // ok
由于这种强弱类型之分根本不是某一个权威机构的定义,所以之后的人们对制定的细节出现了不一样的理解。大致也就是说强类型有更强的类型约束,而弱类型中几乎没有什么约束。

两者之间的区别

强类型语言中不允许有任何的隐式类型转换,而弱类型语言则允许任意的数据隐式类型转换。

// JavaScript
// js报的错误都是在代码层面,运行的时候通过逻辑判断手动抛出的,并不是语法层面的类型限制
// 下面'100'是字符串,在做减法的时候进行了隐式类型转换,转换成了Number类型,最后得到的结果是50,Number类型。
> '100' - 50
50
// 下面字符串进行隐式类型转换,不是数字的成为NaN(不是数字)
> Math.floor('foo')
NaN
// 布尔值进行隐式类型转换,转成了数字1
> Math.floor(true)
1
# python
# 这里无法进行隐式类型转换,会在语法层面上报类型错误
> '100' - 50
TypeError: unsupported operand type(s) for -: 'str' and 'int'
> abs('foo')
TypeError: bad operand type for abs(): 'str'

类型检查 —— 静态类型 VS. 动态类型

都比较统一,没什么争议

静态类型 :一个变量声明时它的类型就是明确的,声明过后,类型不能修改。

动态类型 :运行阶段才可以明确变量的类型,而且变量的类型随时可以改变。所以动态类型语言中的变量没有类型,变量中存放的值时有类型的。

// Java
class Main {
    public static void main(String[] args) {
        // 一开始就定了num的类型是int,如果修改也只能修改成int类型,如果修改成string就会报错
        int num = 100;
        num = 50; // ok
        num = '100' // error
        System.out.printInt(num);
    }
}

// JavaScript
// 可以随意修改num的类型
var num = 100
num = 50 // ok
num = '100' // ok
num = true // ok
console.log(num)

两者之间的区别

静态类型不能修改变量的类型,动态类型可以随时去修改变量的类型。

JavaScript类型系统的特征

JavaScript是弱类型且动态类型的语言,灵活多变,可以进行 隐式转换 ,也可以进行 类型推断 ,但是缺失了类型系统的可靠性。

为什么JS是弱类型且动态类型呢?

  • 早前的JavaScript应用简单,所以并没有复杂的类型系统
  • JavaScript是脚本语言,没有编译环节,所以设计成静态语言是没有意义的

为什么需要类型检查呢?

  • 因为现在的JavaScript应用越来越复杂,开发周期也越来越长,越来越大的项目几百行代码已经不满足现状了,所以现在弱类型已经成为了JavaScript的短板。
  • 这些东西只能通过约定去规避问题,但是在大型项目中通过人为约定存在隐患。

弱类型的不足

  1. 只有在运行阶段才能发现代码的异常,代码没有执行的时候也无法发现代码异常,在隐藏比较深的情况下,测试不能百分百覆盖。
const obj = {}
obj.foo()  // TypeError: obj.foo is not a function

// 下面这个延时器,在时间到了之后才会运行,给测试带来麻烦
setTimeout(() => {
    obj.foo()
}, 100000)
  1. 函数参数类型不确定,输入的结果有偏差
// 不明确是数字,所以结果不一样
function sum (a, b) {
    return a + b
}

console.log(sum(100, 100)) // 200
console.log(sum(100, '100')) // 100100
  1. 隐式类型转换在对象属性名转化成字符串,里面的内容会有很多的问题
// 定义一个字符串,对象的属性名不能是对象,会自动转换成字符串,如果不满足就会有问题
const obj = {}
obj[true] = 100
obj[{name:1}] = 1
console.log(obj) // {true: 100, [object Object]: 1}
console.log(obj['true']) // 100
console.log(obj["[object Object]"]) // 1

强类型的优势

  1. 错误在开发阶段就会暴露,越早发现越好
  2. 代码更智能,编码更准确(开发工具的智能提示因为有变量类型才有提示)
  3. 重构更牢靠(如果项目中很多地方用到的成员,需要删除成员或者修改成员名称的时候,弱类型语言会有隐患)
  4. 减少不必要的类型判断
function sum (a, b) {
    if (typeof a !== 'number' || typeof b !== 'number') {
        throw new TypeError('arguments must be a number')
    }
    return a + b
}

JavaScript自由类型系统问题的解决方案

这里之后会进行内容补充

  • flow —— JavaScript静态类型检查器
  • TypeScript —— JavaScript超集

关于查资料对于编译型语言和解释型语言的知识点补充

编译型语言

使用专门的编译器,针对特定的平台,将高级语言源代码一次性的编译成可被该平台硬件执行的机器码,并包装成该平台所能识别的可执行性程序的格式。

编译型语言一次性的编译成平台相关的机器语言 文件,运行时脱离开发环境,与特定平台相关,一般无法移植到其他平台,现有的C、C++、Objective等都属于编译型语言。

解释型语言

使用专门的解释器对源程序逐行解释成特定平台的机器码并立即执行。是 代码在执行时才被解释器一行行动态翻译成机器语言和执行,而不是在执行之前就完成翻译。

解释型语言每次运行都需要将源代码解释称机器码并执行,只要平台提供相应的解释器,就可以运行源代码,Python、Java、JavaScript等属于解释型语言。

查看原文

赞 0 收藏 0 评论 0

顽皮的雪狐七七 发布了文章 · 11月26日

Rollup —— 适合框架和类库使用的模块打包器

目录

  • Rollup概述

    • Rollup vs Webpack
  • 快速上手
  • 配置文件
  • 插件

    • rollup-plugin-json
    • rollup-plugin-node-resolve(加载npm模块)
    • rollup-plugin-commonjs
  • Code Splitting(代码拆分)

    • Dynamic Imports(动态导入)
  • 多入口打包

    • amd输出格式要注意什么?
  • 指令参数参考大全

这篇文章目前旨在简单了解Rollup是什么并且如何上手操作,之后会再进行深入分析。

Rollup概述

Rollup仅仅是一款JavaScript 模块打包器,也称为ESM打包器,并没有像webpack那样有很多其他额外的功能,它可以将项目中散落的细小模块打包成整块的代码,可以让他们更好的运行在浏览器环境 or Node.js环境 ,目前Vue2.0源码使用的打包器就是Rollup

Rollup vs Webpack

RollupWebpack作用类似,但是Rollup更为小巧,webpack可以在前端开发中完成前端工程化的绝大多数功能,而Rollup仅仅是一款ESM打包器,并没有其他额外的功能。

Rollup中并不支持类似HMR这种高级特性。但是Rollup诞生的目的并不是要与webpack全面竞争,其初衷只是提供一个高效的ES Modules的打包器,充分利用ESM的各项特性构建出结构比较扁平,性能比较出众的类库。

快速上手

  1. 下载模板 Rollup-firstdemo-temp
  2. 安装模块npm i rollup -g
  3. 在命令行中执行rollup ./src/index.js --format iife --file dist/bundle.js可以看到在输出目录中有了一个文件夹,里面的文件输出的文件很干净整洁,并且没有引用的模块并没有打包进去(自带Tree-shaking
  • --format —— 指定输出文件打包格式,例如:iife是自调用函数
  • --file —— 输出文件,后面跟打印路径,不写会打印到控制台

配置文件

创建名称为rollup.config.js文件,同样运行在node.js环境中,因为Rollup会单独处理这个文件,所以我们可以直接使用ES Module

  1. 在文件中编辑
// rollup.config.js
// 这个文件中会导出一个配置对象
export default {
  // input 是打包入口文件路径
  input: 'src/index.js',
  // 输出配置
  output: {、
    // 输出路径及文件名
    file: 'dist/bundle.js',
    // 输出格式
    format: 'iife'
  }
}
  1. 使用命令行要添加--config说要使用配置文件rollup --config,默认是不使用配置文件的。
  2. rollup --config <filename>后面可以指定配置文件的名称,默认是rollup.config.js,也可以自己指定别的文件名。

插件

如果要加载其他类型的资源文件,或者是导入CommonJS模块,或者编译ES6新特性,Rollup同样支持使用插件的方式扩展。

插件是Rollup唯一扩展途径,这个与webpack有所不同,webpackpluginsmoduleoptimization三种途径。

rollup-plugin-json

rollup-plugin-json是一个导入JSON文件的插件。

  1. 安装插件 npm i rollup-plugin-josn --save-dev
  2. rollup-plugin.js中配置插件
// 默认导出是一个插件函数
import json from 'rollup-plugin-json'
export default {
  input: 'src/index.js',
  output: {
    file: 'dist/bundle.js',
    format: 'iife'
  },
  plugins: [
    // 是调用结果放在数组中,而不是函数放进去
    json()
  ]
}
  1. src/index.js中调用,打包之后可以看到json里面的变量已经打包进来了。
// 导入模块成员
import { log } from './logger'
import { name, version} from '../package.json'

log(name) // var name = "first";
log(version) // var version = "1.0.0";

rollup-plugin-node-resolve(加载npm模块)

rollup默认只能按照路径的方式加载本地模块,对于第三方模块并不能想webpack一样通过名称导入,所以需要通过插件处理。

  1. 安装插件 npm i rollup-plugin-node-resolve --save-dev
  2. rollup-plugin.js中配置插件
// 默认导出是一个插件函数
import json from 'rollup-plugin-json'
import resolve from 'rollup-plugin-node-resolve'
export default {
  ...
  plugins: [
    // 是调用结果放在数组中,而不是函数放进去
    json(),
    resolve()
  ]
}
  1. 准备一个第三方模块进行安装npm i lodash-es
  2. src/index.js中调用,可以看到lodash-es的相关代码也导入进去了
// 导入模块成员,这里可以使用node名称来导入而不是路径
import _ from 'lodash-es'
import { log } from './logger'

log(_.camelCase('hello world'))
PS:
这里使用lodashES Modules版本而不是其他版本的原因是因为rollup默认只能处理ES Modules模块,如果要引用其他版本我们需要做额外的处理。

rollup-plugin-commonjs

加载CommonJS模块,目前还是有大量的npm模块使用CommonJS的方式导入成员,为了兼容就有了这个官方插件。

  1. 安装插件npm i rollup-plugin-commonjs --save-dev
  2. rollup-plugin.js中配置插件
import commonjs from 'rollup-plugin-commonjs'
export default {
  ...
  plugins: [
    commonjs()
  ]
}
  1. 在src中新建文件cjs.module.js,编写文件
module.exports = {
  foo: bar
}
  1. src/index.js中引入,可以看到变量已经打包进去。
// 导入模块成员
import { log } from './logger'
import cjs from './cjs.module'

log(cjs)

/* var cjs_module = {
    foo: bar
  };
*/  

Code Splitting(代码拆分)

Dynamic Imports(动态导入)

动态导入,rollup内部会自动处理代码分包,
代码拆分

  1. src/index.js中引入
// import函数返回一个promise对象
// then方法参数是module,由于模块导出的成员都会放在module对象中,所以可以通过解构的方式提取log
import('./logger').then(({ log }) => {
  log('code splitting~')
})  
  1. 修改roll.config.jsoutput里面的配置
export default {
  // input 是打包入口文件路径
  input: 'src/index.js',
  // 输出配置
  output: {
    // 输出目录名称
    dir: 'dist',
    // 输出格式
    format: 'amd'
  }
}
不修改配置文件直接运行rollup --config会报错

UMDiife 是不支持代码拆分方式格式,因为自执行函数会把所有的模块都放到一个函数中,并没有像webpack一样有一些引导代码,所以没有办法做到代码拆分

如果要使用代码拆分,就需要使用AMD or CommonJS等方式。在浏览器中只能使用AMD的方式,所以这里改用输出格式为AMD

况且我们拆分代码输出不同的文件,file属性只是针对一个文件,所以我们需要改用dir去指定文件夹名称,不然还是会报错

  1. 运行代码rollup --config可以看到dist文件夹里面有两个拆分打包的文件。

多入口打包

rollup支持多入口打包,对于不同文件的公共部分也会自动提取到单个文件中作为独立的bundle.js

  1. 模板中将多入口打包的代码开启,可以看到albumindex都引用了fetch.jslogger.js的代码,我们对rollup.config.js进行修改
export default {
  // 这里input要改成数组形式或者对象形式,对象形式可以修改打包的文件名,键对应的就是打包的文件名
  // input: ['src/index.js', 'src/album.js'],
  input: {
    indexjs: 'src/index.js',
    albumjs: 'src/album.js'
  },
  // 输出配置要改成拆分包的配置,以为多入口打包默认会执行打包拆分的特性。所以输出格式要改成amd
  output: {
    dir: 'dist',
    format: 'amd'
  }
}
  1. 命令行执行rollup --config 可以看到dist里面生成了三个文件,其中两个文件打包和一个公共模块的打包,里面包含了loggerfetch模块

amd输出格式要注意什么?

对于amd输出格式的打包文件是不能直接引用到页面上,必须通过实现AMD标准的库去加载。

尝试使用一下

  1. dist下面生成一个HTML文件,尝试引入requirejs
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
 <!--requirejs的cdn地址,data-main是入口文件的名称-->
  <script data-original="https://unpkg.com/requirejs@2.3.6/bin/r.js" data-main="albumjs.js"></script>
</body>
</html>
  1. 浏览器打开可以看到内容正常引入,控制台也正常工作。

指令参数参考大全

  • --format —— 指定输出文件打包格式,例如:iife是自调用函数
  • --file —— 输出文件,后面跟打印路径,不写会打印到控制台
  • --config —— 指定使用配置文件,后面可以加指定配置文件的名称,rollup --config <filename>,默认是rollup.config.js
查看原文

赞 0 收藏 0 评论 0

顽皮的雪狐七七 发布了文章 · 11月19日

JavaScript异步编程

目录

  • JavaScript采用单线程模式工作的原因
  • 单线程的优势和弊端
  • 同步模式与异步模式

    • 同步模式
    • 异步模式
    • 同步模式API和异步模式API的特点
  • 实现JavaScript异步编程的几种方式

    • 回调函数 —— 所有异步编程方案的根基
    • Promise —— 更优的异步编程解决方案
    • Generator
    • async/await —— 异步操作的终极解决方案

JavaScript采用单线程模式工作的原因

最早JavaScript语言就是运行在浏览器端的语言,目的是为了实现页面上的动态交互。实现页面交互的核心就是DOM操作,这就决定了它必须使用单线程模型,否则就会出现很复杂的线程同步问题。

假设在js中有多个线程一起工作,其中一个线程修改了这个DOM元素,同时另一个线程又删除了这个元素,此时浏览器就无法明确该以哪个工作线程为准。所以为了避免线程同步的问题,从一开始,js就设计成了单线程的工作模式。

所以,js是单线程工作模式,如果有多个任务,任务需要排队,一个一个依次去执行。

单线程的优势和弊端

  • 优势:更安全,更简单
  • 弊端:效率低,有些可以同时执行的任务必须等待。如果中间有一个特别耗时的任务,其他的任务就要等待很长的时间,会出现假死的情况。

为了解决这种问题,js有两种任务的执行模式:

同步模式(Synchronous)异步模式(Asynchronous)

同步模式与异步模式

同步模式

指的是代码的任务依次执行,后一个任务必须等待前一个任务结束才能开始执行。程序的执行顺序和代码的编写顺序是完全一致的。在单线程模式下,大多数任务都会以同步模式执行。

console.log('global begin')
function bar () {
    console.log('bar task') 
}
function foo () {
    console.log('foo task')
    bar()
}
foo()
console.log('global end')

// global begin
// foo task
// bar task
//global end

// 使用调用栈的逻辑

为了避免耗时函数让页面卡顿和假死,所以还有异步模式。

异步模式

该模式不会去等待这个任务的结束才开始下一个任务,都是开启过后就立即往后执行下一个任务,此时异步线程会单独执行异步任务,耗时函数的后续逻辑会通过回调函数的方式定义,执行过后会将回调放到消息队列中,js主线程执行完任务过后会依次执行消息队列中的任务。这里要强调,js是单线程的,浏览器不是单线程的,有一些API是有单独的线程去做的。

下面看一个简单的异步模式的例子:

console.log('global begin')
// 延时器
setTimeout(function timer1 () {
    console.log('timer1 invoke')
}, 1800)
// 延时器中又嵌套了一个延时器
setTimeout(function timer2 () {
    console.log('timer2 invoke')
    setTimeout(function inner () {
        console.log('inner invoke')
    }, 1000)
}, 1000)
console.log('global end')

// global begin
// global end
// timer2 invoke
// timer1 invoke
// inner invoke

//除了调用栈,还用到了消息队列和事件循环

异步模式对于JavaScript语言非常重要,没有它就无法同时处理大量的耗时任务。对于开发者而言。单线程下面的异步最大的难点就是代码执行的顺序混乱 ,所以面试题里面百分百会考这里的内容 - -|||

同步模式API和异步模式API的特点

同步模式的API的特点:任务执行完代码才会继续往下走,例如:console.log

异步模式的API的特点:下达这个任务开启的指令之后代码就会继续执行,代码不会等待任务的结束

实现JavaScript异步编程的几种方式

回调函数 —— 所有异步编程方案的根基

回调函数:由调用者定义,交给执行者执行的函数

// callback就是回调函数
// 就是把函数作为参数传递,缺点是不利于阅读,执行顺序混乱。
function foo(callback) {
    setTimeout(function(){
        callback()
    }, 3000)
}

foo(function() {
    console.log('这就是一个回调函数')
    console.log('调用者定义这个函数,执行者执行这个函数')
    console.log('其实就是调用者告诉执行者异步任务结束后应该做什么')
})

还有其他的一些实现异步的方式,例如:事件机制和发布订阅。这些也都是基于回调函数之上的变体。

Promise —— 更优的异步编程解决方案

主要为了解决回调地狱问题,详细了解参考 Promise(更优的异步编程解决方案)

Generator

详细了解参考 Generator -> Generator异步方案

async/await —— 异步操作的终极解决方案

详细了解参考 async&await -> async/await处理多回调异步

查看原文

赞 5 收藏 3 评论 0

顽皮的雪狐七七 发布了文章 · 11月19日

nodemon —— Node服务自动重启实时刷新

目录

  • 起步
  • 使用

nodemon作用就是实时更新,不用每次都运行一个node <filename>

起步

npm i -g nodemon

使用

nodemon <filename>

举个例子:

// 00.js
console.log("test")
nodemon .\00-parpare.js

# [nodemon] 2.0.4
# [nodemon] to restart at any time, enter `rs`
# [nodemon] watching path(s): *.*
# [nodemon] watching extensions: js,mjs,json
# [nodemon] starting `node .\00-parpare.js`
# test    ------>输出结果

修改00.js,无需重启

console.log("test123")
# [nodemon] clean exit - waiting for changes before restart
# [nodemon] restarting due to changes...
# [nodemon] starting `node .\00-parpare.js`
# test123   ------>输出结果
# [nodemon] clean exit - waiting for changes before restart
查看原文

赞 1 收藏 0 评论 0

顽皮的雪狐七七 发布了文章 · 11月12日

ES6-ES10知识整合合集

目录

  • ECMAScript
  • ES2015
  • 新特性的分类
  • ES6-ES10学习版图
  • 基本语法链接整合

历经两个月,终于把所有的ES6-ES10的知识点都发布完成了,这里进行一个小的整合,以后方便查阅资料用。
这些东西打印出来A4纸也有120多页,一本小书的样子( ̄▽ ̄)/

有些东西遇到了网上查和自己整理一遍感觉还是不一样的,也希望自己可以慢慢有一种写作整理的习惯。语法是基础,还是要整体过一遍的,遇到了之后再查,心里没数都不知道从哪里查起。所以将每个部分做了个分类展示,这样查起来也好查✧(^_-✿

还是要对ECMAScript进行一下知识整理的

ECMAScript

ECMAScript通常看做JavaScript的标准化规范,实际上JavaScriptECMAScript的扩展语言,ECMAScript只是提供了最基本的语法。

每个前端人烂熟于心的公式:

JavaScript = ECMAScript + BOM + DOM

ES2015

  • 2015年开始保持每年一个版本的迭代,并且开始按照年份命名。
  • 相比于ES5.1的变化比较大
  • 自此,标准命名规则发生变化
  • ES6泛指是2015年之后的所有新标准,特指2015年的ES版本,以后要看清楚是特指还是泛指

新特性的分类

  • 解决原有语法上的一些问题或者不足
  • 对原有语法进行增强
  • 全新的对象、全新的方法、全新的功能
  • 全新的数据类型和数据结构

ES6-ES10学习版图

ES6-ES10学习版图

基本语法链接整合

ES6

ES7

ES8

ES9

ES10

查看原文

赞 63 收藏 55 评论 2

顽皮的雪狐七七 发布了文章 · 11月12日

ES6(十七)—— Symbol

目录

  • 简介
  • 为什么要有这个数据类型
  • 作用

    • 如果想用toString方法,又怕和原生的冲突,可以用Symbol
    • 更适合做私有属性,因为普通的遍历是访问不到的
    • 目前最主要的作用就是为对象添加独一无二的属性名
  • 使用symbol
  • 取值
  • ES6-ES10学习版图

简介

一种全新的原始数据类型,js的基本数据类型。**不那么深入,就先简单记个笔记

为什么要有这个数据类型

之前当不一样的文件中,对同一个变量进行操作的时候,会有污染的情况,所以为了解决这种问题,约定俗称会在属性名中添加文件名。

作用

如果想用toString方法,又怕和原生的冲突,可以用Symbol

const obj = {
  [Symbol.toStringTag]: 'XObject'
}

console.log(obj.toString()) // [object XObject] 

更适合做私有属性,因为普通的遍历是访问不到的

const obj1 = {
  [Symbol()]: 'symbol value',
  foo: 'normal value'
}

for(let key in obj1) {
  console.log(key)  // foo
}
console.log(Object.keys(obj1)) // [ 'foo' ]

console.log(JSON.stringify(obj1)) // {"foo":"normal value"}

// 使用下面的方法,可以获取到symbol的属性名
console.log(Object.getOwnPropertySymbols(obj1)) // [ Symbol() ]

目前最主要的作用就是为对象添加独一无二的属性名

// shared.js
const cache = {}
// a.js
cache['foo'] = 123
// b.js
cache['foo'] = 234

console.log(cache) // {foo: 234}
// ==============================================
// a.js
cache['a_foo'] = 123
// b.js
cache['b_foo'] = 234

console.log(cache) // {a_foo: 123, b_foo: 234}

使用symbol

// 通过symbol函数创建一个symbol类型的数据
const s = Symbol()
console.log(s) // Symbol()
console.log(typeof s) // symbol
console.log(Symbol() === Symbol()) // false 独一无二的数据

// 我们可以添加描述文本
console.log(Symbol('foo')) // Symbol(foo)
console.log(Symbol('bar')) // Symbol(bar)

// 对象的属性名可以是symbol类型也可以是string类型
// a.js
const name = Symbol()

const person = {
  [name] : 'xm',
  say () {
    console.log(this[name])
  }
}

// b.js
person.say() // xm

取值

有for方法可以获取到那个值

const s1 = Symbol.for('foo')
const s2 = Symbol.for('foo')

console.log(s1 === s2) // true

// 需要注意的是,那个for对应的是字符串,所以true和'true'的效果是一样的
console.log(Symbol.for(true) === Symbol.for('true')) // true
  • JS八种数据类型:Number、String、Boolean、Null、undefined、object、symbol、bigInt
  • JS七种基本数据类型:Number、String、Boolean、Null、undefined、symbol、bigInt

ES6-ES10学习版图

查看原文

赞 0 收藏 0 评论 0

认证与成就

  • 获得 111 次点赞
  • 获得 2 枚徽章 获得 0 枚金徽章, 获得 0 枚银徽章, 获得 2 枚铜徽章

擅长技能
编辑

开源项目 & 著作
编辑

(゚∀゚ )
暂时没有

注册于 2018-06-26
个人主页被 1.5k 人浏览