『web前端开发』javascript学习
本文为个人在学习JS过程中记录的笔记,主要基于 黑马程序员前端JavaScript入门到精通 视频和 JavaScript | 菜鸟教程 进行学习。
目前暂时学习完视频P99,后续有机会再进行学习更新
基本概念
定义:javascript是一种执行在客户端的脚本语言,不需要编译,在运行过程中由js解释器(js引擎)来逐行进行解释并执行
- 常见的js引擎,如chrome使用的v8引擎等
- 虽然javascript最初是用于前端开发,但是现在已经可以用于多种开发,如node.js就是内置了v8引擎,使得js脚本可以在浏览器之外的环境下运行,用于编写服务端代码等。
JS 的作用:
表单动态校验 (密码强度检测) ( JS 产生最初的目的)
比如一个表单字段的输入并不符合规范,如果需要发给服务端,服务端收到之后判断发现并不合规,再返回就会产生比较大的延迟
网页特效
服务端开发(Node.js)
桌面程序(Electron)
App(Cordova)
控制硬件-物联网(Ruff)
游戏开发(cocos2d-js)
浏览器如何执行JS
浏览器分成两部分:渲染引擎和JS引擎
- 渲染引擎(内核):用来解析HTML与CSS ,比如chrome浏览器的blink ,老版本的webkit
- JS引擎(JS解释器):用来读取网页中的JS代码,逐行解释源码并将其转化为机器语言由计算机执行。比如chrome浏览器的V8
JS的三部分组成
- 核心(ECMAScript):即JS语法
- Web APIs:即通过JS去操作html和浏览器
- 文档对象模型(Document Object Model,简称DOM) :通过DOM提供的接口可以对页面上的各种元素进行操作(大小、位置、颜色等)。
- 浏览器对象模型(Browser Object Model,简称BOM):提供了独立于内容的、可以与浏览器窗进行互动的对象结构。通过BOM可以操作浏览器窗口,比如弹出框、控制浏览器跳转、获取分辨率等。
JS的书写位置
(1)内部 javascript
Javascript 脚本代码可被放置在 HTML 页面的 <body>
和 <head>
部分中。
写在
<head>
部分中的js脚本在页面内容加载之前被加载和执行- 这对于需要在页面加载之前进行的一些初始化操作非常有用,例如预加载某些数据或设置全局变量。
- 由于脚本在页面内容加载之前执行,如果脚本执行时间较长,可能会导致页面加载变慢,影响用户体验。
- 如果脚本需要操作 DOM 元素,而这些元素尚未加载完成,可能会导致错误。
写在
<body>
部分的js脚本在页面内容加载完成后执行可以直接写到元素的内部,不需要使用script标签
1
<input type="button" value="唐伯虎" onclik="alert('秋香')">
或者写在body的尾部,使用
script
标签所有 DOM 元素都已经可以被操作,从而避免了在操作 DOM 时遇到的错误。
可以在页面加载后执行一些需要立即生效的脚本,例如初始化页面内容或绑定事件处理程序。
在
<body>
部分放置大量脚本代码可能会导致 HTML 文件过于冗长,影响代码的可读性和可维护性。
如果脚本不依赖于页面内容,可以放在
<head>
部分,以便尽早加载和执行。如果脚本需要操作页面中的 DOM 元素,通常放在
<body>
部分,或者放在<body>
底部,以确保页面内容已经完全加载。
(2)外部 javascript
可以引入外部 JavaScript 文件,外部文件通常包含被多个网页使用的代码。可以实现 HTML 和 JavaScript 的分离,提高代码的可读性和可维护性。使用 <script>
标签中的 src 属性来标注 js 文件的位置
1 | //注意使用外部加载js的时候,script标签中间不能写代码 |
- JS代码执行顺序
- 按HTML文档流顺序执行JavaScript代码
- alert()和prompt()它们会跳过页面渲染先被执行
基础语法
输入和输出
输出语法
(1)document.write
向body内输出内容,如果输出的内容为标签,会被解析成网页元素
1
2
3
4<script>
document.write('我是div标签')
document.write('<h1>我是标题</h1>')
</script>(2)alert
页面弹出警告窗
1
2
3<script>
window.alert(5 + 6)
</script>(3)console.log
控制台输出(页面中并没有效果),用于程序员的调试
1
2
3<script>
console.log('aaaa')
</script>输入语法
(1)prompt
显示一个对话框,对话框中包含一条文字信息,用来提示用户输入文字
1
2
3<script>
prompt('输入你的年龄:')
</script>
变量和常量
变量
初始化(声明+赋值)
1
let age = 18,username = 'isoda'
注意一条语句中声明的多个变量不可以同时赋同一个值:
1
2//这种声明方法是错误的,定义了x,y为undefined,z为1。
let x,y,z=1旧版本使用
var
来定义变量,目前的版本都使用let
来进行定义var 相比 let 存在很多的缺点:
- 可以先使用在声明(打印出来为undefined),不合理
- var声明过的变量可以多次重复声明,不合理
- 比如变量提升、全局变量、没有块级作用域等等
常量
声明的时候必须初始化,且不允许重新赋值
1
2
3const PI = 3.141592653589793;
PI = 3.14; // 报错
PI = PI + 10; // 报错
在开发的时候:
- 基本数据类型:推荐先定义数据为常量const,后续发现这个数据需要修改再改为变量let
- 对象数据类型:定义对象为const
- 仍然可以追加对象中的元素,因为对象数据类型存储的是指向对象的地址,虽然对象变了但是地址没有改变
- 不能将其指向新的对象,这样地址就产生了改变
1
2
3 const arr = ['red','blue']
arr.push('blue') //没有报错
arr = [1,2,3] //产生报错
数据类型
数据类型分类
主要可以分成两大类
- **值类型(基本类型)**:字符串(String)、数字(Number)、布尔(Boolean)、空(Null)、未定义(Undefined)、Symbol。
- 引用数据类型(对象类型):对象(Object)、数组(Array)、函数(Function),还有两个特殊的对象:正则(RegExp)和日期(Date)。
这两种类型的区别可见编程基础.计算机基础知识.堆栈
基本数据类型
1)字符串
1
2let answer = "He is called 'Johnny'";
let answer = 'He is called "Johnny"';字符串拼接:通过
+
运算符,或者反引号1
2
3
4
5
6
7//1. 通过 + 运算符
let age = 15
document.write("我今年"+age+"岁了")
//2. 通过 `` 号
let age = 15
document.write(`我今年${age}岁了`)
2)数字
1
2
3let x1 = 34.00 //使用小数点来写
let x2 = 34 //不使用小数点来写
let y = 123e5 // 123000003)布尔
1
2let x = true
let y = false4)空(null)
null表示赋值了,但是内容为空。一般作为尚未创建的对象
1
2let x = null
console.log(x)注意 undefined 和 null 的区别
null表示空,加上1的话就表示1了
1
2console,log(undefined + 1) //NaN
console.log(null+1) //15)未定义(Undefined)
声明了的对象未赋值,打印出来就是Undefined
1
2let x
console.log(x)6)数组
javascript中列表和数组是一个概念, 没有区别
1
2
3
4
5
6
7let cars = ["Saab","Volvo","BMW"]
let myCars = new Array("Saab","Volvo","BMW");
let myCars=new Array();
// 数组的操作
console.log(cars[0],cars[2])
console.log(cars.length)数组添加新数据
1
2
3
4
5// push()方法将一个或多个元素添加到数组末尾,返回数组的新长度
length = arr.push(a,b,c)
// unshift()方法将一个或多个元素添加到数组开头,返回数组的新长度
length = arr.unshift(a,b,c)数组删除数据
1
2
3
4
5
6
7
8
9// pop()方法从数组中删除最后一个元素,并返回该元素的值
delete_data = arr.pop()
// shift()方法从数组中删除第一个元素,并返回该元素的值
delete_data = arr.shift()
// splice()方法从start位置开始删除掉deletCount个元素
arr.splice(0,1)
arr.aplice(1,3)
检测数据类型
1
typeof x
数据类型转换
为什么需要数据类型转换:
表单、prompt获取到的数据默认是字符串类型的,当做数字类型等会产生错误
1
console.log('10000'+'2000') // 输出结果 100002000
隐式转换
某些运算符被执行时,系统内部自动将数据类型进行转换,这种转换称为隐式转换。
+
号两边只要有一个是字符串,都会把另外一个转成字符串- 除了
+
以外的算术运算符,比如- * /
等都会把数据转成数字类型 +
号作为正号解析可以转换成数字型- 任何数据和字符串相加,都视作字符串拼接,结果都是字符串
1
2
3
4
5
6console.log(1 + 1) //2
console. log('pink' + 1) //pink1
console.log(2 + '2') //22
console.1og(2 - 2) //0
console.1og(2 - '2') //0
console.1og(+'123') //可以看作正负号,将字符串转化为数字型显示转换
编写程序时过度依靠系统内部的隐式转换是不严禁的,因为隐式转换规律并不清晰,大多是靠经验总结的规律。为了避免因隐式转换带来的问题,通常需要对数据进行显示转换。
即:自己写代码告诉系统该转成什么类型
常见的数据类型转换方法
1)字符串转换数字
Number()
1
2
3
4Number("3.14") // 返回 3.14
Number(" ") // 空字符串返回 0
Number("") // 空字符串返回 0
Number("12px") // 其他非纯数字字符串返回 NaNparseInt():只保留整数部分
注意parseInt()和parseFloat()中字符串的开头必须是数字,不然都会返回NaN
1
2parseInt("12px") // 返回12
parseInt("12.13") // 返回12注意这里和上面Number处理非纯数字字符串的区别,这里会直接过滤掉字符部分
parseFloat():保留小数
1
2parseFloat("12px") // 返回12
parseFloat("12.13px") // 返回12.13
2)数字转换字符串
1
2
3
4
5
6
7
8
9// 使用String
String(x) // 将变量 x 转换为字符串并返回
String(123) // 将数字 123 转换为字符串并返回
String(100 + 23) // 将数字表达式转换为字符串并返回
// 使用toString()
x.toString()
(123).toString()
(100 + 23).toString()3)转换为布尔值
1
Boolean(a)
数据类型存储方式
基本数据类型的存储:存储的是数据的值
1
2
3
4let num1 = 10
let num2 = num1
num = 20
console.log(num1) //输出为10对象数据类型的存储:存储的是地址
1
2
3
4
5
6let obj1 = {
age:18
}
let obj2 = obj1
obj2.age = 20
console.log(obj1.age) //输出为20因为obj2中存储的是和obj1相同的地址,所以修改obj2映射到堆中的对象值后,obj1也是映射到这个地址,所以obj1的结果也产生了改变
运算符
运算符的优先级
===
判断值和类型是否完全相同,返回值为 boolean1
2
3// 因为 === 优先级高,先计算得到值相同类型不同,返回false
let c = 2 === "2"
console.log(c) // 输出为false
逻辑中断
1)原理:当有多个表达式(值)时,左边表达式的值可以确定时,就不再继续运算右边表达式的值。
0
、" " (空字符串)
、' '(空字符串)
、null(空值)
、undefined(未定义)
、NaN(非数值)
都表示为false
,除这些之外的为true。例如:这里因为123为真,所以直接返回123,右边的num++并没有执行
1
2
3var num = 0;
document.write(123 || num++); //逻辑中断,返回值123,之后的代码不再继续运行
document.write(num); //num的值仍为02)逻辑与
- 如果第一个表达式的值为真,则返回表达式2
- 如果第一个表达式的值为假,则返回表达式1
1
2
3
4
5
6console.log(false && 20) //false
console.log(5<3 && 20) //false
console.log(undefined && 20) //undefined
console.log(null && 20) //null
console.log(0 && 20) //0,因为0是看作假的
console.log(10 && 20) //203)逻辑或
- 如果第一个表达式的值为真,则返回表达式1
- 如果第一个表达式的值为假,则返回表达式2
1
2
3console.log( 123 || 456 ); // 123
console.log( 0 || 456 ); // 456
console.log( 123 || 456 || 789 ); // 123
语句
分支语句
分支语句可以让我们有选择性的执行想要的代码
1)if分支语句
1
2
3
4
5
6
7
8
9
10
11
12if (time<10)
{
document.write("<b>早上好</b>")
}
else if (time>=10 && time<20)
{
document.write("<b>今天好</b>")
}
else
{
document.write("<b>晚上好!</b>")
}2)三元运算符
语法:条件**?代码1(满足条件执行的代码):**代码2:(不满足条件执行的代码)
一般用于赋值
3)switch语句
1
2
3
4
5
6
7
8
9switch (d)
{
case 6:x="今天是星期六"
break
case 0:x="今天是星期日"
break
default:
x="期待周末"
}- 注意不要漏掉break,不然程序继续执行下一个case,直到遇到break或switch语句结束。
循环语句
当明确循环次数的时候使用for,不确定的时候使用while(true)
1)while循环和do/while循环
1
2
3
4
5
6
7
8
9
10
11
12
13
14//先判断再执行
while (i<5)
{
x=x + "The number is " + i + "<br>"
i++
}
//先执行一次再判断
do
{
x=x + "The number is " + i + "<br>"
i++
}
while (i<5)2)for循环
1
2
3
4
5
6
7
8
9
10
11
12// for循环
for (let i=0;i<cars.length;i++)
{
document.write(cars[i] + "<br>")
}
// for/in语句遍历对象的属性
let person = {fname:"Bill",lname:"Gates",age:56};
for (let key in person) // x 为属性名
{
txt = txt + person[x]
}3)打破循环
break语句:直接跳出循环,执行循环之后的代码
1
2
3
4
5
6
7
8
9for (i=0;i<10;i++)
{
if (i==3)
{
break
}
//if (i==3) break
x=x + "The number is " + i + "<br>"
}continue语句:中断当前循环中的迭代,继续循环下一个迭代
1
2
3
4
5for (i=0;i<=10;i++)
{
if (i==3) continue;
x=x + "The number is " + i + "<br>"
}
函数
函数的声明
1
2
3
4
5
6
7
8
9<script>
// 注意:这样仅仅是声明,只有调用了函数才会执行
function myFunction(let1,let2)
{
code
}
// 调用
myFunction(let1,let2)
</script>具名函数和匿名函数
1)具名函数:function fn() {}
- 调用:fn()
- 具名函数的调用可以写到任何位置,即可以先调用再声明
2)匿名函数:function() {}
无法调用
使用方式:
函数表达式
必须先声明再调用
1
2
3
4
5
6
7
8<script>
let fn = function (let1,let2)
{
code
}
// 调用
fn(x,y)
</script>立即执行函数
不需要调用,直接执行。
多个立即执行函数之间必须要用分号隔开
1
2
3
4
5
6
7
8
9
10
11
12<script>
(function ()
{
code
})();
// 注意多了两个括号
// 第一个括号其实就是将函数声明看作函数,后面的括号直接调用函数
(function (x,y)
{
console.log(x + y)
})(1,2)
</script>这样来看的话,立即执行函数好像和直接写顺序代码执行,不封装为函数没有什么区别?
其实还是有作用的:封装了函数等于构建了一个新的作用域,在这个作用域里面使用的变量和函数外是分离的,可以重复相互不影响。达到了防止变量污染的作用。
对象
对象的定义
对象是一种数据类型,是一种无序的数据的集合,可以用于详细地描述某个事物。
- 对象中由属性和方法组成
例如:
1
2
3
4
5
6
7
8
9
10
11let person = {
// 属性
firstName:"John",
lastName:"Doe",
age:50,
// 方法
fullName : function()
{
return this.firstName + " " + this.lastName;
}
};对象的使用
访问对象中的属性值:
对象名.属性
或对象名["属性"]
(属性名中包含-
的时候只能用后者)访问方法也是一样的
1
2
3
4
5
6
7
8
9// 增(直接赋值一个新的属性即可)
person.hobby = "basketball"
// 删
delete person.hobby
// 改
person.firstName = "isoda"
// 查
console.log(person.firstName)
name = person.fullName()对象的遍历
使用for/in语句遍历对象的属性
1
2
3
4
5let person = {fname:"Bill",lname:"Gates",age:56};
for (let key in person) // key 为属性名,为字符串
{
txt = txt + person[key]
}注意:
这里只能用 person[key] 来访问对象,因为key为字符串,为’fname’,’lname’的形式
所以不能使用person.key,显示出来就是person.’fname’,这种形式明显是错误的,会得到undefined。而 person[‘fname’],这种访问方法才是正确的。
JS DOM
DOM简介
概念
DOM(文档对象模型),是浏览器提供的一套专门用来操作网页内容的功能(API),通过DOM,JavaScript 可以创建动态的HTML,实现用户的交互:
改变页面中的所有 HTML 元素
改变页面中的所有 HTML 属性
改变页面中的所有 CSS 样式
对页面中的所有事件做出反应
DOM树
将HTML文档以树状结构直观的表现出来,即DOM树。可以直观体现标签和标签之间的关系
DOM对象
从DOM树上获取到的所有html标签都是JS对象,即DOM对象
- 所有的标签属性都可以在这个对象上面找到
- 修改这个对象的属性会自动映射到标签身上
- 从dom树来看,最大的dom对象就是document对象,document对象下的属性和方法都是用来访问和操作网页内容的,如
document.write()
用来写入网页内容
获取DOM元素
通过CSS选择器来获取
querySelector:匹配符合的第一个元素,返回对应的HTML元素对象
querySelectorAll:返回符合的HTML元素对象数组
注意,返回的是一个伪数组,有长度有索引号,但是没有pop和push等方法
通过p[0]等来访问
如果只有一个元素,返回的也是仅有一个元素的伪数组
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23<div class = 'box'>123</div>
<div class = 'box'>abc</div>
<p id="nav">导航栏</p>
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
<script>
// 直接获取标签
const box = document.querySelector('div')
// 根据class
const box = document.querySelector('.box')
console.log(box)
// 根据id
const nav = document.querySelector('#nav')
console.log(nav)
nav.style.color = 'red' //可以对其直接修改样式属性
// 获取ul下的第一个li,和CSS的选择方法一样
const li = document.querySelector('ul li:first-child')
console.log(li)
</script>其他获取DOM元素的方式
以下三种是比较老的写法,目前已经逐渐不再使用了
- 通过 id 找到 HTML 元素
- 通过标签名找到 HTML 元素
- 通过类名找到 HTML 元素
1
2
3document.getElementById('nav')
document.getElementsByTagName('div')
document.getElementsByClassName('w')
操作HTML内容
innerText
修改文本内容,仅显示纯文本,不解析标签
1
document.querySelector('.box').innerText = '我是一个盒子'
innerHTML
修改文本内容,可以解析标签
1
document.querySelector('.box').innerHTML = '<strong>我是一个盒子</strong>'
操作元素属性
操作元素基本属性
如src、href、title等
语法:
1
2// 对象.属性 = 值
document.querySelector('img').src = './images/a.webp'
操作元素样式属性CSS
1)通过style属性操作
语法:
1
2
3
4// 对象.style.样式属性 = 值
<div class = "box"> </div>
document.querySelector('.box').style.width = '200px'
box.style.backgroundColor = 'hotpink'属性中存在的数字单位不要忘记添加,如
px
如果属性中含有
-
连接符,可以去掉-
然后将后面的首字母大写如
background-color
可以改成backgroundColor
2)通过className操作
当需要修改的样式比较多的时候,逐一修改style属性比较繁琐,可以设置一个新的class,然后将需要修改的标签的class修改为新创建的class
语法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30// 元素.className = 新的class名称
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Javascript学习</title>
<style>
.class1{
width: 200px;
height: 200px;
background-color: #fff;
}
.class2{
width: 300px;
height: 300px;
background-color:skyblue;
margin: 100px auto;
padding: 10px;
}
</style>
</head>
<body>
<div class="class1">我是文字的内容</div>
<script>
const box = document.querySelector('div')
box.className = 'class2'
console.log(box) // 打印出来的class属性为 class2
</script>
</body>
</html>注意这里使用的是className,因为class是关键字
属性新值换旧值,这里的class1会被class2覆盖掉
如果不想覆盖,可以这样修改:
1
box.className = 'class1 class2'
3)通过classList操作
可以直接追加、删除或者切换类名
语法:
1
2
3
4
5
6
7// 追加一个类
元素.classList.add('类名')
// 删除一个类
元素.classList.remove('类名')
// 切换一个类
// 检查有没有使用这个类,如果有就删除,没有就加上(像灯反复按开关一样)
元素.classList.toggle('类名')操作表单元素属性
1)有很多时候需要修改表单的属性,如点击眼睛可以看到密码文本内容,本质就是将password类型转换为text
1
2表单.value = '用户名'
表单.value = 'password'- 注意,使用innerHTML是不能获取到表单内容的,只能通过value来获取
2)表单属性中添加就有效果,移除就没有效果,一律使用布尔值表示。如果为true代表添加了该属性如果是false代表移除了该属性。如点击全选就勾选所有的内容
如 disabled. checked 、selected,只接受布尔值
1
2
3
4
5
6
7
8
9
10
11// 方框勾选
<input type="checkbox" name = "" id = "" checked>
// 禁用点击
<button disabled>点击</button>
<script>
const ipt = document.querySelector('input')
ipt.checked = true
button.disabled = true
console.log(ipt.checked)
</script>- 如果我们这里写的是
ipt.checked = 'true'
, 仍然会改变成true,因为非空、0的字符串默认认定为true。但是并不提倡这么写,不规范
自定义属性
上面都是标准属性,即标签自带的属性,如class、id、title等,通常我们会自定义一些属性
- 自定义属性要求以
data-
开头,在DOM对象上以dataset. + 删除掉data-的后面部分
对象来获取
1
2
3
4
5
6// 自定义属性data-id,在后面通过dataset.id来获取
<div data-id = '10'>盒子</div>
<script>
const box = document.querySelector('.box')
console.log(box.dataset.id)
</script>- 自定义属性要求以
定时器(间歇函数)
定时器面向的功能:需要每隔一段时间自动执行一段代码,不需要手动去触发
例如:网页中的倒计时。
开启定时器
语法:setInterval(函数,间隔时间)
1
2
3
4
5
6
7
8
9
10// 直接使用函数
function fn(){
console,log('一秒执行一次')
}
setInterval(fn,1000)
// 可以使用匿名函数
setInterval(function(){
console.log('一秒钟执行一次')
},1000)- 注意这里是在过了间隔时间后才第一次执行函数,并不是立即执行一次,然后过了间隔时间后再执行第二次
- 函数名字不需要加括号
- 定时器返回的是一个id数字,表示这是第几个定时器(页面中可能设置了多个定时器)
关闭定时器
语法:clearInterval(定时器id)
1
2let m = setInterval(fn,1000)
clearInterval(m)
DOM事件
基本概念
- 什么是事件:编程时系统内发生的动作或者发生的事情,比如用户在网页上单击一个按钮、图像加载完成、鼠标移动到元素上、输入字段被改变等
- 事件监听:检测是否有时间发生,一旦触发事件,立即调用一个函数做出响应
添加事件监听(绑定)
语法:元素对象.addEventListener(‘事件类型’, 要执行的函数)
- 事件类型:事件用什么方式触发,如鼠标单击click、鼠标经过mouseover等
1
btn.addEventListener('click', myFunction)
事件类型
鼠标事件
- 鼠标点击:click
- 鼠标经过:mouseenter
- 鼠标离开:mouseleave
焦点事件
- 获得焦点:focus
- 失去焦点:blur
注意和点击的区别,例如搜索框,鼠标点击后出现下拉框,离开就消失,这种就是得到鼠标+失去鼠标,为焦点事件
键盘事件
- 键盘按下:Keydown
- 键盘抬起:Keyup
文本事件
- 用户输入事件:input
事件对象
其他
map函数遍历数组:JavaScript中的map()方法详解(均采用es6语法)
功能案例
1. 同意协议倒计时
1 | <!DOCTYPE html> |
2. 轮播图
见视频P97、98
3. 发布评论
见视频P99