美文网首页TypeScript我爱编程
TypeScript 特性学习摘要

TypeScript 特性学习摘要

作者: 舌尖上的大胖 | 来源:发表于2017-01-15 00:09 被阅读0次

    Callback Hell

    首先,向我的偶像 Anders Hejlsberg 致敬!
    本文是根据 慕课网TypeScript入门 的视频教程整理的摘要,详情请访问原作视频。
    完整信息参见官方文档 TypeScript Documentation
    源码:TypeScript 在 GitHub 的仓库

    【目的】
    本文档目的是让熟悉 JavaScript 的同学快速了解 TypeScript 特色,准确信息请务必参考所使用版本 TypeScript 对应的官方文档。

    [TOC]

    一、开发环境
    • 本地

      • 编译环境安装:
      npm install -g typescript
      
      • TypeScript REPL 及执行环境安装:
      npm install -g ts-node
      
    • 在线:TypeScript Playground

    二、变量声明及作用域

    TypeScript可以使用兼容JavaScript的var声明方式,也可以使用letlet解决了JavaScript臭名昭著的作用域问题。代码如下:

    // TypeScript,使用 var 声明变量,不支持块级作用域
    var n = 1;
    {
        var n = 200;
        console.log('块级变量 n 值:' + n);
    }
    console.log('全局变量 n 值:' + n);
    

    被编译成的 JavaScript,与上面的 TypeScript 一样:

    // 使用 var 定义变量的 TypeScript 编译成的 JavaScript
    var n = 1;
    {
        var n = 200;
        console.log('块级变量 n 值:' + n);
    }
    console.log('全局变量 n 值:' + n);
    

    期待的执行结果为:

    块级变量 n 值:200
    全局变量 n 值:1
    

    实际执行结果为:

    块级变量 n 值:200
    全局变量 n 值:200
    

    如果使用 TypeScript 的 let 声明变量:

    // TypeScript,使用 let 声明变量,不支持块级作用域
    let n = 1;
    {
        let n = 200;
        console.log('块级变量 n 值:' + n);
    }
    console.log('全局变量 n 值:' + n);
    

    被编译成的 JavaScript:

    // 使用 let 定义变量的 TypeScript 编译成的 JavaScript
    var n = 1;
    {
        var n_1 = 200;
        console.log('块级变量 n 值:' + n_1);
    }
    console.log('全局变量 n 值:' + n);
    

    发现编译器为块内变量重新命名,执行结果符合预期:

    块级变量 n 值:200
    全局变量 n 值:1
    
    三、字符串新特性
    • 多行字符串,使用反引号 \ 进行引用,解决 JavaScript 中使用\n+` 拼接的麻烦
    var str = `line 1
    line 2
    ...
    line n`;
    
    • 字符串模板,在 \ ` 引用的字符串内,使用 ${变量/函数} 方式,直接引入执行结果
    var content = 'abc';
    console.log(`<div>
    ${content}
    </div>`);
    
    • 自动拆分字符串

    调用时,自动将字符串中的表达式值作为参数传入函数

    function myTest(template, param1, param2) {
        console.log(template);
        console.log(param1);
        console.log(param2);
    }
    
    myTest`测试字符串分割,传入参数 ${111} and ${222} ,再看结果`
    

    从执行结果看,template 是一个数组,这个数组是被字符串中的几个表达式给分割成段的结果。

    四、函数
    • 参数类型
    function testParam(sUserName: string, nUserAge: number): string {
        var ret = `User name: ${sUserName}
    User age: ${nUserAge}`;
        return ret;
    }
    
    var s: string = testParam('bahb', 2);
    console.log(s);
    
    • 参数默认值

    带有默认值的参数,必须放在参数列表最后

    function testArgumentDefaultValue(arg1, arg2, arg3 = 'arg 3 default value') {
        console.log(arg1);
        console.log(arg2);
        console.log(arg3);
        console.log('===============');
    }
    testArgumentDefaultValue('1', '2', '3');
    testArgumentDefaultValue('5', '6');
    
    • 任意参数个数
    function func(...args) {
        args.forEach(function (e) {
            console.log(e);
        });
        console.log('--------------');
    }
    
    func(1, 2, 3);
    func(4, 5, 6, 7, 8);
    
    五、析构表达式

    用于将对象、数组中的元素,拆分到变量中。

    • 析构表达式(基本)
    var {aa, bb} = {
        aa: 11,
        bb: 22
    }
    
    console.log(aa);
    console.log(bb);
    
    • 析构表达式(带别名)
    var {aa: aa_alias, bb: bb_alias, cc:{v2}} = {
        aa: 11,
        bb: 22,
        cc: {
            v1: 333,
            v2: 444
        }
    }
    
    console.log(aa_alias);
    console.log(bb_alias);
    console.log(v2);
    
    • 析构表达式(数组)

    数组,通过直接写逗号 , 来空过元素,通过 ...参数 的形式,读取剩余元素形成数组。

    var [n1, n2, , ...others] = [1, 2, 3, 4, 5];
    console.log(n1);
    console.log(n2);
    //注意通过逗号空过了第三个元素
    console.log(others);
    
    六、箭头表达式

    箭头表达式 主要用于:

    1. 作为匿名函数使用
    2. 解决匿名函数中的 this 问题
    • 一个参数
    var myFunc = arg1 => console.log(arg1);
    myFunc('aaa');
    
    • 多个参数
    var myFunc = (arg1, arg2) => arg1 + arg2;
    console.log(myFunc(111, 222));
    
    • 多行
    var myFunc = (arg1, arg2) => {
        var sum = arg1 + arg2;
        console.log(sum);
        return sum;
    }
    console.log(`exec result: ${myFunc(111, 222)}`);
    
    • 解决 this 问题(重要)

    原来 JavaScript 的书写方式:

    var MyClass = function (name) {
        this.name = name;
        this.counter = 0;
        setInterval(function () {
            this.counter++;
            console.log(this.name + this.counter);
        }, 1000);
    }
    
    var obj = new MyClass('bahb_');
    

    期待每间隔1秒,陆续输出bahb_1、bahb_2...,实际上没有取到值。
    原因是由于通过 new 之后,this 的指代发生了变化,导致取不到值。
    直接把 MyClass 作为函数调用,

    MyClass('bahb_');
    

    倒是可以得到正常的结果,原因是函数作为顶级对象,this 的指代不会变,但是这种形式不能满足需要创建对象的场景。

    通过箭头表达式来解决问题:

    var MyClass = function (name) {
        this.name = name;
        this.counter = 0;
        setInterval(() => {
            this.counter++;
            console.log(this.name + this.counter);
        }, 1000);
    }
    
    var obj = new MyClass('bahb_');
    
    七、循环遍历集合

    JavaScript 中循环遍历方法的问题:

    • for in:不仅遍历数组中的元素,还会遍历到其他属性
    • forEach:通过回调执行遍历方法,不能通过 break 停止遍历
    // TypeScript 的 for of 循环
    let arr = [1, 2, 3, 4];
    arr.desc = 'abc'; // 使用 for of,属性不会被遍历到
    for (let x of arr) {
        console.log(x);
    }
    
    八、泛型(Generic)

    这里只举例说明下基本概念,实际泛型的应用场景会广泛得多。

    let myArr: Array<string> = [];
    myArr.push('aa');
    myArr.push('bb');
    myArr.push(123); // 数据类型不一致,报错
    console.log(myArr);
    

    抄个官方的 Demo 吓吓人:

    class Greeter<T> {
        greeting: T;
        constructor(message: T) {
            this.greeting = message;
        }
        greet() {
            return this.greeting;
        }
    }
    
    let greeter = new Greeter<string>("Hello, world");
    
    let button = document.createElement('button');
    button.textContent = "Say Hello";
    button.onclick = function() {
        alert(greeter.greet());
    }
    
    document.body.appendChild(button);
    
    九、面向对象
    1、类定义、继承、访问权限控制符
    class Animal {
        // 构造函数,当构造函数传入的参数加上了“访问权限控制符”,则同时会声明同名类属性,并赋值
        constructor(public name: string) { }
        protected log(arg) {
            console.log(arg);
        }
        move(distanceInMeters: number = 0) {
            this.log(`${this.name} moved ${distanceInMeters}m.`);
            this.log('==============');
        }
    }
    
    class Snake extends Animal {
        constructor(name: string) {
            // 调用父类构造器
            super(name);
        }
        move(distanceInMeters = 5) {
            this.log("Slithering...");
            // 通过 super 调用父类方法
            super.move(distanceInMeters);
        }
    }
    
    class Horse extends Animal {
        constructor(name: string) { super(name); }
        move(distanceInMeters = 45) {
            this.log("Galloping...");
            super.move(distanceInMeters);
        }
    }
    
    let sam = new Snake("Sammy the Python");
    let tom: Animal = new Horse("Tommy the Palomino");
    
    sam.move();
    tom.move(34);
    
    2、接口

    实现类必须实现接口中声明的每个方法

    interface IPerson {
        eat();
        work();
    }
    
    class Person implements IPerson {
        eat() { }; // 不实现该方法会报错
        work() { }; // 不实现该方法会报错
    }
    
    十、模块(Module)

    每个 .ts 文件就是一个模块,通过 export 来对外部模块暴露元素,通过 import 来引入模块。
    举例:utility.ts 输出,main.ts 引用 utility.ts 中的输出

    // Module Utility: utility.ts
    
    let str: string = 'abc';
    
    function add(arg1, arg2) {
        return arg1 + arg2;
    }
    
    class MyClass {
        doSth() { }
    }
    
    export {str};
    export {add};
    export {MyClass};
    
    // Module Main: main.ts
    
    import {add} from "./utility";
    
    let ret = add(2, 3);
    console.log(ret);
    

    或者

    
    // Module Main: main.ts
    
    import * as myUtility from "./utility";
    
    let ret = myUtility.add(2, 3);
    console.log(ret);
    
    十一、注解(Annotation)

    给框架或IDE用的说明,比如:使用某个类时应该同时引入哪些页面等等,需要时具体看手册即可。

    十二、类型定义文件(*.d.ts)

    类型定义文件 用于描述一个库中所定义的类型
    类型定义文件介绍
    类型定义文件使用

    类型定义文件从这里找:DefinitelyTyped

    DefinitelyTyped - The repository for high quality TypeScript type definitions
    或者访问 DefinitelyTyped 在 GitHub 的仓库
    类型定义文件查找工具

    相关文章

      网友评论

        本文标题:TypeScript 特性学习摘要

        本文链接:https://www.haomeiwen.com/subject/gtlebttx.html