原文地址:https://banggan.github.io/2019/01/18/TypeScript学习笔记-之变量声明/
变量声明
因为TypeScript是JavaScript的超集,所以它本身就支持let和const。const是对let的一个增强,它能阻止对一个变量再次赋值,在TS中,主要就是var let const,就简单说一下用法吧:
var声明
var a =10; //定义变量
function f() { //函数内部定义变量
var message = "Hello, world!";
return message;
}
//函数嵌套调用
function f() {
var a = 1;
a = 2;
var b = g();
a = 3;
return b;
function g() { //g可以获取到f函数定义的a
return a;
}
}
f(); // returns 2
let声明
let的声明与var一样,重要的是,let和var的区别是实现块级作用域,而且不存在变量提升,且不能重复赋值。
这里我们定义了2个变量a和b。 a的作用域是f函数体内,而b的作用域是if语句块里
//内部能访问到b,但是外部就不能访问;
function f(input: boolean) {
let a = 100;
if (input) {
// Still okay to reference 'a'
let b = a + 1;
return b;
}
// Error: 'b' doesn't exist here
return b;
}
//暂时性死区的例子
a++; // illegal to use 'a' before it's declared;
let a;
重复定义
let x = 10;
let x = 20; // 错误,不能在1个作用域里多次声明`x`
并不是要求两个均是块级作用域的声明TypeScript才会给出一个错误的警告。同样会发生错误
function f(x) {
let x = 100; // error: interferes with parameter declaration
}
function g() {
let x = 100;
var x = 100; // error: can't have both declarations of 'x'
}
并不是说块级作用域变量不能用函数作用域变量来声明。 而是块级作用域变量需要在明显不同的块里声明。
function f(condition, x) {
if (condition) {
let x = 100;
return x;
}
return x;
}
f(false, 0); // returns 0
f(true, 0); // returns 100
当let声明出现在循环体里时拥有完全不同的行为。 不仅是在循环里引入了一个新的变量环境,而是针对 每次迭代都会创建这样一个新作用域。 这就是我们在使用立即执行的函数表达式时做的事。
for (let i = 0; i < 10 ; i++) {
setTimeout(function() {console.log(i); }, 100 * i);
} //输出0-9
const声明
const和let基本一致,只是const声明的变量被赋值后不能再改变(所以对于const来说,只声明不赋值,就会报错),作用域同let
const numLivesForCat = 9;
const kitty = {
name: "Aurora",
numLives: numLivesForCat,
}
// Error
kitty = {
name: "Danielle",
numLives: numLivesForCat
};
// all "okay"
kitty.name = "Rory";
kitty.name = "Kitty";
kitty.name = "Cat";
kitty.numLives--;
let vs. const
使用最小特权原则,所有变量除了你计划去修改的都应该使用const。 基本原则就是如果一个变量不需要对它写入,那么其它使用这些代码的人也不能够写入它们,并且要思考为什么会需要对这些变量重新赋值。 使用 const也可以让我们更容易的推测数据的流动。
解构
解构(destructuring assignment)是一种表达式,将数组或者对象中的数据赋给另一变量。
数组解构
let input = [1, 2,3];
let [f, s] = input;
console.log(f); // outputs 1
console.log(s); // outputs 2
交换变量值
[f,s] = [s,f];
console.log(f); // outputs 2
console.log(s); // outputs 1
使用…创建剩余变量
let nums= [1,2,3,4]
let [f, ...rest] = nums;
console.log(f); // 输出: 1
console.log(rest);// 输出: [2,3,4]
忽略数组中某些元素
let [first] = [1, 2, 3, 4];
console.log(first); // 输出: 1
let [, second, , fourth] = [1, 2, 3, 4];
对象解构
let o = {
a: "foo",
b: 12,
c: "bar"
}
let { a, b } = o;
console.log(a); // 输出: foo
console.log(b);// 输出: 12
将对象o.a赋值给a,0.b赋值给b,这里的a,b都是对象属性名且必须一致,而属性c则会忽略
上述例子中声明的变量a和b必须和对象中属性一致,如果想换一个名字,写法如下:
let { a: aa, b: bb } = o;
默认值
let o = {
a: "foo",
b: undefined,
c: "bar"
}
let {a, b=1}= o;
console.log(a); // 输出: foo
console.log(b);// 输出: 1
当属性b的值为undefined时,解构表达式会使用默认值
展开
展开操作符正与解构相反。 它允许你将一个数组展开为另一个数组,或将一个对象展开为另一个对象。
数组展开
let first = [1, 2];
let second = [3, 4];
let bothPlus = [0, ...first, ...second, 5];
console.log(bpthPlus);输出 [0,1,2,3,4,5]
对象展开
let defaults = { food: "spicy", price: "$$", ambiance: "noisy" };
let search = { ...defaults, food: "rich" };
console.log(search); //输出:{food: "rich", price: "$$", ambiance: "noisy"}
对象的展开比数组的展开要复杂的多。 像数组展开一样,它是从左至右进行处理,但结果仍为对象。 这就意味着出现在展开对象后面的属性会覆盖前面的属性。 因此,如果我们修改上面的例子,在结尾处进行展开的话:
let defaults = { food: "spicy", price: "$$", ambiance: "noisy" };
let search = {food: "rich", ...defaults };
console.log(search); //输出:{food: "spicy", price: "$$", ambiance: "noisy"}
对象展开还有其它一些意想不到的限制。 首先,它仅包含对象 自身的可枚举属性。 大体上是说当你展开一个对象实例时,你会丢失其方法:
class C {
p = 12;
m() {
}
}
let c = new C();
let clone = { ...c };
clone.p; // ok
clone.m(); // error!