TypeScript学习笔记

使用VScode调试运行TS的方法:

1.安装node.js,会一起安装好npm,然后使用命令npm install -g typescript 安装ts环境。

2.在ts项目文件夹下使用命令 tsc --init 生成tsconfig.json文件,文件中outDir的值为转换的js文件目录。sourceMap为JS何TS的映射表,使调试时能正确的显示TS行号。

3.按照如下步骤就能在改动TS后自动生成JS

在这里插入图片描述

4.最后把调试配置文件launch.json中的program设置为生成的js文件即可。


VScode设置:

1.自动补全函数参数和括号

typescript.suggest.completefunctioncalls:true

2.活动代码段是否阻止快速建议设为false,就能在使用代码片段后出现补全提示

editor.suggest.snippetspreventquicksuggestions:false


语法关键点记录:

1、字符串使用``可定义多行文本和内嵌表达式。

let words: string = `您好,今年是 ${ name } 发布 ${ years + 1} 周年`;//其中name和years为变量

2、null和undefined使用typeof得到的值分别为object和undefined。

3、"use strict";使用严格模式,对未定义的变量赋值将会报错。不允许删除变量或对象。不允许删除函数。不允许变量重名。不允许使用转义字符。等等。

好处:

  • 消除代码运行的一些不安全之处,保证代码运行的安全;
  • 提高编译器效率,增加运行速度;

4、new 的作用原理

5、this 的原理

6、变量名除了_还能使用$。

7、类型断言,可绕过编译阶段的类型检查。用法有两种:1.尖括号:<type>varName,2.as:varName as type。

let str: string = "1";
let str2: number = <any>str;
//或者
//let str2: number = str as any;
console.log(typeof(str) + " " + typeof(str2));//输出为:string string

8、let与var

let定义的变量才会有块级作用域,比如for循环内定义的变量,在循环体外部无法访问。let变量不允许重名,不存在变量提升(使用前必须定义)。

9、数字字符串和数字相等?

  • "==="运算符也称为严格相等运算符,它用来检测两个操作数是否严格相等。该运算符首先计算其操作数的值,然后比较这两个值,比较过程中并不会首先对操作符进行任何类型转换。
  • "=="运算符称为相等运算符,用来检测两个操作数是否相等,这里“相等”的定义非常宽松,可以允许进行类型转换。相等运算符"=="和"==="运算符类似,但相等运算符的比较并不严格。如果两个操作数不是同一类型,那么相等运算符会尝试对操作数进行一些类型转换,然后进行比较。例如:
  1. 如果一个值是null,另一个是undefined,则他们相等。
  2. 如果一个值是数字,另一个是字符串,比较时现将字符串转换为数字,然后使用转换后的值比较。
  3. 如果其中一个值是true,则将其转换为1再进行比较。
  • 因为在使用过程中"=="会出现类型转换,所以为避免在使用"=="过程中出现意想不到的类型转换而影响比较结果,建议除特殊情况外全部用"==="。
  • 另一个例外是NaN这个特殊的Number与所有其他值都不相等,包括它自己:
    NaN === NaN; // false

  唯一能判断NaN的方法是通过isNaN()函数:

    isNaN(NaN); // true

10、几种遍历方式

let list = [1, "2", true];
for (const k in list) {
    console.log(k);
}//输出:0, 1, 2

for (const v of list) {
    console.log(v);
}//输出:1, 2, true

list.every((v, k) => {
    console.log(k, v);
    return true;//没有返回或者返回false跳出循环
});//输出:0 1, 1 2, 2 true

12、函数定义时形参后面加问号为可选参数,可选参数必须放后面。不传可选参数时值为undefined。与普通可选参数不同的是,带默认值的参数不需要放在必选参数的后面。

//可选参数必须放在必选参数后面
function greeter(content: string, extra?: string): string{
    return "name" + " say: " + content;
}
console.log(greeter("hello"));

//带默认值的参数可以放在必选参数前面
function greeter2(content: string = "defualt", extra: string): string{
    return "name" + " say: " + content;
}
console.log(greeter2(undefined, "hello"));

形参前面加...为剩余参数,可接受不定数量的参数,剩余参数必须为数组。

function greeter(content: string, ...extra: string[]): string{
    let str: string = "";
    for (let i = 0; i < extra.length; ++i) {
        str += extra[i];
    }
    return " say: " + content + " extra: " + str;
}

13、条件判断中 0、""、false、null、undefined、NaN 都为假。

14、特别的函数定义 new Function(),可用于将字符串转换为函数。

let myFunction = new Function("a", "b", "return a * b");//Function接受多个字符串参数,最后一个作为函数体,前面的作为形参。
console.log(myFunction(3, 4));//输出:12

15、匿名函数语法糖 箭头函数(lambda函数),有些需要定义函数类型的地方只能使用箭头函数。箭头函数还会自动捕获this。

//正常写法
let addFunc1 = function (x: number, y: number) {
    return x + y;
}
//箭头函数写法
let addFunc2 = (x: number, y: number) => {
    return x + y;
}



let deck = {
    suits: ["hearts", "spades", "clubs", "diamonds"],
    cards: Array(52),
    createCardPicker: function() {
        //此处如果不使用箭头函数编译会报错。在外边调用此函数时this会是window,在严格模式下会是undefined
        return () => {
            let pickedCard = Math.floor(Math.random() * 52);
            let pickedSuit = Math.floor(pickedCard / 13);

            return {suit: this.suits[pickedSuit], card: pickedCard % 13};
        }
        //下面为编译成JS后的代码。看完我们就知道为什么this能正常使用了。
        // var _this = this;//帮我把this捕获了,并且在后面使用this的地方都替换外使用_this
        // return function () {
        //     var pickedCard = Math.floor(Math.random() * 52);
        //     var pickedSuit = Math.floor(pickedCard / 13);
        //     return { suit: _this.suits[pickedSuit], card: pickedCard % 13 };
        // };
    }
}

let cardPicker = deck.createCardPicker();
let pickedCard = cardPicker();

console.log("card: " + pickedCard.card + " of " + pickedCard.suit);

16、基本包装类型

一、什么是基本包装类型?
为了便于操作基本类型值,ECMAScript还提供了3个特殊的引用类型: Boolean、Number、String。这些类型与其他内置对象类型相似,但同时具有各自的基本类型相应的特殊行为。实际上,每当读取一个基本类型值得时候,后台就会创建一个对应的基本包装类型的对象,从而让我们能够调用一些方法来操作这些数据。
包装类型,是一个专门封装原始类型的值,并提供对原始类型的值执行操作的API对象

二、其他内置对象与基本包装类型对象的区别?
普通的内置对象与基本包装类型的主要区别就是对象的生命期使用new操作符创建的引用类型的实例,在执行流离开当前作用域之前都一直保存在内存中,而自动创建的基本包装类型的对象,则只是存在于一行代码的执行瞬间,然后立即被立即销毁。这意味着我们不能再运行时为基本包装类型值添加属性和方法。
————————————————
版权声明:本文为CSDN博主「冯小东」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/baidu_25343343/article/details/54849475

17、布尔包装类型Boolean需要注意的点。

console.log(false ? "true" : "false");                  //false
console.log(Boolean(false) ? "true" : "false");         //false
console.log(new Boolean(false) ? "true" : "false");     //true

18、数组相关。

let list: number[] = [1, 2, 3, 4];
console.log(list.length);       //4
console.log(list[5]);           //undefined
list[100] = 5;
console.log(list.length);       //101
console.log(list[5]);           //undefined


//数字索引会被转换为字符串索引
 let list: any = {}
 list[1] = 1;
 list["1"] = 2;
 console.log(list[1], list["1"]);//2 2

19、联合类型使用"|"使变量可以赋值为多种类型。let val: number|string;

20、接口interface 只是定义一种属于TS的规范,用于编译过程的检查,最终转换成JS是不存在接口的。

21、数组接口、函数接口

//数组接口
interface namelist { 
   [index:number]:string 
} 
let list2:namelist = ["John",1,"Bran"] / 错误元素 1 不是 string 类型

interface ages { 
   [index:string]:number 
} 
let agelist:ages = {}; 
agelist["John"] = 15   // 正确 
agelist[2] = "nine"   // 错误


//函数接口
interface SearchFunc {
  (source: string, subString: string): boolean;
}
let mySearch: SearchFunc;
mySearch = function(source: string, subString: string) {
  let result = source.search(subString);
  return result > -1;
}


//混合类型
interface Counter {
    (start: number): string;
    interval: number;
    reset(): void;
}

function getCounter(): Counter {
    let counter = <Counter>function (start: number) { };
    counter.interval = 123;
    counter.reset = function () { };
    return counter;
}

let c = getCounter();
c(10);
c.reset();
c.interval = 5.0;

22、prototype 原型,用来继承父类方法。类似lua的元表

23、hasOwnProperty 检查自身的属性和方法,不向父类原型中查找。

24、枚举相关,枚举编译成JS后实际上是生成了一个枚举名与枚举值正反映射的对象。而const声明的枚举是常量枚举,不会生成额外的对象,而是用枚举常量值替换掉代码中的枚举,不存在额外的属性访问,由于并没有真的构造一个枚举类,所以也无法反向映射枚举值。

enum Month { Jan, Feb, Mar, Apr }
//编译后生成的枚举对象
// var Month;
// (function (Month) {
//     Month[Month["Jan"] = 0] = "Jan";
//     Month[Month["Feb"] = 1] = "Feb";
//     Month[Month["Mar"] = 2] = "Mar";
//     Month[Month["Apr"] = 3] = "Apr";
// })(Month || (Month = {}));

const enum Month { Jan, Feb, Mar, Apr }
let month = [Month.Jan, Month.Feb, Month.Mar]; 
// 编译的结果Month 是没有产生额外对象的,只是替换掉了枚举改为数字
//var month = [0 /* Jan */, 1 /* Feb */, 2 /* Mar */];

25、鸭子类型(Duck Typing)

鸭子类型(英语:duck typing)是动态类型的一种风格,是多态(polymorphism)的一种形式。

在这种风格中,一个对象有效的语义,不是由继承自特定的类或实现特定的接口,而是由"当前方法和属性的集合"决定。

可以这样表述:

"当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。"

在鸭子类型中,关注点在于对象的行为,能作什么;而不是关注对象所属的类型。例如,在不使用鸭子类型的语言中,我们可以编写一个函数,它接受一个类型为"鸭子"的对象,并调用它的"走"和"叫"方法。在使用鸭子类型的语言中,这样的一个函数可以接受一个任意类型的对象,并调用它的"走"和"叫"方法。如果这些需要被调用的方法不存在,那么将引发一个运行时错误。任何拥有这样的正确的"走"和"叫"方法的对象都可被函数接受的这种行为引出了以上表述,这种决定类型的方式因此得名。

//关于鸭子类型的测试

interface IPerson {
    name: string;
    age: number;
    greeter(content:string):void;
}

enum Sex {male, female};

class PersonClass {
    name: string;
    age: number;
    sex:Sex;
    constructor(name: string = "defaultName", age: number = 18) {
        this.name = name;
        this.age = age;
        this.sex = Sex.female;
    }
    greeter(content: string, ...extra: string[]): string{
        return this.name + " say: " + content + " extra: " + extra;
    }
}
let person = new PersonClass("mike", 20);

let personObj = {
    name: "mike",
    age: 20,
    sex: Sex.female,
    greeter: function (content:string, ...extra:string[]) { return this.name + " say: " + content + " extra: " + extra; },
    getName: function () {return this.name;}
}

//以下三种情况均能通过编译且输出一致
function testObj(person: {name: string, age: number, greeter: (content:string, ...extra:string[])=>string}) {
    return person.greeter("hello");
}
console.log(testObj(personObj));
console.log(testObj(person));

function testObj2(person: PersonClass) {
    return person.greeter("hello");
}
console.log(testObj2(personObj));
console.log(testObj2(person));

function testObj3(person: IPerson) {
    return person.greeter("hello");
}
console.log(testObj3(personObj));
console.log(testObj3(person));

但是,当存在private或protected属性的时情况就不同了

class Animal {
    private name: string;
    constructor(theName: string) { this.name = theName; }
}

class Rhino extends Animal {
    constructor() { super("Rhino"); }
}

class Employee {
    private name: string;
    constructor(theName: string) { this.name = theName; }
}

let animal = new Animal("Goat");
let rhino = new Rhino();
let employee = new Employee("Bob");

function testFunc(animel: Animal) {
    return animal;
}

testFunc(rhino);//正确
testFunc(employee);// 错误: Animal 与 Employee 不兼容.
let animelObj = {name: "xxx"};
testFunc(animelObj);//错误

27、解构用法(语法糖)

//数组解构
let list = [1, 2, 3 ,4]
let [x,,y] = list;
console.log(x, y);                //1 3

//配合剩余变量使用
let [first, ...rest] = [1, 2, 3, 4];
//编译成JS后:
//var _a = [1, 2, 3, 4], first = _a[0], rest = _a.slice(1);
console.log(first); //1
console.log(rest);  //[ 2, 3, 4 ]

//交换两个变量的值
let x = 1, y = 2;
[x, y] = [y, x];
console.log(x, y);                //2 1

//解构对象
let personObj = {
    name: "mike",
    age: 20,
    sex: Sex.female,
    telephone: undefined,
    greeter: function (content:string, ...extra:string[]) { return this.name + " say: " + content + " extra: " + extra; },
    getName: function () {return this.name;}
}
let {sex: sseexx, age, telephone = 10068,...rest} = personObj;//这里冒号后面不是指类型,是将属性重命名。可以使用=设置默认值,当取值为undefined时使用默认值。
//编译成JS后:
//var sseexx = personObj.sex, age = personObj.age, _a = personObj.telephone, telephone = _a === void 0 ? 10068 : _a, rest = __rest(personObj, ["sex", "age", "telephone"]);
console.log(Sex[sseexx], age, rest.getName());//female 20 10086 mike

28、展开用法

//展开数组
let first = [1, 2];
let second = [3, 4];
let bothPlus = [0, ...first, ...second, 5];//[1,2,3,4,5]

//展开对象
let personObj = {
    name: "mike",
    age: 20,
    sex: Sex.female,
    greeter: function (content:string, ...extra:string[]) { return this.name + " say: " + content + " extra: " + extra; },
    getName: function () {return this.name;}
}
let newPersonObj = {name: "lisa", ...personObj}//展开后name属性会覆盖前面的。
console.log(newPersonObj.name)//mike
console.log(newPersonObj.getName())//mike   这里要注意如果是展开的一个类(Class),是不能获得方法的!。

29、额外的属性检查

interface SquareConfig {
    color?: string;
    width?: number;
}

function createSquare(config: SquareConfig): {color: string, area: number} {
    let square = { color: "white", area: 100}
    if (config.color)
        square.color = config.color;
    if (config.width)
        square.area = config.width * config.width;
    return square
}

console.log(createSquare({color: "red", width: 5, isBig: true}));//这种写发会触发额外属性检查报错,isBig不属于SquareConfig

//三种避免额外属性检查的方法
let obj = {color: "red", width: 5, isBig: true};
console.log(createSquare(obj));//1、使用变量
console.log(createSquare(<SquareConfig>{color: "red", width: 5, isBig: true}));//2、使用类型断言

// interface SquareConfig {
//     color?: string;
//     width?: number;
//     [propName: string]: any;//3、增加字符串索引签名,表示可以存着任意数量的属性
// }

30、参数属性

在构造函数里使用private name: string参数来创建和初始化name成员。 我们把声明和赋值合并至一处。

参数属性通过给构造函数参数添加一个访问限定符来声明。 使用private限定一个参数属性会声明并初始化一个私有成员;对于publicprotected来说也是一样。

class Animal {
    //private name: string;
    //constructor(name: string) { this.name = name }
    constructor(private name: string) { }
    move(distanceInMeters: number) {
        console.log(`${this.name} moved ${distanceInMeters}m.`);
    }
}

31、存取器

TypeScript支持通过getters/setters来截取对对象成员的访问。 它能帮助你有效的控制对对象成员的访问。只带有get不带有set的存取器自动被推断为readonly。

下面来看如何在一个简单的类使用getset。

let passcode = "secret passcode";

class Employee {
    private _fullName: string;

    get fullName(): string {
        return this._fullName;
    }

    set fullName(newName: string) {
        if (passcode && passcode == "secret passcode") {
            this._fullName = newName;
        }
        else {
            console.log("Error: Unauthorized update of employee!");
        }
    }
}

let employee = new Employee();
employee.fullName = "Bob Smith";
if (employee.fullName) {
    console.log(employee.fullName);
}

32、泛型约束

function loggingIdentity<T>(arg: T): T {
    console.log(arg.length);  // 报错,T类型不一定存在length属性
    return arg;
}

//使用约束后
interface Lengthwise {
    length: number;
}

function loggingIdentity<T extends Lengthwise>(arg: T): T {
    console.log(arg.length);  // 正确,现在T类型必定存在length属性
    return arg;
}

在泛型里使用类类型

在TypeScript使用泛型创建工厂函数时,需要引用构造函数的类类型。比如,

class Person {
    name: string;
    constructor(name: string = "default") {
        this.name = name;
    }
}

function create<T>(c: {new(): T; }): T {
    return new c();
}
let person = create(Person);

33、高级类型

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值