前言
最近花了三天的时间,系统地学习了一下JavaScript,由于之前有一些编程语言基础,并且学的都是一些基础内容,所以相对来讲学的比较快,学习方法主要是通过看B站视频和阅读《JavaScript语言精粹》来进行学习,学习的版本是ES5的版本,在此想要做个总结文档,来对这三天的学习进行总结,本文结构分为:对JavaScript的认识、JavaScript核心语法、JavaScript各种结构、数组、函数以及对象。适合有JAVA基础的读者进行参考。
一、对JavaScript的认识
在阅读了《JavaScript语言精粹》这本书的前言以及第一章后,我能够认识到,JavaScript是默认的"网页语言",他是一个轻量级、弱类型的语言,它与浏览器的结合使他成为世界上最流行的编程语言之一。
另一方面,JavaScript存在着许多"糟粕",实际上,JavaScript的很多设计是很鸡肋的,一方面是很多设计不够规范,另一方面就是设计者的一些设计错误。在我的学习过程中,我也能发现很多设计相对于其它的一些编程语言着实有些令人费解。当然了,我们需要注重学习的还是其精华部分。这里借用一句作者说的话,"在JavaScript中,美丽的,优雅的,富有表现力的语言特性就像珍珠和一些鱼目混杂在一起一样。" JavaScript包含ECMAScript(JavaScript语法)、DOM(页面文档对象模型)、BOM(浏览器对象模型)。下文的内容主要是根据ECMAScript展开的。
二、JavaScript核心语法
1.变量:
在JavaScript中,变量的声明是通过var、let、const进行的,其中,let是ES6后才引入的,后文会详细讲,它能够声明块级作用域变量。下文主要通过与Java对比来阐述JavaScript的变量
- 与Java不同的是,JS的变量无需指定类型 ,JS会自动识别类型,这点其实跟PYTHON很像,如果想要查看变量的类型,我们可以通过
typeof 变量
来进行查看。 - 声明变量时(以var举例),我们可以通过
var a = 1 和 a = 1
来声明,推荐使用前者(显式声明);声明多个变量时,我们可以通过var a=1,b=2,c=3
来声明,也可以通过var a=1;b=2;c=3
来声明,但是最好不要用var a=b=c=1
这种方式来声明。 - JS的变量声明后会占用一块内存,每一次新
var
一次,则会产生新的内存,这里我们要区分,var a = 1
之后进行a = 2
不会占用新的内存,只是会修改原有内存的值。我们还要注意,var a = 1
之后进行b = a
,会让b复制a的值,修改b不会影响a。如下
ini
var a = 1;
b = a;
b = 2;
console.log(a) //1
JS中,var,let,const很多情况下是可以相互替换的,那么我们何时使用哪个呢?以下是读者后来的经验以及结合DeepSeek总结出来的一套规范
现代JS变量声明规范
优先使用const
默认一些不需要重新赋值的变量或者常量: 包括:数据,对象,Map,Set,函数表达式以及常量(例如pai)
ini
const PI = 3.14159;
const user = { name: "Alice" };
const numbers = [1, 2, 3];
const map = new Map();
const set = new Set();
const sayHello = () => console.log("Hello");
如果遇到一些基本的数字变量或者字符串变量 可能需要重新赋值 咱们就使用let
ini
let counter = 0;
counter++; // 允许
let result;
if (condition) {
result = computeSomething();
} else {
result = fallbackValue();
}
为什么不推荐使用var,因为我们知道 var会存在预解析(变量提升)造成一些不必要的麻烦,以及因为var是全局变量,可能会带来一些性能上和内存上的开销
如果确定要使用var,一般是在确定要使用全局变量的情况下,那么咱们一般在最前面声明全局变量即可
2.数据结构
- JavaScript中,数据结构主要分为数字型和字符型,布尔型,其中数字型只有一种基本类型Number,包含整数和浮点数,没有单独的整数类型。它的类别不像Java那么多(存在Float、int、long等等),我们可以利用isNaN来判断是否为数字型。
- 通常来说创建变量指定值后,JS会自动识别类型,但是当创建变量没有定义值时,变量会变成undefined。 这里需要区分一下NULL和undefined,对于NULL来说,他是开发人员显示声明的空值,实际上,他是人为赋值的,
typeof null
的结果为object,而typeof undefined
的结果为undefined
。
3.下面是数字类型和字符串类型之间相互的转换:
1.转换为字符串类型
方法一
ini
var num = 3;
num = 3 + "" //此时就会将num转换成字符串类型
方法二
ini
var num2 = 6;
num2 = String(num2); //利用字符串转换函数
2.转换为数字类型
方法一
ini
var str = "9";
str = parseInt(str); //利用parseInt或者parseFloat进行转换
console.log(typeof str)
方法二
ini
var str2 = "10";
str2 = Number(str2) //利用Number进行转换
console.log(typeof str2)
方法三(重点)
ini
var str3 = "22";
str3 = str3 - 12; //利用- * / 进行隐式转换
console.log(typeof str3)
注意:在浏览器的控制台中,Number一般是蓝色,String一般是黑色,null,undefined一般是灰色
3.运算符
运算符这里其实各个语言都是差不多的,本文主要提三点需要注意的地方
1.前置递增运算符和后置递增运算符的区别
前置递增运算符是先++ 后赋值 后置递增运算符是先赋值 后++
举个例子趴
ini
var age1 = 10;
var age2 = 10
console.log(age1++ + 10); //后置 先赋值10 输出20
console.log(++age2 + 10); //前置 先加成11 输出21
2.比较运算符
===
和==
的区别:
前者是严格比较:需要比较类型的同时比较值
后者是默认转换类型后比较
ini
var a = "3";
console.log(Boolean(a === 3)) //这里返回false 因为字符串类型和数字类型不同
console.log(Boolean(a == 3)) //这里返回true 因为JS的==会转换类型后比较值 发现相同
3.逻辑 && 与 ||
javascript
//1. 逻辑与 表达式1 && 表达式2 如果表达式1 值为真 则返回表达式2 如果表达式1 值为假 则返回表达式1
console.log(123 && 456) //返回456
console.log(0 && 1 && 2 && 3) //返回0
console.log(undefined && 23) //返回undefined
console.log(null && 23) //返回null
//2. 逻辑或 表达式1 && 表达式2 如果表达式1 值为真 则返回表达式1 如果表达式1 值为假 则返回表达式2
console.log(123 || 456) //返回123
console.log(undefined || 77 || 88 || 99) //返回77
三、JavaScript各种结构
1.if分支结构
if的语法和java一样,都是使用if(表达式){代码片段}。下面举一个经典的计算分数来巩固
xml
<script>
var score = parseInt(prompt("请输入你的分数"));
// 根据分数进行判断
if (score >= 90)
{
console.log("A"); //如果语句长的话可以用{},短的话可以不用
console.log("你太优秀啦 快来领取奖励!")
}
else if (score >= 80)
console.log("B");
else if (score >= 70)
console.log("C");
else if (score >= 60)
console.log("D");
else
console.log("E")
</script>
2.switch语句
这个也是和Java很像,下面引出一段代码 来巩固
dart
var num = prompt("输入")
num = parseInt(num)
switch (num){
case 1:
alert("666");
break; //注意一定要写break 符合规范,并且不容易出错
case 3:
alert("succeed!")
break;
default:
alert("saf" + num)
}
关于if分支和switch其实可以相互替换,各自根据不同的场景进行使用:如果根据表达式判断稍微复杂一点的内容,推荐使用if;而如果对某个变量进行判断,推荐使用switch
3.for循环
JS中的for循环仍旧和java的语法极度相似,我们使用类比的方式来呈现,就能看到他们有多像了
求1-100之间所有数的和
Java代码:
ini
int sum = 0;
for (int i = 1; i <= 100; i++) {
sum += i;
}
System.out.println(sum);
JavaScript代码:
ini
var sum = 0;
for (let i = 1; i <= 100 ; i++) {
sum += i;
}
console.log(sum)
拓展几个常用的for
- for of
typescript
var str1 = [1,2,3]; //数组中常用的遍历方法 当然也可以用传统的fori模型 还可以遍历(Map,Set,Sting等)
for (const number of str1) {
console.log(number)
}
- for in
arduino
const mymap = {
name:'zangsa',
age:15
} //对象常用的遍历方法 遍历的是对象的键
for (const mymapElement in mymap) {
console.log(mymapElement)
}
JS中的for循环需要注意一个点:for循环括号里面最好使用let声明变量i,因为变量i没必要放到全局中,所以我们不推荐使用var。
4.while循环
同样地,我们将类比Java和JavaScript的方式来进行学习while的使用
打印人的一生,从一岁到一百岁
java语法:
csharp
int i = 1;
while(i <= 100){
System.out.println("我" + i + "岁啦!");
i++;
}
JavaScript语法
css
var i = 1
while (i <= 100) {
console.log('我' + i + '岁啦!');
i++;
}
这里提一下:上述if、for、switch、while各自结构里面的{}的变量就是一个我们上文提到的块级作用域,实际上如果用let声明:执行完块的内容 则变量会变得不可用,如果用var声明:执行完块的内容 变量的值仍然存在
四、数组
1.数据的创建方式
数据的创建方式有两种:
第一种是通过var arr = new Array()
,使用此方式可以创建一个空的数组,第二种是通过var arr = [1,2,3.14,'zhangsan',true,'wangwu']
2.数组可以存放不同的数据类型
在JS的数组中,可以存放不同类型的数据,这点和JAVA很不相同,java需要明确定义数组的类型并且不能存放不同类型的数组。而JS的数组比较灵活,我们完全可以存放各式各样的元素,也可以创建一个空数组
3.数组的获取以及遍历
获取数组元素是使用[]下标的方式即可,和JAVA一样,从0开始存放第一个元素,length代表数组的长度
ini
var arr = [1,2,'yao',true];
console.log(arr[2]); //输出"yao"
console.log(arr.length) //输出4
数组的遍历案例
ini
//求数组的最大值
tempArr = [2,6,1,77,52,25,7]
let max = 1;
for (const ele of tempArr) {
if(ele > max)
max = ele;
}
4.二维数组的创建
在我们以后的应用中,肯定经常会使用到二维数组的创建,所以单独拎出来讲讲二维数组的创建 创建3行3列值全为0的数组
const
const cols = 3;
const matrix = [];
for (let i = 0; i < rows; i++) {
matrix[i] = []; // 初始化第 i 行
for (let j = 0; j < cols; j++) {
matrix[i][j] = 0; // 填充 0
}
}
console.log(matrix); // [[0, 0, 0], [0, 0, 0], [0, 0, 0]]
5.数组的引用知识
数组实际上我们使用typeof会发现它是一个对象,所以它是可以引用的,引用:可以使两个变量指向同一块内存区域,更改值实际上是更改同一块内存区的值
perl
var arr1 = ['green', 'blue', 'yellow'];
var arr2 = arr1; // arr2 直接引用 arr1 的内存地址
console.log("arr2直接引用arr1:" + arr2); // ["green", "blue", "yellow"]
arr2[1] = 'ggg'; // 修改 arr2 会影响 arr1
console.log("arr2修改后 arr1的结果:" + arr1); // ["green", "ggg", "yellow"]
如果我们不想引用,只是想复制目标数组,JS提供了以下的方法,我测试了都是可行的
css
var arr2 = [...arr1]; // 创建新数组,复制元素
arr2[1] = 'ggg';
console.log(arr1); // ['green', 'blue', 'yellow'](不受影响)
console.log(arr2); // ['green', 'ggg', 'yellow']
var arr2 = arr1.slice(); // 复制数组
arr2[1] = 'ggg';
console.log(arr1); // ['green', 'blue', 'yellow'](不受影响)
ini
如果用var定义 数组 再用let引用数组 会怎么样? 答:实际上无影响 仍然会引用
var str1 = [1,2,3];
let str2 = str1;
console.log("str2的值是" + str2); 1,2,3
str2[0] = 6;
console.log("str1修改后的值是" + str1) 6,2,3
五、函数
函数我觉得算是JavaScript的一个重难点之一了,它与java的函数也不是很像,所以学过java的读者可以将重心放在JavaScript的函数这边
1. 函数的创建方式
函数的创建方式有两种,第一种是
javascript
//函数声明:创建有名函数
function sayHello(){
console.log("Hello")
}
第二种是
javascript
//函数表达式:创建匿名函数
const sayHello = function(){
console.log("Hello");
}
上述两种方式都是定义函数的方式,我们都可以通过sayHello()
来调用具体的函数
何时使用哪种方式?
当需要提升(预解析)时,或者需要给函数命名时,一般情况下优先使用函数声明
当需要动态赋值、作为回调函数或者需要将函数作为变量传递时,使用函数表达式
现代JavaScript避免使用var
声明函数表达式,而是使用const或者let
来避免变量提升
2.函数的作用域
在ES5和ES6中,在函数内声明的var
变量作用域都只存在于函数内部,超出函数无法访问,实际上ES6引入的let
和const
都是作用于块级作用域,与函数无关。
3.函数的参数
函数的形参不需要指定类型,形参的个数和实参要一样,但是其不会像java一样严格要求数量与类型要一致。若形参与实参的个数不匹配,会产生一系列问题
javascript
如果实参个数 > 形参的个数
会正常取到形参的个数
如果实参个数 < 形参的个数
多余的形参定义为undefined(把形参看成变量,定义后没有赋值)
函数还存在一个默认的arguments:传入的参数集合
javascript
var souta = function() {
console.log(arguments)
}
souta(1,2,3,4,5) //会输出1,2,3,4,5
4. 函数的返回值
在JS中,函数可以定义返回值,也可以不定义返回值
如果不定义返回值,使用变量取函数的话,则会取到undefined
javascript
function fn(){
console.log("我就只输出 不返回值")
}
var tempFn = fn();
console.log(tempFn)
//此时控制台会输出 "我就只输出 不返回值"和undefined
当然了,函数也可以通过return设置返回值,语法和java也是类似的
return最多只能返回一个值,下面是return的一个应用
javascript
function fn2(){
console.log("我不仅输出,我还返回值")
return 666;
}
var getFn2 = fn2();
console.log(getFn2)
5.箭头函数(ES6新特性)
下面是箭头函数的一些用法和特性
箭头函数的用法
javascript
// 普通函数
function sum(a, b) {
return a + b;
}
// 箭头函数
const sum = (a, b) => a + b;
// 普通函数
function get(a){
return a;
}
// 箭头函数
const get = a => a; //箭头后面是要return的内容 单条参数可省略
const sayHi = () => { //若使用箭头函数 无参的话记得保留括号
console.log("Hi");
};
箭头函数的特性
1.箭头函数不绑定自己的this
javascript
const person = {
name: "Alice",
sayName: function() {
console.log(this.name); // 正常输出"Alice"
},
sayNameArrow: () => {
console.log(this.name); // 输出undefined(this指向外层作用域)
}
};
2.箭头函数不绑定arguments对象
js
const showArgs = () => {
console.log(arguments); // 报错:arguments is not defined
};
showArgs(1, 2, 3);
显而易见,我们会发现箭头函数适合回调函数(一个函数作为参数传递给另一个函数),不适合对象的构造函数。
六、对象
JS中的对象是一组无无序的相关属性和方法的集合,所有的事务都是对象,例如字符串,数值,数组,函数等。和JAVA一样,对象是需要创建的,这里我学习的版本是ES5,同时也比较基础,所以咱们讨论的是不存在类的情况。
1.对象的创建方式
JS中对象有三种创建方式:
- 方式一:
var obj = new Object()
- 方式二:
var obj = {name: 'zhangsan',age: 18 }
- 方式三:
var obj = new Constructor(name,age)
2.JS在ES5中的对象和JAVA的关联
JS中关于对象的部分 其实和JAVA 可以关联着记忆:
- JAVA中存在着类,对象一般是创建某个类的对象,而JS不存在类的概念,JS中的构造方法和类很像,可以将其理解成类
- 不论是JAVA创建某个类的对象,还是JS中利用构造方法方便地创建对象 我们都称之为实例化
- JS跟JAVA还有些不同的是:不需要定义类,可以直接创建对象。并且不会报错
3.对象的构造方法
kotlin
function WzryRole(name,type,blood,attack){
this.name = name;
this.type = type;
this.blood = blood;
this.attack = attack;
this.operation = function(){
console.log("已选择" + this.name + "的类型是" + this.type + ",他的血量为" + blood + ",他的攻击方式是" + attack)
}
}
需要注意的几点
- 对象的构造方法要符合驼峰命名,适当使用大写;
- 在调用对象的构造方法时,需要加上
new
关键字 - 在对象的构造方法里面 一定要记得加this
4.对象的遍历和取值
对象的遍历推荐使用for in
,遍历的是对象的key ,我们可以通过obj[key]来获取值
javascript
var NZ = new WzryRole("哪吒","战士",1666,"乾坤拳");
for (const nzKey in NZ) {
console.log("对象的key是" + nzKey); //name,type,blood,attack operation
console.log("对象的value是" + NZ[nzKey]) // 哪吒 展示 1666 乾坤拳 function(){con...}
}
在以后的使用中,在某些简单的情况下,可以把对象当map使用,实际上,这样做可以存储不重复的key、也可以根据key获取值,时间复杂度为O(1)
七、总结
整个这是本人在掘金发的第一篇博客,所以文中有许多不足,后续会改进,本文内容内容主要是参考B站BV1ux411d75J,《JavaScript语言精粹》、以及DeepSeek。