TypeScript从精通到入门2:变量声明

var 的声明

在let和const没出现的时候,我们都是通过var关键字定义JavaScript变量。

var a = 10;

在现实使用中,使用var声明变量会产生一些问题;

1. 作用域问题
使用 var 声明的变量存在函数作用域而非块级作用域。这意味着在使用 var 声明的变量在函数内部的任何位置都是可见的,而不仅仅是在声明的块级范围内。

function f(shouldInitialize: boolean) {
    if (shouldInitialize) {
        var x = 10;
    }

    return x;
}

f(true);  // returns '10'
f(false); // returns 'undefined'

这个例子中,变量 x是定义在if语句里面,但是我们却可以在语句的外面访问它。 这是因为 var声明可以在包含它的函数,模块,命名空间或全局作用域内部任何位置被访问,包含它的代码块对此没有什么影响。 有些人称此为* var作用域或函数作用域*。 函数参数也使用函数作用域。
这会引发一些错误:

function sumMatrix(matrix: number[][]) {
    var sum = 0;
    for (var i = 0; i < matrix.length; i++) {
        var currentRow = matrix[i];
        for (var i = 0; i < currentRow.length; i++) {
            sum += currentRow[i];
        }
    }

    return sum;
}

这里很容易看出一些问题,里层的for循环会覆盖变量i,因为所有i都引用相同的函数作用域内的变量。 有经验的开发者们很清楚,这些问题可能在代码审查时漏掉,引发无穷的麻烦。

for (var i = 0; i < 10; i++) {
    setTimeout(function() { console.log(i); }, 100 * i);
}

结果:

10101010101010101010

解决方法:
1: 将变量i改为let声明的变量而不是全局变量,此时的for循环就会变成是一个单独的作用域,作用与闭包形成的块级作用将相同,因此能够输出想要的结果

for(let i=0;i<10;i++){
        setTimeout(function(){
         console.log(i);  
        },i*1000);
    }

2: 使用立即执行的函数表达式(IIFE)来捕获每次迭代时i的值

for (var i = 0; i < 10; i++) {
    // capture the current state of 'i'
    // by invoking a function with its current value
    (function(i) {
        setTimeout(function() { console.log(i); }, 100 * i);
    })(i);
}

结果:

0123456789

2. 变量提升问题
使用 var 声明的变量存在变量提升(hoisting)现象。这意味着变量的声明会被提升到作用域的顶部,在变量声明之前使用变量也不会报错。

function example() {
  console.log(x); // 输出:undefined
  var x = 10;
}

在这个例子中,尽管在打印 x 的语句之前才实际声明变量 x,但由于变量提升的原因,x 的值被视为 undefined。

3. 全局污染问题
使用 var 声明的变量会成为全局变量,会污染全局命名空间,容易造成命名冲突和意外的行为。

var x: number = 10;
 
function example() {
  var x: number = 20;
  console.log(x); // 输出:20
}
 
example();
 
console.log(x); // 输出:10

在这个例子中,var 声明的变量 x 在函数内部重新声明并赋值为 20,但它不会影响全局变量 x 的值。

为了避免以上var引起的缺陷,TypeScript 推荐使用 let 或 const 来声明变量;

let 和 const 声明

let 用于声明可变(可重新赋值)的变量,而 const 用于声明不可变(不可重新赋值)的变量(常量)。变量声明的一般语法如下

let variableName: string = 'hello';
const constantName: string = 'hello';
variableName = '改变的值'; // 可以
constantName= '改变的值'; // 报错,常量不能重新赋值

let 变量声明
let 关键字用于声明可变的变量。它的作用范围被限制在块级作用域内。块级作用域是由花括号 {} 包围的一段代码。例如:

{
  let num: number = 10;
  console.log(num); // 输出 10
}
console.log(num); // 报错,num 在这里不可访问, 因为let有块级作用域

const 声明
const关键字用于声明不可变的变量,但是可以对引用类型里的属性,可以进行改变!

const num = 1;
num = 2; // 报错,不能重新赋值
const obj = {name: 'Alice', age: 18}
obj.age = 20 // 可以修改属性
obj = {name: 'tom', age: 19} // 报错,不能重新赋值
const arr = [1,2,3]
arr[0] = 4;
console.log(arr); // [4,2,3]
arr = [4,2,3] // 报错,不能重新赋值

解构赋值

TypeScript 支持解构赋值,可以从数组或对象中提取值并赋给变量。
1. 解构数组:

// 普通结构
let input = [1, 2];
let [first, second] = input;
console.log(first); // outputs 1
console.log(second); // outputs 2
// 作用于函数参数
function f([first, second]: [number, number]) {
    console.log(first);
    console.log(second);
}
f(input);

2. 解构对象:

// 普通结构
let o = {
    a: "foo",
    b: 12,
    c: "bar"
};
let { a, b } = o;
// 可以在对象里使用...语法创建剩余变量
let { a, ...passthrough } = o;
let total = passthrough.b + passthrough.c.length;
// 给属性取别名
let { a: newName1, b: newName2 } = o;
// 额外进行设置指定类型
let {a, b}: {a: string, b: number} = o;

3. 函数声明:

解构也能用于函数声明

type C = { a: string, b?: number }
function f({ a, b }: C): void {
    // ...
}

但是,通常情况下更多的是指定默认值,解构默认值有些棘手。 首先,你需要在默认值之前设置其格式。

function f({ a="", b=0 } = {}): void {
    // ...
}
f();

使用结构,可以将一个数组展开为另一个数组,或将一个对象展开为另一个对象

// 数组
let first = [1, 2];
let second = [3, 4];
let bothPlus = [0, ...first, ...second, 5];
// 对象
let defaults = { food: "spicy", price: "$$", ambiance: "noisy" };
let search = { ...defaults, food: "rich" };
  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值