语法学习
**
- let : 作用——声明变量,但声明的变量只能在let命令所在的代码块内有效。
- eg:
for (let i = 0; i < 10; i++) {
// ...
}
console.log(i);
但是定义的i只在for循环中生效。
TypeScript 中,使用 : 指定变量的类型,: 的前后有没有空格都可以。
let decLiteral: number = 6;
**
关于for循环的变量作用域:
var a = [];
for (let i = 0; i < 10; i++) {
a[i] = function () {
console.log(i);
};
}
a[6](); // 输出结果为:6
上面代码中,变量i是let声明的,当前的i只在本轮循环有效,所以每一次循环的i其实都是一个新的变量,所以最后输出的是6。你可能会问,如果每一轮循环的变量i都是重新声明的,那它怎么知道上一轮循环的值,从而计算出本轮循环的值?这是因为 JavaScript 引擎内部会记住上一轮循环的值,初始化本轮的变量i时,就在上一轮循环的基础上进行计算。
另外,for循环还有一个特别之处,就是设置循环变量的那部分是一个父作用域,而循环体内部是一个单独的子作用域。
for (let i = 0; i < 3; i++) {
let i = 'abc';
console.log(i);
}
// 输出结果为:
// abc
// abc
// abc
上面代码正确运行,输出了 3 次abc。这表明函数内部的变量i与循环变量i不在同一个作用域,有各自单独的作用域。
使用let定义变量,在变量定义之前调用变量,程序会报错.
暂时性死区
只要块级作用域内存在let命令,它所声明的变量就“绑定”(binding)这个区域,不再受外部的影响。
var tmp = 123;
if (true) {
tmp = 'abc'; // ReferenceError
let tmp;
}
上面代码中,存在全局变量tmp,但是块级作用域内let又声明了一个局部变量tmp,导致后者绑定这个块级作用域,所以在let声明变量前,对tmp赋值会报错。
不允许重复声明
let不允许在相同作用域内,重复声明同一个变量。
// 报错
function func() {
let a = 10;
var a = 1;
}
// 报错
function func() {
let a = 10;
let a = 1;
}
块级作用域
ES6 允许块级作用域的任意嵌套。
{{{{
{let insane = 'Hello World'}
console.log(insane); // 报错
}}}};
上面代码使用了一个五层的块级作用域,每一层都是一个单独的作用域。第四层作用域无法读取第五层作用域的内部变量。
const
const声明一个只读的常量。一旦声明,常量的值就不能改变。
const PI = 3.1415;
PI // 3.1415
PI = 3;
// TypeError: Assignment to constant variable.
const的作用域与let命令相同:只在声明所在的块级作用域内有效
数值定义§
使用 number 定义数值类型:
let decLiteral: number = 6;
let hexLiteral: number = 0xf00d;
// ES6 中的二进制表示法
let binaryLiteral: number = 0b1010;
// ES6 中的八进制表示法
let octalLiteral: number = 0o744;
let notANumber: number = NaN;
let infinityNumber: number = Infinity;
编译结果:
var decLiteral = 6;
var hexLiteral = 0xf00d;
// ES6 中的二进制表示法
var binaryLiteral = 10;
var octalLiteral = 484;
var notANumber = NaN;
var infinityNumber = Infinity;
字符串(模板类型)-------${myName}.(可以使用变量替换模板)
let myName: string = 'Tom';
let myAge: number = 25;
// 模板字符串
let sentence: string = `Hello, my name is ${myName}.
I'll be ${myAge + 1} years old next month.`;
空值§
在 TypeScript 中,可以用 void 表示没有任何返回值的函数:
function alertName(): void {
alert('My name is Tom');
}
注意:undefined 和 null 是所有类型的子类型。也就是说 undefined 类型的变量,可以赋值给 number 类型的变量。
什么是任意值类型------any
any是任意值类型,被定义成any型的数据可以在后续过程中被替换成任意的数值。
let anyThing: any = 'hello';
console.log(anyThing.myName);
console.log(anyThing.myName.firstName);
也允许调用任何方法:
let anyThing: any = 'Tom';
anyThing.setName('Jerry');
anyThing.setName('Jerry').sayHello();
anyThing.myName.setFirstName('Cat');
可以认为,声明一个变量为任意值之后,对它的任何操作,返回的内容的类型都是任意值. 且当数据赋值时没有指出数据类型则默认为any类型
对象的类型——接口
什么叫做接口
(在面向对象语言中,接口(Interfaces)是一个很重要的概念,它对行为的抽象,而具体如何行动需要由类(classes)去实现(implemnt)。
TypeScript 中的接口是一个非常灵活的概念,除了可用于对类的一部分行为进行抽象以外,也常用于对「对象的形状(Shape)」进行描述。)
我们定义了一个接口 Person,接着定义了一个变量 tom,它的类型是 Person。这样,我们就约束了 tom 的形状必须和接口 Person 一致。
定义的变量比接口少了一些属性是不允许的,多了一些属性也是不允许的 赋值的时候,变量的形状必须和接口的形状保持一致。:
interface Person {
name: string;
age: number;
}
let tom: Person = {
name: 'Tom'
};
如果我们希望变量和接口的形状可以存在差异那么我们就可以使用?变量名的形式定义变量:
interface Person {
name: string;
age?: number;
}
let tom: Person = {
name: 'Tom'
};
//age?: number;这种属性的形式被称为可选属性。
但是即使是可选属性可不可以增加属性-------为了解决此问题可以用 任意属性:
interface Person {
name: string;
age?: number;
[propName: string]: any; //可以接收多定义出来的属性。
//使用 [propName: string] 定义了任意属性取 string 类型的值。
}
需要注意的是,一旦定义了任意属性,那么确定属性和可选属性的类型都必须是它的类型的子集:
let list: any[] = ['xcatliu', 25, { website: 'http://xcatliu.com' }];
//这种写法可以让数组中的元素是各种不同类型的。
有关函数问题
函数声明——函数在js中是至关重要的存在。
在 JavaScript 中,有两种常见的定义函数的方式——函数声明(Function Declaration)和函数表达式(Function Expression):
// 函数声明(Function Declaration)
function sum(x, y) {
return x + y;
}
// 函数表达式(Function Expression)
let mySum = function (x, y) {
return x + y;
};
在 TypeScript 的类型定义中,=> 用来表示函数的定义,左边是输入类型,需要用括号括起来,右边是输出类型。
let mySum: (x: number, y: number) => number = function (x: number, y: number): number {
return x + y;
};
用接口定义函数的形状§
我们也可以使用接口的方式来定义一个函数需要符合的形状:
interface SearchFunc {
(source: string, subString: string): boolean;
}
let mySearch: SearchFunc;
mySearch = function(source: string, subString: string) {
return source.search(subString) !== -1;
}
采用函数表达式|接口定义函数的方式时,对等号左侧进行类型限制,可以保证以后对函数名赋值时保证参数个数、参数类型、返回值类型不变。
函数的定义
es5中定义方法{
定义方式:函数声明法
function run(){
return "run";
}
定义方式:匿名函数
var run2 = function(){
return "run2";
}
}
ts中函数定义方法{
function run(): string{
return "run";
// run是函数名,string是返回的数据类型;如果不知道返回类型是什么可以使用any,如果函数没有返回值可以使用viod。
}
定义方式:匿名函数
var run2 = function():{
return "run2";
}
}
在函数调用的时候可以进行参数的传递,但是传递的参数必须指定类型。而且形参和实参数量需要一样,如果不一样需要在形参位置使用可选参数“变量名?:类型”eg k? : any
函数的重载:同样的函数传入不同的函数实现不同的方法。
箭头函数:
setTimeout(()=> {
alert('kk')
},1000)
//使用箭头函数时此时this的指向是上下文。
编译成js的代码————————————————————————————————————————————————
setTimeout(function () {
alert('kk');
}, 1000);
js中类的定义(原型链):
function person(){
this.name = "zhangsan"
this.age = 21
this.run = function(){
console.log("名字是"+this.name)
};
}
var p = new person();
p.run()
使用原型链给类添加属性或者方法,可以直接通过实例化对象调用。
ffunction person(){
this.name = "zhangsan"
this.age = 21
this.run = function(){
console.log("名字是"+this.name)
};
}
person.getInfo=function(){/* getInfo是静态方法名*/
console.log("这是一个静态方法可以直接通过类名.方法名进行使用")
}
person.getInfo()
person.prototype.xb = '男'
person.prototype.print= function(){
console.log("性别是"+this.xb)
}
var p = new person();
p.run();
p.print();
继承的实现:对象冒充实现继承:没有办法继承到原型链上的方法:
function web()
{
//通过对像冒充实现继承
person.call(this);
}
function person(){
this.name = "zhangsan"
this.age = 21
this.run = function(){
console.log("名字是"+this.name)
};
}
person.prototype.xb = '男'
person.prototype.print= function(){
console.log("性别是"+this.xb)
}
function web(){
person.call(this); //通过对像冒充实现继承
}
var w = new web()
w.run()//直接调用了person中的run方法 但是没有办法继承到原型链上的方法所以继承是不完整的 要想完整就要加上原型链继承:
web.prototype = new person()//实现原型链继承。
ts中实现类
class Porson {
private name: string; //属性,前面省略了public关键词
constructor(name: string) {//构造函数 在实例化类的时候触发的函数方法
this.name = name;
}
run(): any{
return`${this.name}在运动`
}
}
var p = new Porson("张三");
p.run();
class web extends Porson {//继承父类的属性和方法
constructor(name: string) {
super(name)//把参数传到父类中
}
}
var w = new web("小hehe");
w.run();
抽象类与抽象方法
pass
接口
调用接口时传入的数值必须和接口定义的一样否则就会报错。
interface Animal{
eat():void;
}//动物接口 类类接口其作用和抽象类一样
interface Person extends Animal{
work():void;
}//人类接口且继承了动物接口(接口的扩展)
class Web implements Person{//需要实现人类接口和动物接口中的两个方法
name: string
constructor(name:string){
this.name = name
}
eat () {
console.log(`${this.name}喜欢吃馒头`)
}
work(){
console.log(`${this.name}写代码`)
}
}
var w = new Web('ruby')
w.eat()
w.work()
泛型
我们为了实现传参的自由性可以使用any定义参数类型,但是怎么做我们就放弃了类型检查的功能,即使传入的是字符型,返回的是数值型也不会报错,为了很好的解决这个问题我们使用泛型定义变量:其作用就是传入参数的类型和返回数值的类型必须一致。
function createArray<T>(length: number, value: T): Array<T> {
let result: T[] = [];
for (let i = 0; i < length; i++) {
result[i] = value;
}
return result;
}
createArray<string>(3, 'x'); // ['x', 'x', 'x']