JavaScript,这个语言也是当下最流行的编程语言之一。
虽然 JS 主要是用于前端页的开发,但实际上,也可以进行 服务器开发 / 客户端程序的开发。其实,JavaScript 在 HTML篇中,我就已经简单介绍过,JavaScript的原名是 ECMAScript,简称 ES。
之所以改名字,是因为蹭 Java 热度。
虽然叫做JavaScript,但是语言上还是跟 Java 有区别的。
另外,在后端开发这里,可选择的语言有很多很多。
【Java,C/C++,Go,Python,PHP…】
但是,前端开发这里,JavaScript 就是一家独大。
既然是一家独大,那么势必会惹人眼红,想要代替 JS 的、
因此,JS 也是有挑战者的!
1、Dart(谷歌开发的,下一代开发平台上面自带的编程语言)
不得不说,谷歌野心很大,不仅想要取代 Java 的位置,同时也希望这门语言能实现 浏览器 /桌面 上的开发。但是Dart,目前还只是处于发展阶段,而且从近几年的情况来看,进展还不是很大。短期内取代 js 的 希望不大。
2、WebAssembly
是最近今年新搞出来的东西。
WebAssembly 类似于 “汇编语言”,在浏览器上定义了一组基础指令。
可以通过一些第三方工具,把其它的主流编程语言给转换成 WebAssembly。
这时候,像 C/C++,Java,Python…都可以通过这个途径来实现页面的开发了。
由此可见,WebAssembly 的野心也不小!
但是这项技术,目前还不够成熟。
短期内不可能对 JS 造成影响。
3、TypeScript
TypeScript 是最有希望的挑战者!而且是目前进展非常迅猛的一个大将!
知识渊博的朋友,应该知道 JS的代码 是可以在 TS 上运行的!
JS 和 TS 的关系,就类似于 C 和 C++的关系。【兼容】
就是说,TS 完全支持 JS当下的语法,并且引入了一些新的语法规则,让程序员敲的更爽!
其实这件事就是复刻 C++,当年起家的过程。
当年C++也是一样,最开始C语言火了,但是C语言也有一些缺陷。
于是当时就有一个老哥就发明了一个C++,然后就宣传说:“我们这个东西,基本和C语言一样!你只要会C就很容易上手我们的C++。同时我们又引入了一些新的东西,让大家敲起C++代码更爽一些1”
这个 TS 的吸引力,对于现有的这些前端开发程序员来说,是非常大的!
这就不像前面两个(Dart 和 WebAssembly),它们是有着自己一套体系。
相比于去学习新东西,还不如在基于目前 JS 的基础上,再“搭建一些东西”。
TS 相当于施展了一波 釜底抽薪 / 挖墙脚 的操作。
所以,TS的发展就非常快了!
话虽如此,TS 至今仍未彻底取代JS。
也就是说,前端这个圈子还是以 JS 为主。
你去应聘 前端开发,人家优先关注你能不能写JS!
TS 就是一个加分项,会加分,不会也不影响。
但是如果你 JS 不会,那你肯定是找不到工作。
所以,本篇博文讲的是 JS。
既然是讲 JavaScript,我们肯定是要知道 JavaScript 的作者JavaScript 之父 布兰登 * 艾奇 (Brendan Eich)
这是曾今的布兰登-爱奇
发明JavaScript之后的布兰登
1995 年, 用 10 天时间完成 JS 的设计 (由于设计时间太短,语言的一些细节考虑得不够严谨,导致后来很长一段时间,Javascript 写出来的程序混乱不堪)
最初在网景公司, 命名为 LiveScript,
一般认为,当时 Netscape 之所以将 LiveScript 命名为 JavaScript,是因为 Java 是当时最流行的
编程语言,带有 “Java” 的名字有助于这门新生语言的传播。
其实 Java 和 JavaScript 之间的语法风格相去甚远。
HTML: 网页的结构(骨)
CSS: 网页的表现(皮)
JavaScript: 网页的行为(魂)
HTML写出来的代码,就相当于是页面的框架,相当于是“骨”。
CSS 就是在 HTML 的基础上,进行装饰,相当于套了一层“皮”,使其页面变得好看。
但是 此时的页面,仍是一个静态的!
当我们加入了JS之后,我们就相当于给网页赋予了灵魂。
所谓的灵魂,就是指的交互性。
其目的,就是让网页不再是一个 纯粹静态的、干巴巴的、不动的一个东西了。
而是会和程序员进行一些交互。
就是我们在页面中进行操作,页面也给予我们一定的反馈。
所以,这个时候,彼此之间就会出现一个动态变换的过程。
JS 和前面的 HTML / CSS 类似,都是运行在浏览器上。
在浏览器中,内置了一个 JS 的执行引擎。
所谓的引擎,就对标了我们的 JVM(Java-Virtual-Machine:Java虚拟机)。
有的人可能会说,这不就是一门解释型语言吗?
注意!现在都已经2022年,已经没有“编译型” 和 “解释型”的说法了。
因为现在的这些编程语言,具体去执行的时候,不管是“编译型” ,还是 “解释型,这里面都会有一些很模糊的界限。
比如:
像那种 逐句执行的,难道就能说它是执行一行,翻译一行吗?
不能!它也会事先对整体代码进行扫描一遍,进行一些预先处理操作。
所以这两者界限,已经越来越模糊了。
拓展:JS,Node.js,vue.js,这三者之间有什么关系?
JS 是编程语言。要想执行JS,需要有一个 JS 的执行引擎【就是前面说到浏览器提供的引擎】。
其中Chrome 浏览器,内置的 JS引擎,也是谷歌自己研发的,是一个非常知名的引擎,叫做 V8 引擎。
因为这个 引擎 做得实在是太好了。
以至于又有大佬,把这个 V8 引擎 单独从浏览器里面 摘取出来。
然后,又重新包装了一层,就成为了 Node.js
所以 Node.js 就是一个 JS 的运行平台,对标的是浏览器。
只不过浏览器是运行中客户端上。
而 Node.js,既可以运行在客户端上,也可以运行在服务器上。
Node.js 就是一个单独的执行程序。
那么,通过这个东西,就可以给 JS 赋予 客户端开发 / 服务器开发 的能力、这就好比:
将 Chrome浏览器,比作一辆车。
这辆车做的很好,而且车里面的发动机(JS 引擎)也做的很多。
然后,就有那种动手能力贼强的大佬,把那个发动机给拆下来了,装到自己的一搜轮艇上进行使用。
这就很厉害了,这个发动机(浏览器内置引擎),不仅能在车上(客户端)起到效果,而且还能在水上跑(服务器)。
所以Node.js,这就给 JS赋予了更多的功能
至于 vue.js,这个只是一个单纯基于js实现的,在浏览器上运行的 库 / 框架
总得来说,Node.js是一个更加底层的组件,对标的是浏览器。
JS 是执行某些逻辑所需要用到的基本的编程语言。
而 vue.js,是我们为了写 JS 代码更方便,而发明的一个库。
对于运行在浏览器上的 JS 来说,可以视为分成三个部分:
1、JS 核心语法
2、DOM API:浏览器提供的一组,操作页面元素的API
3、BOM API:浏览器提供的一组,操作浏览器窗口的API。这两组 API,其实就是给 JS 核心语法 打下手的。
换句话说:你光有一个语法核心,缺少API,是很难实现一些有实际价值的程序的。
这跟我们学Java一样,光学个 JavaSE ,能写什么?好像也写不了什么。
所以我们想要写出一些复杂程序,就需要搭配一些 库 /框架。
在后面,我会讲的 Servlet 和 spring,这些就是复合框架。
对于 Node.js 上执行的 JS 来说,就是分成2个部分
1、JS 核心语法
2、Node.js 提供的 API这个API 和 DOM / BOM 无关了。
因为它已经不是在写网页了。
没有网页就谈不上 DOM 了。
也没有浏览器,也就谈不上 BOM。
但是这API 提供了一些其他的功能。
比如:
文件操作,网络操作,系统级的操作…
虽然 Node.js 给 JS 提供了更多的发挥空间。
但实际上,JS 最主要的使用场景,还是用来开发页面。
因为服务器这个圈子里非常卷了,所以 JS 这个东西在这个方面有没有都一样。
相比之下,Node.js 也可以开发服务器,但是并不是主流的开发方式。
反而在浏览器页面开发这里,JS 是无可替代的。因此,本文主要还是针对 运行在浏览器上的 JS 情况。
大家在这里,一定要明白一点:
JS 是运行在浏览器上的。
JS 很多的功能都是浏览器给它提供的能力。
这就好比,我们Java是运行 JVM 上的,java的很多能力都是 JVM 提供的。
此时,JavaScript的最简单的一个程序,输出 hello world就完成了。
具体是通过 alert 函数进行弹框来实现的。
这个 alert,可以视为是浏览器提供的一个 BOM API 之一。另外,js 里面的语句后面的 分号,不是必须的。
可写可不写。
但是建议:还是写上。
1、内嵌式
2、行内式
3、外部式
把 js 写到一个单独的 .js 文件中,再通过 script0 标签来引入
这种方式,是实际开发中最长用的一种引入 js 代码的方式。
因为这种方式,可以把 js 代码分成多个文件来去分别表示。
但是本篇博文主要还是以内嵌式为主。
因为写起来简单,而且我们目前写的代码就不难。
拓展:
如果 script 标签中,既有 src 属性,内部又有 JS 代码,会怎么样?
就会导致里面的代码不会被执行的效果。
如果你想要全部输出,只能将其拆分成2个script标签,来分别执行。
JS 和 用户交互,不止弹窗这种方式。
还有很多其它的方式。
输入: prompt
弹出一个输入框
这个操作,类似 Java 语言的 Scanner 。
但是在实际开发中,很少会用到,比较小众。
因为我们可以借助 input 标签进行输入,借助用户点击按钮也能进行输入。
输出: alert
输出:console.log
在控制台打印一个 日志(供程序员看)
这个是开发中,最常用的一种输出方式。
主要是因为 alert 弹框,体验并不好。这就好比,你在看剧,突然弹个广告。
你还得必须看完。
你说,难不难受?
弹框也是一样,弹出框,你就必须要处理。
否则,无法进行其他的页面操作。
我们在写代码,后者调试代码的时候,一般就会通过 console.log 来打印。
console.log 会把 日志给输出到控制台中。
注意!这个控制台不是电脑系统的控制台,而是 浏览器开发者工具中的标签页中的控制台
同时 console.log 不仅仅能打印日志,还能打印一些错误。
错误:
1、 JS 代码中的语法错误
2、JS代码运行时错误
这两种错误,都会在控制台中显示出来。
JavaScript 虽然一些设计理念和 Java 相去甚远, 但是在基础语法层面上还是有一些相似之处的。有了 Java 的基础很容易理解 JavaScript 的一些基本语法。
接下来的介绍,如果是和 Java一样,我就快速通过。
重点讲 和 Java 不一样的地方
定义一个变量
格式: var 变量名 = 初始值;
你会发现:不管你创建的变量是什么类型,此时都是统一使用 var 这个关键字来表示的。
至于你变量具体是什么类型,取决于初始化的值是什么类型。有的人可能较劲,你是不是少写一个浮点型数据?嗯?
这是因为 JS 是不区分 整形 和 浮点型数据的。
统一认为是数字类型的数据。
另外,JS的变量可以不初始化,但是不初始化的时候,变量是一个特殊的值:“undefined”,类型也是 undefined。
但是!你不能创建一个未初始化的变量,同时指定它是一个 字符串 / 数字 / 数组 类型。
这种操作是不被允许的!!!!
小拓展:
其实,这种风格的写法,是更主流的。
更多的编程语言是这样设定的。
包括像 C++,Go 这些语言,也支持上述类似的写法。
使用变量
这个没啥说的。
和 Java是一样的。
1、读取
2、修改
但是!说是一样,但还是有一个地方有着很大区别的。
就在于“修改”!如果 a 本来是一个 数字类型,在赋值的时候,可以给它赋一个数字类型,也可以赋一个字符串类型,也可以赋一个数组类型…
总得来说,你可以给它赋任意类型的数据。
此时,a 变量的类型,就会随之发生改变。
比如:
a 是数字类型的变量,我给它赋一个 字符串的数据,这是可以成功的。
而且 a 变量的类型 也变成了字符串类型。
因此,得出一个结论:变量的类型,可以在运行的过程中,随着赋值而发生改变。
这种行为,称为“动态类型”’。
动态:运行时
像 Java 这种语言,是不支持这种 运行时 类型发生改变的情况。
Java的这种行为,被称为“静态类型”
支持“动态类型”的编程语言还有:Python,PHP,Ruby…
支持“静态类型”的编程语言还有:C/C++,Go,Rust…
有的人可能基础扎实,认为 Java 也是支持 动态类型。
因为 Java 里面存在着 很多类型转换的方法。
注意!如果你有这种想法,那么你的Java基础肯定是有缺陷的!
你要明白Java类型转换,是将一个类型的值,转换后(旧的变量类型没有发生改变),赋给一个新的,并且与转换后的数据类型是对应的变量对象。【不然是进行转换的,必须类型是对应的】
而 JS 初始化 确定了 var 的类型,将一个与初始化指定类型,不相符的类型数据,强行塞给它。
非但没有出现类型的排斥,反而还把 var 初始化指定的数值类型给改变了!!
由此,就能看出 js 和 java ,关于 变量修改的区别了。
从侧面来思考,为什么写 var,而不是去写一个具体类型?
因为 它的类型老变嘛,你一开始是整形,回头它就给一个 字符串。
整形 对 字符串,大眼瞪小眼,不就很尴尬嘛。。。
拓展:动态类型 和 静态类型 谁更好?
其实最开始的时候,这个是有争议的。
但是,随着时间的推移,至现今。
在 2022 年,这样视角来看待,静态类型的阵营完胜!
现在业界基本达成共识,认为静态类型比动态类型更好!因为静态类型,编译器就可以做更多的检查,让有些 问题/错误 被提前发现了。
开发工具(IDE),也可以基于类型做更多的分析,提供更加丰富的支持。
当然不是说,动态类型就没有优点!
动态类型的优点:
1、代码更灵活。
2、用最少的代码,解决复杂的问题
比如:
写一个函数,就可以同时支持多种不同的类型参数。
完全不需要“重载 / 泛型”,这样语法。
因为 JS 天生就自带这种功能。
虽然有这个优势,,但是整体来说,利大于弊。
静态类型,哪怕说我多写一点 泛型 / 重载 类型的代码,也不要紧。
因为我的编译器都有充分的检查,所以我写完之后,也不会特别慌。
但是要是动态类型的话,很可能你写完代码,都不知道它对不对。
因为无论你输入什么样的参数,它都能接收。
因此,只要你一个参数错了,可能你就要在电脑面前坐几天。
在动态类型的语言中,Python 已经发展一些 “投敌”的苗头了。
本来 Python 也是 js 差不多。
a=10 都不带类型的。
现在Python 支持这种写法:a:int = 10
可以显式的去声明一个类型类。
但是!有一个点,需要补充。
随着时间推移,关于 JS 变量的类型,出现了一个新的类型 let。
而且,现在更倾向于 使用 let 来 代替 var。
下面,我们就来看一下 let 的效果。
从效果上来看,var 和 let 的效果是一样的。
但是我们更倾向于使用 let ,为什么呢?
这是因为 var 是 旧版本(早期的设计),有很多的地方,其实是违背直觉的!
比如,下面的这个例子
得出结论: var定义的变量,并不会收到大括号的限制。【作用域不会被限制】
我们再来看下面 let 定义的变量 的表现。
var 和 let,还有很多的一些小细节。
这里就不一样列举了。
总得来生活 let 要比 var 更符合我们程序员的直觉
JS 中内置的几种类型
number: 数字. 不区分整数和小数.
boolean: true 真, false 假.
string: 字符串类型.
undefined: 只有唯一的值 undefined. 表示未定义的值.
null: 只有唯一的值 null. 表示空值
上面三个类型,这个相比大家都很熟悉。
而下面的两个类型,就是独属于 js 了。
undefined:可以作为一个类型,同时表示唯一的值,也是 undefined。表示这个变量没有被定义;或者这个变量定义了,但是没有赋予初值的情况。
null:表示控制,虽然也是表示唯一的值,但是与 undefined 还有一定区别的。
而且这个空值 和 Java中 空值也是不一样的。
Java中的空值,是一个 Object 类型的。
而 JS 的空值的类型,和 undefined的情况一样,与表示的值一样,也是null类型。
像下面这种,8,16,2进制数,在Java也是支持的。
let a = 07; // 八进制整数, 以 0 开头
let b = 0xa; // 十六进制整数, 以 0x 开头
let c = 0b10; // 二进制整数, 以 0b 开头
特殊数字值
Infinity: 无穷大, 大于任何数字. 表示数字已经超过了 JS 能表示的范围.
-Infinity: 负无穷大, 小于任何数字. 表示数字已经超过了 JS 能表示的范围.
NaN: 表示当前的结果不是一个数字.
需要注意的是 负无穷大,不是负无穷小。
负无穷小,表示的是一个无限接近于 0 的数、
负无穷大,是指无穷大的相反数
但是,这两个比较少见。
见的最多的还是 NaN。
NaN:Not a Number - 不是一个数字
什么时候会出现 NaN ?
如果 运算结果,得到的不是数字的值,就会出现 NaN。
但是!如果我们把减法换成加法,这就不一样了!
效果就和 Java一样,会起到一个字符串拼接的作用。
另外, JS 中 提供了一个 isNaN的方法,用于判断 得到数据是否是一个数字。
返回数值类型 是 布尔类型。
如果是数字,返回false
反之,返回true
字符串类型,这里的很多操作和Java是类似的。
首先,字符串 可以通过单引号定义,也可以通过双引号定义。
如果字符串本身就包含了 引号,这种情况是被允许的。
这个时候,就可以通过 单双引号 灵活搭配的形式,来避免使用转义字符。
JS中的转义字符,不仅仅是 +单双引号,还有其它的转义字符,也是支持的。
像 \t(水平制表符),\n(换行符),\f(换页符)…这些转义字符,也是同样支持的。
字符串求长度
字符串拼接
与Java的boolean类型不同,
JS 中的 布尔类型,会当成 0 和 1 来处理。
而Java的 boolean 类型,取值能使 false 和 true,绝对不会和数字扯上关系的。
上面的代码中,js 把 true 当成 1 来处理了。
这种设定方式,认为其实并不科学!
其中涉及到 隐式类型转换,因为 隐式类型转换 本就是不科学的设定。
有一种说法:
如果一个编程语言,越支持隐式类型转换,就认为类型越弱。
如果一个编程语言,越不支持隐式类型转换,就认为类型越强。
Java,Go,Python 认为是强类型的编程语言
C,JS,PHP 认为是弱类型的编程语言注意,静态类型 / 动态类型 vs 强类型 / 弱类型
这两者是没有关系的,是两个不同的体系。
那么,弱类型 vs 强类型,谁更好呢?
肯定是 强类型。
因为强类型,就意味着类型之间的差异更大,界限是更明确的。
代码在类型上出现问题,也是最容易锁定位置的。
如果是弱类型,意味着类型之间的差异不大,界限是更模糊的。
代码在类型上出现问题,是最难看出来的。
因为它什么参数都是可以接收的。
就是代码能跑,但是效果不对。
这就很麻烦!
JavaScript 中的运算符和 Java 用法基本相同. 此处不做详细介绍了
- / %
这里的不同点,体现在 除法: /
但是!大部分编程语言,1/2 => 0
只有极少部分的编程语言【(JS,Python3(Python2不是),Dart】,1 / 2 == 0.5
重点在于,比较 相等/不相等 的 符号。
JS中比较相等的符号,有两种:
== 和 ===
不相等的符号也有两种:
!= 和 !==== 和 != 是一组风格、
=== 和 !== 是一组风格。先说, == 和 != 这一组。
只是比较两个变量的值,不比较 两个变量的类型。
如果两个变量能够通过隐式类型转换,转换相同的值,
那么,此时就认为 两个变量 是相等的。
再来看,=== 和 !== 这一组。
既要比较 两个变量的值,又要比较两个变量的类型。
如果类型不相同,就直接认为两个变量不相等。
比较的过程中,就不存在隐式类型转换的说法。
————————————————
注意哦!这里和 java的 equals 是不一样的!
equals 是 比较值。
== 是比较身份。
而且 equals 涉及到对象的比较。
谈到对象的比较,有三个维度的比较
1、比较身份(比较地址,看是不是同一个对象)【JS中没有比较身份的函数】
2、比较值(标对对象中存储的数据是否相同)
3、比较类型(两个对象的类型是否是同一个类型)
而且 Java中 equals 是可以被重写的。
equals 不重写,默认也是比较身份。
可以以通过重写 来设定成 比较值。
在Java中的 instanceOf 就是比较类型的。
用于计算多个 boolean 表达式的值.
&& 与: 一假则假
|| 或: 一真则真
!: 非&& || ,这两个行为,和Java中 && 和 || 差别就挺大的。
Java 中的 && 和 || 行为非常简单!
效果就是返回一个布尔类型的值: true,或者 false。
而 JS 中的 && 和 || ,返回是其中的表达式。
创建数组
和 Java 创建数组很像,但不完全一样。
更像 Java中创建对象的写法。
注意!这种写法很少用,因为比较麻烦。
2、使用字面量方式创建 [常用]
这里就和Java差别很大。
Java 定义一个数组的时候,通过关键字 new,并标明类型,以及容量,才能创建。
Java 针对数组初始化,是使用大括号来将元素括起来。
而JS定义一个数组的时候,不需要声明数组类型,以及容量。
JS 针对数组初始化,使用的是中括号,将元素括起来。
而且,我们 JS 数组,还可以像下面这么写。
什么类型的元素,都可以往里面放。这是 Java 普通数组所做不到的,需要借助数据结构才能实现。
但是 一般也不是这么用,都是会指定一个类型放入数组中。有的人可能会问:这是一个什么类型的数组?
我只能说兄弟们格局小了!
没有什么类型,这就是一个数组。
在 JS 中,只有一种数组,就叫做数组,里面的元素可以是不同类型的!
另外,JS 中数组,是不分类型的,并且无法强制要求数组只能放入一种类型的数据。
要想数组里存入的都是同一种类型的数据,就只能通过“人为”的方式去完成。
就是 你给这个数组的元素都是 同一种类型的。
而且,还没完!
JS 中数组的元素,还可以是数组类型的元素。
是不是颠覆各位的认知了。
其实这都是基操,动态类型的语言都是这么玩的。
比如:Python,PHP等等…
但是 Python 已经有“投敌”的苗头了、
在 Python中,有一个这样的写法:arr:List[int]
这种写法的意思,就是要求这个元素必须是整数了。
打印数组
JS 中直接通过 console.log 就可以打印数组的内容。
这要比 Java方便的多,Java要输出一个数组的元素的方法。
1、循环打印数组元素
2、通过 Arrays.toString(数组名)来实现打印数组元素
获取数组元素
前方高能预警!
将会彻底打破我们对数组的常规理解。
按照之前的理解,在Java中要求数组下标,是 0 - 数组长度-1。
如果 超出这个范围,就会抛出一个数组越界异常。
但是,在 JS 中,就不是这样了!
下面的代码,就更狠了。
还没完,还有一个变态操作。
可能大家对 -1 看作是一个 属性 / 键值对 的感触不是很深,再来一个操作,帮你们加强一下印象
总得来说,JS 的数组,就不是一个正经的数组。
除了能接数组的活,还能接 Map 的活(表示键值对)。
进一步的来说,与其说数组是 Map,不如说是一个“对象”。【这是更准确的说法】在JS里,是可以在运行时,给对象新增属性的。
arr[‘hello’] = 10,这个操作其实就是在给 arr 这个对象,新增了一个属性。
属性的名字是hello,属性的值是 10
这些语法都不是 JS 独有的,动态类型的语言都是这样设定的。
PHP 的设定方式 几乎和 JS 一模一样。
Python 略有差异,但是本质不变。
上述的代码,相比对后端的朋友,产生了一万点暴击。
其实这些都是属于 “静态类型” vs “动态类型”,两个流派之间的碰撞。
这两个流派之间的语法,是有很大区别的。
通过 .length 就能获取到
JS 中 length 属性是可以改的!!!!在Java中,这种操作是想都不敢想的!!!
原码,你怎么改?下面,我们就来看看看 length属性,是怎么改的。
效果很明显,不但数组长度变成了 3,而且 元素也被删除了。
如果,我们把 数组长度改成 5 呢?
最常见的插入操作,是通过 push 方法,能够给 数组末尾 追加一个元素。
就相当于 Java 中 ArrayList 里面的 add 操作。
JS中删除数组中的元素,所依赖的方法,非常nb。
叫做 splice,这个方法,准确的说是针对 数组中的某讴歌片段,进行替换。
所以,这个方法既可以用来插入元素,又可以用来删除元素。
删除操作
替换操作
首先,大家要明白,JS中的函数(function),在 Java中 叫做 方法(Method)。
注意!函数就是方法,方法就是函数,两者就是同一个东西,只是叫法不同而已。通常情况下,不去考虑这两个概念的区别。
但是如果非要考量,可以这么理解:
函数,是和 “对象” 独立开来的代码片段。
而方法,是依托与 “对象” 的代码片段。
函数一般不会与 “对象” 绑定在一起,而方法一般是与 “对象” 绑定在一起。
因此方法,可以认为是成员函数。
成员函数:可以认为函数里面的一种具体的形态。
在类里面写的,与 “对象” 相关的成员函数,将其称为 方法。
在 Java 中,由于本身这样的代码片段都是依托于类和对象的,就没有这种独立的函数。
因此,Java中就都是方法。
也印证了Java的一句话:万物皆对象。
相对于 JS,JS 中的方法,大多数都是脱离对象存在的,所以称为函数。
// 创建函数/函数声明/函数定义
function 函数名(形参列表) {
函数体
return 返回值;
}
// 函数调用
函数名(实参列表) // 不考虑返回值
返回值 = 函数名(实参列表) // 考虑返回值
注意!在 创建函数/函数声明/函数定义 的时候,形参列表不用写形参类型。
其实也很好理解,因为 JS 是动态类型的语言,你写了类型都不好使!
形参的类型,完全取决于程序运行时,你给函数传的什么参数。
同样的, 函数也不必写返回值类型,因为没意义。
其它的就没什么好说的。
再来看函数调用
实参列表 要与 形参列表匹配。
同时也通过一个变量来接收 函数的返回值
下面先看看 不带参的函数的定义和调用
我们再来看看带参方法的定义 和 调用
在 JS中,函数是 “一等公民”。
一等公民:一个“一等公民”的函数,函数可以像一个普通的变量一样,赋值给 其他的变量。同时,可以作为另一个函数的参数,还可以作为另一个函数的返回值。
简单来说:函数和普通的变量,并没有本质区别。
只不过函数这个变量,相对于普通变量来说,多了一个功能(可调用)。
所谓的函数表达式,其实就是把一个函数赋值给一个变量了。
这里讲的比较浅显,其实还可以更复杂的来讲。
让一个函数 返回另外一个函数,在返回函数的同时,不光是返回函数本身,还返回了这个函数依赖的上下文,这个时候就构成了闭包。
只是知道有这么回事,并不做深入。
某个标识符名字在代码中的有效范围.
在 ES6 标准之前, 作用域主要分成两个
全局作用域: 在整个 script 标签中, 或者单独的 js 文件中生效.
局部作用域/函数作用域: 在函数内部生效.上面提及到 ES6,是 JS 的版本。【最新的版本,已经是 ES十几】
之所以称为 ES6,是因为 JS 原名是 ECMAScript,简称ES。
故 JS 的版本都是使用 ES数字,来进行表示的。
这个就类似于 Java 的版本。
我们主要学习的是 Java 8(jdk1.8),Java 最新的版本已经到 18 了。
这里的 ES6 和 Java8,都是属于 JS 和 Java 中的应用最广泛的大版本。
其实在 ES6 之前,就没有 let 这个东西。
let 是在 ES6之后 才引入的。
也就是说,在ES6版本之前,只有 var。
即 变量 只会被函数划分作用域,而函数里面的代码块,是不会影响到作用域的。
也就是说,在ES6之前,只有 全局 + 函数 作用域,没有块级作用域(大括号挂起来的区域)。
这个是前讲过的。
但是这一点,其实在我们的后续,有了ES6之后,也就是有了 let 之后。
这个东西也就发生了改变,也就是有了 块级作用域。
一个变量在{}内部定义,是无法被大括号外部访问。
即便如此,JS 和 Java的作用域,还是有区别的。
在 JS 中,{}内部代码 是可以访问 {} 外部的代码的。
在上图的例子中,如果你们想要强行访问 外面的 num 值。
只能通过 给 hello2 加个形参,然后调用的时候,把 num 作为 实参来传进来。
修改 hello2的num值。
此时, hello2中的num值,就是外面 num的值了。
【注意!想直接外面的num值,是不可能的!】
另外,说一点。
尤其是学Java的朋友,当你们产生使用 this ,这个想法的时候,就非常危险了。
因为 JS 的函数中,确实能用 this。
但是,这个 this非常危险!
它有着一系列复杂的规则,来决定 this 的 指向。
就是说,JS 的 this,不像 Java中那样是固定指向的。
JS的this,是会改变指向了。
对象,就是一些属性 和 方法的集合。
这个概念,与传统意义上的Java对象的概念是类似的。
但是有一个比较大的区别。
在 Java中,我们的对象是需要先有类,然后针对类进行实例化才能产生对象。
等于就是说,类就是一个模具,可以批量生产相同规格的产物。
而在 JS 中,对象 是不依托于 类的。
就是说:在 JS 中,无需借助类,就能创建一个对象。
另外,Java 中的类,可以视为是一种 自定义类型的类。
例如: Cat 类 和 Dog 类,是两个不同类型。
但是在 JS 中,所有的对象,都是一个类型【object类型】。
很简单,直接通过 {} 的方式,就可以创建对象。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
// {} 就是 一个 匿名对象
// 将这个对象赋予 变量 student
let student = {
// 属性
name: 'cxk',
age: 20,
height: 178,
weiight: 120,
// 方法
sing: function(){
console.log('鸡你太美')
},
jump: function(){
console.log('一段舞蹈');
},
};
</script>
</body>
在 js 对象中,我们可以很直观的看到 js的对象是有方法 和 变量。
js的对象,与其说是一个对象,不如说是一个 键值对 结构 更加合适。
js对象中的每个属性和方法,其实都是通过“键值对” 这样的方式来表达的。
{} 表示这是一个对象
键值对之间,采用 逗号 进行分割。
键和值之间,采用 冒号 进行分割。
定义出这样的一个对象之后,我们就可以采用 student.属性名 / 方法名 的方式 来进行访问了
上面创建的对象的方式,是属于 使用字面量创建对象。
在JS 中,还有两种创建对象的方式。
第二种: 使用 new Object 创建对象
通过创建一个 Object的空对象,再把创建好的对象赋予 一个变量。
通过 变量名.属性名 = 值 的方式,进行动态创建对象属性。
在 JS 中,一个对象里面有哪些成员,也是动态可以改变的。
也就是说,假设本来 这个对象只有 2个属性(name 和 age),但是执行代码之后,感觉还可以加上一些属性,通过 变量名.属性名 = 值 的方式,就可以添加新的属性。
更不用说,可以去修改属性的值了。
let student = new Object(); // 和创建数组类似
student.name = "蔡徐坤";
student.height = 175;
student['weight'] = 170;
student.sayHello = function () {
console.log("hello");
}
第三种:使用 构造函数 创建对象。
当前我们所说的构造函数,其实是一种特殊的函数。
该构造函数,目的是为了 批量的创建出一组 类似的对象。
function 构造函数名(形参) {
this.属性 = 值;
this.方法 = function...
}
let obj = new 构造函数名(实参);
注意!对象里面 的 属性和方法,是通过 this. 的方式来创建的。
换个说法,当我们看到函数内部,通过 this.方式来创建属性和方法的时候,此时这个函数大概率就是构造函数了。
使用的时候, new + 构造函数名 +(实参)的方式来使用的。
这个就跟Java的带参的构造方法很相似。
其实 JS 中后两种创建对象的方式,其实JS自己也觉得别扭的。
所以,现在我并不建议大家使用后面两种方式来创建对象。
更推荐使用第一种方式来创建对象。
因为毕竟 JS 并没有引入类 这样的概念。注意!JS 没有 clss 这个概念,适用于 ES6 之前。
ES6 之后,也引入了 class 关键字,可以让定义对象,就更接近与 Java 了。
但是!这种方式,仍然没有第一种方式来的简单。
而且,第一种创建对象的方式,火了。
不仅仅 JS 会用,其他各种语言,在各种场景下,都会用到。
从这种写法中,延伸出了 JSON(数据的组织格式)
服务器 和 客户端 之间 进行交互的时候,就会经常使用到 JSON 。
i其实,再我们了解了对象的概念之后。
就可以明确,其实再我们的 JS 里面也有一些现成的对象。
这些现成的对象,就可以帮我们完成一些复杂的工作。
像我们的数组,本质上也可以视为是一个对象。
因此对象拥有的一些特性,其实我们的数组也都是有的。
比如说:
我们可以随时给这个对象,动态增加一些属性。
数组也是可以的。(array[‘字符串’])还需要说一点,虽然 JS 有对象,但是 JS 算是一个面向对象的编程语言吗?
一般认为,不算!
大家需要明确,不是有了对象,它就是面向对象的。
面向对象,需要有一些相关的特性。
比如,JS中没有 封装,继承,多态,这样的一些面向对象的典型特性。
光有一个对象,其实很难把它和面向对象,画上等号。封装中的 public,private,protected,通过这些关键字来限制访问权限。
让调用者,不用关注里面的实现细节。
继承也是一样。有了父类,就可以让子类继承父类,简化子类的一个编写。
虽然 JS 中没有原生提供 继承的机制,但是JS有一种 “曲线救国” 的方式“原型”。
基于这种原型的机制,它可以模拟实现一个类似于继承的效果。
把一个对象的所有属性,给自动加入到一个新的对象中。
但是!这种模拟实现的过程,并不优雅。
这就不像 Java 直接来个 extends,就没有那么细节让我们去考虑。
JS 直接在语法上,就不支持这个。
多态也差不多,这就不说。
学习了上述 JS 语法,能做什么?
感觉除了 会写了个 hello world,就啥也做不了。
要想写实际的程序,光会语言是不够的,还需要掌握相关的“生态”。
生态:配套的库/'框架。对于 在 浏览器上运行的 JS 来说,最核心的库,就是 DOM API。
DOM API 是浏览器给 JS 提供的原生接口。
基于这样的接口,就可以针对页面上的元素进行操作了。
具体 DOM API 是什么?
DOM =》Document 0bject Model - 文档对象模型
文档:每一个 HTML 就是一个文档
对象:JS 里面的对象。
合在一起,文档对象:在HTML中,会把每个 html 标签,都视为是一个 JS 中可以操作的对象。通过操作这个对象就可以影响到界面的显示。
这个就是文档对象模型。
文档指的是页面显示的部分。对象指的是 JS中代码操作的部分。
JS 这边操作 对象,文档那边就会发生改变。
所以 这两个的关系,是一个相辅相成的关系。
于是,我们就可以通过这样的一组 API,阿里完一些功能更加丰富的页面了。
以上 j就是 DOM 的含义。
JS 中 原生的 DOM API 能用,但是并不方便。
因此除了 原生的 API之外,也就存在了大量的第三方库/框架。
其中 JQuery 就是最知名的一个库。
不过本文,主要还是以 原生的 DOM API 为主。
Vue,React等前端框架,本质上还是 DOM 的封装。
不管你用的什么复合框架,终究是离不开 DOM 这一层东西的。
其实 浏览器,给JS提供的API非常丰富,也有很多组。
前面讲到过的两个
DOM API
BOM API
除此之外,还有一些其它的
WebSocket API,canvas API…
这些 API 统称为 WebAPI。
这里就不做深究,知道 浏览器提供的 API 不止 DOM 和 BOM 就可以了。
另外这里,分享一个资源 API参考文档
另外,也可以在搜索引擎中按照 “MDN + API 关键字” 的方式搜索, 也能快速找到需要的 API 文档
DOM 全称为 Document Object Model.
W3C 标准给我们提供了一系列的函数, 让我们可以操作:
网页内容
网页结构
网页样式
一个页面的结构是一个树形结构, 称为 DOM 树
页面结构形如:
DOM 树结构形如:
重要概念:
文档: 一个页面就是一个 文档, 使用 document 表示.
元素: 页面中所有的标签都称为 元素. 使用 element 表示.
节点: 网页中所有的内容都可以称为 节点(标签节点, 注释节点, 文本节点, 属性节点等). 使用 node表示.
这些文档等概念在 JS 代码中就对应一个个的对象.
所以才叫 “文档对象模型” .
获取元素,是使用 DOM 的基础。
要想使用操作页面上的元素,就需要先拿到对应的JS 对象。
获取 JS 对象的过程,就是获取元素的过程。怎么去获取元素呢?
其实 JS 中提供好几种方法来获取元素。
其中,本文主要就介绍两种最核心的方法。
1、querySelector
2、querySelectorAll
这两个函数,其实是一个 document 这样的对象的属性。
document:是属于 页面中的 一个 全局对象。页面中的全局对象
一个页面加载好了,就会自动生成一个全局变量,就叫做 document。
这里面就有一些属性和方法,让我们来操作页面的内容。
document的中文意思就是文档的意思,所以我们当前所看到的 document 对象,就是我们页面文档,在JS中的一个“身外化身”。
下面,我们就来具体的写一下代码,演示一下关于 querySelector / querySelectorAll,获取元素的基础操作。
以上就是 querySelector / querySelectorAll,两种主流 获取 DOM 元素的 原生API。
当然了,像我们后续的 jquery 和 vue 的一些框架里面,有一些更加方便的方式来获取 DOM 对象。这些方法也是基于 DOM 的 原生 API。
大家只要把 DOM 原生的 API学习,后面即使我们遇到更加 复杂的 / 强大的 一些方法,也是能理解的。
这个时候,通过这样的组成部分,就可以基于这个事件来去完成一些具体的工作。
举个例子:
我更推荐 第一种写法。
因为第二种写法,容易把页面写的很乱。
我们所期望的代码:是将 页面的结构,样式,行为,能够充分的分离。
第二种写法,就相当于把上述的这样给揉在一起了。
一旦代码复杂,就显得更加混乱。
而第一种写法,将上述的这些充分的分离了。
看起来更舒服。
操作 == 获取 + 修改
1、操作元素内容
2、操作元素的属性
3、操作元素的样式
通过 对象 里面的一个属性 innerHTML 来实现。
innerHTML:就是看看元素里面包含的 html 代码是什么样子的。
通过 这个方法,就能获取到 元素的内容。
进一步,就能修改元素的内容。
上面虽然获得了元素的内容,但是只是一个文本内容。
那么如果不是文本内容呢?会有什么效果?
下面,我们在修改对象内容,该怎么做。
我们来实践一下:数字自增器
搞一个 div显示整数
再搞一个 按钮,每次点击这个按钮,就会让里面的整数加一。
另外,拓展一个点:
innerHTML 是无法获取 input 中的字符串。
因为 input 是一个 单标签,作为一个单标签是不配拥有 innerHTML的。
因为 innerHTML 是属于 开始标签 和 结束标签中间,夹着那一块。
input 是通过 value 属性,来获取元素内部的内容的。
通过 DOM 对象.属性名 就可以进行操作了。
注意!一个 HTML 标签里,能写哪些属性,就能通过 JS 中的 DOM 对象.属性名来获取 来获取到拓展:
如果你们还想知道一个 DOM 对象里面,一共还有那些属性。
可以通过 console.dir( DOM 对象) 这个方法,打印出一个 DOM 对象的全部属性和 值。
表单(主要是指 input 标签)的以下属性都可以通过 DOM 来修改
value: input 的值.
disabled: 禁用
checked: 复选框会使用
selected: 下拉框会使用
type: input 的类型(文本, 密码, 按钮, 文件等)
这些属性,都属于表单元素专有的属性
value属性,用来获取到input标签的值 。
这个在前面的 数字自增器中,就已经讲过了。
这里我就不细说,就再举个例子给你们看看
使用 checked 属性:实现 全选 / 取消全选 按钮
就是说,现在我们有一组复选框。
当我们点击 ‘我全都要’的时候,下面的4选项都被勾选了。
如果,我们将其中的某一项给取消勾选了,那么‘我全都要’将会自动取消勾选。
这里就需要使用到 input 的 checked 属性。
通过修改 checke 属性的值,来切换选中的状态。
有的人可能会觉得这样做,是不是时间复杂度有点高。
请放心!什么时候会涉及到 “时间复杂度”?
等你的数据量上了千万级别的时候,才会涉及到时间复杂度。
也就是说,百万级的数据,一般都不配谈时间复杂度。
因为此时的 O(N) 和 O(1) 区别不大。
CSS 中指定给元素的属性, 都可以通过 JS 来修改.
操作 元素样式,本质上就是操作元素属性。
可参考 CSS 这篇文章跟样式相关的属性,主要有2个:
1、style:对应 行内样式(直接把样式写到 style 里面)
2、className / classList :对应 内部样式 / 外部样式,应用了 一个 / 一组 CSS 类名。
我们通过实例来了解上述的两种属性。
修改字体大小【 style 】
唯一需要注意的是:
修改 CSS 属性值的时候,一定要注意加上单位!
如果不加,就没有效果。
className / classLis
在HTML中,表示类名的属性,就是 class、
但是在 JS 里,属性名就变成了 className / classList为什么不直接使用 class 这个名字呢?
理由很简单。
class 在 JS中也是一个关键字。
前面也说到过,在 JS 的 ES6 版本以上,也引入了 类 这个概念。
既然 class 在 JS中,已经有了它自己的定位。
所以,class自然就不能再当做 属性名。下面,我们再来沟通过一个案例,来了解它。
如果修改的样式比较多,这个时候通过 style 来修改就比较麻烦了。
更好的做法,可以直接借助 css 类来修改、
我们可以把这些 css 要改的样式,写到一个类里名。
直接通过修改类名,就可以达到一样的效果。夜间模式
就是起着一个类似于开灯 和 关灯的效果。本来我们是一个浅色背景,深色文字(开灯的状态)
然后,我们一点关灯
浅色背景变成了深色背景,深色文字变成了浅色文字。
看小说的软件都有这个功能。
上面的都是针对元素操作,其实是操作元素的属性,获取 / 改变 内容。
但元素本身并没有发生改变。
现在我们是针对节点进行操作,其实是 新增 / 删除 / 移动 节点。
我们主要讲新增 和 删除,这两个操作
新增节点:
1、创建新节点
2、把节点挂在 DOM 树上。
新增节点 ,就好比生孩纸。
创建新节点,就是孩纸出生了。
把节点挂在树上,就是给孩纸上户口、光生孩纸,不给孩纸上户口,那它就是黑户!
是不会记录在户口本上的。创建节点也一样,光创建,却不把它挂在DOM树上。
那它就不是合法节点。
网页当然也就不会显示它了
删除节点
我们通过removeChil 函数 来实现。
猜数字
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>猜数字游戏</title>
</head>
<body>
<button id="resetButton">重新开始一局游戏</button>
<br>
<span>请输入要猜的数字: </span>
<input type="text">
<button id="guessButton">猜</button>
<br>
<span>已经猜的次数: </span> <span id="guessCount">0</span>
<br>
<span>结果: </span><span id="result"></span>
<script>
// 1、先把上面需要用到元素都拿到
let resetButton = document.querySelector('#resetButton');
let input = document.querySelector('input');
let guessButton = document.querySelector('#guessButton');
let guessCount = document.querySelector('#guessCount');
let result = document.querySelector('#result');
// 2、生成一个 1-100的 随机数
let toGuess = Math.floor(Math.random( ) * 100) + 1
console.log(toGuess);
// 3、实现点击 猜按钮的逻辑
guessButton.onclick = function(){
// 1、读取到input中输入的内容,并转换为整数
if(input.value == ''){
return;
}
let currentNum = parseInt(input.value);
// 2、进行判断大小关系,并给出提示
if(currentNum < toGuess){
result.innerHTML = '猜小了';
result.style.color ='orange';
}else if(currentNum > toGuess){
result.innerHTML = "猜大了!";
result.style.color = 'red';
}else{
result.innerHTML = '猜对了!';
result.style.color = 'green';
}
// 3、更新猜的次数。
let guessC = parseInt(guessCount.innerHTML);
guessCount.innerHTML = guessC+1;
}
// 4、实现 reset 重置按钮的操作
resetButton.onclick = function(){
// 非常简单,让页面刷新一下,就可以了。
// 通过 location.reload() 来实现 刷新页面。
// location 是 和 document 并列关系的对象
// document 是操作页面的内容
// location 是操作页面的链接/地址。
// reload,就是重新加载的意思
location.reload();
// 刷新页面也就意味着,程序重新执行。
// 一切回归原点。
}
</script>
</body>
</html>
表白墙
大概的效果就是,显示谁对谁说了一句什么话。
点击提交后,下面会显示 这条记录。
先来实现页面样式和框架
写到这里页面也就完成了。
不要看我截的图很长,其实实现并不难。
更多的只是一个调节的过程。
你得让这个页面看起来,还不错。
难就难在这一点,做出来,你得拿得出手。
也就要求你有一定的艺术细胞。
下面我就完成 js 部分的代码。
使其具有更丰富的交互效果。我们要实现的效果就是:当用户点击 submit 提交按钮。
就可以获取到 input 中的内容,从而把内容构造成一个 div。
然后插入到页面末尾。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>告白墙</title>
</head>
<body>
<!-- 通过内部样式style标签,引入CSS样式 -->
<style>
*{
/* 首先先去除浏览器样式 */
/* 将 内外边距设置为0,设置盒子模型为向内挤压 */
margin: 0;
padding: 0;
box-sizing: border-box;
}
.containner{
width: 100%;
}
h3{
/* 文本居中 */
text-align: center;
/* 上下边距为 20px,左右边距为0 */
padding: 20px 0;
font-size: 24px;
}
p{
text-align: center;
color: #666;
padding: 10px 0;
}
.row{
width: 400px;
height: 50px;
/* 上下外边距为零,左右外边距自适应 */
/* 就是元素水平居中操作 */
margin: 0 auto;
/* 弹性布局 */
display: flex;
/* 水平居中 */
justify-content: center;
/* 垂直居中 */
align-items: center;
}
.row span{
width: 60px;
font-size: 17px;
}
.row input{
width: 300px;
height: 40px;
line-height: 40px;
font-size: 20px;
text-indent: 0.5em;
outline: none;
}
.row #submit{
width: 360px;
height: 40px;
font-size: 20px;
line-height: 40px;
margin: 0 auto;
color: white;
background-color: orange;
border: none;
border-radius: 15px;
outline: none;
}
/* 当鼠标点击按钮的时候,会改变按钮颜色 */
.row #submit:active{
background-color: grey;
}
</style>
<div class="container">
<h3>表白墙</h3>
<p>输入后点击提交,会将信息显示在表格中</p>
<br>
<div class="row">
<span>谁: </span>
<input type="text">
</div>
<div class="row">
<span>对谁: </span>
<input type="text">
</div>
<div class="row">
<span>说什么: </span>
<input type="text">
</div>
<div class="row">
<button id="submit">提交</button>
</div>
</div>
<script>
let submitBtn = document.querySelector('#submit');
submitBtn.onclick = function(){
// 1、获取 3个input 文本框中的数据
let inputs = document.querySelectorAll('input');
let from = inputs[0].value;
let to = inputs[1].value;
let say = inputs[2].value;
if(from == ''|| to == '' || say == ''){
// 用户填写的数据,并不完整。所以不提交。
return;
}
// 2、生成一个新的 div,内容就是 三个 input 文本框的内容拼接
// 再把这个 元素,挂在DOM树上。
let newDiv = document.createElement('div');
newDiv.innerHTML = from + "对" + to +"说:" + say;
newDiv.className = 'row';
// 将新建节点,挂在 container 这个节点下面
let container = document.querySelector('.container');
container.appendChild(newDiv);
}
</script>
</body>
</html>
我们再来优化一下显示。
每次提交之后,三个input文本框的数据将会被清空
但是!目前这个表白墙还存在缺陷。
就是我们一旦刷新网页,这些记录都会被清空。就好像韩剧里面,男主刚和女主表白,女主就被车撞得失忆了、
这niao性,啧啧。。。上面的表白墙代码,是通过一些 div.row 来保存咱们提交的 消息。
这些 div.row 是挂在 DOM 树上,就是在内存中。
在 内存存储数据,是容易失去的。
一旦页面 刷新 / 关闭了。
此时,我们之前内存中保存的数据,就没了。
那这个表白墙,还能算是有用吗?
有,但用处不大。为了解决上述 数据容易丢失的问题,有以下解决方案:
1、可以把提交的数据,保存在浏览器本地。
浏览器提供了 localStorage / indexDB 这样的机制,能够实现本地存储。
本质上,是通过浏览器,把你要存的数据,存在当前的磁盘上。
问题:只有我们在自己的电脑上能看到,别人 是看不到的
2、可以把提交的数据,通过网络通信,传输给服务器,由服务器来进行保存.
1、服务器保存在内存里(不靠谱)
2、服务器保存在文件里(可行)
3、服务器保存在数据库里(可行)
转载于:https://blog.csdn.net/DarkAndGrey/article/details/124548667