TypeScript 变量声明详细教程

文章目录

  • 前言
  • var 声明
  • 作用域规则
  • 变量获取怪异之处
  • let 声明
  • 块作用域
  • 重定义及屏蔽
  • 块级作用域变量的获取
  • const 声明
  • let vs. const
  • 解构数组
  • 对象解构
  • 属性重命名
  • 后言

前言

hello world欢迎来到前端的新世界


😜当前文章系列专栏:vue.js
🐱‍👓博主在前端领域还有很多知识和技术需要掌握,正在不断努力填补技术短板。(如果出现错误,感谢大家指出)🌹
💖感谢大家支持!您的观看就是作者创作的动力

var 声明

一直以来我们都是通过var关键字定义JavaScript变量。

var a = 10;

大家都能理解,这里定义了一个名为a值为10的变量。

我们也可以在函数内部定义变量:

function f() {var message = "Hello, world!";return message;
}

并且我们也可以在其它函数内部访问相同的变量。

function f() {var a = 10;return function g() {var b = a + 1;return b;}
}var g = f();
g(); // returns 11;

上面的例子里,g可以获取到f函数里定义的a变量。 每当 g被调用时,它都可以访问到f里的a变量。 即使当g在f已经执行完后才被调用,它仍然可以访问及修改a。

function f() {var a = 1;a = 2;var b = g();a = 3;return b;function g() {return a;}
}f(); // returns 2

作用域规则

对于熟悉其它语言的人来说,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);
}

介绍一下,setTimeout会在若干毫秒的延时后执行一个函数(等待其它代码执行完毕)。

好吧,看一下结果:

10
10
10
10
10
10
10
10
10
10

很多JavaScript程序员对这种行为已经很熟悉了,但如果你很不解,你并不是一个人。 大多数人期望输出结果是这样:

0
1
2
3
4
5
6
7
8
9

还记得我们上面讲的变量获取吗?

每当g被调用时,它都可以访问到f里的a变量。

让我们花点时间考虑在这个上下文里的情况。 setTimeout在若干毫秒后执行一个函数,并且是在for循环结束后。 for循环结束后,i的值为10。 所以当函数被调用的时候,它会打印出 10!

一个通常的解决方法是使用立即执行的函数表达式(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);
}

这种奇怪的形式我们已经司空见惯了。 参数 i会覆盖for循环里的i,但是因为我们起了同样的名字,所以我们不用怎么改for循环体里的代码。

let 声明

现在你已经知道了var存在一些问题,这恰好说明了为什么用let语句来声明变量。 除了名字不同外, let与var的写法一致。

let hello = "Hello!";

主要的区别不在语法上,而是语义,我们接下来会深入研究。

块作用域

当用let声明一个变量,它使用的是词法作用域或块作用域。 不同于使用 var声明的变量那样可以在包含它们的函数外访问,块作用域变量在包含它们的块或for循环之外是不能访问的。

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 herereturn b;
}

这里我们定义了2个变量a和b。 a的作用域是f函数体内,而b的作用域是if语句块里。

在catch语句里声明的变量也具有同样的作用域规则。

try {throw "oh no!";
}
catch (e) {console.log("Oh well.");
}// Error: 'e' doesn't exist here
console.log(e);

拥有块级作用域的变量的另一个特点是,它们不能在被声明之前读或写。 虽然这些变量始终“存在”于它们的作用域里,但在直到声明它的代码之前的区域都属于 时间死区。 它只是用来说明我们不能在 let语句之前访问它们,幸运的是TypeScript可以告诉我们这些信息。

a++; // illegal to use 'a' before it's declared;
let a;

注意一点,我们仍然可以在一个拥有块作用域变量被声明前获取它。 只是我们不能在变量声明前去调用那个函数。 如果生成代码目标为ES2015,现代的运行时会抛出一个错误;然而,现今TypeScript是不会报错的。

function foo() {// okay to capture 'a'return a;
}// 不能在'a'被声明前调用'foo'
// 运行时应该抛出错误
foo();let a;

重定义及屏蔽

我们提过使用var声明时,它不在乎你声明多少次;你只会得到1个。

function f(x) {var x;var x;if (true) {var x;}
}

在上面的例子里,所有x的声明实际上都引用一个相同的x,并且这是完全有效的代码。 这经常会成为bug的来源。 好的是, let声明就不会这么宽松了。

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重写之前的sumMatrix函数。

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

这个版本的循环能得到正确的结果,因为内层循环的i可以屏蔽掉外层循环的i。

通常来讲应该避免使用屏蔽,因为我们需要写出清晰的代码。 同时也有些场景适合利用它,你需要好好打算一下。

块级作用域变量的获取

在我们最初谈及获取用var声明的变量时,我们简略地探究了一下在获取到了变量之后它的行为是怎样的。 直观地讲,每次进入一个作用域时,它创建了一个变量的 环境。 就算作用域内代码已经执行完毕,这个环境与其捕获的变量依然存在。

function theCityThatAlwaysSleeps() {let getCity;if (true) {let city = "Seattle";getCity = function() {return city;}}return getCity();
}

因为我们已经在city的环境里获取到了city,所以就算if语句执行结束后我们仍然可以访问它。

回想一下前面setTimeout的例子,我们最后需要使用立即执行的函数表达式来获取每次for循环迭代里的状态。 实际上,我们做的是为获取到的变量创建了一个新的变量环境。 这样做挺痛苦的,但是幸运的是,你不必在TypeScript里这样做了。

当let声明出现在循环体里时拥有完全不同的行为。 不仅是在循环里引入了一个新的变量环境,而是针对 每次迭代都会创建这样一个新作用域。 这就是我们在使用立即执行的函数表达式时做的事,所以在 setTimeout例子里我们仅使用let声明就可以了。

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

会输出与预料一致的结果:

0
1
2
3
4
5
6
7
8
9

const 声明

const 声明是声明变量的另一种方式。

const numLivesForCat = 9;

它们与let声明相似,但是就像它的名字所表达的,它们被赋值后不能再改变。 换句话说,它们拥有与 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--;

除非你使用特殊的方法去避免,实际上const变量的内部状态是可修改的。 幸运的是,TypeScript允许你将对象的成员设置成只读的。 接口一章有详细说明。

let vs. const

现在我们有两种作用域相似的声明方式,我们自然会问到底应该使用哪个。 与大多数泛泛的问题一样,答案是:依情况而定。

使用最小特权原则,所有变量除了你计划去修改的都应该使用const。 基本原则就是如果一个变量不需要对它写入,那么其它使用这些代码的人也不能够写入它们,并且要思考为什么会需要对这些变量重新赋值。 使用 const也可以让我们更容易的推测数据的流动。

另一方面,用户很喜欢let的简洁性。 这个手册大部分地方都使用了 let。

跟据你的自己判断,如果合适的话,与团队成员商议一下。有趣的是,TypeScript允许您指定对象的成员为只读。关于接口的章节有详细说明。

解构

解构数组

最简单的解构莫过于数组的解构赋值了:

let input = [1, 2];
let [first, second] = input;
console.log(first); // outputs 1
console.log(second); // outputs 2

这创建了2个命名变量 first 和 second。 相当于使用了索引,但更为方便:

first = input[0];
second = input[1];

解构作用于已声明的变量会更好:

// swap variables
[first, second] = [second, first];

作用于函数参数:

function f([first, second]: [number, number]) {console.log(first);console.log(second);
}
f(input);

你可以使用…name语法创建一个剩余变量列表:

let [first, ...rest] = [1, 2, 3, 4];
console.log(first); // outputs 1
console.log(rest); // outputs [ 2, 3, 4 ]

当然,由于是JavaScript, 你可以忽略你不关心的尾随元素:

let [first] = [1, 2, 3, 4];
console.log(first); // outputs 1

或其它元素:

let [, second, , fourth] = [1, 2, 3, 4];

对象解构

你也可以解构对象:

let o = {a: "foo",b: 12,c: "bar"
}
let {a, b} = o;

这通过 o.a and o.b 创建了 a 和 b 。 注意,如果你不需要 c 你可以忽略它。

就像数组解构,你可以用没有声明的赋值:

({a, b} = {a: "baz", b: 101});

注意,我们需要用括号将它括起来,因为Javascript通常会将以 { 起始的语句解析为一个块。

属性重命名

你也可以给属性以不同的名字:

let {a: newName1, b: newName2} = o;

这里的语法开始变得混乱。 你可以将 a: newName1 读做 “a 作为 newName1”。 方向是从左到右,好像你写成了以下样子:

let newName1 = o.a;
let newName2 = o.b;

令人困惑的是,这里的冒号不是指示类型的。 如果你想指定它的类型, 仍然需要在其后写上完整的模式。

let {a, b}: {a: string, b: number} = o;

后言

创作不易,要是本文章对广大读者有那么一点点帮助 不妨三连支持一下,您的鼓励就是博主创作的动力

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/191139.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【MySQL】视图 + 用户管理

视图 前言正式开始视图用户管理user表创建新用户修改用户密码权限管理给用户赋权剥夺权限 前言 本篇所讲的视图和我上一篇事务中所讲的读视图不是一个东西&#xff0c;二者没有任何关系&#xff0c;如果看过我前一篇博客的同学不要搞混了。 其实视图和用户管理本来是想着分开…

华为OD机试真题-虚拟游戏理财-2023年OD统一考试(C卷)

题目描述: 在一款虚拟游戏中生活,你必须进行投资以增强在虚拟游戏中的资产以免被淘汰出局。现有一家Bank,它提供有若干理财产品m,风险及投资回报不同,你有N(元)进行投资,能接受的总风险值为X。 你要在可接受范围内选择最优的投资方式获得最大回报。 说明: 在虚拟游戏中…

CAPL通过在函数内改变全局变量的值

CAPL通过&在函数内改变全局变量的值 先定义一个全局变量。 variables {int tiancihaoche; }再定义一个函数如下: void change_1(int test) {test=555; }测试下: on key 2 {

大数据Doris(三十二):Doris高级功能

文章目录 Doris高级功能 一、​​​​​​​表结构变更

VMware Workstation Pro 17及 Windows 11 虚拟机的安装与激活

六点钟&#xff1a; 吃晚饭吗 不吃&#xff0c;胖胖 十点钟&#xff1a; 阿昊要吃夜宵对不对 ——CSDN&#xff0c;记录牛马生活 本文是在学习 Linux 期间&#xff0c;使用 VMware 时顺带学习 Windows 11 虚拟机的安装与激活 VMware Workstation Pro 17及 Windows 11 虚拟机…

Java Throwable

如图展示了 Java 整个异常体系的关系。 Throwable 的 Java 异常体系的基类, 他的直接子类有 Error 和 Exception 2 个。 1 Error Error 表示的是由于系统错误, Java 虚拟机抛出的异常, 例如 Java 虚拟机崩溃, 内存不够等, 这种情况仅凭程序自身是无法处理的, 在程序中也不会…

JTag 刷写TC397 的Flash

本文实现一个Trace32脚本的示例&#xff0c;用于连接JTAG端口并刷写基于TC397芯片的Flash&#xff1a; /SILENT /NOQUIET /OPENDEBUGGER /VERSION /IFCONNECTION JTAG /CPU TC397 ; 根据具体芯片的型号选择正确的CPU类型 /CONNECT /PROTOCOL JTAG /FREQUENCY 10000000 …

004、简单页面-基础组件

之——基础组件 目录 之——基础组件 杂谈 正文 1.Image 1.0 数据源 1.1 缩放 1.2 大小 1.3 网络图片 2.Text 2.0 数据源 2.1 大小 2.2 粗细 2.3 颜色 2.5 样式字体 2.6 基础示例 2.7 对齐 2.8 省略 2.9 划线 3.TextInput 3.1 输入类型 3.2 提示文…

量子测量-技术点杂录

目录: 高质量文章导航-持续更新中_GZVIMMY的博客-CSDN博客 前置:量子测量设备 电子显微镜:电子显微镜可以在非常高分辨率下观察生物组织、细胞和分子结构。通过调整电子束的强度和聚焦来观察细胞内部的微小结构。但是,电子显微镜需要对样品进行切片处理,而且在真空中进行…

【Android知识笔记】兼容适配专题

屏幕适配 常规适配手段 使用像素密度无关的尺寸单位 避免写死控件,尽量多使用wrap_content、match_parent、weight控件距离使用dp 字体大小使用sp不要用写死的px值布局方面 使用相对布局,禁用绝对布局使用约束布局ConstraintLayout使用百分比布局使用布局限定符 使用尺寸限…

HbuilderX 项目打包文件过大问题优化

文章目录 HbuilderX 项目打包文件过大问题优化主要操作收效甚微&#xff0c;但又有那么点用的方法使用 gulp 压缩&#xff08;最后一步&#xff09;使用与配置 网上找的 gulp 优化压缩配置还未尝试可能有用的方法 尝试过程中看到的一些优质文章 HbuilderX 项目打包文件过大问题…

Shell循环:for(三)

示例&#xff1a;使用for实现批量主机root密码的修改 一、前提 已完成密钥登录配置&#xff08;ssh-keygen&#xff09;定义主机地址列表并了解远程修改密码的方法 [rootlocalhost ~]# ssh-keygen #设置免密登录[rootlocalhost ~]# ssh-copy-id 192.168.151.151 二、演示…

科研学习|论文解读——Open government research over a decade: A systematic review

Open government research over a decade: A systematic review 十年来的开放政府研究&#xff1a;一个系统性综述 摘要 在过去十年中&#xff0c;对开放政府的学术研究蓬勃发展。然而&#xff0c;对开放政府的全面审查是有限的。这一研究空白不仅阻碍了我们对开放政府整体知…

mysql区分大小写吗

mysql在windows下默认是不区分大小写的&#xff0c;在linux下默认是区分大小写的。 所以&#xff0c;为了避免出问题&#xff0c;许多公司的数据库编程规范中明确规定&#xff1a;库名、表名、列名、索引名一律小写&#xff0c;不同单词之间以下划线分割&#xff0c;且控制在3…

重庆市失业金申领流程

1.领失业金的前提&#xff1a;非本人意愿中断就业。个人理解就是不是主动辞职才能领。 2.因此公司在处理社保减员的时候&#xff0c;不能是劳动者主动提出离职。 3.社保减员可选择原因&#xff1a;其他原因中断缴费 / 由单位提出双方协商一致解除劳动合同。 4.当社保暂停缴费…

算法——滑动窗口

滑动窗口大致分为两类&#xff1a;一类是窗口长度固定的&#xff0c;即left和right可以一起移动&#xff1b;另一种是窗口的长度变化&#xff08;例如前五道题&#xff09;&#xff0c;即right疯狂移动&#xff0c;left没怎么动&#xff0c;这类题需要观察单调性(即指针)等各方…

以下哪项不是在内蒙古举办的?()

需要查看更多试题和答案&#xff0c;可以前往题海舟&#xff08;题海舟&#xff09;进行搜题查看。可以搜“题干关键词”。 以下哪项不是在内蒙古举办的&#xff1f;&#xff08;&#xff09; A.中蒙博览会 B.东北亚区域合作高峰论坛 C.中蒙俄合作高层论坛 D.中日经济合作会 …

C++基础 -32- 逻辑运算符重载

逻辑运算符重载格式 bool operator&&(data1&a,data2&b) {if(a.a&&b.a){return true;}elsereturn false; }举例使用单目运算符重载 #include "iostream"using namespace std;class data1 {public :int a;data1(int a):a(a){} };class d…

(数据结构)顺序表的查找

静态分配代码&#xff1a; #include<stdio.h> #include<stdlib.h> #define MAX 100 typedef struct LinkList {int data[MAX];int lenth; }Link; //初始化 void CreateList(Link* L) {L->lenth 0;for (int i 0; i < MAX; i){L->data[i] 0;} } //插入 …

策略模式与简单工厂模式:终结if-else混乱,让代码更清爽

阅读建议 嗨&#xff0c;伙计&#xff01;刷到这篇文章咱们就是有缘人&#xff0c;在阅读这篇文章前我有一些建议&#xff1a; 本篇文章大概4500多字&#xff0c;预计阅读时间长需要5分钟。本篇文章的实战性、理论性较强&#xff0c;是一篇质量分数较高的技术干货文章&#x…