TypeScript相关知识点归纳
来源:互联网 发布:无约束最优化问题 编辑:程序博客网 时间:2024/06/11 18:35
前言
TypeScript 是 JavaScript 的一个超集,主要提供了类型系统和对 ES6 的支持,它由 Microsoft 开发,代码开源于 GitHub 上。
第一个版本发布于 2012 年 10 月,经历了多次更新后,现在已成为前端社区中不可忽视的力量,不仅在 Microsoft 内部得到广泛运用,而且 Google 的 Angular2 也使用了 TypeScript 作为开发语言。
TypeScript :官方手册及其非官方中文版
前置知识:JavaScript、ECMAScript6
简介
官网的定义:
TypeScript is a typed superset of JavaScript that compiles to plain JavaScript. Any browser. Any host. Any OS. Open source.
翻译成中文即是:
TypeScript 是 JavaScript 的类型的超集,它可以编译成纯 JavaScript。编译出来的 JavaScript 可以运行在任何浏览器上。TypeScript 编译工具可以运行在任何服务器和任何系统上。TypeScript 是开源的。
优势:
- 增加代码可读性和可维护性
- 拥有活跃的社区
- TypeScript 是 JavaScript 的超集,.js 文件可以直接重命名为 .ts 即可
- 即使不显式的定义类型,也能够自动做出类型推论
- 可以定义从简单到复杂的一切类型
- 即使 TypeScript 编译报错,也可以生成 JavaScript 文件
- 兼容第三方库,即使第三方库不是用 TypeScript 写的,也可以> 编写单独的类型文件供 TypeScript 读取
TypeScript的安装:
npm install -g typescript
TypeScript文件编译,使用tsc命令:
tsc hello.ts
支持TypeScript的编辑器:
Visual Studio Code(推荐使用)
Sublime Text
Atom
WebStorm
Vim
Emacs
Eclipse
Visual Studio 2015
Visual Studio 2013
基础
- 原始数据类型
- 任意值
- 类型推论
- 联合类型
- 接口
- 数组
- 函数
- 类型断言
- 声明文件
- 内置对象
原始数据类型
JavaScript 的类型分为两种:原始数据类型(Primitive data types)和对象类型(Object types)。
原始数据类型包括:布尔值、数值、字符串、null、undefined 以及 ES6 中的新类型 Symbol。
布尔值
布尔类型,使用boolean定义
let isDone: boolean = false;//或者let createdByBoolean: boolean = Boolean(1);
布尔对象,使用Boolean构造函数
let createdByNewBoolean: Boolean = new Boolean(1);
数值
数值类型,使用number定义
let decLiteral: number = 6;let hexLiteral: number = 0xf00d;// ES6 中的二进制表示法(ES6)let binaryLiteral: number = 0b1010;// ES6 中的八进制表示法(ES6)let octalLiteral: number = 0o744;let notANumber: number = NaN;let infinityNumber: number = Infinity;
数值对象,使用Number构造函数
字符串
字符串类型,使用string定义:
let myName: string = 'Xcat Liu';let myAge: number = 25;// 模板字符串let sentence: string = `Hello, my name is ${myName}.I'll be ${myAge + 1} years old next month.`;// 模板字符串编译结果//var sentence = "Hello, my name is " + myName + //".\nI'll be " + (myAge + 1) + " years old next //month.";
其中 ` 用来定义 ES6 中的模板字符串,${expr} 用来在模板字符串中嵌入表达式。
空值
JavaScript 没有空值(Void)的概念,在 TypeScirpt 中,可以用 void 表示没有任何返回值的函数:
function alertName(): void { alert('My name is xcatliu');}
声明一个 void 类型的变量没有什么用,因为你只能将它赋值为 undefined 和 null:
let unusable: void = undefined;
Null 和 Undefined
在 TypeScript 中,可以使用 null 和 undefined 来定义这两个原始数据类型:
let u: undefined = undefined;let n: null = null;
undefined 类型的变量只能被赋值为 undefined,null 类型的变量只能被赋值为 null。
与 void 的区别是,undefined 和 null 是所有类型的子类型。也就是说 undefined 类型的变量,可以赋值给 number 类型的变量,而 void 类型的变量不能赋值给 number 类型的变量:
// 这样不会报错let num: number = undefined;// 这样也不会报错let u: undefined;let num: number = u;
任意值
如果是一个普通类型,在赋值过程中改变类型是不被允许的,但
如果是 any 类型,则允许被赋值为任意类型。任意值(Any)用来表示允许赋值为任意类型。
在任意值上访问任何属性都是允许的,也允许调用任何方法。
声明一个变量为任意值之后,对它的任何操作,返回的内容的类型都是任意值
变量如果在声明的时候,未指定其类型,那么它会被识别为任意值类型
let myFavoriteNumber: any = 'seven';myFavoriteNumber = 7;let anyThing: any = 'hello';console.log(anyThing.myName);anyThing.setName('Jerry Lee').sayHello();
类型推论
如果没有明确的指定类型,那么 TypeScript 会依照类型推论(Type Inference)的规则推断出一个类型。
TypeScript 2.1 中,编译器会考虑对 myFavoriteNumber 的最后一次赋值来检查类型。
let myFavoriteNumber = 'seven';//等价于let myFavoriteNumber: string = 'seven';
联合类型
- 联合类型(Union Types)表示取值可以为多种类型中的一种。
- 联合类型使用 | 分隔每个类型。
let myFavoriteNumber: string | number;myFavoriteNumber = 'seven';myFavoriteNumber = 7;
这里的 string | number 的含义是,允许 myFavoriteNumber 的类型是 string 或者 number,但是不能是其他类型。
访问联合类型的属性和方法:
当 TypeScript 不确定一个联合类型的变量到底是哪个类型的时候,我们只能访问此联合类型的所有类型里共有的属性或方法。
function getString(something: string | number): string { return something.toString(); //ERROR return something.length;}
联合类型的变量在被赋值的时候,会根据类型推论的规则推断出一个类型
接口
TypeScript 中的接口是一个非常灵活的概念,除了可用于对类的一部分行为进行抽象以外,也常用于对「对象的形状(Shape)」进行描述。
interface Person { name: string; age: number;}
使用接口类型赋值的时候,变量的形状必须和接口的形状保持一致。属性数量和类型保持一致,不允许添加未定义的属性。
let xcatliu: Person = { name: 'Xcat Liu', age: 25,};
可选属性,有时我们希望不要完全匹配一个形状,那么可以用可选属性。使用”?”
interface Person { name: string; age?: number;}let xcatliu: Person = { name: 'Xcat Liu',};
任意属性,有时候我们希望一个接口允许有任意的属性
interface Person { name: string; age?: number; [propName: string]: any;}let xcatliu: Person = { name: 'Xcat Liu', website: 'http://xcatliu.com',};
注意:一旦定义了任意属性,那么确定属性和可选属性都必须是它的子属性
interface Person { name: string; age?: number; [propName: string]: string;}let xcatliu: Person = { name: 'Xcat Liu', age: 25,//error,25是number类型,不是string的子属性 website: 'http://xcatliu.com',};
只读属性,对象中的一些字段只能在创建的时候被赋值,用 readonly 定义只读属性。
注意:只读的约束存在于第一次给对象赋值的时候,而不是第一次给只读属性赋值的时候
interface Person { readonly id: number;}//第一次给对象赋值,激活只读约束let xcatliu: Person = { id: 89757,};xcatliu.id = 9527;//error
数组
定义数组:有多种定义方式
1. 「类型 + 方括号」表示法
let fibonacci: number[] = [1, 1, 2, 3, 5];
数组的项中不允许出现其他的类型
2. 使用数组泛型(Generic) Array 来表示数组
let fibonacci: Array<number> = [1, 1, 2, 3, 5];
- 用接口表示数组
interface NumberArray { [index: number]: number;}let fibonacci: NumberArray = [1, 1, 2, 3, 5];//NumberArray 表示:只要 index 的类型是 number,那么值的类型必须是 number。
- any 表示数组中允许出现任意类型
let list: any[] = ['Xcat Liu', 25, { website: 'http://xcatliu.com' }];
- 类数组
常见的类数组都有自己的接口定义,如 IArguments, NodeList, HTMLCollection (内置对象)等
function sum() { let args: IArguments = arguments;}
函数
(1)定义函数的两种方式:函数申明,函数表达式
- 函数申明
function sum(x: number, y: number): number { return x + y;}
调用时输入多余的(或者少于要求的)参数,是不被允许的。
- 函数表达式
let mySum = function (x: number, y: number): number { return x + y;};
上面的代码只对等号右侧的匿名函数进行了类型定义,而等号左边的 mySum,是通过赋值操作进行类型推论而推断出来的。
(2)接口中的函数
interface SearchFunc { (source: string, subString: string): boolean;}let mySearch: SearchFunc;mySearch = function(source: string, subString: string) { return source.search(subString) !== -1;}
(3)函数中参数
1. 可选参数(使用”?”表示)
function buildName(firstName: string, lastName?: string) {}
注意:可选参数后不允许出现必须参数(参数默认值除外)
2. 参数默认值
function buildName(firstName: string = 'Xcat', lastName: string) {}
在 ES6 中,TypeScript 会将添加了默认值的参数识别为可选参数,此时不受可选参数位置限制。
3. 剩余参数(rest参数)
function push(array, ...items) { items.forEach(function(item) { array.push(item); });}//items 是一个数组。所以我们可以用数组的类型来定义它function xpush(array: any[], ...items: any[]) { items.forEach(function(item) { array.xpush(item); });}
**注意:**rest参数只能是函数中最后一个参数
(4)函数重载
重载允许一个函数接受不同数量或类型的参数时,作出不同的处理。
function reverse(x: number): number;function reverse(x: string): string;function reverse(x: number | string): number | string {...}
类型断言
类型断言(Type Assertion)可以用来绕过编译器的类型推断,手动指定一个值的类型(即程序员对编译器断言)。
类型断言不是类型转换。
(1)语法:
<类型>值
// 或
值 as 类型
// 在TSX语法 (React的JSX语法的TS版)中必须用后一种
(2)例子:使用类型断言,将一个联合类型的变量指定为一个更加具体的类型。
function toBoolean(something: string | number): boolean { return <string>something;}function getLength(something: string | number): number { let length=something.length;//error let xlength=something.length;//OK}
注意:只能断言成联合类型中存在的类型。
声明文件
当时用第三方库时,我们需要引用它的声明文件。
申明语句:使用declare关键字来定义类型
如:我们需要使用第三方库Jquery获取一个id是foo的元素
declare var jQuery: (string) => any;//类型声明jQuery('#foo');//单独书写时typescript并不识别$或者jquery
通常我们会将类型声明放在一个单独的文件中,约定声明文件以.d.ts为后缀。
// jQuery.d.tsdeclare var jQuery: (string) => any;
使用声明文件:用「三斜线指令」表示引用了声明文件
/// <reference path="./jQuery.d.ts" />jQuery('#foo');
TypeScript2.0推荐使用@types来管理,即使用nm安装对应声明模块
如:npm install @types/jquery --save-dev
官方搜索声明文件链接:http://microsoft.github.io/TypeSearch/
内置对象
内置对象是指根据标准在全局作用域(Global)上存在的对象。这里的标准是指 ECMAScript 和其他环境(比如 DOM)的标准。
这些定义文件都在 TypeScript 核心库的定义文件中:
ES标准提供的内置对象有:
Boolean、Error、Date、RegExp 等DOM 和 BOM 提供的内置对象有:
Document、HTMLElement、Event、NodeList 等
注意: Node.js不是内置对象的一部分,需要引入声明文件: npm install @types/node --save-dev
进阶
- 类型别名
- 字符串字面量类型
- 元组
- 枚举
- 类
- 类与接口
- 泛型
- 声明合并
- 扩展阅读
类型别名
使用 ‘type’ 创建类型别名,类型别名用来给一个类型起个新名字。类型别名常用于联合类型。
type Name = string;type NameResolver = () => string;type NameOrResolver = Name | NameResolver;function getName(n: NameOrResolver): Name {...}
字符串字面量
字符串字面量类型用来约束取值只能是某几个字符串中的一个。
类型别名与字符串字面量类型都是使用 ‘type’ 进行定义。
type EventNames = 'click' | 'scroll' | 'mousemove';function handleEvent(ele: Element, event: EventNames) { // do something}handleEvent(document.getElementById('hello'), 'scroll'); // 没问题handleEvent(document.getElementById('world'), 'dbclick'); // 报错,event 不能为 'dbclick'
元组
数组合并了相同类型的对象,而元组(Tuple)合并了不同类型的对象。
元组起源于函数编程语言(如 F#),在这些语言中频繁使用元组。
(1)定义元组:
let tuple: [string, number];let xtuple: [string, number] = ['Xcat Liu', 25];
(2)元组的赋值:
1.定义元组时赋值
let xtuple: [string, number] = ['Xcat Liu', 25];
2.使用元组索引赋值,索引从0开始,可只赋值其中一项
xcatliu[0] = 'Xcat Liu';
3.直接对元组赋值,需要提供所有元组类型中指定的项。
let xcatliu: [string, number];xcatliu = ['Xcat Liu', 25];
4.越界元素赋值
当赋值给越界的元素时,它类型会被限制为元组中每个类型的联合类型。
let xcatliu: [string, number];xcatliu = ['Xcat Liu', 25, 'http://xcatliu.com/'];//'http://xcatliu.com/'满足联合类型 string | number
(3)元组元素的访问
1.使用索引访问
let xcatliu: [string, number] = ['Xcat Liu', 25];xcatliu[0].slice(1);xcatliu[1].toFixed(2);
2.访问越界元素
当访问一个越界的元素,也会识别为元组中每个类型的联合类型。(如果一个值是联合类型,我们只能访问此联合类型的所有类型里共有的属性或方法-联合类型)
let xcatliu: [string, number];xcatliu = ['Xcat Liu', 25, 'http://xcatliu.com/'];console.log(xcatliu[2].slice(1));//error// index.ts(4,24): error TS2339: Property 'slice' does not exist on type 'string | number'.
枚举
(1)定义
使用 ‘enum’ 关键字来定义枚举类型;
enum Days {Sun, Mon, Tue, Wed, Thu, Fri, Sat};
(2)枚举项赋值
1.默认赋值
枚举成员会被赋值为从 0 开始递增的数字,同时也会对枚举值到枚举名进行反向映射。如编译结果所示
var Days;(function (Days) { Days[Days["Sun"] = 0] = "Sun"; Days[Days["Mon"] = 1] = "Mon"; Days[Days["Tue"] = 2] = "Tue"; Days[Days["Wed"] = 3] = "Wed"; Days[Days["Thu"] = 4] = "Thu"; Days[Days["Fri"] = 5] = "Fri"; Days[Days["Sat"] = 6] = "Sat";})(Days || (Days = {}));
2.手动赋值
(1)使用数字类型
enum Days {Sun = 7, Mon = 1, Tue, Wed, Thu, Fri, Sat};
此时,未手动赋值的枚举项会接着上一个枚举项以1递增(使用小数或负数也如此),即:Tue=2,Wed=3,Thu=4,Fri=5,Sat=6。
枚举项的值重复仍可通过编译,只是值被覆盖了。
enum Days {Sun = 3, Mon = 1, Tue, Wed, Thu, Fri, Sat};//原先Days[3]的值为Sun,现在为Wed
(2)不使用数字
手动赋值的枚举项可以不是数字,此时需要使用类型断言来让tsc无视类型检查 (编译出的js仍然是可用的)。
enum Days {Sun = 7, Mon, Tue, Wed, Thu, Fri, Sat = <any>"S"};
编译结果:
var Days;(function (Days) { Days[Days["Sun"] = 7] = "Sun"; Days[Days["Mon"] = 8] = "Mon"; Days[Days["Tue"] = 9] = "Tue"; Days[Days["Wed"] = 10] = "Wed"; Days[Days["Thu"] = 11] = "Thu"; Days[Days["Fri"] = 12] = "Fri"; Days[Days["Sat"] = "S"] = "Sat";})(Days || (Days = {}));
3.枚举项类型
枚举项有两种类型:常数项(constant member)和计算所得项(computed member)。
(1)常数项
enum Days {Sun, Mon, Tue, Wed, Thu, Fri, Sat};
(2)计算所得项
enum Color {Red, Green, Blue = "blue".length};
常数枚举
使用const enum
定义常数枚举。
常数枚举与普通枚举的区别是,它会在编译阶段被删除,并且不能包含计算成员。
const enum Directions { Up, Down, Left, Right}let directions = [Directions.Up, Directions.Down, Directions.Left, Directions.Right];
上例的编译结果是:
var directions = [0 /* Up */, 1 /* Down */, 2 /* Left */, 3 /* Right */];
外部枚举
(1)使用 declare enum
定义外部枚举。
(2)declare 定义的类型只会用于编译时的检查,编译结果中会被删除。
(3)外部枚举与声明语句一样,常出现在声明文件中。
declare enum Directions { Up, Down, Left, Right}let directions = [Directions.Up, Directions.Down, Directions.Left, Directions.Right];
上例的编译结果是:
(4)declare 和 const 可以同时使用。
var directions = [Directions.Up, Directions.Down, Directions.Left, Directions.Right];declare const enum Directions { Up, Down, Left, Right}let directions = [Directions.Up, Directions.Down, Directions.Left, Directions.Right];
编译结果:
var directions = [0 /* Up */, 1 /* Down */, 2 /* Left */, 3 /* Right */];
类
传统方法:JavaScript 通过构造函数实现类的概念,通过原型链实现继承。
ES6 中:我们终于迎来了 class。
TypeScript中类的用法
访问修饰符:public、private、protected
public,允许直接访问实例的属性:
class Animal { public name; public constructor(name) { this.name = name; }}let a = new Animal('Jack');console.log(a.name); // Jacka.name = 'Tom';console.log(a.name); // Tom
private修饰的属性是无法直接存取的,所修饰的属性和方法不允许在子类中访问:
class Animal { private name; public constructor(name) { this.name = name; }}let a = new Animal('Jack');console.log(a.name); // Jacka.name = 'Tom';//errorclass Cat extends Animal { constructor(name) { super(name);//error console.log(this.name); }}
protected 修饰的属性或方法,允许在子类中被访问。
抽象类
abstract
用于定义抽象类和其中的抽象方法。
抽象类是不允许被实例化,抽象类中的抽象方法必须被子类实现。
abstract class Animal { public name; public constructor(name) { this.name = name; } public abstract sayHi();}class Cat extends Animal { public sayHi() { console.log(`Meow, My name is ${this.name}`); }}let cat = new Cat('Tom');
给类加上 TypeScript 的类型很简单,与接口类似:
class Animal { name: string; constructor(name: string) { this.name = name; } sayHi(): string { return `My name is ${this.name}`; }}let a: Animal = new Animal('Jack');console.log(a.sayHi()); // My name is Jack
类与接口
一个类可以实现多个接口
interface Alarm { alert();}interface Light { lightOn(); lightOff();}class Car implements Alarm, Light { alert() { console.log('Car alert'); } lightOn() { console.log('Car light on'); } lightOff() { console.log('Car light off'); }}
接口继承接口
interface Alarm { alert();}interface LightableAlarm extends Alarm { lightOn(); lightOff();}
接口继承类
class Point { x: number; y: number;}interface Point3d extends Point { z: number;}let point3d: Point3d = {x: 1, y: 2, z: 3};
泛型
泛型(Generics)是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性。
泛型使用在函数上
(1)定义返回值
function createArray<T>(length: number, value: T): Array<T> { let result = []; for (let i = 0; i < length; i++) { result[i] = value; } return result;}createArray<string>(3, 'x'); // ['x', 'x', 'x']
(2)多个类型参数
定义泛型的时候,可以一次定义多个类型参数:
function swap<T, U>(tuple: [T, U]): [U, T] { return [tuple[1], tuple[0]];}swap([7, 'seven']); // ['seven', 7]
泛型约束
(1)在函数内部使用泛型变量的时候,由于事先不知道它是哪种类型,所以不能随意的操作它的属性或方法:
function loggingIdentity<T>(arg: T): T { console.log(arg.length);//error return arg;}interface Lengthwise { length: number;}function loggingIdentity2<T extends Lengthwise>(arg: T): T { console.log(arg.length);//OK return arg;}loggingIdentity(7);//error,传入的 arg 不包含 length
(2)多个类型参数之间也可以互相约束:
function copyFields<T extends U, U>(target: T, source: U): T { for (let id in source) { target[id] = (<T>source)[id]; } return target;}let x = { a: 1, b: 2, c: 3, d: 4 };copyFields(x, { b: 10, d: 20 });
上例中,我们使用了两个类型参数,其中要求 T 继承 U,这样就保证了 U 上不会出现 T 中不存在的字段。
泛型接口
(1)反省参数在接口中的函数上:
interface CreateArrayFunc { <T>(length: number, value: T): Array<T>;}let createArray: CreateArrayFunc;createArray = function<T>(length: number, value: T): Array<T> { let result = []; for (let i = 0; i < length; i++) { result[i] = value; } return result;}createArray(3, 'x'); // ['x', 'x', 'x']
(2)泛型参数在接口名上:
interface CreateArrayFunc<T> { (length: number, value: T): Array<T>;}let createArray: CreateArrayFunc<any>;//注意这里需要定义泛型的类型createArray = function<T>(length: number, value: T): Array<T> { let result = []; for (let i = 0; i < length; i++) { result[i] = value; } return result;}createArray(3, 'x'); // ['x', 'x', 'x']
注意,此时在使用泛型接口的时候,需要定义泛型的类型。
泛型类
与泛型接口类似,泛型也可以用于类的类型定义中:
class GenericNumber<T> { zeroValue: T; add: (x: T, y: T) => T;}let myGenericNumber = new GenericNumber<number>();myGenericNumber.zeroValue = 0;myGenericNumber.add = function(x, y) { return x + y; };
声明合并
如果定义了两个相同名字的函数、接口或类,那么它们会合并成一个类型。
函数的合并(重载)
function reverse(x: number): number;function reverse(x: string): string;function reverse(x: number | string): number | string { if (typeof x === 'number') { return Number(x.toString().split('').reverse().join('')); } else if (typeof x === 'string') { return x.split('').reverse().join(''); }}
类与接口的合并
接口中的属性和方法合并,类的合并和接口的合并规则一致:
interface Alarm { price: number; alert(s: string): string;}interface Alarm { weight: number; alert(s: string, n: number): string;}相当于:interface Alarm { price: number; weight: number; alert(s: string): string; alert(s: string, n: number): string;}
扩展阅读
此处记录了官方手册(中文版)中包含,但是本书未涉及的概念。
我认为它们是一些不重要或者不属于 TypeScript 的概念,所以这里只给出一个简单的释义,详细内容可以点击链接深入理解。
- Never(中文版):永远不存在值的类型,一般用于错误处理函数
- Variable Declarations(中文版):使用
let
和const
替代var
,这是 ES6 的知识 - this:箭头函数的运用,这是 ES6 的知识
- Using Class Types in Generics(中文版):创建工厂函数时,需要引用构造函数的类类型
- Best common type(中文版):数组的类型推论
- Contextual Type(中文版):函数输入的类型推论
- Type Compatibility(中文版):允许不严格符合类型,只需要在一定规则下兼容即可
- Advanced Types(中文版):使用 & 将多种类型的共有部分叠加成一种类型
- Type Guards and Differentiating Types(中文版):联合类型在一些情况下被识别为特定的类型
- Discriminated Unions(中文版):使用 | 联合多个接口的时候,通过一个共有的属性形成可辨识联合
- Polymorphic this types(中文版):父类的某个方法返回 this,当子类继承父类后,子类的实例调用此方法,返回的 this 能够被 TypeScript 正确的识别为子类的实例。
- Symbols(中文版):新原生类型,这是 ES6 的知识
- Iterators and Generators(中文版):迭代器,这是 ES6 的知识
- Namespaces(中文版):避免全局污染,现在已被 ES6 Module 替代
- Decorators(中文版):修饰器,这是 ES7 的一个提案
- Mixins(中文版):一种编程模式,与 TypeScript 没有直接关系,可以参考 ES6 中 Mixin 模式的实现
参考书:https://github.com/xcatliu/typescript-tutorial
参考入门教程:http://es6.ruanyifeng.com/
- TypeScript相关知识点归纳
- 金融相关知识点的归纳
- TypeScript知识点
- 知识点归纳
- TypeScript 知识点提要
- TypeScript常用知识点汇总
- DBA 知识点归纳(一)
- DBA 知识点归纳(二)
- DBA 知识点归纳(三)
- JavaScript基础知识点归纳
- Struts详细归纳知识点
- 项目管理知识点归纳
- jsp常用知识点归纳
- C++学习--知识点归纳
- makefile知识点归纳
- Java知识点总结归纳
- jsp 知识点归纳
- DOM知识点归纳一
- Ubuntu14+OpenCV3.1+Caffe虚拟机安装笔记(一)
- Android之Service学习篇一:Service启动方式之startService
- ti.sysbios.timers.dmtimer.Timer: line 1142: E_freqMismatch: Frequency mismatch:
- Disruptor之粗糙认识
- 一个人能够哲学思考的条件
- TypeScript相关知识点归纳
- 编程规范
- 工作日记20170628
- 设计思想之一功能模块化
- 理解
- 设计模式之一适配器模式
- 内存池——第一章 几种常用的内存池技术
- 关于QTreeWidget显示类似QTableWidget网格的实现
- Kotlin简单实用方法既使用Kotlin优雅的开发Android应用