JavaScript

205 阅读10分钟

一、JavaScript 运行过程及组成

  • 编写的代码是保存在文件中的, 也就是存储在硬盘(外存上).
  • 双击 .html 文件浏览器(应用程序)就会读取文件, 把文件内容加载到内存中(数据流向: 硬盘 => 内存)
  • 浏览器会解析用户编写的代码, 把代码翻译成二进制的, 能让计算机识别的指令(解释器的工作)
  • 得到的二进制指令会被 CPU 加载并执行(数据流向: 内存 => CPU)

image.png

JavaScript 的组成: :::info ECMAScript(简称 ES):JavaScript 语法。
**DOM:**页面文档对象模型,对页面中的元素进行操作。
**BOM:**浏览器对象模型,对浏览器窗口进行操作。 :::

二、JavaScript 的书写形式

2.1 行内式

直接嵌入到 html 元素内部:

<!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>
    <input type="button" value="点击我" onclick="alert('prprpr!')">
</body>
</html>

运行结果:
image.png :::warning 注意, JS 中字符串常量可以使用单引号表示, 也可以 使用双引号表示.
HTML 中推荐使用双引号, JS 中推荐使用单引号 :::

2.2 内嵌式

写到 script 标签中:

<body>
    <script>
        alert('prprpr!');
    </script>
</body>

运行结果:
image.png

2.3 外部式

写到单独的** .js 文件**中

alert('prprpr~~~');
<body>
    <script src="demo7.js"></script>
</body>

运行结果:
image.png :::warning 注意, 这种情况下 script 标签中间不能写代码. 必须空着(写了代码也不会执行).
适合代码多的情况. :::

三、输入输出

3.1 输入:prompt

弹出一个输入框

<body>
    <script>
        prompt("请输入你的姓名:");
    </script>
</body>

运行结果:
image.png

3.2 输出:alert

弹出一个警示对话框,输出结果:

<body>
    <script>
        alert("hello world!");
    </script>
</body>

运行结果:
image.png

3.3 输出:console.log

在控制台打印一个日志(供程序员看)

<body>
    <script>
        console.log("这是一条日志");
    </script>
</body>

注意: 在 VSCode 中直接输入 "log" 再按 tab 键, 就可以快速输入 console.log

打开开发者工具(F12)==》Console(控制台) 标签页才能看到结果:
image.png

重要概念:console

console 是一个 js 中的 "对象"
. 表示取对象中的某个属性或者方法,**.**可以直观理解成 "的"
console.log 就可以理解成: 使用 "控制台" 对象 "的" log 方法

四、语法介绍

4.1 变量的使用

JS 中创建的变量,不需要显式指定类型。
变量的实际类型,根据初始化/赋值来确定。

var name = 'zhangsan';
var age = 20;

使用变量:

console.log(age); // 读取变量的内容
age = 100;	// 修改变量的内容
console.log(age);

image.png

4.2 理解动态类型

  1. JS 的变量类型式程序运行过程中才确定的。
var a = 10; // 数字
var b = "hello"; // 字符串
  1. 随着程序运行,变量的类型可能会发送改变。
var a = 10; // 数字
a = "world"; // 字符串

4.3 基本数据类型

JS 中内置的几种类型

  • number: 数字. 不区分整数和小数.
  • boolean: true 真, false 假.
  • **string: **字符串类型.
  • undefined: 只有唯一的值 undefined. 表示未定义的值.
  • null: 只有唯一的值 null. 表示空值.

number 数字类型

JS 中不区分整数和浮点数, 统一都使用 "数字类型" 来表示。

数字进制表示

var a = 07; // 八进制整数,以 0 开头
var b = 0xa; // 十六进制整数,以 0x 开头
var c = 0b10; // 二进制整数,以 0b 开头

注意:

  • 一个八进制数字对应三个二进制数字。
  • 一个十六进制数字对应四个二进制数字. (两个十六进制数字就是一个字节)。

特殊的数字值

  • Infinity: 无穷大, 大于任何数字. 表示数字已经超过了 JS 能表示的范围.
  • -Infinity: 负无穷大, 小于任何数字. 表示数字已经超过了 JS 能表示的范围.
  • NaN: 表示当前的结果不是一个数字.
var max = Number.MAX_VALUE;
// 得到 Infinity
console.log(max * 2);
// 得到 -Infinity
console.log(-max * 2);
// 得到 NaN
console.log('hello' - 10);

image.png

注意:

  1. 负无穷大 和 无穷小 不是一回事. 无穷小指无限趋近与 0, 值为 1 / Infinity
  2. 'hehe' + 10 得到的不是 NaN, 而是 'hehe10', 会把数字隐式转成字符串, 再进行字符串拼接.
  3. 可以使用 isNaN 函数判定是不是一个非数字.
console.log(isNaN(10));
console.log(isNaN('hello'-10));

image.png

string 字符串类型

var a = "hello";
var b = 'hello';
var c = hello; // 运行出错
var msg = "My name is "zhangsan"";    // 出错
var msg = "My name is \"zhangsan\"";  // 正确, 使用转义字符. \" 来表示字符串内部的引号. 
var msg = "My name is 'zhangsan'";    // 正确, 搭配使用单双引号
var msg = 'My name is "zhangsan"';    // 正确, 搭配使用单双引号

求长度

使用 string 的 length 属性即可:

var a = 'hello';
console.log(a.length);
var b = '啊啊';
console.log(b.length);

结果如下:
image.png单位为 字符 的数量。

字符串拼接

使用 + 进行拼接:

var a = "my name is "; 
var b = "zhangsan";
console.log(a + b); // my name is zhangsan

注意, 数字和字符串也可以进行拼接:

var c = "my score is "; 
var d = 100;
console.log(c + d); // my score is 100

注意, 要认准相加的变量到底是字符串还是数字:

console.log(100 + 100); // 200
console.log('100' + 100); // 100100

boolean 布尔类型

Boolean 参与运算时当做 1 和 0 来看待:

console.log(true + 1); // 2
console.log(false + 1); // 1

这样的操作其实是不科学的. 实际开发中不应该这么写.

undefined 未定义数据类型

如果一个变量没有被初始化过, 结果就是 undefined, 是 undefined 类型:

var a;
console.log(a);

image.png
undefined 和字符串进行相加, 结果进行字符串拼接:

console.log(a + "10"); // undefined10

undefined 和数字进行相加, 结果为 NaN:

console.log(a + 10);

null 空值类型

null 表示当前的变量是一个 "空值".

var b = null; 
console.log(b + 10); // 10
console.log(b + "10"); // null10

注意: null 和 undefined 都表示取值非法的情况, 但是侧重点不同. null 表示当前的值为空. (相当于有一个空的盒子) undefined 表示当前的变量未定义. (相当于连盒子都没有)

运算符

JavaScript 中的运算符和 Java 用法基本相同. 此处不做详细介绍了。
注意:

  1. 比较运算符:
  • **==** 比较相等,会进行隐式类型转换
  • **===** 比较相等,不会进行隐式类型转换
var a = 10;
var b = '10'
console.log(a == b);
console.log(a === b);

image.png

  1. **||**运算符:

JS 中的逻辑或 和 Java 差别很大。
java || 运算返回的一定式 boolean 值
JS 的或运算,a || b

  • 若 a 为 true,整个表达式的值就为 a 的值。
  • 若 b 为 true,整个表达式的值就为 b 的值。
// 若 b 值为非空,就把 a 设为 b
// 若 b 值为空(null/undefined),就把 a 设为 0
let a = b || 0;

数组

创建数组

Java / C 中,要求数组中的变量是相同类型的!
但是 JS 中不要求(其他动态类型语言都不要求)。

有两种创建数组的方式:

  1. 通过 **new** 关键字来创建,数组在 JS 中也就是一个 “对象”。
var arr = new Array();
  1. 实际开发中,更常用的是,通过 字面量 的方式来创建。
let arr = [];
//也可以在创建数组的时候,填入内容
let arr2 = [1,2,'hello',true,undefined];

// 查看数组
console.log(arr);
console.log(arr2);

image.png

获取数组元素

使用下标的方式访问数组元素(从 0 开始):

var arr = ['艾希','诺克萨斯','盖伦'];
console.log(arr);
console.log(arr[0]);
console.log(arr[1]);
console.log(arr[2]);

arr[2] = '塞拉斯';
console.log(arr);
console.log(arr[2]);

image.png
注意:

  1. 如果尝试读取数组中不存在的下标,就会出现 undefined 的结果:
var arr = ['艾希','诺克萨斯','盖伦'];
console.log(arr[3]);

image.png

  1. 如果尝试写入数组中不存在的下标,就会往这个下标插入元素,同时可能就会修改数组的长度,但是中间元素的值,相当于没有定义,仍然是 undefined的:
var arr = ['艾希','诺克萨斯','盖伦'];
arr[5] = '劫';
console.log(arr);

console.log(arr[3]); // 同时如果读取元素不存在的下标,还是会 undefined

image.png

  1. JS 可以给数组指定一个 字符串 类型的 “下标”。此处的 “下标” 更应该理解成是 “属性”,也就相当于 “键值对”:
let arr = [];
arr['hello'] = 'world'; // 跟 arr.hello = 'world' 等价
console.log(arr);
console.log(arr['hello']);
console.log(arr.hello);

image.png
对于上例,本质上是在创建一个 hello 属性,类似的,也可以通过 **[]**或者**.**的方式都能访问属性!!

**注意:**不要给数组名直接赋值,此时数组中的所有元素就都没了。 相当于本来 arr 是一个数组,重新赋值后变成字符串了。

var arr = ['盖伦','艾希','光辉'];
arr = '小丑';

新增数组元素

  1. 通过修改 length 新增:

相当于在末尾新增元素. 新增的元素默认值为 undefined

var arr = [9, 5, 2, 7]; 
arr.length = 6; 
console.log(arr);
console.log(arr[4], arr[5]);

image.png

  1. 通过下标新增

如果下标超出范围赋值元素, 则会给指定位置插入新元素

var arr = [];
arr[2] = 10;
console.log(arr);

image.png

此时这个数组的 [0] 和 [1] 都是 undefined

  1. 使用 push 进行追加元素(常用)

**代码示例:**给定一个数组,把数组中的奇数放到一个 newArr 中。

var arr = [1,2,3,4,5,6,7,8,9];
var newArr = [];
for (var i = 0; i < arr.length; i++) {
  if (arr[i] % 2 != 0) {
    newArr.push(arr[i]);
  }
}
console.log(newArr);

image.png

删除数组中的元素

使用 splice 方法删除元素:

var arr = [1,2,3,4,5,6];
// 第一个参数表示从下标为 2 的位置开始删除,
// 第二个参数表示要删除的元素个数为 3 个。
arr.splice(2,3);
console.log(arr);

image.png

4.4 函数

语法格式

// 创建函数/函数声明/函数定义 
function 函数名(形参列表) { 
  函数体 
  return 返回值;
}

// 函数调用 
函数名(实参列表)					 // 不考虑返回值
返回值 = 函数名(实参列表) // 考虑返回值
  • 函数定义并不会执行函数体内容, 必须要调用才会执行. 调用几次就会执行几次.
function hello() {
  console.log("hello world!!");
}
// 如果不调用函数,则没有执行打印语句
hello();

image.png

  • 调用函数的时候进入函数内部执行, 函数结束时回到调用位置继续执行. 可以借助调试器来观察.
  • 函数的定义和调用的先后顺序没有要求. (这一点和变量不同, 变量必须先定义再使用)
// 调用函数 
hello(); 
// 定义函数 
function hello() { 
  console.log("hello");
}

参数个数

实参和形参之间的个数可以不匹配. 但是实际开发一般要求形参和实参个数要匹配

  1. 如果实参个数比形参个数多,则多出的参数不参与函数运算:
function add(a,b) {
  return a+b;
}
console.log(add(10,20,30,40));

image.png

  1. 如果实参个数比形参个数少, 则此时多出来的形参值为 undefined:
function add(a,b) {
  return a+b;
}
console.log(add(10));

image.png

函数表达式

var add = function() {
  var sum = 0;
  for (var i = 0; i < arguments.length; i++) {
    sum += arguments[i];
  }
  return sum;
}
console.log(add(10,20));    //30
console.log(add(1,2,3,4));  //10

console.log(typeof add);    // function

image.png
此时形如 **function() { }** 这样的写法定义了一个匿名函数, 然后将这个匿名函数用一个变量来表示. 后面就可以通过这个 add 变量来调用函数了.

作用域链

背景:

  • 函数可以定义在函数内部
  • 内层函数可以访问外层函数的局部变量.

内部函数可以访问外部函数的变量. 采取的是链式查找的方式. 从内到外依次进行查找.

var num = 1;
function test1() {
  var num = 10;

  function test2() {
    var num = 20;
    console.log(num);
  }
  test2();
}
test1();

image.png

4.5 对象

  1. 使用 字面量 创建对象(常用)

使用 { } 创建对象

var a = {};     // 创建了一个空的对象

var student = {
  name: '蔡徐坤', 
  height: 175, 
  weight: 170, 
  sayHello: function() { 
    console.log("hello");
  }
};
  • 使用 { } 创建对象
  • 属性和方法使用键值对的形式来组织.
  • 键值对之间使用 , 分割. 最后一个属性后面的 , 可有可无
  • 键和值之间使用 : 分割.
  • 方法的值是一个匿名函数.

使用对象的属性和方法:

// 1. 使用 . 成员访问运算符来访问属性 `.` 可以理解成 "的" 
console.log(student.name); 
// 2. 使用 [ ] 访问属性, 此时属性需要加上引号 
console.log(student['height']); 
// 3. 调用方法, 别忘记加上 ()
student.sayHello();
  1. **使用 ****new Object**创建对象
var student = new Object(); // 和创建数组类似 
student.name = "蔡徐坤"; 
student.height = 175; 
student['weight'] = 170; 
student.sayHello = function () { 
  console.log("hello");
}

console.log(student.name); 
console.log(student['weight']);
student.sayHello();

注意, 使用 { } 创建的对象也可以随时使用 student.name = "蔡徐坤"; 这样的方式来新增属性

  1. 使用 构造函数 创建对象

前面的创建对象方式只能创建一个对象. 而使用构造函数可以很方便 的创建 多个对象.
例如: 创建几个猫咪对象:

function Cat(name, type, sound) { 
  this.name = name; 
  this.type = type; 
  this.miao = function () { 
    console.log(sound); // 别忘了作用域的链式访问规则
  } 
}
var mimi = new Cat('咪咪', '中华田园喵', '喵'); 
var xiaohei = new Cat('小黑', '波斯喵', '猫呜'); 
var ciqiu = new Cat('刺球', '金渐层', '咕噜噜');
console.log(mimi);
mimi.miao();