目前前端发展得百花齐放、技术日新月异,就连各种编辑器也好用到匪夷所思,又层出不穷。近两年来,我从Sublime Text,到Atom,到Brackets,再到今天的VSCode。从一开始不断玩各种插件配置软件,到现在几乎不怎么配置插件就能满足到我的基本需求,编辑器软件之间的竞争也甚是激烈。
VSCode全称是Visual Studio Code, 是一款免费开源的现代化轻量级代码编辑器,内置JavaScript、TypeScript,支持 Node.js、ES6、AngularJS、ReactJS等,几乎所有主流的开发语言的语法高亮、智能代码补全、自定义热键、括号匹配、代码片段、代码对比 Diff、GIT 等特性,而且拥有丰富的插件生态系统,可通过安装插件来支持C++、C#、Python、PHP等其他语言,并针对网页开发和云端应用开发做了优化。跨平台支持Windows,OS X和Linux。运行速度非常流畅,不亚于Sublime Text,打开大文件甚至更有优势。VSCode和Atom、Brackets一样,都属于是Webkit类的编辑器。
轻量级、开源、跨平台,听起来就不像是微软能做出来的事。但犹如能生产出TypeScript一样,现代的微软也正在做很现代的事情,实在令人刮目相看。
由C#的首席架构师安德斯·海尔斯伯格领衔开发的TypeScript,是JavaScript的一个超集,向JavaScript添加了可选的静态类型,强类型,以及模块系统和基于类的面向对象编程。TypeScript是为大型应用之开发而设计,而编译时它产生 JavaScript 以确保兼容性。
按我的理解是,微软希望你学会了C#以后,前端、后端、数据库全部都能编写程序。
首先,我们先来了解一下在vscode如何安装、配置TypeScript。Visual Studio2015和Visual Studio 2013 Update 2默认包含了TypeScript。如果你没有安装包含TypeScript的Visual Studio ,可以通过NPM进行安装:
npm install -g typescript
安装完成以后,就可以在vscode中编写以及汇编ts文件了。在vscode中汇编只需要在ts文件下,按着按shift+ctrl+b,会提示没有配置文件,点击配置后会自动在根目录生成一个.vscode文件夹,里面有个tasks.json文件,按默认配置就可以。
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "0.1.0",
"command": "tsc",
"isShellCommand": true,
"args": ["-p", "."],
"showOutput": "silent",
"problemMatcher": "$tsc"
}
接着在根目录里面,新建一个tsconfig.json的生成配置文件。
{
"compilerOptions": {
"target": "ES5",
"noImplicitAny": false,
"module": "amd",
"removeComments": false,
"sourceMap": true
}
}
以上,配置ts文件已经完成。下面我们来建一个新类:class Duck
class Duck {
DuckName: string;
Age: number;
Fly: string;
constructor(duckName: string, age: number, fly: string) {
this.DuckName = duckName;
this.Age = age;
this.Fly = fly;
}
greet() {
let outString: string = "我是鸭子,我的名字是" + this.DuckName + ",我现在" + this.Age + "岁," + this.Fly;
console.log(outString);
}
}
var getduck = new Duck("黄鸭子", 1, "我会飞");
getduck.greet();//我是鸭子,我的名字是黄鸭子,我现在1岁,我会飞
接着我们遇到一个客户,他要求把橡皮鸭也要加入我们的系统当中。第一反应就是,好吧,我做一个继承类。
class Duck {
DuckName: string;
Age: number;
Fly: string;
constructor(duckName: string, age: number, fly: string) {
this.DuckName = duckName;
this.Age = age;
this.Fly = fly;
}
greet() {
let outString: string = "我是鸭子,我的名字是" + this.DuckName + ",我现在" + this.Age + "岁," + this.Fly;
console.log(outString);
}
}
class RubberDuck extends Duck {
constructor(duckName: string, age: number, fly: string) {
super(duckName, age, fly);
}
greet() {
let outString: string = "我是橡皮鸭子,我的名字是" + this.DuckName + ",我现在" + this.Age + "岁," + this.Fly;
console.log(outString);
}
}
var getduck = new Duck("黄鸭子", 1, "我会飞");
var getrubberduck: Duck = new RubberDuck("小鸭子", 0, "我不会飞");
getduck.greet();//我是鸭子,我的名字是黄鸭子,我现在1岁,我会飞
getrubberduck.greet();//我是橡皮鸭子,我的名字是小鸭子,我现在0岁,我不会飞
其实我们也可以使用get/set封装,而不用继承,就可以达到上面的目的。
class Duck {
DuckName: string;
Age: number;
Fly: string;
private _className: string;
constructor(duckName: string, age: number, fly: string) {
this.DuckName = duckName;
this.Age = age;
this.Fly = fly;
}
get className(): string {
return this._className;
}
set className(newname: string) {
this._className = newname;
let outString: string = "我是" + newname + ",我的名字是" + this.DuckName + ",我现在" + this.Age + "岁," + this.Fly;
console.log(outString);
}
}
var getduck = new Duck("黄鸭子", 1, "我会飞");
getduck.className = "鸭子"; //我是鸭子,我的名字是黄鸭子,我现在1岁,我会飞
var getduck = new Duck("小黄", 0, "我不会飞");
getduck.className = "橡皮鸭子"; //我是橡皮鸭子,我的名字是小黄,我现在0岁,我不会飞
其实继承类的目的是在于继承的同时进行功能的扩展。下面我们除了将鸭子的类名加进来,还增加多一个rubbergreet()的方法:
class Duck {
DuckName: string;
Age: number;
Fly: string;
constructor(duckName: string, age: number, fly: string) {
this.DuckName = duckName;
this.Age = age;
this.Fly = fly;
}
greet() {
let outString: string = "我是鸭子,我的名字是" + this.DuckName + ",我现在" + this.Age + "岁," + this.Fly;
console.log(outString);
}
}
class getDuck extends Duck {
private classname:string;
constructor(duckName: string, age: number, fly: string,classname:string) {
super(duckName, age, fly);
this.classname=classname;
}
greet() {
let outString: string = "我是"+this.classname+",我的名字是" + this.DuckName + ",我现在" + this.Age + "岁," + this.Fly;
console.log(outString);
}
rubbergreet(){
console.log("zzz,zzz");
}
}
var getduck = new Duck("黄鸭子", 1, "我会飞");
var getrubberduck= new getDuck("小鸭子", 0, "我不会飞","橡皮鸭子");
getduck.greet();//我是鸭子,我的名字是黄鸭子,我现在1岁,我会飞
getrubberduck.greet();//我是橡皮鸭子,我的名字是小鸭子,我现在0岁,我不会飞
getrubberduck.rubbergreet();//zzz,zzz
在类名前加上abstract
说明这个类是抽象类,抽象类只需要写出类的定义,由实现类完成它的具体细节.
如果去实体化一个抽象类,将会产生一个错误。不信,你拿上面的Duck类试试。
假设由于我们经营得当,系统需要升级。新来的工程师看了我们的代码,叫了一声:搞什么嘛,这完全可以用接口!
什么是接口呢?类是对象的抽象,那么接口就是类的抽象。有一个编程原则是这样子说的:针对接口编程,而不是针对现实编程。
下面我们写一个简单的接口,把鸭子的类和接口先组合。
interface DuckConfig {
DuckName?: string;
Age?: number;
Fly?: string;
Duckclass?: string;
}
function createDuck(config:DuckConfig):{out:string;}
{
var newDuck={out:"我是"};
if (config.Duckclass){
newDuck.out+=config.Duckclass;
}
else{
newDuck.out+="鸭子";
}
if (config.DuckName){
newDuck.out+=",我的名字是"+config.DuckName;
}
if (config.Age){
newDuck.out+=",我今年"+config.Age+"岁";
}
if (config.Fly){
newDuck.out+=","+config.Fly;
}
console.log(newDuck.out);
return newDuck;
}
var blackDuck = createDuck({DuckName:"小黑",Age:1,Fly:"我会飞",Duckclass: "黑鸭子"});//我是黑鸭子,我的名字是小黑,我今年1岁,我会飞
var blackDuck = createDuck({DuckName:"小黄",Age:1,Fly:"我不会飞"});//我是鸭子,我的名字是小黄,我今年1岁,我不会飞
var rubberDuck = createDuck({Duckclass: "橡皮鸭子"});//我是橡皮鸭子
这样写挺规范的,这样一来,我们来多少种鸭子都不怕了。我想看看js是怎么处理“接口”的,一看却傻眼了:
function createDuck(config) {
var newDuck = { out: "我是" };
if (config.Duckclass) {
newDuck.out += config.Duckclass;
}
else {
newDuck.out += "鸭子";
}
if (config.DuckName) {
newDuck.out += ",我的名字是" + config.DuckName;
}
if (config.Age) {
newDuck.out += ",我今年" + config.Age + "岁";
}
if (config.Fly) {
newDuck.out += "," + config.Fly;
}
console.log(newDuck.out);
return newDuck;
}
var blackDuck = createDuck({ DuckName: "小黑", Age: 1, Fly: "我会飞", Duckclass: "黑鸭子" });
var blackDuck = createDuck({ DuckName: "小黄", Age: 1, Fly: "我不会飞" });
var rubberDuck = createDuck({ Duckclass: "橡皮鸭子" });
config就是一个对象,什么接不接口的,烦不烦啊,JavaScript就是这么任性~~
我们的经营不断拓展,公司已经有计划的要引进多种动物。我们现在需要若干个类来管理各种动物的叫声,通过类和接口相互约束,我们要写出扩展性强、易于修改的代码。
interface IBark{
bark();
}
class cat implements IBark{
bark(){
console.log("喵喵")
}
}
class dog implements IBark{
bark(){
console.log("旺旺")
}
}
var catbark=new cat();
catbark.bark();//喵喵
var dogbark=new dog();
dogbark.bark();//旺旺
下面的代码就是汇编而成的JavaScript,由于没有接口的约束,.bark命名并没有硬性规定,所以想写成什么都可以。在这种小代码上当然看不出什么问题,甚至还会觉得下面的代码比较好理解。当成一个大系统、当要系统修改时问题就不少了。
var cat = (function () {
function cat() {
}
cat.prototype.bark = function () {
console.log("喵喵");
};
return cat;
}());
var dog = (function () {
function dog() {
}
dog.prototype.bark = function () {
console.log("旺旺");
};
return dog;
}());
var catbark = new cat();
catbark.bark();
var dogbark = new dog();
dogbark.bark();
最后来说说模块,TypeScript的模块分为内部模块、外部模块。
TypeScript也有外部模块的概念。使用外部模块的两种情况:node.js 和require.js,这里不做介绍。
下面说说内部模块,“内部模块”现在称做“命名空间”。 “外部模块”现在则简称为“模块”。TypeScript 1.5里术语名已经发生了变化。module X {} 相当于现在推荐的写法 namespace X {}。
最后设计一个稍微难点的程序,将上面的知识点做一个总结:
我们假设,产品越来越多,从供应商那里取得产品的编码。但是供应商很乱,有时提供6位数编码,有时提供产品名称。我们就用了正则表达式进行核对,如果是6位数字的,那么加入编码的数组,如果是单纯字母名称的,加入名称数组。如果混合的,我们就当它是废品。
开头我们就用上了namespace Vcode,然后再立接口,接口将返回一个boolean,以及一个写入数组的方法。
namespace Vcode {
export interface StringCode {
isAcceptable(s: string): boolean;
toarr(s);
} //接口
var nameRegexp = /^[A-Za-z]+$/;
var CodeRegexp = /^[0-9]+$/;
var namearr: string[] = [];
var codearr: string[] = [];
export class nameOnlyValidator implements StringCode {
isAcceptable(s: string) {
return nameRegexp.test(s);
}
toarr(s) {
namearr.push(s);
}
}
export class CodeValidator implements StringCode {
isAcceptable(s: string) {
return s.length === 6 && CodeRegexp.test(s);
}
toarr(s) {
codearr.push(s);
}
}
var strings: string[] = ['littDuck', '345633', 'No:177', 'alexzeng'];
var namestring = new Vcode.nameOnlyValidator();
var codestring = new Vcode.CodeValidator();
for (var i in strings) {
if (codestring.isAcceptable(strings[i])) {
codestring.toarr(strings[i]);
}
if (namestring.isAcceptable(strings[i])) {
namestring.toarr(strings[i]);
}
}
console.log(namearr);
console.log(codearr);
}
最终汇编成js文件:
var Vcode;
(function (Vcode) {
var nameRegexp = /^[A-Za-z]+$/;
var CodeRegexp = /^[0-9]+$/;
var namearr = [];
var codearr = [];
var nameOnlyValidator = (function () {
function nameOnlyValidator() {
}
nameOnlyValidator.prototype.isAcceptable = function (s) {
return nameRegexp.test(s);
};
nameOnlyValidator.prototype.toarr = function (s) {
namearr.push(s);
};
return nameOnlyValidator;
}());
Vcode.nameOnlyValidator = nameOnlyValidator;
var outarr = (function () {
function outarr() {
}
return outarr;
}());
Vcode.outarr = outarr;
var CodeValidator = (function () {
function CodeValidator() {
}
CodeValidator.prototype.isAcceptable = function (s) {
return s.length === 6 && CodeRegexp.test(s);
};
CodeValidator.prototype.toarr = function (s) {
codearr.push(s);
};
return CodeValidator;
}());
Vcode.CodeValidator = CodeValidator;
var strings = ['littDuck', '345633', 'No:177', 'alexzeng'];
var namestring = new Vcode.nameOnlyValidator();
var codestring = new Vcode.CodeValidator();
for (var i in strings) {
if (codestring.isAcceptable(strings[i])) {
codestring.toarr(strings[i]);
}
if (namestring.isAcceptable(strings[i])) {
namestring.toarr(strings[i]);
}
}
console.log(namearr);
console.log(codearr);
})(Vcode || (Vcode = {}));
最后说一下,无论是module X {} 还是 namespace X {},汇编出来的js都是一样的。
以上的例程,全部是我自己码出来的,想代码比写文章困难,转帖的时候请把凉风有兴或者AlexZeng.net进行署名。本文版权声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证)