imaxue / progress Goto Github PK
View Code? Open in Web Editor NEWLicense: MIT License
License: MIT License
Dart
是强类型的, 但是 Dart 可以 推断类型(就是可以根据 value 推断类型), 所以类型注释可可选的.dynamic
.如: List <dynamic>
.如: main()
final
类型但不能是 const
类型.==
运算符用来测试两个对象是否相等...
语法为 级联调用 (cascade)。 使用级联调用, 可以简化在一个对象上执行的多个操作。toString()
转换r
前缀, 可以创建 原始 raw 字符串var halogens = {'fluorine', 'chlorine', 'bromine', 'iodine', 'astatine'};
=
来赋值默认值void doStuff(
{List<int> list = const [1, 2, 3],
Map<String, String> gifts = const {
'first': 'paper',
'second': 'cotton',
'third': 'leather'
}}) {
print('list: $list');
print('gifts: $gifts');
}
doStuff(list: [123, 344], gifts: {
'first': 'zhangsan'
})
printPerson(String name,{int age = 14,String gender}){
print("name=$name,age=$age,gender=$gender");
}
printPerson("李四");
printPerson("李四",age: 20);
printPerson("李四",age: 20,gender: "Male");
printPerson("李四",gender: "Male");
printPerson2(String name,[int age = 15,String gender]){
print("name=$name,age=$age,gender=$gender");
}
printPerson2("张三");
printPerson2("张三",18);
printPerson2("张三",18,"Female");
// 除后向下取整
10 ~/ 2.1 = 2;
if (emp is Person) {
// Type check
emp.firstName = 'Bob';
}
// 使用 as 运算符进行缩写:
(emp as Person).firstName = 'Bob';
// 当 value 为 null 时, 才会赋值给 b
b ??= value;
// 如果实例类 p 成员变量 y 为非 null,则设置它变量 y 的值为 4
p?.y = 4;
示例:
querySelector('#confirm') // 获取对象
..text = 'Confirm' // 调用成员变量
..classes.add('important') // 添加类名
..onClick.listen((e) => window.alert('Confirmed!')); // 注册事件
嵌套:
final addressBook = (AddressBookBuilder()
..name = 'jenny'
..email = '[email protected]'
..phone = (PhoneNumberBuilder()
..number = '415-555-0100'
..label = 'home')
.build())
.build();
if-else
判断条件必须是 布尔值
assert
语句中的布尔条件为 false
, 会中断程序(只在开发环境有效).var a = const ImmutablePoint(1, 1); // 创建一个常量对象
var b = ImmutablePoint(1, 1); // 创建一个非常量对象
assert(!identical(a, b)); // 两者不是同一个实例!
class Point {
num x; // 声明示例变量 x,初始值为 null 。
num y; // 声明示例变量 y,初始值为 null 。
num z = 0; // 声明示例变量 z,初始值为 0 。
}
通过创建一个与其类同名的函数来声明构造函数.
最常见的构造函数形式, 即生成构造函数, 创建一个类的实例:
// 传统方式
class Point {
num x, y;
Point(num x, num y) {
// 还有更好的方式来实现下面代码,敬请关注。
this.x = x;
this.y = y;
}
}
// Dart 自身的语法糖精简了这些代码
class Point {
num x, y;
// 在构造函数体执行前,
// 语法糖已经设置了变量 x 和 y。
Point(this.x, this.y);
}
在没有生命构造函数的情况下, Dart 会提供一个默认的构造函数.
子类不会继承父类的构造函数.
使用命名构造函数可为一个类实现多个构造函数, 也可以使用早含函数来更清晰的表明函数意图.
class Point {
num x, y;
Point(this.x, this.y);
// 命名构造函数
Point.origin() {
x = 0;
y = 0;
}
}
切记, 构造函数不能够被继承, 这意味着 子类不能继承.
class Person {
String firstName;
// 命名构造函数
Person.fromJson(Map data) {
print('in Person');
}
}
// Employee 类的构造函数调用了父类 Person 的命名构造函数
class Employee extends Person {
// Person 没有默认的构造函数, 所以必须要用 super.fromJson(data)手动继承一下
Employee.fromJson(Map data) : super.fromJson(data) {
print('in Employee');
}
}
main() {
var emp = new Employee.fromJson({});
// Prints:
// in Person
// in Employee
if (emp is Person) {
// Type check
emp.firstName = 'Bob';
}
(emp as Person).firstName = 'Bob';
}
import 'dart:math';
class Point {
// 定义构造函数参数
final num x;
final num y;
final num distanceFromOrigin;
// 初始化参数
Point(x, y)
: x = x,
y = y,
distanceFromOrigin = sqrt(x * x + y * y);
}
main() {
var p = new Point(2, 3);
print(p.distanceFromOrigin);
}
有时构造函数的唯一目的是重定向到 同一个类 中的另一个构造函数.
class Point {
num x, y;
// 类的主构造函数。
Point(this.x, this.y);
// 指向主构造函数
Point.alongXAxis(num x) : this(x, 0);
}
如果该类生成的对象是固定不变的, 那么就可以把这些对象定义为编译时常量。
当执行构造函数 并不总是创建 这个类的一个 新实例时,则使用 factory 关键字。
Getter和Setter 是用于对象属性读和写的特殊方法。
class Rectangle {
num left, top, width, height;
Rectangle(this.left, this.top, this.width, this.height);
// 定义两个计算属性: right 和 bottom。
num get right => left + width;
set right(num value) => left = value - width;
num get bottom => top + height;
set bottom(num value) => top = value - height;
}
void main() {
var rect = Rectangle(3, 4, 20, 15);
assert(rect.left == 3);
rect.right = 12;
assert(rect.left == -8);
}
使用 abstract 修饰符来定义, 抽象类通常用来定义接口,以及部分实现。
每个类都 隐式的定义 了一个接口, 接口包含了该类所有的实例成员及其实现的接口.
一个类可以通过 implements
关键字来实现一个或者多个接口, 并实现每个接口要求的API.
// person 类。 隐式接口里面包含了 greet() 方法声明。
class Person {
// 包含在接口里,但只在当前库中可见。
final _name;
// 不包含在接口里,因为这是一个构造函数。
Person(this._name);
// 包含在接口里。
String greet(String who) => 'Hello, $who. I am $_name.';
}
// person 接口的实现。
class Impostor implements Person {
get _name => '';
String greet(String who) => 'Hi $who. Do you know who I am?';
}
String greetBob(Person person) => person.greet('Bob');
void main() {
print(greetBob(Person('Kathy')));
print(greetBob(Impostor()));
}
使用
extends
关键字来创建子类, 使用super
关键字来引用父类.
子类可以重写实例方法, getter和setter. 可以使用
@override
注解指出想要重写的成员:
当代码尝试使用不存在的方法或实例变量时, 通过重写
noSuchMethod()
方法, 来实现检测和应对处理:
枚举类型也成为
enumerations
或enums
, 是一种特殊的类, 用于表示 数量固定的常量值.
枚举中的每个值都有一个 index getter
方法, 该方法的返回值是 所在枚举类型定义中的位置 (从0开始).
枚举类型具有以下限制:
void main() {
var currentSeason = Season.spring;
print(currentSeason.index);
print(Season.values);
// 如果不处理所有枚举值, 会受到警告:
switch(currentSeason){
case Season.spring:
print("1-3月");
break;
case Season.summer:
print("4-6月");
break;
case Season.autumn:
print("7-9月");
break;
case Season.winter:
print("10-12月");
break;
}
}
enum Season{
spring,
summer,
autumn,
winter
}
Mixin
是 复用类代码 的一种途径, 复用的类可以在不同层级可以不存在 继承关系.
静态变量(类变量)对于类级别的状态是非常有用的
class Queue {
static const initialCapacity = 16;
// ···
}
void main() {
// 静态变量直到它们被使用的时候才会初始化.
assert(Queue.initialCapacity == 16);
}
静态方法(类方法) 不能再实例上使用, 因此不能访问
this
.
在类型安全上通常需要泛型支持, 它的好处不仅仅是保证代码的正常运行:
// T 是一个备用类型, 属于类型占位符, 在开发者调用该接口的时候会指定具体类型.
abstract class Cache<T> {
T getByKey(String key);
void setByKey(String key, T value);
}
var names = <String>['Seth', 'Kathy', 'Lars'];
var ages = <num>[1,21,2,3];
var aaa = <dynamic>[1,21,2,3, 'sad', {'name': 'zhang'}, [12,21,3]];
var uniqueNames = <String>{'Seth', 'Kathy', 'Lars'};
var pages = <String, String>{
'index.html': 'Homepage',
'robots.txt': 'Hints for web robots',
'humans.txt': 'We are people, not machines'
};
// 定义 Set 数据接口中类型为 string
var nameSet = Set<String>.from(names);
// 创建了一个 key 为 integer, value 为 View 的 map 对象:
var views = Map<int, View>();
Dart 中泛型类型是 固化的, 也就是说在 运行时是携带者类型信息的.
使用泛型类型的时候, 可以使用
extends
实现参数类型的限制.
T first<T>(List<T> ts) {
// 做一些初始化工作, 然后...
T tmp = ts[0];
// 做一些额外的处理和检查, 然后...
return tmp;
}
T
)List<T>
)// 引入第三方库文件
import 'package:flutter/material.dart';
// 引入项目中的文件
import './animated_cross_fade_demo.dart';
// 引入 dart 提供的库文件
import 'dart:html
// 当不同库出现同样标识符, 可以用库前缀解决解决命名冲突
import 'package:lib1/lib1.dart';
import 'package:lib2/lib2.dart' as lib2;
import 'package:lib1/lib1.dart' show foo; // 仅导入 foo
import 'package:lib2/lib2.dart' hide foo; // 除了 foo 外都导出
// 当使用的时候在加载
import 'package:deferred/hello.dart' deferred as hello;
// 使用的时候
hello.loadLibrary();
使用 `library` 加上一个标识符 `point` 定义当前库的名字
library point;
如果一个库的所有代码都卸载一个文件中, 会导致文件太大不好维护, 可以用 part
& part of
关键字拆分文件.
library
语句所在的主文件中可以使用 import
和 part
语句, 但是 part of
所在的实现文件中不能使用任何 import/library/part语句. 库使用的所有 import 和 part 语句都必须放在主文件中声明.
// 主文件 定义一个 math 库。它由 base,random 两部分组成
library math;
part 'base.dart';
part 'random.dart';
// 在 base.dart 文件的开头
part of math
// 在 random.dart 文件的开头
part of math;
可以使用 export 语句重新导出库.
比如:
把多个较小的库组合为一个较大的库或者重新导出库的一部分作为一个新的库.
既可以导出库的全部, 也可以导出库的一部分(使用 show 和 hide).
// 定义函数参数类型与返回值
typedef int CalFunc(int num1, int num2);
main() {
int num1 = 1;
int num2 = 2;
// 参数调用与传递
int calculate(CalFunc func) {
return func(num1, num2);
};
int result = calculate((int num1, int num2) {
return num1 - num2;
});
print(result);
}
let myName: string = 'Tom';
let myAge: number = 25;
let isDone: boolean = false;
let unusable: void = undefined;
let u: undefined = undefined;
let n: null = null;
let myFavoriteNumber: any = 'seven';
function sayHello(person:string): any {
console.log('===');
return {
age: '12312',
name: 'asds a'
};
}
let myFavoriteNumber: string | number;
// myFavoriteNumber 既可以是 string 也可以是 number
访问联合类型的属性或方法
// 报错
function getLength(something: string | number): number {
return something.length;
}
// 正确
function getString(something: string | number): string {
return something.toString();
}
在 TypeScript 中,我们使用接口(Interfaces
)来定义对象的类型。
在面向对象语言中, 接口(interfaces)是对象行为的抽象, 而具体如何行动需要由类(classes) 去实现(implements)
在 TS 中接口是一个非常灵活的概念, 除了可用于对类的一部分行为进行抽象, 也常用于对 对象的形状(Shape)
进行描述
interface Person {
// 只读属性初始化时必须赋值, 赋值后不可更改
readonly id: number,
name: string,
age?: number,
gender?: string,
[propName: string]: any
}
let tom: Person = {
name: 'Tom',
age: 12,
gender: 'man',
hobby: '奥术大师多',
lll: '123撒大声地'
}
一旦定义了任意属性,那么确定属性和 可选属性 的类型都必须是它的类型的子集
interface Person {
age?: number,
[propName: string]: string
}
// age 可选属性的类型是 number 与任意属性的类型产生了冲突就是报错
let tom: Person = {
age: 12,
hobby: '奥术大师多',
lll: '123撒大声地'
}
// 报错, 参数必须都是 number
let fibonacci: number [] = [1, 1, 2, 3, 5, '啊'];
// 通过, 参数可以是 number 或则是 string
let fibonacci: (number | string) [] = [1, 1, 2, 3, 5, '啊'];
// 报错, 不可以通过各种方式加入其它类型数据
fibonacci.push([213,12,3])
// 通过
let fibonacci : Array<number|string> = [123,123,12,3, '1231'];
// 只要 index 的类型是 number, 那么值得类型必须是 number
interface MyArray {
[index: number]: number
}
let aaa : MyArray = [123,12,412]
let list: any[] = ['Xcat Liu', 25, { website: 'http://xcatliu.com' }];
// 这只定义了俩个函数, 多传参数或者少传参数都会报错
function sum(x: number, y: number): number {
return x + y;
}
let mySum = function (x: number, y: number): number {
return x + y;
};
interface SearchFunc {
(source: string, subString: string): boolean;
}
let mySearch: SearchFunc;
mySearch = function(source: string, subString: string) {
return source.search(subString) !== -1;
}
// 可选参数必选在必须参数后面, 否则会报错
function buildName(firstName: string, lastName?: string) {
if (lastName) {
return firstName + ' ' + lastName;
} else {
return firstName;
}
}
let tomcat = buildName('Tom', 'Cat');
let tom = buildName('Tom');
function buildName(firstName: string = 'Tom', lastName: string) {
return firstName + ' ' + lastName;
}
let tomcat = buildName('Tom', 'Cat');
let cat = buildName(undefined, 'Cat');
function push(array: any[], ...items: any[]) {
items.forEach(function(item) {
array.push(item);
});
}
let a = [];
push(a, 1, 2, 3);
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('');
}
}
比如我们访问一个不确定类型的属性或方法时, 比如:
function getLength(something: string | number): number {
if ((<string>something).length) {
return (<string>something).length;
} else {
return something.toString().length;
}
}
当使用第三方库时, 需引用其声明文件, 才能获得对应的代码补全和接口提示等功能
比如在 ts
中使用 jQuery
, 需要使用 declare var
来定义其类型:
// 定义 jQuery, 参数是字符串, 返回值是任意类型
declare var jQuery: (selector: string) => any;
/**
* declare var 并没有真的定义一个变量,
* 只是定义了全局变量 `jQuery` 的类型,
* 仅仅回用于编译时的检查.
*/
.d.ts
为后缀。// src/jQuery.d.ts
declare var jQuery: (selector: string) => any;
/path/to/project
├── README.md
├── src
| ├── index.ts
| └── jQuery.d.ts
└── tsconfig.json
假如仍然无法解析,那么可以检查下 tsconfig.json
中的 files
、include
和 exclude
配置,确保其包含了 jQuery.d.ts
文件。
这只是全局变量模式的声明文件形式.
推荐的是使用 @types
统一管理第三方库的声明文件.
@types
的使用方式很简单, 直接用 npm
安装对应的声明模块即可, 类似:
npm install @types/jquery --save-dev
当一个第三方库没有提供声明文件时, 我们就需要自己书写声明文件了.
在不同的场景下, 声明文件的内容和使用方式会所区别.
库的使用场景主要有以下几种:
<script>
标签引入第三方库,注入全局变量ES6
模块规范<script>
标签引入,又可以通过 import
导入import
导入后,可以改变另一个模块的结构<script>
标签引入后,改变一个全局变量的结构。比如为 String.prototype
新增了一个方法使用全局变量的声明文件时, 如果是以 npm install @types/xxx --save-dev
安装的, 则不需要任何配置.
全局变量的声明文件主要有一下几种语法
declare var
声明全局变量declare function
声明全局方法declare class
声明全局类declare enum
声明全局枚举类型declare namespace
声明全局对象(含有子属性)interface
和 type
声明全局类型export {
name,
getName,
Animal,
Directions,
Options
}
export namespace foo {
const name: string;
namespace bar {
function baz(): string;
}
}
export default function foo(): string;
// 整体导出
module.exports = foo;
// 单个导出
exports.bar = bar;
// 整体导入
const foo = require('foo');
// 单个导入
const bar = require('foo').bar;
// 整体导入
import * as foo from 'foo';
// 单个导入
import { bar } from 'foo';
// 整体导入
import foo = require('foo');
// 单个导入
import bar = require('foo').bar;
注意,只有 function
、class
和 interface
可以直接默认导出,其他的变量需要先定义出来,再默认导出:
type Name = string;
type NameResolver = () => string;
type NameOrResolver = Name | NameResolver;
function getName(n: NameOrResolver): Name {
if (typeof n === 'string') {
return n;
} else {
return n();
}
}
以输入值得类型作为输出值得类型
// 函数名后的 <T>, 其中 T 用来指任意输入的类型, 在后面的输入 value: T 和输出 Array<T> 中即可使用.
function createArray<T>(length: number, value: T): Array<T> {
let result: T[] = [];
for (let i = 0; i < length; i++) {
result[i] = value;
}
return result;
}
createArray<string>(3, 'x'); // ['x', 'x', 'x']
O(f(n))
表示运行算法所需要执行的指令数, 和f(n)
成正比二分查找法 O(logn) => 所执行指令书: a * logn
寻找数组中的最大/最小值 O(n) => 所执行指令书: b * n
归并排序算法 O(nlogn) => 所执行指令书: c * nlogn
选择排序法 O(n^2) => 所执行指令书: d * n^2
算法A: O(n)
所需执行指令数: 10000*n
算法B: O(n^2)
所需之星指令数: 10 * n^2
n | A指令数 10000n | B指令数 10n^2 | 倍数 |
---|---|---|---|
10 | 10^5 | 10^3 | 100 |
100 | 10^6 | 10^5 | 10 |
1000 | 10^7 | 10^7 | 1 |
10000 | 10^8 | 10^9 | 0.1 |
10^5 | 10^9 | 10^11 | 0.01 |
10^6 | 10^10 | 10^13 | 0.001 |
时间复杂度 O 衡量的是量级上的差距,
这种量级上的差距表现在,当数据达到一个临界点时,
时间复杂度低的算法,就一定比时间复杂度高的算法要快,
而且n越大,差距就越大
再有一些复杂度很高的算法可能有常数级小的优势, 数据规模小的时候是有意义的.
对于所有的高级排序算法, 当数据规模小到一定程度时可选择插入排序法来进行优化, 优化的效率大约能有10%-15%左右.
O(Cn) -> O(n2) -> O(n) -> O(logn) -> O(1)
符号 | 名称 |
---|---|
O (1) | 常数 (阶,下同) |
O (logn) | 对数 |
O [(logn)] | 多对数 |
O (n) | 线性 ,次线性 |
O (n log* n) | log* n 为迭代对数 |
O (nlogn) | 线性对数 ,或对数线性、拟线性、超线性 |
O (n2) | 平方 |
O (n%), Integer (c> 1) | 多项式 ,有时叫作'代数'(阶) |
O (cn) | 指数 ,有时叫作'几何'(阶) |
O (n!) | 阶乘 ,有时叫做'组合'(阶) |
在学术界,严格的讲, O(f(n)) 表示算法执行的上界
什么是算法执行的上界?
归并排序算法的时间复杂度是O(nlogn)的, 同时也是O(n^2)
在业界,我们就是用O来表示算法执行的最低上界
.
虽然在学术界归并排序算法复杂度是O(n^2)是对的, 但是在业界我们一般不会说你归并排序是O(n^2)的, 而是O(nlogn).
这不是一个严谨不严谨的问题, 是在业界约定俗成的问题, 世界各大公司都是默认这样的
如果我们设计了一个算法,我们以量级最高的时间复杂度作为主导
比如
O (nlogn + n) = O(nlogn)
O (nlogn + n^2) = O(n^2)
StatelessWidget 只负责显示的
StatefulWidget 组件是可以拥有状态的
在Dart中 甚至连 数字、方法和null都是对象
所有的对象都继承于Object类
Dart动态类型语言, 尽量给变量定义一个类型,会更安全,没有显示定义类型的变量在 debug 模式下会类型会是dynamic(动态的)
Dart会在运行之前解析你的所有代码,指定数据类型和编译时的常量,可以提高运行速度
Dart中的类和接口是统一的,类即接口,你可以继承一个类,也可以实现一个类(接口),自然也包含了良好的面向对象和并发编程的支持
Dart函数
支持顶级函数 (例如main())
支持在类中定义函数, 如静态函数和实例函数
还可以在方法中定义方法(嵌套方法或者局部方法)
类似的,Dart支持顶级变量,以及依赖于类或对象(静态变量和实例变量)变量。实例变量有时被称为域或属性
Dart不具备关键字public,protected和private。如果一个标识符以下划线()开始,那么它和它的库都是私有的
标识符可以字母或()开始,或者是字符加数字的组合开头
声明变量
var name = 'name';
dynamic name1 = 'name1';
String name2 = 'name2';
// 变量的赋值
name = 'a';
name1 = 'a1';
name2 = 'a2';
var name;
dynamic name1;
String name2;
在声明变量的时候,你可以选择加上具体的类型:
String name2 = 'name2';
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.