强类型和弱类型的语言有什么区别
强类型语言也称为强类型定义语言。是一种总是强制类型定义的语言,要求变量的使用要严格符合定义,所有变量都必须先定义后使用。
java、.NET、C++等都是强制类型定义的。也就是说,一旦一个变量被指定了某个数据类型,如果不经过强制转换,那么它就永远是这个数据类型了。
例如你有一个整数,如果不显式地进行转换,你不能将其视为一个字符串。
与其相对应的是弱类型语言:数据类型可以被忽略的语言。它与强类型定义语言相反, 一个变量可以赋不同数据类型的值。
强类型语言有更强的类型约束,而弱类型语言几乎没有约束.
强类型不允许随意的类型转换,弱类型是允许的
弱类型语言
弱类型语言也称为弱类型定义语言。与强类型定义相反。像vb,php等就属于弱类型语言·
一种类型可以被忽略的语言,与强类型定义相反。VBScript是弱类型定义的。在VBScript中,可以将字符串‘12’和整数3进行连接得到字符串‘123’,然后可以把它看成整数123,而不需要显示转换。
但其实它们的类型没有改变,VB只是在判断出一个表达式含有不同类型的变量之后,自动在这些变量前加了一个clong()或(int)()这样的转换函数而已。能做到这一点其实是归功于VB的编译器的智能化而已,这并非是VB语言本身的长处或短处。
泛型
静态语言是指在编译时变量的数据类型即可确定的语言,多数静态类型语言要求在使用变量之前必须声明数据类型。例如:C++、Java、Delphi、C#等。
动态语言是在运行时确定数据类型的语言。变量使用之前不需要类型声明,通常变量的类型是被赋值的那个值的类型。例如:Python、Ruby、Perl等
指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定具体类型的一种特性。
引入
下面创建一个函数, 实现功能: 根据指定的数量 count
和数据 value
, 创建一个包含 count
个 value
的数组 不用泛型的话,这个函数可能是下面这样:
function createArray(value: any, count: number): any[] {
const arr: any[] = []
for (let index = 0; index < count; index++) {
arr.push(value)
}
return arr
}
const arr1 = createArray(11, 3)
const arr2 = createArray('aa', 3)
console.log(arr1[0].toFixed(), arr2[0].split(''))
使用函数泛型
function createArray2<T>(value: T, count: number) {
const arr: Array<T> = []
for (let index = 0; index < count; index++) {
arr.push(value)
}
return arr
}
const arr3 = createArray2<number>(11, 3)
console.log(arr3[0].toFixed())
// console.log(arr3[0].split('')) // error
const arr4 = createArray2<string>('aa', 3)
console.log(arr4[0].split(''))
// console.log(arr4[0].toFixed()) // error
多个泛型参数的函数
一个函数可以定义多个泛型参数
function swap<K, V>(a: K, b: V): [K, V] {
return [a, b]
}
const result = swap<string, number>('abc', 123)
console.log(result[0].length, result[1].toFixed())
泛型接口
在定义接口时, 为接口中的属性或方法定义泛型类型 在使用接口时, 再指定具体的泛型类型
interface IbaseCRUD<T> {
data: T[]
add: (t: T) => void
getById: (id: number) => T
}
class User {
id?: number //id主键自增
name: string //姓名
age: number //年龄
constructor(name, age) {
this.name = name
this.age = age
}
}
class UserCRUD implements IbaseCRUD<User> {
data: User[] = []
add(user: User): void {
user = { ...user, id: Date.now() }
this.data.push(user)
console.log('保存user', user.id)
}
getById(id: number): User {
return this.data.find(item => item.id === id)
}
}
const userCRUD = new UserCRUD()
userCRUD.add(new User('tom', 12))
userCRUD.add(new User('tom2', 13))
console.log(userCRUD.data)
泛型类
在定义类时, 为类中的属性或方法定义泛型类型 在创建类的实例时, 再指定特定的泛型类型
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
}
let myGenericString = new GenericNumber<string>()
myGenericString.zeroValue = 'abc'
myGenericString.add = function(x, y) {
return x + y
}
console.log(myGenericString.add(myGenericString.zeroValue, 'test'))
console.log(myGenericNumber.add(myGenericNumber.zeroValue, 12))
泛型约束
如果我们直接对一个泛型参数取 length
属性, 会报错, 因为这个泛型根本就不知道它有这个属性
// 没有泛型约束
function fn<T>(x: T): void {
// console.log(x.length) // error
}
我们可以使用泛型约束来实现
interface Lengthwise {
length: number
}
// 指定泛型约束
function fn2<T extends Lengthwise>(x: T): void {
console.log(x.length)
}
我们需要传入符合约束类型的值,必须包含必须 length
属性:
fn2('abc')
// fn2(123) // error number没有length属性
接口
TypeScript 的核心原则之一是对值所具有的结构进行类型检查。我们使用接口(Interfaces)来定义对象的类型。接口是对象的状态(属性)和行为(方法)的抽象(描述)
接口初探
需求: 创建人的对象, 需要对人的属性进行一定的约束
id是number类型, 必须有, 只读的
name是string类型, 必须有
age是number类型, 必须有
sex是string类型, 可以没有
下面通过一个简单示例来观察接口是如何工作的:
/*
在 TypeScript 中,我们使用接口(Interfaces)来定义对象的类型
接口: 是对象的状态(属性)和行为(方法)的抽象(描述)
接口类型的对象
多了或者少了属性是不允许的
可选属性: ?
只读属性: readonly
*/
/*
需求: 创建人的对象, 需要对人的属性进行一定的约束
id是number类型, 必须有, 只读的
name是string类型, 必须有
age是number类型, 必须有
sex是string类型, 可以没有
*/
// 定义人的接口
interface IPerson {
id: number
name: string
age: number
sex: string
}
const person1: IPerson = {
id: 1,
name: 'tom',
age: 20,
sex: '男'
}
类型检查器会查看对象内部的属性是否与 IPerson 接口描述一致, 如果不一致就会提示类型错误。
可选属性
接口里的属性不全都是必需的。 有些是只在某些条件下存在,或者根本不存在。
interface IPerson {
id: number
name: string
age: number
sex?: string
}
带有可选属性的接口与普通的接口定义差不多,只是在可选属性名字定义的后面加一个 ?
符号。
可选属性的好处之一是可以对可能存在的属性进行预定义,好处之二是可以捕获引用了不存在的属性时的错误。
const person2: IPerson = {
id: 1,
name: 'tom',
age: 20
// sex: '男' // 可以没有
}
只读属性
一些对象属性只能在对象刚刚创建的时候修改其值。 你可以在属性名前用 readonly
来指定只读属性:
interface IPerson {
readonly id: number
name: string
age: number
sex?: string
}
一旦赋值后再也不能被改变了。
const person2: IPerson = {
id: 2,
name: 'tom',
age: 20
// sex: '男' // 可以没有
// xxx: 12 // error 没有在接口中定义, 不能有
}
person2.id = 2 // error
readonly vs const
最简单判断该用 readonly
还是 const
的方法是看要把它做为变量使用还是做为一个属性。 做为变量使用的话用 const
,若做为属性则使用 readonly
。
函数类型
接口能够描述 JavaScript 中对象拥有的各种各样的外形。 除了描述带有属性的普通对象外,接口也可以描述函数类型。
为了使用接口表示函数类型,我们需要给接口定义一个调用签名。它就像是一个只有参数列表和返回值类型的函数定义。参数列表里的每个参数都需要名字和类型。
/*
接口可以描述函数类型(参数的类型与返回的类型)
*/
interface SearchFunc {
(source: string, subString: string): boolean
}
这样定义后,我们可以像使用其它接口一样使用这个函数类型的接口。 下例展示了如何创建一个函数类型的变量,并将一个同类型的函数赋值给这个变量。
const mySearch: SearchFunc = function(source: string, sub: string): boolean {
return source.search(sub) > -1
}
console.log(mySearch('abcd', 'bc'))
类类型
在类中实现接口
与 C# 或 Java 里接口的基本作用一样,TypeScript 也能够用它来明确的强制一个类去符合某种契约。
-
/* 类类型: 实现接口 1. 一个类可以实现多个接口 2. 一个接口可以继承多个接口 */ interface Alarm { alert(): any } interface Light { lightOn(): void lightOff(): void } // 再类中使用接口的方法 class Car implements Alarm { alert() { console.log('Car alert') } }
一个类可以实现多个接口
class Car2 implements Alarm, Light {
alert() {
console.log('Car alert')
}
lightOn() {
console.log('Car light on')
}
lightOff() {
console.log('Car light off')
}
}
接口继承接口
和类一样,接口也可以相互继承。 这让我们能够从一个接口里复制成员到另一个接口里,可以更灵活地将接口分割到可重用的模块里。
interface LightableAlarm extends Alarm, Light {}
总结
TypeScript 是 JavaScript 的一个超集,支持 ECMAScript 6 标准。
TypeScript 由微软开发的自由和开源的编程语言。
TypeScript 设计目标是开发大型应用,它可以编译成纯 JavaScript,编译出来的 JavaScript 可以运行在任何浏览器上。
(个人查看笔记学习笔记)