本博客主要为黑马程序员网课中关于 JS 模块(网课在下面有介绍)所整理记录的学习笔记,仅作学习用途,如有侵权,烦请联系本人侵删。同时感谢黑马老师们的辛苦教导付出,如有不足之处,欢迎各位小伙伴、官老爷们多多提意见或建议,感谢!
JS入门到精通、DOM BOM操作
了解变量、数据类型、运算符等基础概念,能够实现数据类型的转换,结合四则运算体会如何编程。
体会现实世界中的事物与计算机的关系
理解什么是数据并知道数据的分类
理解变量存储数据的“容器”
掌握常见运算符的使用,了解优先级关系
知道 JavaScript 数据类型隐式转换的特征
掌握 JavaScript 的引入方式,初步认识 JavaScript 的作用
引入方式
JavaScript 程序不能独立运行,它需要被嵌入 HTML 中,然后浏览器才能执行 JavaScript 代码。通过 script
标签将 JavaScript 代码引入到 HTML 中,有两种方式:
内部方式
通过 script
标签包裹 JavaScript 代码
JavaScript 基础 - 引入方式
外部形式
一般将 JavaScript 代码写在独立的以 .js 结尾的文件中,然后通过 script
标签的 src
属性引入
// demo.js
document.write('嗨,欢迎来传智播学习前端技术!')
JavaScript 基础 - 引入方式
如果 script 标签使用 src 属性引入了某 .js 文件,那么 标签的代码会被忽略!!!如下代码所示:
JavaScript 基础 - 引入方式
注释和结束符
通过注释可以屏蔽代码被执行或者添加备注信息,JavaScript 支持两种形式注释语法:
单行注释
使用 //
注释单行代码
JavaScript 基础 - 注释
多行注释
使用 /* */
注释多行代码
JavaScript 基础 - 注释
注:编辑器中单行注释的快捷键为 ctrl + /
结束符
在 JavaScript 中 ;
代表一段代码的结束,多数情况下可以省略 ;
使用回车(enter)替代。
JavaScript 基础 - 结束符
实际开发中有许多人主张书写 JavaScript 代码时省略结束符 ;
输入和输出
输出和输入也可理解为人和计算机的交互,用户通过键盘、鼠标等向计算机输入信息,计算机处理后再展示结果给用户,这便是一次输入和输出的过程。
举例说明:如按键盘上的方向键,向上/下键可以滚动页面,按向上/下键这个动作叫作输入,页面发生了滚动了这便叫输出。
输出
JavaScript 可以接收用户的输入,然后再将输入的结果输出:
alert()
、document.wirte()
以数字为例,向 alert()
或 document.write()
输入任意数字,他都会以弹窗形式展示(输出)给用户。
输入
向 prompt()
输入任意内容会以弹窗形式出现在浏览器中,一般提示用户输入一些内容。
JavaScript 基础 - 输入输出
JavaScript的组成
ECMAScript:
规定了js基础语法核心知识。
比如:变量、分支语句、循环语句、对象等等
Web APIs :
DOM 文档对象模型, 定义了一套操作HTML文档的API
BOM 浏览器对象模型,定义了一套操作浏览器窗口的API
理解变量是计算机存储数据的“容器”,掌握变量的声明方式
变量是计算机中用来存储数据的“容器”,它可以让计算机变得有记忆,通俗的理解变量就是使用【某个符号】来代表【某个具体的数值】(数据)
声明
声明(定义)变量有两部分构成:声明关键字、变量名(标识)
JavaScript 基础 - 声明和赋值
关键字是 JavaScript 中内置的一些英文词汇(单词或缩写),它们代表某些特定的含义,如 let
的含义是声明变量的,看到 let
后就可想到这行代码的意思是在声明变量,如 let age;
let
和 var
都是 JavaScript 中的声明变量的关键字,推荐使用 let
声明变量!!!
赋值
声明(定义)变量相当于创造了一个空的“容器”,通过赋值向这个容器中添加数据。
JavaScript 基础 - 声明和赋值
关键字
JavaScript 使用专门的关键字 let
和 var
来声明(定义)变量,在使用时需要注意一些细节:
以下是使用 let
时的注意事项:
允许声明和赋值同时进行
不允许重复声明
允许同时声明多个变量并赋值
JavaScript 中内置的一些关键字不能被当做变量名
以下是使用 var
时的注意事项:
允许声明和赋值同时进行
允许重复声明
允许同时声明多个变量并赋值
大部分情况使用 let
和 var
区别不大,但是 let
相较 var
更严谨,因此推荐使用 let
,后期会更进一步介绍二者间的区别。
变量名命名规则
关于变量的名称(标识符)有一系列的规则需要遵守:
只能是字母、数字、下划线、$,且不能能数字开头
字母区分大小写,如 Age 和 age 是不同的变量
JavaScript 内部已占用于单词(关键字或保留字)不允许使用
尽量保证变量具有一定的语义,见字知义
注:所谓关键字是指 JavaScript 内部使用的词语,如 let
和var
,保留字是指 JavaScript 内部目前没有使用的词语,但是将来可能会使用词语。
JavaScript 基础 - 变量名命名规则
概念:使用 const 声明的变量称为“常量”。
使用场景:当某个变量永远不会改变的时候,就可以使用 const 来声明,而不是let。
命名规范:和变量一致
const PI = 3.14
注意: 常量不允许重新赋值,声明的时候必须赋值(初始化)
计算机世界中的万事成物都是数据。
计算机程序可以处理大量的数据,为了方便数据的管理,将数据分成了不同的类型:
注:通过 typeof 关键字检测数据类型
JavaScript 基础 - 数据类型
数值类型
即我们数学中学习到的数字,可以是整数、小数、正数、负数
JavaScript 基础 - 数据类型
JavaScript 中的数值类型与数学中的数字是一样的,分为正数、负数、小数等。
字符串类型
通过单引号( ''
) 、双引号( ""
)或反引号包裹的数据都叫字符串,单引号和双引号没有本质上的区别,推荐使用单引号。
注意事项:
无论单引号或是双引号必须成对使用
单引号/双引号可以互相嵌套,但是不以自已嵌套自已
必要时可以使用转义符 \
,输出单引号或双引号
JavaScript 基础 - 数据类型
布尔类型
表示肯定或否定时在计算机中对应的是布尔类型数据,它有两个固定的值 true
和 false
,表示肯定的数据用 true
,表示否定的数据用 false
。
JavaScript 基础 - 数据类型
undefined
未定义是比较特殊的类型,只有一个值 undefined,只声明变量,不赋值的情况下,变量的默认值为 undefined,一般很少【直接】为某个变量赋值为 undefined。
JavaScript 基础 - 数据类型
注:JavaScript 中变量的值决定了变量的数据类型。
理解弱类型语言的特征,掌握显式类型转换的方法
在 JavaScript 中数据被分成了不同的类型,如数值、字符串、布尔值、undefined,在实际编程的过程中,不同数据类型之间存在着转换的关系。
隐式转换
某些运算符被执行时,系统内部自动将数据类型进行转换,这种转换称为隐式转换。
JavaScript 基础 - 隐式转换
注:数据类型的隐式转换是 JavaScript 的特征,后续学习中还会遇到,目前先需要理解什么是隐式转换。
补充介绍模板字符串的拼接的使用
显式转换
编写程序时过度依靠系统内部的隐式转换是不严禁的,因为隐式转换规律并不清晰,大多是靠经验总结的规律。为了避免因隐式转换带来的问题,通常根逻辑需要对数据进行显示转换。
Number
通过 Number
显示转换成数值类型,当转换失败时结果为 NaN
(Not a Number)即不是一个数字。
JavaScript 基础 - 隐式转换
理解什么是流程控制,知道条件控制的种类并掌握其对应的语法规则,具备利用循环编写简易ATM取款机程序能力
运算符
语句
综合案例
算术运算符
数字是用来计算的,比如:乘法 * 、除法 / 、加法 + 、减法 - 等等,所以经常和算术运算符一起。
算术运算符:也叫数学运算符,主要包括加、减、乘、除、取余(求模)等
运算符 | 作用 |
---|---|
+ | 求和 |
- | 求差 |
* | 求积 |
/ | 求商 |
% | 取模(取余数),开发中经常用于作为某个数字是否被整除 |
注意:在计算失败时,显示的结果是 NaN (not a number)
// 算术运算符
console.log(1 + 2 * 3 / 2) // 4
let num = 10
console.log(num + 10) // 20
console.log(num + num) // 20
// 1. 取模(取余数) 使用场景: 用来判断某个数是否能够被整除
console.log(4 % 2) // 0
console.log(6 % 3) // 0
console.log(5 % 3) // 2
console.log(3 % 5) // 3
// 2. 注意事项 : 如果我们计算失败,则返回的结果是 NaN (not a number)
console.log('pink老师' - 2)
console.log('pink老师' * 2)
console.log('pink老师' + 2) // pink老师2
赋值运算符
赋值运算符:对变量进行赋值的运算符
= 将等号右边的值赋予给左边, 要求左边必须是一个容器
运算符 | 作用 |
---|---|
+= | 加法赋值 |
-+ | 减法赋值 |
*= | 乘法赋值 |
/= | 除法赋值 |
%= | 取余赋值 |
自增/自减运算符
符号 | 作用 | 说明 |
---|---|---|
++ | 自增 | 变量自身的值加1,例如: x++ |
-- | 自减 | 变量自身的值减1,例如: x-- |
++在前和++在后在单独使用时二者并没有差别,而且一般开发中我们都是独立使用
++在后(后缀式)我们会使用更多
注意:
只有变量能够使用自增和自减运算符
++、-- 可以在变量前面也可以在变量后面,比如: x++ 或者 ++x
比较运算符
使用场景:比较两个数据大小、是否相等,根据比较结果返回一个布尔值(true / false)
运算符 | 作用 |
---|---|
> | 左边是否大于右边 |
< | 左边是否小于右边 |
>= | 左边是否大于或等于右边 |
<= | 左边是否小于或等于右边 |
=== | 左右两边是否类型 和值 都相等(重点) |
== | 左右两边值 是否相等 |
!= | 左右值不相等 |
!== | 左右两边是否不全等 |
逻辑运算符
使用场景:可以把多个布尔值放到一起运算,最终返回一个布尔值
符号 | 名称 | 日常读法 | 特点 | 口诀 |
---|---|---|---|---|
&& | 逻辑与 | 并且 | 符号两边有一个假的结果为假 | 一假则假 |
|| | 逻辑或 | 或者 | 符号两边有一个真的结果为真 | 一真则真 |
! | 逻辑非 | 取反 | true变false false变true | 真变假,假变真 |
A | B | A && B | A || B | !A |
---|---|---|---|---|
false | false | false | false | true |
false | true | false | true | true |
true | false | false | true | false |
true | true | true | true | false |
运算符优先级
逻辑运算符优先级: !> && > ||
表达式和语句
分支语句可以根据条件判定真假,来选择性的执行想要的代码
分支语句包含:
if分支语句(重点)
三元运算符
switch语句
if 分支语句
语法:
if(条件表达式) {
// 满足条件要执行的语句
}
小括号内的条件结果是布尔值,为 true 时,进入大括号里执行代码;为false,则不执行大括号里面代码
小括号内的结果若不是布尔类型时,会发生类型转换为布尔值,类似Boolean()
如果大括号只有一个语句,大括号可以省略,但是,俺们不提倡这么做~
if双分支语句
如果有两个条件的时候,可以使用 if else 双分支语句
if (条件表达式){
// 满足条件要执行的语句
} else {
// 不满足条件要执行的语句
}
例:
if 多分支语句
使用场景: 适合于有多个条件的时候
三元运算符(三元表达式)
使用场景: 一些简单的双分支,可以使用 三元运算符(三元表达式),写起来比 if else双分支 更简单。符号:? 与 : 配合使用
语法:条件 ? 表达式1 : 表达式2
例如:
// 三元运算符(三元表达式)
// 1. 语法格式
// 条件 ? 表达式1 : 表达式2
// 2. 执行过程
// 2.1 如果条件为真,则执行表达式1
// 2.2 如果条件为假,则执行表达式2
// 3. 验证
// 5 > 3 ? '真的' : '假的'
console.log(5 < 3 ? '真的' : '假的')
// let age = 18
// age = age + 1
// age++
// 1. 用户输入
let num = prompt('请您输入一个数字:')
// 2. 判断输出- 小于10才补0
// num = num < 10 ? 0 + num : num
num = num >= 10 ? num : 0 + num
alert(num)
switch语句(了解)
使用场景: 适合于有多个条件的时候,也属于分支语句,大部分情况下和 if多分支语句 功能相同
注意:
switch case语句一般用于等值判断, if适合于区间判断
switchcase一般需要配合break关键字使用 没有break会造成case穿透
if 多分支语句开发要比switch更重要,使用也更多
例:
// switch分支语句
// 1. 语法
// switch (表达式) {
// case 值1:
// 代码1
// break
// case 值2:
// 代码2
// break
// ...
// default:
// 代码n
// }
作用:学习时可以帮助更好的理解代码运行,工作时可以更快找到bug
浏览器打开调试界面
按F12打开开发者工具
点到源代码一栏 ( sources )
选择代码文件
断点:在某句代码上加的标记就叫断点,当程序执行到这句有标记的代码时会暂停下来
使用场景:重复执行 指定的一段代码,比如我们想要输出10次 '我学的很棒'
学习路径:
1.while循环
2.for 循环(重点)
while : 在…. 期间, 所以 while循环 就是在满足条件期间,重复执行某些代码。
语法:
while (条件表达式) { // 循环体 } 例:
// while循环: 重复执行代码
// 1. 需求: 利用循环重复打印3次 '月薪过万不是梦,毕业时候见英雄'
let i = 1
while (i <= 3) {
document.write('月薪过万不是梦,毕业时候见英雄~
')
i++ // 这里千万不要忘了变量自增否则造成死循环
}
循环三要素:
1.初始值 (经常用变量)
2.终止条件
3.变量的变化量
例:
break
中止整个循环,一般用于结果已经得到, 后续的循环不需要的时候可以使用(提高效率)
continue
中止本次循环,一般用于排除或者跳过某一个选项的时候
1.while(true) 来构造“无限”循环,需要使用break退出循环。(常用)
2.for(;;) 也可以来构造“无限”循环,同样需要使用break退出循环。
// 无限循环
// 需求: 页面会一直弹窗询问你爱我吗?
// (1). 如果用户输入的是 '爱',则退出弹窗
// (2). 否则一直弹窗询问
// 1. while(true) 无限循环
// while (true) {
// let love = prompt('你爱我吗?')
// if (love === '爱') {
// break
// }
// }
// 2. for(;;) 无限循环
for (; ;) {
let love = prompt('你爱我吗?')
if (love === '爱') {
break
}
}
掌握 for 循环语句,让程序具备重复执行能力
for
是 JavaScript 提供的另一种循环控制的话句,它和 while
只是语法上存在差异。
for语句的基本使用
实现循环的 3 要素
变化量和死循环,for
循环和 while
一样,如果不合理设置增量和终止条件,便会产生死循环。
跳出和终止循环
结论:
JavaScript
提供了多种语句来实现循环控制,但无论使用哪种语句都离不开循环的3个特征,即起始值、变化量、终止条件,做为初学者应着重体会这3个特征,不必过多纠结三种语句的区别。
起始值、变化量、终止条件,由开发者根据逻辑需要进行设计,规避死循环的发生。
当如果明确了循环的次数的时候推荐使用for
循环,当不明确循环的次数的时候推荐使用while
循环
注意:
for
的语法结构更简洁,故for
循环的使用频次会更多。
利用循环的知识来对比一个简单的天文知识,我们知道地球在自转的同时也在围绕太阳公转,如果把自转和公转都看成是循环的话,就相当于是循环中又嵌套了另一个循环。
// 1. 外面的循环 记录第n天
for (let i = 1; i < 4; i++) {
document.write(`第${i}天
`)
// 2. 里层的循环记录 几个单词
for (let j = 1; j < 6; j++) {
document.write(`记住第${j}个单词
`)
}
}
记住,外层循环循环一次,里层循环循环全部
倒三角
// 外层打印几行
for (let i = 1; i <= 5; i++) {
// 里层打印几个星星
for (let j = 1; j <= i; j++) {
document.write('★')
}
document.write('
')
}
九九乘法表
样式css
span {
display: inline-block;
width: 100px;
padding: 5px 10px;
border: 1px solid pink;
margin: 2px;
border-radius: 5px;
box-shadow: 2px 2px 2px rgba(255, 192, 203, .4);
background-color: rgba(255, 192, 203, .1);
text-align: center;
color: hotpink;
}
javascript
// 外层打印几行
for (let i = 1; i <= 9; i++) {
// 里层打印几个星星
for (let j = 1; j <= i; j++) {
// 只需要吧 ★ 换成 1 x 1 = 1
document.write(`
${j} x ${i} = ${j * i}
`)
}
document.write('
')
}
共同点
都能实现多分支选择, 多选1
大部分情况下可以互换
区别:
switch…case语句通常处理case为比较确定值的情况,而if…else…语句更加灵活,通常用于范围判断(大于,等于某个范围)。
switch 语句进行判断后直接执行到程序的语句,效率更高,而if…else语句有几种判断条件,就得判断多少次
switch 一定要注意 必须是 === 全等,一定注意 数据类型,同时注意break否则会有穿透效果
结论:
当分支比较少时,if…else语句执行效率高。
当分支比较多时,switch语句执行效率高,而且结构更清晰。
分析:
①:提示输入框写到循环里面(无限循环)
②:用户输入4则退出循环 break
③:提前准备一个金额预先存储一个数额 money
④:根据输入不同的值,做不同的操作
(1) 取钱则是减法操作, 存钱则是加法操作,查看余额则是直接显示金额
(2) 可以使用 if else if 多分支 来执行不同的操作
完整代码:
知道什么是数组及其应用的场景,掌握数组声明及访问的语法。
数组:(Array)是一种可以按顺序保存数据的数据类型
使用场景:如果有多个数据可以用数组保存起来,然后放到一个变量中,管理非常方便
数组的基本使用
定义数组和数组单元
通过 []
定义数组,数据中可以存放真正的数据,如小明、小刚、小红等这些都是数组中的数据,我们这些数据称为数组单元,数组单元之间使用英文逗号分隔。
使用数组存放数据并不是最终目的,关键是能够随时的访问到数组中的数据(单元)。其实 JavaScript 为数组中的每一个数据单元都编了号,通过数据单元在数组中的编号便可以轻松访问到数组中的数据单元了。
我们将数据单元在数组中的编号称为索引值,也有人称其为下标。
索引值实际是按着数据单元在数组中的位置依次排列的,注意是从0
开始的,如下图所示:
观察上图可以数据单元【小明】对应的索引值为【0】,数据单元【小红】对应的索引值为【2】
数组做为数据的集合,它的单元值可以是任意数据类型
重申一次,数组在 JavaScript 中并不是新的数据类型,它属于对象类型。
数组做为对象数据类型,不但有 length
属性可以使用,还提供了许多方法:
push 动态向数组的尾部添加一个单元
unshit 动态向数组头部添加一个单元
pop 删除最后一个单元
shift 删除第一个单元
splice 动态删除任意单元
使用以上4个方法时,都是直接在原数组上进行操作,即成功调任何一个方法,原数组都跟着发生相应的改变。并且在添加或删除单元时 length
并不会发生错乱。
forEach() 方法用于调用数组的每个元素,并将元素传递给回调函数
注意:
1.forEach 主要是遍历数组
2.参数当前数组元素是必须要写的, 索引号可选。
使用场景:
map 可以遍历数组处理数据,并且返回新的数组
语法:
map 也称为映射。映射是个术语,指两个元素的集之间元素相互“对应”的关系。
map重点在于有返回值,forEach没有返回值(undefined)
filter() 方法创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素
主要使用场景: 筛选数组符合条件的元素,并返回筛选之后元素的新数组
作用:join() 方法用于把数组中的所有元素转换为一个字符串
语法:
reduce 返回累积处理的结果,经常用于求和等
原理:
如果没有起始值,则上一次值为数组的第一个元素值
每一次新循环,上一次值为上次循环的返回值
如果有起始值,则起始值为上一次值
find查找元素,返回符合测试条件的 第一个 数组元素值,如果没有则返回 undefined
更多API请查看官方文档:
Array.prototype.find() - JavaScript | MDN (mozilla.org)
练习
理解封装的意义,能够通过函数的声明实现逻辑的封装,知道对象数据类型的特征,结合数学对象实现简单计算功能。
理解函数的封装的特征
掌握函数声明的语法
理解什么是函数的返回值
知道并能使用常见的内置函数
函数可以把具有相同或相似逻辑的代码“包裹”起来,通过函数调用执行这些被“包裹”的代码逻辑,这么做的优势是有利于精简代码方便复用。
声明(定义)一个完整函数包括关键字、函数名、形式参数、函数体、返回值5个部分
声明(定义)的函数必须调用才会真正被执行,使用 ()
调用函数。
JavaScript 基础 - 声明和调用
注:函数名的命名规则与变量是一致的,并且尽量保证函数名的语义。
小案例: 小星星
通过向函数传递参数,可以让函数更加灵活多变,参数可以理解成是一个变量。
声明(定义)一个功能为打招呼的函数
传入数据列表
声明这个函数需要传入几个数据
多个数据用逗号隔开
JavaScript 基础 - 函数参数
总结:
声明(定义)函数时的形参没有数量限制,当有多个形参时使用 ,
分隔
调用函数传递的实参要与形参的顺序一致
总结:
声明函数时为形参赋值即为参数的默认值
如果参数未自定义默认值时,参数的默认值为 undefined
调用函数时没有传入对应实参时,参数的默认值被当做实参传入
形参:声明函数时写在函数名右边小括号里的叫形参(形式上的参数)
实参:调用函数时写在函数名右边小括号里的叫实参(实际上的参数)
形参可以理解为是在这个函数内声明的变量(比如 num1 = 10)实参可以理解为是给这个变量赋值
开发中尽量保持形参和实参个数一致
JavaScript 基础 - 函数参数
arguments
是函数内部内置的伪数组变量,它包含了调用函数时传入的所有实参。
总结:
arguments
是一个伪数组
arguments
的作用是动态获取函数的实参
总结:
...
是语法符号,置于最末函数形参之前,用于获取多余的实参
借助 ...
获取的剩余实参,是个真数组
函数的本质是封装(包裹),函数体内的逻辑执行完毕后,函数外部如何获得函数内部的执行结果呢?要想获得函数内部逻辑的执行结果,需要通过 return
这个关键字,将内部执行结果传递到函数外部,这个被传递到外部的结果就是返回值。
JavaScript 基础 - 函数返回值
总结:
在函数体中使用return 关键字能将内部的执行结果交给函数外部使用
函数内部只能出现1 次 return,并且 return 下一行代码不会再被执行,所以return 后面的数据不要换行写
return会立即结束当前函数
函数可以没有return,这种情况默认返回值为 undefined
作用域(scope)规定了变量能够被访问的“范围”,离开了这个“范围”变量便不能被访问,作用域分为全局作用域和局部作用域。
标签和
.js
文件的【最外层】就是所谓的全局作用域,在此声明的变量在函数内部也可以被访问。
处于全局作用域内的变量,称为全局变量
全局作用域中声明的变量,任何其它作用域都可以被访问,如下代码所示:
总结:
为 window
对象动态添加的属性默认也是全局的,不推荐!
函数中未使用任何关键字声明的变量为全局变量,不推荐!!!
尽可能少的声明全局变量,防止全局变量被污染
JavaScript 中的作用域是程序被执行时的底层机制,了解这一机制有助于规范代码书写习惯,避免因作用域导致的语法错误。
作用于函数内的代码环境,就是局部作用域。 因为跟函数有关系,所以也称为函数作用域。
处于局部作用域内的变量称为局部变量
在函数内部声明的变量只能在函数内部被访问,外部无法直接访问。
不同函数内部声明的变量无法互相访问
函数执行完毕后,函数内部的变量实际被清空了
在 JavaScript 中使用 {}
包裹的代码称为代码块,代码块内部声明的变量外部将【有可能】无法被访问。
JavaScript 中除了变量外还有常量,常量与变量本质的区别是【常量必须要有值且不允许被重新赋值】,常量值为对象时其属性和方法允许重新赋值。
总结:
let
声明的变量会产生块作用域,var
不会产生块作用域
const
声明的常量也会产生块作用域
不同代码块之间的变量无法互相访问
推荐使用 let
或 const
注:开发中 let
和 const
经常不加区分的使用,如果担心某个值会不小被修改时,则只能使用 const
声明成常量。
在解释什么是作用域链前先来看一段代码:
函数内部允许创建新的函数,f
函数内部创建的新函数 g
,会产生新的函数作用域,由此可知作用域产生了嵌套的关系。
如下图所示,父子关系的作用域关联在一起形成了链状的结构,作用域链的名字也由此而来。
作用域链本质上是底层的变量查找机制,在函数被执行时,会优先查找当前函数作用域中查找变量,如果当前作用域查找不到则会依次逐级查找父级作用域直到全局作用域,如下代码所示:
总结:
嵌套关系的作用域串联起来形成了作用域链
相同作用域链中按着从小到大的规则查找变量
子作用域能够访问父作用域,父级作用域无法访问子级作用域
函数可以分为具名函数和匿名函数
匿名函数:没有名字的函数,无法直接使用。
// 声明
let fn = function() {
console.log('函数表达式')
}
// 调用
fn()
(function(){ xxx })();
(function(){xxxx}());
无需调用,立即执行,其实本质已经调用了
多个立即执行函数之间用分号隔开
闭包是一种比较特殊的函数,使用闭包能够访问函数作用域中的变量。从代码形式上看闭包是一个做为返回值的函数,如下代码所示:
总结:
1.怎么理解闭包?
闭包 = 内层函数 + 外层函数的变量
2.闭包的作用?
封闭数据,实现数据私有,外部也可以访问函数内部的变量
闭包很有用,因为它允许将函数与其所操作的某些数据(环境)关联起来
3.闭包可能引起的问题?
内存泄漏
变量提升是 JavaScript 中比较“奇怪”的现象,它允许在变量声明之前即被访问
总结:
变量在未声明即被访问时会报语法错误
变量在声明之前即被访问,变量的值为 undefined
let
声明的变量不存在变量提升,推荐使用 let
变量提升出现在相同作用域当中
实际开发中推荐先声明再访问变量
注:关于变量提升的原理分析会涉及较为复杂的词法分析等知识,而开发中使用 let
可以轻松规避变量的提升,因此在此不做过多的探讨,有兴趣可查阅资料。
函数提升与变量提升比较类似,是指函数在声明之前即可被调用。
总结:
函数提升能够使函数的声明调用更灵活
函数表达式不存在提升的现象
函数提升出现在相同作用域当中
箭头函数是一种声明函数的简洁语法,它与普通函数并无本质的区别,差异性更多体现在语法格式上。
总结:
箭头函数属于表达式函数,因此不存在函数提升
箭头函数只有一个参数时可以省略圆括号 ()
箭头函数函数体只有一行代码时可以省略花括号 {}
,并自动做为返回值被返回
箭头函数中没有 arguments
,只能使用 ...
动态获取实参
箭头函数不会创建自己的this,它只会从自己的作用域链的上一层沿用this。
解构赋值是一种快速为变量赋值的简洁语法,本质上仍然是为变量赋值,分为数组解构、对象解构两大类型。
数组解构是将数组的单元值快速批量赋值给一系列变量的简洁语法,如下代码所示:
总结:
赋值运算符 =
左侧的 []
用于批量声明变量,右侧数组的单元值将被赋值给左侧的变量
变量的顺序对应数组单元值的位置依次进行赋值操作
变量的数量大于单元值数量时,多余的变量将被赋值为 undefined
变量的数量小于单元值数量时,可以通过 ...
获取剩余单元值,但只能置于最末位
允许初始化变量的默认值,且只有单元值为 undefined
时默认值才会生效
立即执行函数 (function(){})() 跟 数组解构 都需要在前面加上分号,避免跟上一行没加分号代码粘连
支持多维解构赋值,比较复杂后续有应用需求时再进一步分析
例:
对象解构是将对象属性和方法快速批量赋值给一系列变量的简洁语法,如下代码所示:
总结:
赋值运算符 =
左侧的 {}
用于批量声明变量,右侧对象的属性值将被赋值给左侧的变量
对象属性的值将被赋值给与属性名相同的变量名
对象中找不到与变量名一致的属性时变量值为 undefined
允许初始化变量的默认值,属性不存在或单元值为 undefined
时默认值才会生效
支持多维对象解构赋值,如下所示
对象是 JavaScript 数据类型的一种,之前已经学习了数值类型、字符串类型、布尔类型、undefined。对象数据类型可以被理解成是一种数据集合。它由属性和方法两部分构成。本节需知道对象数据类型的特征,能够利用数组对象渲染页面
理解什么是对象,掌握定义对象的语法
掌握数学对象的使用
声明一个对象类型的变量与之前声明一个数值或字符串类型的变量没有本质上的区别。
JavaScript 基础 - 对象语法
数据描述性的信息称为属性,如人的姓名、身高、年龄、性别等,一般是名词性的。
属性都是成 对出现的,包括属性名和值,它们之间使用英文 :
分隔
多个属性之间使用英文 ,
分隔
属性就是依附在对象上的变量
属性名可以使用 ""
或 ''
,一般情况下省略,除非名称遇到特殊符号如空格、中横线等
JavaScript 基础 - 对象语法
声明对象,并添加了若干属性后,可以使用 .
或 []
获得对象中属性对应的值,我称之为属性访问。
JavaScript 基础 - 对象语法
扩展:也可以动态为对象添加属性,动态添加与直接定义是一样的,只是语法上更灵活。
JavaScript 基础 - 对象语法
数据行为性的信息称为方法,如跑步、唱歌等,一般是动词性的,其本质是函数。
方法是由方法名和函数两部分构成,它们之间使用 : 分隔
多个属性之间使用英文 ,
分隔
方法是依附在对象中的函数
方法名可以使用 ""
或 ''
,一般情况下省略,除非名称遇到特殊符号如空格、中横线等
JavaScript 基础 - 对象方法
声明对象,并添加了若干方法后,可以使用 .
或 []
调用对象中函数,我称之为方法调用。
JavaScript 基础 - 对象方法
扩展:也可以动态为对象添加方法,动态添加与直接定义是一样的,只是语法上更灵活。
JavaScript 基础 - 对象方法
注:无论是属性或是方法,同一个对象中出现名称一样的,后面的会覆盖前面的。
null 也是 JavaScript 中数据类型的一种,通常只用它来表示不存在的对象。使用 typeof 检测类型它的类型时,结果为 object
。
let obj = {
uname: 'pink'
}
for(let k in obj) {
// k 属性名 字符串 带引号 obj.'uname' k === 'uname'
// obj[k] 属性值 obj['uname'] obj[k]
}
for in 不提倡遍历数组 因为 k 是 字符串
构造函数是专门用于创建对象的函数,如果一个函数使用 new
关键字调用,那么这个函数就是构造函数。
总结:
使用 new
关键字调用函数的行为被称为实例化,可用来快速创建多个类似对象
实例化构造函数时没有参数时可以省略 ()
构造函数的返回值即为新创建的对象
构造函数内部的 return
返回的值无效!
注:实践中为了从视觉上区分构造函数和普通函数,习惯将构造函数的首字母大写。
通过构造函数创建的对象称为实例对象,实例对象中的属性和方法称为实例成员。
总结:
构造函数内部 this
实际上就是实例对象,为其动态添加的属性和方法即为实例成员
为构造函数传入参数,动态创建结构相同但值不同的对象
注:构造函数创建的实例对象彼此独立互不影响。
在 JavaScript 中底层函数本质上也是对象类型,因此允许直接为函数动态添加属性或方法,构造函数的属性和方法被称为静态成员。
总结:
静态成员指的是添加到构造函数本身的属性和方法
一般公共特征的属性或方法静态成员设置为静态成员
静态成员方法中的 this
指向构造函数本身
回想一下我们曾经使用过的 console.log
,console
其实就是 JavaScript 中内置的对象,该对象中存在一个方法叫 log
,然后调用 log
这个方法,即 console.log()
。
除了 console
对象外,JavaScritp 还有其它的内置的对象
在 JavaScript 中的字符串、数值、布尔具有对象的使用特征,如具有属性和方法,例:
之所以具有对象特征的原因是字符串、数值、布尔类型数据是 JavaScript 底层使用 Object 构造函数“包装”来的,被称为包装类型。
String
是内置的构造函数,用于创建字符串。
总结:
实例属性 length
用来获取字符串的度长(重点)
实例方法 split('分隔符')
用来将字符串拆分成数组(重点)
实例方法 substring(需要截取的第一个字符的索引[,结束的索引号])
用于字符串截取(重点)
实例方法 startsWith(检测字符串[, 检测位置索引号])
检测是否以某字符开头(重点)
实例方法 includes(搜索的字符串[, 检测位置索引号])
判断一个字符串是否包含在另一个字符串中,根据情况返回 true 或 false(重点)
实例方法 toUpperCase
用于将字母转换成大写
实例方法 toLowerCase
用于将就转换成小写
实例方法 indexOf
检测是否包含某字符
实例方法 endsWith
检测是否以某字符结尾
实例方法 replace
用于替换字符串,支持正则匹配
实例方法 match
用于查找字符串,支持正则匹配
注:String 也可以当做普通函数使用,这时它的作用是强制转换成字符串数据类型。
Number
是内置的构造函数,用于创建数值。
总结:
推荐使用字面量方式声明数值,而不是 Number
构造函数
实例方法 toFixed
用于设置保留小数位的长度
Math
是 JavaScript 中内置的对象,称为数学对象,这个对象下即包含了属性,也包含了许多的方法。
属性
Math.PI,获取圆周率
// 圆周率
console.log(Math.PI);
方法
Math.random,生成 0 到 1 间的随机数
// 0 ~ 1 之间的随机数, 包含 0 不包含 1
Math.random()
// 生成 N~M 之间的随机数
Math.floor(Math.random() * (M - N + 1)) + N
Math.ceil,数字向上取整
// 舍弃小数部分,整数部分加1
Math.ceil(3.4)
Math.floor,数字向下取整
// 舍弃小数部分,整数部分不变
Math.floor(4.68)
// 找到数组中随机的一个字符
let arr = ['red', 'green', 'blue']
Math.floor(Math.random() * arr.length)
Math.round,四舍五入取整
// 取整,四舍五入原则
Math.round(5.46539)
Math.round(4.849)
Math.max,在一组数中找出最大的
// 找出最大值
Math.max(10, 21, 7, 24, 13)
Math.min,在一组数中找出最小的
// 找出最小值
Math.min(24, 18, 6, 19, 21)
Math.pow,幂方法
// 求某个数的多少次方
Math.pow(4, 2) // 求 4 的 2 次方
Math.pow(2, 3) // 求 2 的 3 次方
Math.sqrt,平方根
// 求某数的平方根
Math.sqrt(16)
小例子:随机点名系统
let arr = ['赵云','马超', '张飞', '黄忠', '关羽']
let random = Math.floor(Math.random() * arr.length)
document.write(arr[random])
数学对象提供了比较多的方法,这里不要求强记,通过演示数学对象的使用,加深对对象的理解。
掌握 Date 日期对象的使用,动态获取当前计算机的时间。
ECMAScript 中内置了获取系统时间的对象 Date,使用 Date 时与之前学习的内置对象 console 和 Math 不同,它需要借助 new 关键字才能使用。
// 1. 实例化
// const date = new Date(); // 系统默认时间
const date = new Date('2020-05-01') // 指定时间
// date 变量即所谓的时间对象
console.log(typeof date)
// 1. 实例化
const date = new Date();
// 2. 调用时间对象方法
// 通过方法分别获取年、月、日,时、分、秒
const year = date.getFullYear(); // 四位年份
const month = date.getMonth(); // 0 ~ 11
getFullYear 获取四位年份
getMonth 获取月份,取值为 0 ~ 11
getDate 获取月份中的每一天,不同月份取值也不相同
getDay 获取星期,取值为 0 ~ 6
getHours 获取小时,取值为 0 ~ 23
getMinutes 获取分钟,取值为 0 ~ 59
getSeconds 获取秒,取值为 0 ~ 59
时间戳是指1970年01月01日00时00分00秒起至现在的总秒数或毫秒数,它是一种特殊的计量时间的方式。
注:ECMAScript 中时间戳是以毫秒计的。
// 1. 实例化
const date = new Date()
// 2. 获取时间戳
console.log(date.getTime())
// 还有一种获取时间戳的方法
console.log(+new Date())
// 还有一种获取时间戳的方法
console.log(Date.now())
获取时间戳的方法,分别为 getTime 和 Date.now 和 +new Date()
BOM (Browser Object Model ) 是浏览器对象模型
window对象是一个全局对象,也可以说是JavaScript中的顶级对象
像document、alert()、console.log()这些都是window的属性,基本BOM的属性和方法都是window的
所有通过var定义在全局作用域中的变量、函数都会变成window对象的属性和方法
window对象下的属性和方法调用的时候可以省略window
语法规范:window.setTimeout(调用函数,延时时间)
setTimeout() 在调用时,window、延时时间都可以省略,延时时间单位为毫秒,省略的话默认为0
如果有很多定时器的话,可以给定时器加标示符 let timer = setTimeout(function(){},1000)
setTimeout() 也称为回调函数 callback,需要等到时间到了才去调用这个函数
(回调:回头调用,指上一件事干完在回头调用这个函数)
以前讲的 element.onclick = function(){} 或 element.addEventListener('click', fn) 里面的函数也是回调函数
语法规范:window.setInterval(回调函数,间隔时间),注意事项跟 setTimeout 一样
location (地址) 它拆分并保存了 URL 地址的各个组成部分, 它是一个对象
属性/方法 | 说明 |
---|---|
href | 属性,获取完整的 URL 地址,赋值时用于地址的跳转 |
search | 属性,获取地址中携带的参数,符号 ?后面部分 |
hash | 属性,获取地址中的啥希值,符号 # 后面部分 |
reload() | 方法,用来刷新当前页面,传入参数 true 时表示强制刷新 |
navigator是对象,该对象下记录了浏览器自身的相关信息
常用属性和方法:
通过 userAgent 检测浏览器的版本及平台(F12,切换成移动手机模式)
// 检测 userAgent(浏览器信息)
(function () {
const userAgent = navigator.userAgent
// 验证是否为Android或iPhone
const android = userAgent.match(/(Android);?[\s\/]+([\d.]+)?/)
const iphone = userAgent.match(/(iPhone\sOS)\s([\d_]+)/)
// 如果是Android或iPhone,则跳转至移动站点
if (android || iphone) {
location.href = 'http://m.itcast.cn'
}})();
history (历史)是对象,主要管理历史记录, 该对象与浏览器地址栏的操作相对应,如前进、后退等
使用场景
history对象一般在实际开发中比较少用,但是会在一些OA 办公系统中见到。
本地存储:将数据存储在本地浏览器中
常见的使用场景:
Vanilla ES6 • TodoMVC 页面刷新数据不丢失
好处:
1、页面刷新或者关闭不丢失数据,实现数据持久化
2、容量较大,sessionStorage和 localStorage 约 5M 左右
作用: 数据可以长期保留在本地浏览器中,刷新页面和关闭页面,数据也不会丢失
特性:**以键值对的形式存储,并且存储的是字符串, 省略了window,多窗口(页面)共享**(同一浏览器可以共享)
本地存储-localstorage
特性:
用法跟localStorage基本相同,在同一窗口(页面)下数据可以共享,以键值对形式存储使用
区别是:当页面浏览器被关闭时,存储在 sessionStorage 的数据会被清除(声明周期为关闭浏览器)
存储:sessionStorage.setItem(key,value)
获取:sessionStorage.getItem(key)
删除:sessionStorage.removeItem(key)
删除所有数据:sessionStorage.clear()
问题:本地只能存储字符串,无法存储复杂数据类型.
解决:需要将复杂数据类型转换成 JSON字符串,在存储到本地
语法:JSON.stringify(复杂数据类型)
JSON字符串:
首先是1个字符串
属性名使用双引号引起来,不能单引号
属性值如果是字符串型也必须双引号
问题:因为本地存储里面取出来的是字符串,不是对象,无法直接使用
解决: 把取出来的字符串转换为对象
语法:JSON.parse(JSON字符串)
学习 JavaScript 中基于原型的面向对象编程序的语法实现,理解面向对象编程的特征。
面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候再一个一个的依次
调用就可以了。
面向对象是把事务分解成为一个个对象,然后由对象之间分工与合作。
在面向对象程序开发思想中,每一个对象都是功能中心,具有明确分工。
面向对象编程具有灵活、代码可复用、容易维护和开发的优点,更适合多人合作的大型软件项目。
面向对象的特性:
封装性
继承性
多态性
对比以下通过面向对象的构造函数实现的封装:
封装是面向对象思想中比较重要的一部分,js面向对象可以通过构造函数实现的封装。
同样的将变量和函数组合到了一起并能通过 this 实现数据的共享,所不同的是借助构造函数创建出来的实例对象之间是彼此不影响的
总结:
构造函数体现了面向对象的封装特性
构造函数实例创建的对象彼此独立、互不影响
封装是面向对象思想中比较重要的一部分,js面向对象可以通过构造函数实现的封装。
前面我们学过的构造函数方法很好用,但是存在内存浪费的问题
构造函数通过原型分配的函数是所有对象所 共享的。
JavaScript 规定,每一个构造函数都有一个 prototype 属性,指向另一个对象,所以我们也称为原型对象
这个对象可以挂载函数,对象实例化不会多次创建原型上函数,节约内存
我们可以把那些不变的方法,直接定义在 prototype 对象上,这样所有对象的实例就可以共享这些方法。
构造函数和原型对象中的this 都指向 实例化的对象
了解了 JavaScript 中构造函数与原型对象的关系后,再来看原型对象具体的作用,如下代码所示:
构造函数 Person
中未定义任何方法,这时实例对象调用了原型对象中的方法 sayHi
,接下来改动一下代码:
构造函数 Person
中定义与原型对象中相同名称的方法,这时实例对象调用则是构造函中的方法 sayHi
。
通过以上两个简单示例不难发现 JavaScript 中对象的工作机制:当访问对象的属性或方法时,先在当前实例对象是查找,然后再去原型对象查找,并且原型对象被所有实例共享。
总结:结合构造函数原型的特征,实际开发重往往会将封装的功能函数添加到原型对象中。
在哪里? 每个原型对象里面都有个constructor 属性(constructor 构造函数)
作用:该属性指向该原型对象的构造函数, 简单理解,就是指向我的爸爸,我是有爸爸的孩子
使用场景:
如果有多个对象的方法,我们可以给原型对象采取对象形式赋值.
但是这样就会覆盖构造函数原型对象原来的内容,这样修改后的原型对象 constructor 就不再指向当前构造函数了
此时,我们可以在修改后的原型对象中,添加一个 constructor 指向原来的构造函数。
对象都会有一个属性 proto 指向构造函数的 prototype 原型对象,之所以我们对象可以使用构造函数 prototype
原型对象的属性和方法,就是因为对象有 proto 原型的存在。
注意:
proto 是JS非标准属性
[[prototype]]和proto意义相同
用来表明当前实例对象指向哪个原型对象prototype
proto对象原型里面也有一个 constructor属性,指向创建该实例对象的构造函数
继承是面向对象编程的另一个特征,通过继承进一步提升代码封装的程度,JavaScript 中大多是借助原型对象实现继承
的特性。
龙生龙、凤生凤、老鼠的儿子会打洞描述的正是继承的含义。
基于原型对象的继承使得不同构造函数的原型对象关联在一起,并且这种关联的关系是一种链状的结构,我们将原型对象的链状结构关系称为原型链
① 当访问一个对象的属性(包括方法)时,首先查找这个对象自身有没有该属性。
② 如果没有就查找它的原型(也就是 proto指向的 prototype 原型对象)
③ 如果还没有就查找原型对象的原型(Object的原型对象)
④ 依此类推一直找到 Object 为止(null)
⑤ proto对象原型的意义就在于为对象成员查找机制提供一个方向,或者说一条路线
⑥ 可以使用 instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上
了解 DOM 的结构并掌握其基本的操作,体验 DOM 的在开发中的作用
知道 ECMAScript 与 JavaScript 的关系,Web APIs 是浏览器扩展的功能。
知道 ECMAScript 与 JavaScript 的关系
了解 DOM 的相关概念及DOM 的本质是一个对象
掌握查找节点的基本方法
掌握节点属性和文本的操作
能够使用间歇函数创建定时任务
严格意义上讲,我们在 JavaScript 阶段学习的知识绝大部分属于 ECMAScript 的知识体系,ECMAScript 简称 ES 它提供了一套语言标准规范,如变量、数据类型、表达式、语句、函数等语法规则都是由 ECMAScript 规定的。浏览器将 ECMAScript 大部分的规范加以实现,并且在此基础上又扩展一些实用的功能,这些被扩展出来的内容我们称为 Web APIs。
ECMAScript 运行在浏览器中然后再结合 Web APIs 才是真正的 JavaScript,Web APIs 的核心是 DOM 和 BOM。
扩展阅读:ECMAScript 规范在不断的更新中,存在多个不同的版本,早期的版本号采用数字顺序编号如 ECMAScript3、ECMAScript5,后来由于更新速度较快便采用年份做为版本号,如 ECMAScript2017、ECMAScript2018 这种格式,ECMAScript6 是 2015 年发布的,常叫做 EMCAScript2015。
关于 JavaScript 历史的扩展阅读。
知道 DOM 相关的概念,建立对 DOM 的初步认识,学习 DOM 的基本操作,体会 DOM 的作用
DOM(Document Object Model)是将整个 HTML 文档的每一个标签元素视为一个对象,这个对象下包含了许多的属性和方法,通过操作这些属性或者调用这些方法实现对 HTML 的动态更新,为实现网页特效以及用户交互提供技术支撑。
简言之 DOM 是用来动态修改 HTML 的,其目的是开发网页特效及用户交互。
观察一个小例子:
上述的例子中当用户分分别点击【开始】或【结束】按钮后,通过右侧调试窗口可以观察到 html 标签的内容在不断的发生改变,这便是通过 DOM 实现的。
概念
DOM 树
标题
文本
链接名
文本
如下图所示,将 HTML 文档以树状结构直观的表现出来,我们称之为文档树或 DOM 树,文档树直观的体现了标签与标签之间的关系。
节点是文档树的组成部分,每一个节点都是一个 DOM 对象,主要分为元素节点、属性节点、文本节点等。
【元素节点】其实就是 HTML 标签,如上图中 head
、div
、body
等都属于元素节点。
【属性节点】是指 HTML 标签中的属性,如上图中 a
标签的 href
属性、div
标签的 class
属性。
【文本节点】是指 HTML 标签的文字内容,如 title
标签中的文字。
【根节点】特指 html
标签。
其它...
document
是 JavaScript 内置的专门用于 DOM 的对象,该对象包含了若干的属性和方法,document
是学习 DOM 的核心。
上述列举了 document
对象的部分属性和方法,我们先对 document
有一个整体的认识。
getElementById()
:通过 id 属性获取一个元素节点对象
getElementsByClassName()
:通过 class 属性获取一个元素节点对象
getElementsByTagName()
:通过标签名获取一组元素节点对象
getElementsByTagName("body")[0]
:获取 body 标签,等价于:document.body
getElementsByTagName("*")
:获取页面所有元素
querySelector()
: 满足条件的第一个元素
querySelectorAll()
: 满足条件的元素集合 返回伪数组
查找元素类型节点
从整个 DOM 树中查找 DOM 节点是学习 DOM 的第一个步骤。
- 元素1
- 元素2
- 元素3
- 元素4
通过修改 DOM 的文本内容,动态改变网页的内容。
innerText
: 将文本内容添加/更新到任意标签位置,文本中包含的标签不会被解析。
innerHTML
:将文本内容添加/更新到任意标签位置,文本中包含的标签会被解析。
- AAA
- 元素2
- 元素3
- 元素4
总结:如果文本内容中包含 html
标签时推荐使用 innerHTML
,否则建议使用 innerText
属性。
getElementsByTagName()
:返回当前节点的指定标签名后代节点
childNodes
:表示当前节点的所有子节点,如果子节点的话可以用:当前节点.children
firstChild
:表示当前节点的第一个子节点
lastChild
:表示当前节点的最后一个子节点
nextElementSibling
:表示当前节点的下一个兄弟节点
- 元素1
- 元素2
- 元素3
- 元素4
createElement
:创建元素节点对象
createTextnode
:创建文本节点对象
appendChild
:把新的子节点添加到指定节点
- 元素1
- 元素2
- 元素3
- 元素4
父.insertBefore(新,旧)
:把新的节点插入到指定节点
- 元素1
- 元素2
- 元素3
- 元素4
- 元素1
- 元素2
- 元素3
- 元素4
父.replaceChild(新,旧)
:将旧节点替换为新节点
- 元素1
- 元素2
- 元素3
- 元素4
父.removeChild(子)
:删除子节点,但这种得找父节点,不方便,下面这种比较常用
子.parentNode.removeChild(子)
- 元素1
- 元素2
- 元素3
- 元素4
document.write 是直接将内容写入页面的内容流,但是文档流执行完毕,则它会导致页面全部重绘
innerHTML
是将内容写入某个 DOM 节点,不会导致页面全部重绘
创建多个元素效率更高(不要拼按字符串,采取数组形式拼接)结构稍微复杂
createElement()创建多个元素效率稍低一点点,但是结构更清晰
元素.属性 = 新值
:直接能过属性名修改,最简洁的语法
方式一:元素.style.样式名
方式二:getComputedStyle(元素).样式名
(该方法不支持IE8以下浏览器)
方法三:元素.currentStyle.样式名
(仅支持IE8浏览器)
随便添加一些文字