JavaScript是什么?做什么?

  • 是一种运行在客户端(浏览器)的编程语言,实现人机交互效果。
  • 网页特效、表单验证、数据交互。
  • 服务端编程(Node.js)。
  • 组成:ECMAScript(语言基础)、Web API(浏览器提供的API)(包括页面文档对象模型DOM、浏览器对象模型BOM)
  • DOM:操作文档,比如对页面元素进行移动、大小、添加删除等操作。
  • BOM:操作浏览器,比如页面弹窗,检测窗口宽度,存储数据到浏览器等。

JavaScript书写位置

  • 内部嵌入:在HTML文件中使用<script>标签嵌入JavaScript代码。(上方)
    原因:浏览器会按照代码在文件中的顺序加载HTML,如果先加载的JavaScript想修改下方的HTML,可能由于HTML尚未加载而失效。
  • 外部引入:在HTML文件中使用<script>标签引入外部JavaScript文件。
    1
    2
    3
    4
    <body>
    <!--中间写内容也会被忽略-->
    <script src="my.js"></script>
    </body>
  • 内联:在HTML标签中使用onclick等事件属性。
    1
    <button onclick="alert('Hello')">点我</button>

JavaScript结束符

  • JavaScript语句以分号;结尾,但是可以省略。风格统一,要么都加要么都不加。

输入、输出语法

输出语法

  • 第一种,向body输出内容,如果内容是标签,也会被解析成网页元素。
    1
    document.write("Hello World!");
  • 第二种,页面弹出警告对话框
    1
    alert("Hello World!");
  • 第三种,控制台输出语法,程序员调试使用
    1
    console.log("Hello World!");

输入语法

  • 弹出输入对话框,用户输入的内容会被赋值给变量name
    1
    var name = prompt("请输入你的名字:");

代码执行顺序

  • 按HTML文档流顺序执行JavaScript代码,alert()和prompt()会跳过页面渲染先执行。

字面量

  • 字面量是一种固定值,不可改变,比如数字、字符串、布尔值、数组、对象等。

变量

是什么?

  • 变量是存储数据的容器,可以存储任何类型的数据。

基本使用

  • 声明变量:let、var
  • 区别:let和const是ES6新增的,var是ES5的,let和const有块级作用域,var没有。
  • 赋值:let name = "Tom";
  • 更新:name = "Jerry";

变量的本质

  • 变量是存储在内存中的数据,变量名是对数据的引用。

命名规则与规范

  • 变量名只能包含字母、数字、下划线、美元符号,不能以数字开头。
  • 变量名区分大小写。
  • 变量名不能使用保留字。
  • 变量名要有意义,驼峰命名法。
  • 变量名不能使用中文。
  • 变量名不能使用特殊符号。
1
2
var有一些不合理之处,比如变量提升、全局变量污染等,所以在ES6中引入了let和const。
可以先使用再声明,重复声明,并不合理。

数组

  • 数组是一种特殊的对象,用于存储多个数据。
  • 可以存储不同类型的数据。
    1
    2
    let arr = [1, 2, 3, 4, 5];
    arr[0] // 1

常量

  • 常量是一种固定值,不可改变。
  • 声明常量:const PI = 3.14;

数据类型

JavaScript是一种弱类型语言,变量不需要指定数据类型,可以存储任何类型的数据。

  • number 数字
  • string 字符串 单引号(推荐)、双引号、反引号(模板字符串)
  • boolean 布尔值
  • null 空值
  • undefined 未定义 声明变量但未赋值
  • object 对象 引用数据类型

NaN:Not a Number,表示不是一个数字,黏性,任何数和NaN计算都是NaN。NaN与NaN不相等。

模版字符串:反引号包裹,可以换行,可以插入变量,使用${}

null和undefined的区别:

  • null表示空值,是一个对象,表示没有对象。
  • undefined表示未定义,是一个数据类型,表示未定义的值。
  • null == undefined // true
  • null === undefined // false

检测数据类型:

  • typeof() // 返回数据类型的字符串形式
  • Object.prototype.toString.call() // 返回对象的类型

类型转换:

  • 强制类型转换:Number()、String()、Boolean()、parseInt()、parseFloat()。
  • 自动类型转换:隐式类型转换,比如字符串和数字相加,数字会转换成字符串。

运算符

赋值运算符

对变量进行赋值的运算符。

1
2
3
4
5
6
let a = 10;
a += 5; // a = a + 5
a -= 5; // a = a - 5
a *= 5; // a = a * 5
a /= 5; // a = a / 5
a %= 5; // a = a % 5

一元运算符

对一个变量进行操作的运算符。正负号、自增自减。

1
2
3
let a = 10;
a++; // a = a + 1
a--; // a = a - 1

前置自增自减:先加先减。
后置自增自减:先赋值再加减。

比较运算符

比较两个值的大小,返回布尔值。

1
2
3
4
5
6
7
8
9
10
11
12
13
let a = 10;
let b = 20;
a > b; // false
a < b; // true
a >= b; // false
a <= b; // true
a == b; // false 两个等号是比较值是否相等
a != b; // true
a === b; // false 三个等号是比较值和类型是否相等
a !== b; // true
null == undefined; // true
null === undefined; // false
NaN === NaN; // false

字符串比较:比较的是Unicode编码,从第一个字符开始比较,如果相等则继续比较下一个字符,直到不相等为止。

逻辑运算符

逻辑运算符用于连接两个或多个表达式,返回布尔值。

  • 与:&& 两个表达式都为真,结果为真。
  • 或:|| 两个表达式有一个为真,结果为真。
  • 非:! 表达式为真,结果为假。
  • 短路逻辑:如果第一个表达式的结果可以确定整个表达式的结果,就不会执行第二个表达式。
  • 逻辑运算符的优先级:! > && > ||

运算符优先级

  • 一元运算符 > 算术运算符 > 比较运算符 > 逻辑运算符 > 赋值运算符
  • 优先级相同的运算符,从左到右执行。
  • 可以使用括号改变运算顺序。

语句

  • 语句是一组指令,用于执行特定的任务。

表达式和语句

  • 表达式是一个值或变量或运算符的组合,可以计算出一个值。
  • 语句是一组表达式的组合,用于执行特定的任务。

分支语句

顺序结构、分支结构、循环结构。

  • if语句
    1
    2
    3
    4
    5
    if (条件) {
    // 条件为真执行
    } else {
    // 条件为假执行
    }
  • if…else if…else
    1
    2
    3
    4
    5
    6
    7
    if (条件1) {
    // 条件1为真执行
    } else if (条件2) {
    // 条件2为真执行
    } else {
    // 条件1和条件2都为假执行
    }
  • switch语句
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    switch (表达式) {
    case1:
    // 表达式等于值1执行
    break;
    case2:
    // 表达式等于值2执行
    break;
    default:
    // 表达式不等于值1和值2执行
    }
  • 三元运算符:条件 ? 成功执行 : 失败执行

循环语句

断点调试

  • 断点:在代码中设置一个断点,程序执行到断点时会暂停。
  • 单步执行:逐行执行代码。
  • 查看变量:查看变量的值。
  • 查看调用栈:查看函数的调用关系。
  • 重启:重新执行代码。

while循环

三要素:变量起始值、终止条件、变量更新。

1
2
3
while (条件) {
// 条件为真执行
}
  • break: 跳出循环。
  • continue: 跳过本次循环。

for循环

1
2
3
for (初始化; 条件; 更新) {
// 条件为真执行
}
  • 循环数组
    1
    2
    3
    4
    let arr = [1, 2, 3, 4, 5];
    for (let i = 0; i < arr.length; i++) {
    console.log(arr[i]);
    }

无限循环是一种循环结构,没有终止条件,只能通过break跳出。

1
2
3
for (;;) {
// 无限循环
}

循环嵌套:在循环中再嵌套一个循环。

1
2
3
4
5
for (let i = 0; i < 5; i++) {
for (let j = 0; j < 5; j++) {
console.log(i, j);
}
}

数组

  • 数组是一种特殊的对象,用于存储多个数据。
  • 可以存储不同类型的数据。
  • 数组的本质是对象,数组的索引是对象的属性名。
  • 数组的长度是动态的,可以随时增加或删除元素。
  • 数组的索引是从0开始的。
  • 数组的最后一个元素的索引是数组长度减1。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    let arr = [1, 2, 3, 4, 5];
    let array = new Array(1, 2, 3, 4, 5);
    arr[0] // 1
    arr[1] // 2
    arr[2] // 3
    arr[3] // 4
    arr[4] // 5
    // 基本使用
    arr.length // 5
    arr.push(6); // 6 这是从数组末尾添加元素 返回数组的新长度
    arr.pop(); // 5 这是从数组末尾删除元素
    arr.shift(); // 1 这是从数组开头删除元素
    arr.unshift(0); // 0 这是从数组开头添加元素 返回数组的新长度
    arr.splice(1, 2); // 2, 3 这是删除元素,第一个参数是索引,第二个参数是删除的个数
    arr.splice(1, 0, 2, 3); // 1, 2, 3, 2, 3, 4, 5 这是添加元素,第一个参数是索引,第二个参数是删除的个数,后面是添加的元素
    //排序
    arr.sort(); // 1, 2, 3, 4, 5
    arr.reverse(); // 5, 4, 3, 2, 1
    // 升序
    arr.sort((a, b) => a - b);
    // 降序
    arr.sort((a, b) => b - a);

函数

  • 函数是一段可以重复使用的代码块。
  • 函数可以接受参数,也可以返回值。
  • 函数的本质是对象。
  • 函数的参数是局部变量,只能在函数内部使用。
  • 函数的返回值是函数执行的结果。默认返回undefined
  • 函数的参数可以有默认值。
  • 函数的参数可以有不定参数。
  • 函数的参数可以有解构赋值。
  • 函数的参数可以有剩余参数。
  • 如果形参和实参个数不一致,多余的实参会被忽略,缺少的实参会被赋值为undefined。
    1
    2
    3
    4
    // 默认值,如果没有传入参数,就使用默认值
    function add(a = 0, b = 0) {
    return a + b;
    }
  • 作用域:全局作用域、局部作用域、块级作用域。
  • 作用域链:函数的作用域是嵌套的,内部函数可以访问外部函数的变量,外部函数不能访问内部函数的变量。
  • 闭包:函数嵌套函数,内部函数可以访问外部函数的变量,外部函数不能访问内部函数的变量。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    function outer() {
    let a = 10;
    function inner() {
    console.log(a);
    }
    return inner;
    }
    let fn = outer();
    fn(); // 10
  • 闭包的作用:保护变量、延长变量的生命周期。
  • 闭包的缺点:内存泄漏,变量无法被释放。
  • 递归:函数调用自身。
  • 尾递归:递归调用是函数的最后一步操作。
  • 递归的缺点:占用内存,调用栈溢出。
  • 如果函数内部变量没有声明直接赋值,会自动声明为全局变量。(不推荐)
  • 在能够访问到的情况下先使用局部变量,再使用全局变量。

匿名函数

  • 匿名函数没有函数名,可以赋值给变量,也可以作为函数的参数。
  • 区别:函数声明会提升,匿名函数不会提升。
  • 避免全局污染。
    1
    2
    3
    4
    let fn = function() {
    console.log("Hello");
    }
    fn(); // Hello

立即执行函数

  • 立即执行函数是匿名函数的一种,定义完立即执行。
  • 避免全局污染。
  • 必须要加分号
  • 不需要调用,自动执行。
    1
    2
    3
    (function() {
    console.log("Hello");
    })();

箭头函数

  • 箭头函数是匿名函数的简写,可以省略function关键字。
  • 箭头函数没有this,this指向的是定义时所在的对象,而不是执行时所在的对象。
  • 箭头函数没有arguments,可以使用剩余参数代替。
  • 箭头函数没有new.target,不能使用new调用。
  • 箭头函数没有原型,不能作为构造函数。
  • 箭头函数不能使用yield命令,不能用作Generator函数。
  • 箭头函数不能使用call、apply、bind方法。
    1
    2
    3
    4
    let fn = () => {
    console.log("Hello");
    }
    fn(); // Hello

逻辑中断

  • && 逻辑与:都是真,返回最后一个真值;有一个假,返回第一个假值。
  • || 逻辑或:有一个真,返回第一个真值;都是假,返回最后一个假值。

转换为Boolean型

  • 0、NaN、null、undefined、空字符串、false转换为false,其他转换为true。

对象

  • 对象是一种复合数据类型,可以存储多个数据。
  • 对象的本质是键值对的集合。
  • 对象的键是字符串,值可以是任意类型的数据。
  • 对象的键是唯一的,值可以重复。
  • 对象的键可以是变量,值可以是变量。
  • 无序的,不能通过索引访问。
    1
    2
    3
    4
    let obj = {
    name: "Tom",
    age: 18
    }

声明

  • 对象字面量:let obj = {name: "Tom", age: 18};
  • 带有方法的对象:let obj = {name: "Tom", age: 18, say: function() {console.log("Hello")}};

使用

  • 查:obj.name / obj["name"]
  • 改:obj.name = "Jerry"
  • 增:obj.hobby="swim"
  • 删:delete obj.hobby(了解)

遍历

  • for…in 这个是遍历对象的键
    1
    2
    3
    for (let key in obj) {
    console.log(key, obj[key]);
    }
  • Object.keys() 这个是遍历对象的键
    1
    2
    3
    Object.keys(obj).forEach(key => {
    console.log(key, obj[key]);
    });
  • Object.values() 这个是遍历对象的值
    1
    2
    3
    Object.values(obj).forEach(value => {
    console.log(value);
    });
  • Object.entries() 这个是遍历对象的键值对
    1
    2
    3
    Object.entries(obj).forEach(([key, value]) => {
    console.log(key, value);
    });

foreach()是数组的方法,是用来遍历数组的,具体用法是:其中item是数组的元素,index是数组的索引。

1
2
3
4
let arr = [1, 2, 3, 4, 5];
arr.forEach((item, index) => {
console.log(item, index);
});

数学内置对象

内置对象是什么?

  • 内置对象是JavaScript自带的对象,不需要引入,可以直接使用。
  • 内置对象有很多种,比如Math、Date、RegExp等。
  • 内置对象是全局对象的属性,可以直接使用。

Math对象

  • Math对象是数学内置对象,提供了很多数学方法。
  • Math对象的方法是静态方法,直接通过Math调用。
  • 比如:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    Math.PI  // 3.141592653589793
    Math.abs(-10) // 10 绝对值
    Math.ceil(3.1) // 4 向上取整
    Math.floor(3.9) // 3 向下取整
    Math.round(3.5) // 4 四舍五入
    Math.max(1, 2, 3, 4, 5) // 5 最大值
    Math.min(1, 2, 3, 4, 5) // 1 最小值
    Math.pow(2, 3) // 8 2的3次方
    Math.sqrt(9) // 3 开平方
    Math.random() // 0-1之间的随机数
    // m和n之间的随机数
    function random(m, n) {
    return Math.floor(Math.random() * (n - m + 1) + m);
    }

其他数据结构

Set

  • Set是一种数据结构,类似于数组,但是成员的值是唯一的。
  • Set的本质是对象,不是数组。
  • Set的成员是无序的,不能通过索引访问。
  • 详细用法:
    1
    2
    3
    4
    5
    6
    let set = new Set([1, 2, 3, 4, 5]); // 通过数组创建
    set.add(6); // 添加元素
    set.delete(6); // 删除元素
    set.has(6); // 判断是否存在
    set.clear(); // 清空元素
    set.size; // 元素个数
  • 遍历:
    1
    2
    3
    set.forEach(value => {
    console.log(value);
    });

Map

  • Map是一种数据结构,类似于对象,但是键可以是任意类型的数据。
  • Map的本质是对象,不是数组。
  • Map的成员是有序的,可以通过键访问。
  • 详细用法:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    let map = new Map([[1, "Tom"], [2, "Jerry"]]); // 通过数组创建
    map.get(1); // Tom 通过键获取值
    map.entries(); // 返回键值对
    map.keys(); // 返回键
    map.values(); // 返回值
    map.set(3, "Spike"); // 添加元素
    map.delete(3); // 删除元素
    map.has(3); // 判断是否存在
    map.clear(); // 清空元素
    map.size; // 元素个数
  • 遍历:
    1
    2
    3
    map.forEach((value, key) => {
    console.log(key, value);
    });

引用数据类型:对象、数组、函数、Set、Map等。
简单数据类型放在栈内存,引用数据类型在栈内存中存储地址,堆内存中存储数据。

如果一个对象定义了一个叫[Symbol.iterator]的方法,那么这个对象就是一个迭代器。比如数组、字符串、Set、Map等。