干货! 快速上手typescript的学习笔记 (对比JS的新特性,环境搭建,webpack配置,ts编译配置)
前提需要有js的基础
文章目录
- TypeScript
- 产生背景
- 什么是TypeScript?
- TypeScript增加了什么
- TypeScript开发环境搭建
- 基本类型
- 编译文件配置
- tsconfig.json常用的各配置项
- 使用webpack打包ts代码
- 安装webpack
- 配置webpack
- 配置ts编译规则
- 增加package.json中script的命令
- 试编译ts代码
- 安装html-webpack-plugin
- 安装webpack-dev-server
- 安装 clean-webpack-plugin
- 配置webpack,告诉它引用的模块
- 安装babel.js以提高兼容性
- 面向对象
- 类(class)
- 定义类
- 实例及各部分解释
- 继承
- super关键字
- 抽象类
- 接口
- 属性的封装
- 泛型
TypeScript
产生背景
TypeScript 起源于使用JavaScript开发的大型项目 。由于JavaScript语言本身的局限性,难以胜任和维护大型项目开发。因此微软开发了TypeScript ,TypeScript能运行JavaScript 的代码,并扩展了JavaScript 的语法。相比于JavaScript ,它还增加了静态类型、类、模块、接口和类型注解方面的功能,更易于大项目的开发。
什么是TypeScript?
- 以JavaScript为基础构建的语言
- 一个JavaScript的超集
- 可以在任何支持JavaScript的平台中执行
- TypeScript扩展了JavaScript并添加了类型
- TS不能被JS解析器直接执行 TS—编译—>JS
TypeScript增加了什么
- 类型:抽象、枚举等
- 支持ES的新特性
- 添加ES不具备的新特性
- 丰富的配置选项:比如TS可以被配置为编译成任何版本的JS
- 强大的开发工具
TypeScript开发环境搭建
下载Node.js
安装node.js
使用npm全局安装typescript
进入命令行
输入:npm i -g typescript
- 查看是否安装成功
创建一个ts文件
使用tsc对ts文件进行编译
自动生成JS文件
基本类型
类型声明
如果类型声明和赋值分开
let/const/var 变量名 : 类型
如果声明和赋值一起写,会自动进行类型检测
类型
类型 例子 描述 number 1,-33,20 任意数字 string ‘hello’, “hello” 任意字符串 boolean true,false 布尔值true或false 字面量 其本身 限制变量的值就是该字面量的值 any * 任意类型 unknown * 类型安全的any void 空值(undefined) 没有值(或undefined) nerver 没有值 不能是任何值 object {name: ‘abc’} 任意的JS对象 array [1,12,3] 任意JS数组 tuple [4,5] 元组,TS新增类型,固定长度数组 enum enum{A,B} 枚举,TS中新增类型 字面量
let a: 10; a = 10;let b: "male" | "female"; // 赋值为"male" |或"female" b = "male"; b = "female"; b = "hello" // 报错 ERROR!
any
表示是任意类型,一个变量设置类型为any后相当于对该变量关闭了TS的类型检测
let a = any; // 显式声明any let a; //隐式声明any // 赋值 a = 10; // 正常 a = 'hello'; // 正常 a = true; // 正常
unknown
类型安全的any,不能直接给其他类型的变量
let e: unknown; e = 10; e = 'hello'; e = true;
看似和any一样,其实不然。
any举例:
let s:string; let d; // any s = d; // 不报错
unknown举例:
let e: unknown; let s: string; s = e; // 报错// 如果非要赋值 // if语句 if(typeof e === 'string') {s = e; } // 类型断言,告诉编译器e是字符串 s = e as string; // 另外一种写法 s = <string>e;
void
主要作为返回值,用来表示空,以函数为例,就表示没有返回值的函数。
function fn(): void {// ... }
never
表示永远不会返回结果
function fn2(): never{// ... } // 有一些函数永远不会返回结果 function showError(): never{throw new Error('报错了!'); }
object
object表示一个js对象
// 一般不使用这种声明 let a: object; a = {}; a = function () {} // 一般使用下面的写法,要求格式和类型 let b: {name: string}; b = {name: '孙悟空'} // 在属性名后面加上? 表示属性是可选的 let c: {name: string, age?: number}; c = {name: '孙悟空'}; // 不报错// 但是不能把需要的所有属性都写一边,所以就用下面的语法 // [propName: string]: any便是任意类型的属性 let d: {name: string, [propName: string]: any}; c = {name: '猪八戒', age: 1, gender: '男'};// 声明function结构的类型声明 // (形参: 类型, 形参: 类型) => 返回值 let e: (a: number, b: number) => number; d = function(n1: number, n2: number): number {return 10; }
array
// 声明1:类型[] let arr1: string[]; arr1 = ['a', 'b', 'c']; // 声明2:Array<类型> let arr2: Array<number>; arr2 = [1, 2, 3];
tuple
元组,固定长度的数组
// 简单的声明 let arr3: [string, string]; arr3 = ['a', 'b']; arr3 = ['a', 'b', 'c']; //报错
enum
枚举,把所有可能的类型列出来
enum Gender{Male = 0;Female = 1; }let i: {name: string, gender: Gender}; i = {name: '孙悟空',gender: Gender.Male; // 0 }console.log(i.gender === Gender.Male);
| 和 &
let a: number | string let b: {name: string} & {age: number}; b = {name: '孙悟空', age: 18};
类型的别名
// 引例: let k: 1 | 2 | 3 | 4; let l: 1 | 2 | 3 | 4; // 麻烦 // 解决 type myType = 1 | 2 | 3 | 4; let k: myType; let j: myType;
编译文件配置
我们写一行代码手动敲tsc命令编译一次是很耗时的,相当麻烦,也无法自动编译,而且无法编译所有的ts文件。
所以目录下增加tsconfig.json
配置文件用来编译所有的ts文件。
这时就算没有写内容,打开这个目录,执行tsc,它会自动编译这个目录下所有的ts文件成js文件,下面介绍各配置项
tsconfig.json常用的各配置项
以下列出常用的tsconfig.json
配置
{/*tsconfig.json ts编译器的配置文件*//*"include" 用来指定哪些ts文件需要被编译-> 路径: ** 表示任意目录 * 表示任意文件*/"include": ["./src/**/*"],/*"exclude" 用来指定哪些ts文件不希望被编译,比如node_module下的ts文件默认值: ["node_modules", "bower_components", "jspm_packages"]*/"exclude": ["./src/abc/*"],/*"extends" 定义被继承的配置文件下面会让此配置文件中自动包含config下base.json中的所有配置信息*/"extends": "./config/base",/*"files" 指定被编译文件的列表,只有需要编译的文件少时才会用到列表中的文件都会被TS编译器所编译*/"files": ["core.ts","sys.ts"],/*"compilerOptions" 编译选项时配置文件中非常重要也比较复杂的配置选型在compilerOptions中包含多个子选项,用来完成对编译的配置*/"compilerOptions": {// target// 设置ts代码编译成JS的目标版本// 可选值:ES3(默认),ES5,ES6/ES2015,ES7/ES2016,ES2017,ES2018,ES2019,ES2020,ES2021"target": "ES2015",// lib// 指定代码运行时所包含的库(宿主环境) 一般情况下不改动// 可选值:ES5,ES6/ES2015,ES7/ES2016,ES2017,ES2018,ES2019,ES2020,ESNext,DOM..."lib": ["ES2015", "DOM"],// module// 设置编译后代码使用的模块化系统,指定模块化的规范// 可选值:CommonJS,AMD,UMD,ES2020.ESNext,None,System"module": "CommonJS",// outDir// 用来指定编译后js文件所在的目录"outDir": "./distJS",// outFile// 将代码合并为一个文件,全局作用域中的代码会合并// 如果要合并几个模块,要用AMD或者System"outFile": "./distJS/app.js",// allowJs// 是否对js文件进行搬移,默认是false"allowJs": false,// checkJs// 是否检查js代码是否符合语法规范,默认是false"checkJs": false,// strict 建议开启// 所有严格检查的开关 默认false"strict": false,// removeComments// 是否移除注释,默认false"removeComments": false,// noEmit // 不生成编译后的js文件"noEmit": false,// noEmitOnError// 当有错误时,它不会生成编译后的js文件,默认false"noEmitOnError": true,// alwaysStrict// 用来设置编译后的文件是否使用严格模式,默认false"alwaysStrict": false,// noImplicitAny// 检查隐式any,true为不允许隐式any 默认false"noImplicitAny": true,// noImplicitThis// 检查隐式this,来源不明确的this 默认false"noImplicitThis": true,// strictNullChecks// 严格的检查空值null隐患 默认值false"strictNullChecks": false}
}
使用webpack打包ts代码
安装webpack
npm i -D webpack webpack-cli typescript ts-loader
配置webpack
只讲ts有关的,打包css,js图片等loader配置可以看我另外一个文章
webpack.config.js
// 引入一个包
const path = require('path');
// webpack中的所有配置信息都应该写在module.exports中
module.exports = {// 指定入口文件entry: "./src/index.ts",// 指定打包文件所在的目录output: {// 指定打包文件的目录path: path.resolve(__dirname,'dist'),// 打包后文件的名字filename: "bundle.js"},// 指定wegpack打包时要使用的模块module: {// 指定要加载的规则rules: [{// test指定的时规则生效的文件test: /\.ts$/,// 要使用的loaderuse: 'ts-loader',// 要排除的文件exclude: /node_modules/}]}
}
配置ts编译规则
tsconfig.json
{"compilerOptions": {"module": "ES2015","target": "ES2015","strict": true}
}
增加package.json中script的命令
"build": "webpack"
这样最基本的配置已经完成了
试编译ts代码
编写index.ts
function sum(a: number, b: number): number{return a + b;
}console.log(sum(1, 10));
npm run build
报的错是没指定环境是生产模式还是开发模式
想要解决这个错误就在webpack配置文件里增加mode配置,指定模式即可。
安装html-webpack-plugin
npm i -D html-webpack-plugin
让webpack自动生成html
配置插件
// 引入一个包
const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');// webpack中的所有配置信息都应该写在module.exports中
module.exports = {// 指定入口文件entry: "./src/index.ts",// 指定打包文件所在的目录output: {//...},// 指定wegpack打包时要使用的模块module: {//...},"mode":"development",// 配置webpack插件plugins: [new HtmlWebpackPlugin(),]
}
这样它会在build以后自动生成html并且自动引入ts编译后的js文件
安装webpack-dev-server
npm i -D webpack-dev-server
可以让我们的项目跟浏览器联系在一起,代码一修改后自动刷新,展现在浏览器中
安装完后增加script命令
"strat": "webpack serve --open chrome.exe"
安装 clean-webpack-plugin
npm i -D clean-webpack-plugin
可以在每次新编译后,将dist文件夹里面的文件清空,把新的文件加进去,让dist文件夹里面都是最新编译的文件。
配置webpack,告诉它引用的模块
因为如果你再创建一个ts,在index.ts中引入了新建的ts,webpack并不知道新建的ts可以作为模块引入,它会报错。
增加webpack配置
// 用来设置引入模块resolve: {extensions: ['.js', '.ts']}
安装babel.js以提高兼容性
因为现在编译出来的js为ES6的语法,对于像IE的兼容性很不好,所以现在引入babel.js将我们的语法编译成像ES5提高兼容性。
安装
npm i -D @babel/core @babel/preset-env babel-loader core-js
preset-env 是babel预置不同的环境使得兼容不同的浏览器
core-js 模拟js运行环境,可以让老浏览器很好的运行js
配置webpack
// 引入一个包
const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');
const { CleanPlugin } = require('webpack');// webpack中的所有配置信息都应该写在module.exports中
module.exports = {// 指定入口文件entry: "./src/index.ts",// 指定打包文件所在的目录output: {// 指定打包文件的目录path: path.resolve(__dirname,'dist'),// 打包后文件的名字filename: "bundle.js"},// 指定wegpack打包时要使用的模块module: {// 指定要加载的规则rules: [{// test指定的时规则生效的文件test: /\.ts$/,// 要使用的loaderuse: [// 配置babel{// 指定加载器loader: "babel-loader",// 设置babeloptions: {// 设置预定义的环境presets: [[// 指定环境的插件"@babel/preset-env",// 配置信息{targets: {// 指定兼容浏览器的版本"chrome": "58","ie": "11",},// 指定corejs的版本"corejs":"3",// 使用corejs的方式"usage表示按需加载"useBuiltIns":"usage"}]]}},'ts-loader'],// 要排除的文件exclude: /node_modules/}]},"mode":"development",// 配置webpack插件plugins: [new HtmlWebpackPlugin({//title:"这是一个自定义的title"template: "./src/index.html"}),new CleanPlugin()],// 用来设置引入模块resolve: {extensions: ['.js', '.ts']}
}
index.ts:
const abc = {name:'abc', age: 12};
console.log(abc);
不使用babel进行编译,他编译后的变量声明是const
使用babel进行编译,编译后变成了var声明变量
像ie11没有Promise,如果我们使用了Promise,corejs会产生效果,使用自己实现的promise加在编译后的js中
index.ts
const abc = {name:'abc', age: 12};
console.log(abc);
console.log(Promise);
进行编译,corejs会加入很多自己实现的东西
但是这样在ie中还是会出错,因为webpack使用了箭头函数,我们可以增加配置项让webpack不使用箭头函数
output:{// ...// 告诉webpack不使用箭头函数environment: {arrowFunction: false}
}
这样ie就不会报错了
面向对象
以下话摘抄与尚硅谷的视频教程
面向对象是程序中一个非常重要的思想,它被很多同学理解成了一个比较难,比较深奥的问题,其实不然。面向对象很简单那,简而言之就是程序之中所有的操作都需要通过对象来完成。
- 举例来说
- 操作浏览器需要用到window对象
- 操作网页要用到document对象
- 操作控制台要用到console对象
一切操作都要通过对象,也就是所谓的面向对象,那么对象到底是什么呢?这就要先说到程序是社么,计算机程序的本质就是对现实事务的抽象,抽象的反义词是具体,比如:照片是一个具体的人的抽象,汽车模型是对具体汽车的抽象等等。程序也是对事务的抽象,在程序中我们可以表示一个人,一条狗,一把枪,一颗子弹等等所有的事物,一个事物到了程序中就变成了一个对象。
在程序中所有的对象都被分成了两个部分,数据和功能,以人为例,人的姓名、性别、年龄、身高等属于数据,人可以说话吃饭睡觉,这些属于人的功能,数据在对象中被称为属性,而功能就被称为方法,所以简而言之,在程序中一切皆是对象。
类(class)
要想面向对象,操作对象,首先要拥有对象,那么下一个问题就是如果创建对象,要创建对象,必须要先定义类,所谓的类可以理解为对象的模型,程序中可以根据类创建指定类型的对象,举例来说,可以通过Person类创建人的对象,Dog类创建狗的对象。
定义类
class 类名 {属性名 : 类型;constructor(参数: 类型) {this.属性名 = 参数;}方法名() {// ...}
}
实例及各部分解释
class Dog{// 通过直接定义的属性是实例属性,需要通过对象的实例去访问name: string;age: number;// 通过static开头的属性是静态属性(类属性)可以通过类直接访问static footNum: number = 4;// readonly 把属性变成只读的属性,无法更改readonly color: string = 'white';// 每次定义一个对象,构造函数规定了定义对象的规则,每次调用new Dog构造函数就会每次执行。// 如果有参构造,那么创建对象的时候,必须传入构造对象规定的参数// 构造函数会根据传入的参数构造相应的对象constructor(name: string, age: number) {// 在构造函数中,你创建了新对象,this则会指向新对象this.name = name;this.age = age;}// 如果无参构造,那么创建对象的时候,不用传参直接创建即可,但是无参构造会创建name和age相同的对象。eat():void {console.log('吃肉');}sound():void {console.log('汪汪');}
}// 定义对象
const dogOne = new Dog('小毛', 12);// 创建了name为小毛,age为12的对象
const dogTwo = new Dog('小黑', 8); // 创建了name为小黑,age为8的对象
console.log(dogOne.name) // '小毛'
console.log(Dog.footNum) // 4
dogOne.eat(); // '吃肉'
继承
Dog类
class Dog{name: string;age: number;constructor(name: string, age: number) {this.name = name;this.age = age;}eat():void {console.log('吃肉');}sound():void {console.log('汪汪');}
}
Cat类
class Cat{name: string;age: number;constructor(name: string, age: number) {this.name = name;this.age = age;}eat():void {console.log('吃鱼');}sound():void {console.log('喵喵喵');}
}
我们发现Cat类和Dog类都有name和age属性,都有eat和sound方法。这样代码重复性很高,我们如果要定义别的动物类,还要再写一遍相同的内容。所以就引出了继承这个概念:
Cat类和Dog类它们其实都算动物这个大类,动物都有自己的昵称和年龄,都会有爱吃的东西和自己的叫声。所以Cat类和Dog类相当于继承了Animal类。此时Animal类被称为父类,Cat类和Dog类被称为子类。继承后子类将会拥有父类所有的方法和属性。
举例:
Animal类
class Animal{name: string;age: number;food: string;sound: string;constructor(name: string, age: number, food: string, sound: string) {this.name = name;this.age = age;this.food = food;this.sound = sound;}eat():void {console.log('吃:' + food);}showSound():void {console.log(sound);}
}
Dog类
class Dog extends Animal{}
Cat类:
class Cat extends Animal{}
创建对象:
const DogOne = new Dog('小黑', 8, '肉', '汪汪汪~');
const CatOne = new Cat('小喵', 12, '鱼', '喵喵喵~');
console.log(DogOne);
console.log(CatOne);
DogOne.eat();
DogOne.showSound();
CatOne.eat();
CatOne.showSound();
通过继承可以将多个类中共有的代码写在一个父类里,这样只需要写一次即可让所有的子类都同时拥有父类中的属性和方法,如果希望在子类中添加一些父类没有的属性或方法直接加在子类中即可。
如果再子类中添加了和父类相同的方法,则子类方法会覆盖掉父类的相同的方法。
比如:
class Dog extends Animal{eatShit():void{console.log('吃shi');}
}
class Cat extends Animal{kneading():void{console.log('踩奶');}
}
super关键字
在类的方法中,super就表示当前类的父类
Animal类:
class Animal{name: string;age: number;constructor(name: string, age: number) {this.name = name;this.age = age;}eat():void {console.log('吃饭');}}
Dog类:
class Dog extends Animal{eat():void {super.eat(); // 吃饭}
}
上述例子用得不多
如果我们要往子类中添加了新的不同于父类的属性,那么我们需要调用构造函数去创建对象
如果子类中写了构造函数,在子类构造函数中,必须对父类的构造函数进行调用
class Dog extends Animal{gender: string;constructor(gender: string, name: string, age: number) {// 不加super是会报错的,如果调用了super需要传入父类构造函数定义的形参super(name,age);this.gender = gender;}
}
抽象类
如果像我们之前写的Animal类
class Animal{name: string;age: number;constructor(name: string, age: number) {this.name = name;this.age = age;}eat():void {console.log('吃饭');}}
它是可以去创建对象的
const animalOne = new Animal('小青', 12);
但是这样是有问题的,创建的范围过大,我们无法分别它是什么动物。
我们开始创建Animal类是希望它去定义一些动物共同的属性和方法,让它的不同的分支动物去继承它,不再重复的编写代码。但是像上述代码,是有问题的。所以我们希望Animal类**只去定义共同的属性和方法,没法创建对象,而且我们希望定义的方法,没有具体的实现内容,只定义了我这个大类,有这种方法的概念,具体如何实现,是各分支子类的任务,我只负责定义概念。**这就引出了抽象类的概念。
抽象类就是,当你不希望这个类创建对象的时候,你可以将它定义为抽象类,抽象类里面可以定义抽象方法,抽象方法没有实现内容,且只能定义在抽象类里。
定义抽象类:
定义一个抽象方法:
使用abstract关键字开头
没有方法体
抽象方法只能定义在抽象类中
子类必须对抽象方法进行重写
定义Animal类:
abstract class Animal{name: string;constructor(name: string) {this.name = name;}abstract showSound():void; // 没有方法体
}
定义Dog类
class Dog extends Animal{// 必须重写showSound() {console.log('汪汪汪~')}
}
定义Cat类:
class Cat extends Animal{// 必须重写showSound() {console.log('喵喵喵~')}
}
接口
接口是一种规范的定义,它定义了行为和动作规范,在程序设计里面,接口起到一种限制和规范的作用。接口定义了某一批所需要遵守的规范,它不在乎类内部的状态,只在乎你是否提供了它规定的规则,比如应该含有的方法,应该含有的属性。
像我们电脑,鼠标的接口是usb的,现在手机数据线的接口是type-C的,它规定了这类规范的产物,只能用于对应插口的使用,而不能混着插。你必须是含有这类接口定义规范的产物才可以使用对应口。
之前声明类型的时候,我们声明一个对象的类型可以写为
type myType = {name: string;age: number;
}const obj: myType = {name: 'sss',age: 12
}
定义接口用interface
关键字
interface myInterface{name: string;age: number;
}const obj: myInterface = {name: 'sss',age: 111
}
接口用来定义一个类结构,用来定义一个类中应该包含哪些属性和方法
同时接口也可以当成类型声明去使用(↑上述例子)
在我们ts中,接口可以重复定义,如果是同名的接口,则是几个同名接口的并集
interface myInterface{name: string;age: number;
}
interface myInterface{gender: string;
}
const obj: myInterface = {name: 'sss',age: 111,gender: '男';
}
接口限制类的特点:
接口在定义类的时候去限制类结构
接口中的所有属性都不能有实际的值
接口只定义对象的结构,而不考虑实际值
在接口中所有的方法都是抽象方法
定义一个接口:
interface myInter{name: string;sayHello(): void;
}
定义一个类,实现接口(就是满足接口的要求)
class MyClass implements myInter{name: string;constructor(name: string){this.name = name;}sayHello() {console.log('hello~');}
}
接口规范类结构有什么好处,比如一款FPS游戏,枪都可以开,雷都可以扔了炸,头饰、背饰、等等等,但是它们都需要实现一个特定的规范和结构后它们才能成为各种道具。
抽象类中可以有抽象方法和普通方法,接口中只能有抽象方法。抽象类使用extends,接口使用implements。
属性的封装
JavaScript
中,创建出来的对象,它的属性的值是不安全的,像Java中,有private
public
final
等修饰符,set
get
方法, JavaScript
是没有的。它就导致:
class Person {name: string;age: number;constructor(name: string, age: number) {this.name = name;this.age = age;}
}const personOne = new Person('abc', 12);
// 你还可以修改它的属性值
personOne.name = 'aaa';
personOne.age = -12;
console.log(personOne);
没有一个人的岁数是负值。这就是JavaScript
遗留下的一个坑。
在typescript
中,新增了类似Java
中的变量修饰符
public
修饰的属性可以在任意位置访问(修改) 默认值private
私有属性,私有属性只能在类内部进行访问(修改)- 如果要访问私有属性,可以添加
get
set
方法间接的访问私有属性
- 如果要访问私有属性,可以添加
protected
保护属性,在类的里面,子类里可以访问,在类的外部无法访问。readOnly
只读属性,将属性设置为只读的。 只读属性必须在声明时或构造函数里被初始化
修改上述例子:
class Person {name: string;age: number;constructor(name: string, age: number) {this.name = name;this.age = age;}getName() {return this.name;}setName(value: string) {this.name = value;}
}const personOne = new Person('abc', 12);
console.log(personOne.getName());
personOne.setName('ccc');
console.log(personOne);
这样做的好处就是,虽然还是能修改,但是修改权不在调用他的人,而在定义它的人,它可以设置有没有get
set
方法,如果要修改则可以定义修改的规则。
TS中设置getter方法的方式(跟之前的直接赋值的观感是一样的)
get name() {return this.name;
}
set name(value: string) {this.name = name;
}// 调用:
const personOne = new Person('abc', 12);
personOne.name = 'aaa';
console.log(personOne.name) // 'aaa'
泛型
在定义函数或是类时,如果遇到类型不明确就可以使用泛型
泛型的定义
function fn<T>(a: T): T{return a;
}
如果使用any:
function fn(a: any): any{return a;
}
首先泛型这样定义,函数的返回值的类型和a的类型时一样的。这样就比下面这样用any的方法好。使用any定义我们并不知道a的类型和函数的返回类型是否一致。
泛型也可以定义多个
function fn2<T, K>(a: T, b: K): T{console.log(b);return a;
}
建议最好在使用的时候,将泛型的类型限制一下
fn2<number, string>(123, 'abc');
我们如果要限制一下泛型的范围,我们可以用接口限制一下,这表示泛型T必须是Inter的实现类(子类)
interface Inter{length: number;
}
function fn3<T extends Inter>(a: T): number{return a.length;
}
这下我们传的时候,至少包含length这个属性
fn3('123') // 字符串有length属性
fn3( { name: 'asd'} ); //没有length属性
干货! 快速上手typescript的学习笔记 (对比JS的新特性,环境搭建,webpack配置,ts编译配置)相关推荐
- Android学习笔记之(一)开发环境搭建
Android学习笔记之(一)开发环境搭建 zouxy09@qq.com http://blog.csdn.net/zouxy09 至于说Android是什么之类的俺就不啰嗦了,因为它离我们太近了.直 ...
- oracle exacc,【学习笔记】Oracle 11GR2新特性Adaptive Cursor Sharing(ACS)
天萃荷净 Oracle研究中心学习笔记:分享一篇关于Oracle 11.2.0.1 11Gr2数据库最新版本中最新特性Adaptive Cursor Sharing(ACS)深入研究笔记. 本站文章除 ...
- oracle12c口令文件,学习笔记:Oracle 12C ASM 新特性 共享密码文件
天萃荷净 测试试验ORACLE 12C ASM 新特性 共享密码文件的详细过程 在ORACLE 12C之前大家都知道密码文件是存放在?/dbs或者?/database中,如果要修改修改sysdba权限 ...
- oracle中overwrite写法,【学习笔记】Oracle 11G新特性restart的深入研究案例
[学习笔记]Oracle 11G新特性restart的深入研究案例 时间:2016-11-26 22:35 来源:Oracle研究中心 作者:网络 点击: 次 天萃荷净 Oracle研究中 ...
- Python学习笔记 (1)Hello World(环境搭建+输出Hello World!)...
随想 高考发挥失常.科三遇火车发挥失常,各种不顺--突然发现假期都快没了,才想起高考前想象的这个假期要做的一堆事,现在来多完成一件吧. 这几篇博客仅只是我的学习笔记,凑合看吧.我这个python小白看 ...
- angularjs学习笔记一——了解angularjs、开发环境搭建、第一个angularjs程序
一.什么是angularJS angularJS是基于javascript的框架,所谓框架,自然就是封装了很多功能,举个例子,使用原生javascript,如果你要写一个网页幻灯片,你可能需要几十行代 ...
- 学习笔记之-java8的新特性-函数式接口,lambda表达式,方法引用,Stream API,Optional类
1.Lambda表达式 用匿名内部类的方法去创建多线程1.new Thread2.参数传递new Runnable3.重写run方法4.在run方法中去设置线程任务5.调用start问题:我们最终目标 ...
- 【Java学习笔记1】Java概述 -背景+环境搭建
文章目录 Java语言概述 Java介绍 1. 软件开发概述 2. 计算机语言 3. Java 简史 4. Java 技术体系平台 6. Java语言特点 7. Java核心机制一-Java虚拟机 8 ...
- 2022 最新 Android 基础教程,从开发入门到项目实战【b站动脑学院】学习笔记——第一章:Android开发环境搭建
第 1 章 Android开发环境搭建 本章介绍了如何在个人电脑上搭建Android开发环境,主要包括:Android开发的发展历史是怎样的.Android Studio的开发环境是如何搭建的.如何创 ...
最新文章
- java实习生入职_Java实习生入职测试
- java c语言union转换_C语言联合体(union)的使用方法及其本质-union
- 最新!中国内地大学 ESI 排名出炉:362 所高校上榜!
- nssl1317-灵魂分流药剂【分组背包,二维费用背包】
- 分布式一致性算法:可能比你想象得更复杂
- Ubuntu16.04安装搜狗输入法后有黑边问题的解决方法
- 作者:鲁鸣鸣(1978-),男,博士,中南大学信息科学与工程学院副教授,中国计算机学会会员。...
- RS232和RS485
- QT_UDP传输小结
- 关于pdf转html的个人方法,pdf转html的另类方法
- plc仿真实训软件_博途STEP7仿真软件与真实PLC的区别
- 【python学习】python实现利用pygame绘画基本图形、显示图片,实现图形图片随机效果。python绘制行列图片
- Matlab故障树的最小割集的求解
- 云计算和云服务区别是什么
- verilogHDL实现pwm控制
- C++语言基础:输出一个整数的个位十位百位上的数字
- Luogu P1144 最短路计数
- Unity-UGUI提高开发效率的插件集合
- 当区块链是数字经济和数字社会的基石时,我们如何发现机遇?
- OpenCV C++ imread填写路径下有图片却读不出 求助!!!!!!