JavaScript学习笔记
1、JavaScript简介
JavaScript 是一门弱类型脚本语言,其源代码在发往客户端运行之前不需经过编译,而是将文本格式的字符代码发送给浏览器由浏览器解释运行。是一种具有函数优先的轻量级,解释型或即时编译型的编程语言。虽然它是作为开发 Web 页面的脚本语言而出名,但是它也被用到了很多非浏览器环境中,JavaScript基于原型编程、多范式的动态脚本语言,并且支持面向对象、命令式和声明式(如函数式编程)风格。
- Native 原生 JS 开发
ECMAScript 是一种由 Ecma 国际通过 ECMA-262 标准化的脚本程序设计语言。这种语言在万维网上应用广泛,它往往被称为 JavaScript 或 JScript,所以它可以理解为是 JavaScript 的一个标准,但实际上后两者是 ECMA-262 标准的实现和扩展。
原生 JS 开发,也就是让我们按照【ECMAScript】标准的开发方式,简称是ES,特点是所有浏览器都支持。截止到当前博客发布时间,ES标准已发布如下版本:
- ES3
- ES4(内部,未正式发布 )
- ES5(全浏览器支持)
- ES6(常用,当前主流版本:webpack 打包成为ES5支持)
- ES7
- ES8
- ES9(草案阶段)
区别就是逐步增加新特性。
- TypeScript 微软的标准
TypeScript 是一种由微软开发的自由和开源的编程语言。它是 JavaScript 的一个超集,而且本质上向这个语言添加了可选的静态类型和基于类的面向对象编程。由安德斯·海尔斯伯格(C#、Delphi、TypeScript之父,.NET创立者)主导。该语言的特点就是除了具备 ES 的特性之外还纳入了许多不在标准范围内的新特性,所以会导致很多浏览器不能直接支持 TypeScript 语法,需要编译后(编译成 JS)才能被浏览器正确执行。
- JavaScript 框架
- Angular:Google 收购的前端框架,由一群 Java 程序员开发,其特点是将后台的 MVC 模式搬到了前端并增加了模块化开发的理念,与微软合作,采用 TypeScript 语法开发,对后台程序员友好,对前端程序员不太友好,最大的缺点是版本迭代不合理(如:1代 --> 2代,除了名字,基本就是两个东西,截止发表博客时已推出了 Angular6)
- React:Facebook 出品,一款高性能的 JS 前端框架,特点是提出了新概念【虚拟DOM】用于减少真实DOM操作,在内存中模拟DOM操作,有效的提升了前端渲染效率,缺点是使用复杂,因为需要额外学习一门【JSX】语言。
- Vue:一款渐进式 JavaScript 框架,所谓渐进式就是逐步实现新特性的意思,如实现模块化开发、路由、状态管理等新特性。其特点是综合了 Angular(模块化)和 React(虚拟 DOM)的优点。
- Axios:前端通信框架,因为 vue 的边界很明确,就是为了处理 DOM,所以并不具备通信能力,此时就需要额外使用一个通信框架与服务器交互,当然也可以直接选择使用 jQuery 提供的 AJAX 通信功能。
2、快速入门
2.1、引入JavaScript
- 内部标签
<!--方式一:<script>标签内写入JavaScript代码-->
<script>
alert("hello,JavaScript") //弹窗
</script>
script 标签可以放在 head 或 body 或页面的其他位置中,通常会放在 head 或 body 中。
- 外部引入
<!--方式二:引入外部 JS文件,注意不能使用自闭和标签-->
<script src="js/hello.js"></script>
外部引入不能使用自闭和标签,必须成对出现
如果 script 标签是用于引入 JS 文件的,就不要在该标签内部再书写 JS 代码,否则写了也不会执行。
2.2、控制台使用
键盘 F12 打开浏览器控制台
- Debug断点调试
- 浏览器本地存储
- 基本语法
在浏览器控制台可以输入代码调试 JavaScript 程序
alert(score) //弹窗
console.log(score) //在浏览器的控制台打印变量
2.2、严格检查模式
JavaScript 是门非常随意的语言,为了防止它的随意性产生的问题,通过设置 use strict
检查代码。
<!--
前提:IDEA 需要设置支持 ES6 语法
'use strict';严格检查模式,预防Javascript的随意性导致产生的一些问题,必须写在JavaScript的第一行
局部变量建议都使用 let 去定义
-->
<script>
"use strict"
let i=1; //ES6
</script>
"use strict" 必须写在 <script>
第一行,在 JavaScript 中严格区分大小写,JS的注释符合和Java的注释符合相同
3、数据类型
3.1、字符串
JS 中的字符串可以用单引号或双引号括起来
- 注意转译字符
\
<!DOCTYPE html>
<!-- <html lang="en"> -->
<head>
<meta charset="UTF-8">
<title>Document</title>
<script>
"use strict"
var str1 = "Hello \'"; //输入引号
var str2 = 'Hello \n JS'; //换行
var str3 = 'Hello \t'; //Tab
var str4 = '\u4e2d'; //u#### Unicode字符
var str5 = '\x41'; //ASCII字符
</script>
</head>
<body>
</body>
</html>
- 多行字符串编写,使用
``
符号包裹
<script>
"use strict"
var str1 = `1234567
哈哈哈哈哈哈
2333333333333`; //``在Tab键上面,Esc键下面
</script>
- 模板字符串
<script>
"use strict"
let name= "秦疆";
let hello=`你好,${name}`; //使用 ``
</script>
- 字符串属性与函数
<script>
"use strict"
let name= "student";
console.log(name.length); //打印字符串长度
console.log(name[0]); //打印字符串的第一个字符
console.log(name.substring(1,3)); //截取 [1,3) 字符串,左闭右开
console.log(name.toUpperCase()); //打印大写字符串
console.log(name.toLowerCase()); //打印小写字符串
</script>
- 注意:与 Java 一样,字符串具有不变性
3.2、数组
Array 对象用于在单个的变量中存储多个值,可以包含任意类型的数据。
<script>
"use strict"
var arr = [1,2,3,4,"1","5","6"];
console.log(arr.length); //打印数组长度,假如给arr赋值,数组大小就会发生变化,如果赋值过小,元素就会丢失
console.log(arr.indexOf(1)); //通过元素获取下标
console.log(arr.indexOf("1")) //字符串的"1"和数字1不同的
console.log(arr.slice(1,3)) //截取arr的一部分,类似String的substring
arr.push("a","b"); //压入到尾部
arr.pop(); //弹出尾部的一个元素
arr.unshift("c","d"); //压入到头部
arr.shift(); //弹出头部的一个元素
arr.sort(); //排序
arr.reverse(); //元素反转
var arr1 = arr.concat([1,2,3]); //拼接数组,返回一个新数组,不改变原数组
arr.join('-'); //打印拼接数组,使用特定的字符串拼接
var arr2 = [[1,2],[3,4],[5,6]]; //多维数组
console.log(arr2[1][1]); //打印结果为:4
</script>
3.3、Boolean
- 与 Java 一样,JavaScript 的布尔值也为 true 和 false。
3.4、null、NaN和undefined
- null:空
- NaN:not a number
- undefined:未定义
3.5、对象
若干个键值对,在 JavaScript 中所有键都是字符串,值是任意类型。
<script>
"use strict"
//定义了一个user对象,具备四个属性
var user = {
name: "liuxiang",
age: 3,
email: "2625445438@qq.com",
score: 100 //最后一个不需要,
}
user.name = "liuxiangplus" //对象赋值
console.log(user.hhh); //使用一个不存在的属性,不会报错
delete user.age; //通过 delete 动态删除属性
user.hhh = "哈哈哈" //动态添加属性,直接给新属性赋值即可
"toString" in user; //判断属性值是否在对象中,结果:true
user.hasOwnProperty("toString") //判断一个属性是否是这个对象自身的,结果:false
user.hasOwnProperty("score") //结果:true
</script>
3.6、Map和Set
Map 与 Set 是 EC6 的新特性
- Map(kv键值对)
<script>
"use strict"
var map = new Map([["tom",100],["jack",90],["lisa",80]]);
var name = map.get("tom"); //通过key获得value
console.log(name);
map.set("admin",123456); //新增或修改key-value
var psd = map.get("admin");
console.log(psd);
map.delete("jack"); //删除一个元素
</script>
- Set(无序不重复集合)
<script>
"use strict"
var set = new Set([1,2,3,4,5,3,4,5]); //会去除重复的值
set.add(8); //添加元素
set.delete(3); //删除元素
console.log(set.has(5)); //是否包含某个元素
</script>
3.7、iterator迭代
- 遍历数组
<script>
"use strict"
var arr = [3,4,5];
for (let x of arr){
console.log(x);
}
</script>
- 遍历Map
<script>
"use strict"
var map = new Map( [["tom", 100],[ "jack",90],[ "haha",80]]);
for (let x of map){
console.log(x);
}
</script>
- 遍历Set
<script>
"use strict"
let set = new Set([1,2,3]);
for (let x of set){
console.log(x);
}
</script>
4、流程控制
- if判断
<script>
"use strict"
var score = 80;
if(score>60){
alert("及格");
}else if(score<30){
alert("垃圾");
}else{
alert("其它");
}
</script>
- switch语句
<script>
"use strict"
var score = 80;
switch(score){
case 60:
alert("及格");
break;
case 80:
alert("优秀");
break;
default:
alert("其它");
}
</script>
- while循环
<script>
"use strict"
var score = 60;
while(score<100){
score = score + 1;
console.log(score);
}
</script>
- for循环
<script>
"use strict"
var score = 60;
for(let i=0;i<score;i++){
console.log(i);
}
</script>
- forEach循环
<script>
"use strict"
var arr = [1,2,3,4,5,6]
//函数
arr.forEach(function (value){
console.log(value);
})
</script>
- forin
<script>
"use strict"
var arr = [1,2,3,4,5,6]
//for (var index in object) {}
for (var key in arr) {
if (arr.hasOwnProperty(key)) {
console.log(arr[key]);
}
}
</script>
5、函数
5.1、定义函数
- 定义方式一
function abs(x){ //绝对值函数
if (x >= 0){
return x;
}else{
return -x;
}
}
function
指出这是一个函数定义abs
是函数的名称(x)
括号内列出函数的参数,多个参数以,
分隔{ ... }
之间的代码是函数体,可以包含若干语句,甚至可以没有任何语句
函数体内部的语句在执行时,一旦执行到 return 时,函数就执行完毕,并将结果返回。因此,函数内部通过条件判断和循环可以实现非常复杂的逻辑。
如果没有 return 语句,函数执行完毕后也会返回结果,只是结果为 undefined。
由于 JavaScript 的函数也是一个对象,上述定义的 abs() 函数实际上是一个函数对象,而函数名abs可以视为指向该函数的变量。
- 定义方式二
var abs = function (x){
if (x >= 0){
return x;
}else{
return -x;
}
};
在这种方式下,function (x) { ... }
是一个匿名函数,它没有函数名。但是,这个匿名函数赋值给了变量abs,所以,通过变量 abs 就可以调用该函数。
上述两种定义完全等价,注意第二种方式按照完整语法需要在函数体末尾加一个;
,表示赋值语句结束。
5.2、调用函数
- 调用函数时,按顺序传入参数即可:
abs(10); // 返回10
abs(-9); // 返回9
- JS 中允许传任意个参数,也可以不传递参数:
abs(10, 'blablabla'); // 返回10
abs(-9, 'haha', 'hehe', null); // 返回9
abs(); // 返回NaN
- 参数进来是否存在问题?假设不存在参数,如何进行规避?
function abs(x) {
//手动抛出异常来判断
if (typeof x !== 'number') { //typeof 判断参数的类型
throw 'Not a number';
}
if (x >= 0) {
return x;
} else {
return -x;
}
}
- arguments
JavaScript 还有一个免费赠送的关键字 arguments
,它只在函数内部起作用,并且永远指向当前函数的调用者传入的所有参数。arguments 类似 Array 但它不是一个 Array:
function abs(x) {
console.log('x = ' + x); // 10
for (var i=0; i<arguments.length; i++) {
console.log('arg ' + i + ' = ' + arguments[i]); // 10, 20, 30
}
if (x >= 0) {
return x;
} else {
return -x;
}
}
abs(10, 20, 30);
arguments 包含所有的参数,有时我们只想使用多余的参数来进行附加操作,需要排除已有参数。
- rest
ES6的新特性,获取除了已经定义的参数之外的参数:
rest 参数只能写在最后面,并且格式为 ...rest
function aaa(a,b,...rest) {
console.log("a=>"+a);
console.log("b=>"+b);
console.log(rest);
}
5.3、变量作用域
在 JavaScript 中,用 var 定义的变量实际上是有作用域的。
- 如果一个变量在函数体内部申明,则该变量的作用域为整个函数体,在函数体外不可引用该变量:
<script>
'use strict';
function f1() {
var x = 1;
x = x + 1;
}
x = x + 2; //ReferenceError! 无法在函数体外引用变量x
</script>
ps:非要想实现的话,可以研究一下闭包
- 如果两个不同的函数各自申明了同一个变量,那么该变量只在各自的函数体内起作用。换句话说,不同函数内部的同名变量互相独立,互不影响:
<script>
'use strict';
function f1() {
var x = 1;
x = x + 1;
}
function f2() {
var x = 'A';
x = x + 'B';
}
</script>
- 由于 JavaScript 的函数可以嵌套,此时,内部函数可以访问外部函数定义的变量,反过来则不行:
<script>
'use strict';
function f1() {
var x = 1;
function f2() {
var y = x + 1; //f2可以访问f1的变量x
}
var z = y + 1; // ReferenceError! f1不可以访问f2的变量y
}
</script>
- 如果内部函数和外部函数的变量名重名怎么办?
<script>
'use strict';
function f1(){
var x = 1;
function f2() {
var x = 'A';
console.log('x in bar() = ' + x); // 'A'
}
console.log('x in foo() = ' + x); // 1
f2();
}
f1();
</script>
这说明 JavaScript 的函数在查找变量时从自身函数定义开始,由 "内" 向 "外" 查找。如果内部函数定义了与外部函数重名的变量,则内部函数的变量将屏蔽外部函数的变量。
5.4、变量提升
JavaScript 的函数定义有个特点,它会先扫描整个函数体的语句,把所有声明的变量提升到函数顶部:
<script>
'use strict';
function f1() {
var x = 'Hello, ' + y;
console.log(x);
var y = 'tom';
}
f1();
</script>
运行结果:y = undefined,这是因为 JavaScript 自动提升了变量 y 的声明,但不会提升变量y的赋值。
由于 JavaScript 的这一怪异的特性,我们在函数内部定义变量时,请严格遵守在函数顶部首先申明所有变量这一规则。最常见的做法是用一个 var 申明函数内部用到的所有变量:
<script>
'use strict';
function f1() {
var x = 1, // x初始化为1
y = x + 1, // y初始化为2
z, i; // z和i为undefined
//之后随意使用
}
</script>
5.5、全局变量
不在任何函数内定义的变量就具有全局作用域。实际上,JavaScript 默认有一个全局对象 window
,全局作用域的变量实际上被绑定到 window
的一个属性:
<script>
"use strict"
//全局变量
var x ="helloWord";
function f1(){
alert(x);
alert(window.x);//默认所有全局变量都会绑定到window对象上
}
</script>
alert() 这个函数本身也是一个 window
变量
<script>
"use strict"
//全局变量
var old_alert =window.alert;
old_alert(123);
window.alert=function () {
}
window.alert(345); //失效
window.alert=old_alert; //恢复
window.alert(678); //生效
</script>
由于函数定义有两种方式,以变量方式 var foo = function () {}
定义的函数实际上也是一个全局变量,因此,顶层函数的定义也被视为一个全局变量,并绑定到 window
对象:
<script>
"use strict"
function f1() {
alert(123);
}
f1(); //直接调用f1()
window.f1(); //通过window.foo()调用
</script>
Javascript 实际上只有一个全局作用域,任何变量(函数也可以视为变量),假设没有在函数作用范围内找到,就会向外查找,如果在全局作用域都没有找到,报错 RefrenceError
。
- 规范
由于我们所有的全局变量都会绑定到我们的 window
上,如果不同的 js 文件,使用了相同的全局变量,就会引发冲突。如何能够减少冲突?
<script>
"use strict"
//唯一全局变量 不绑定到window上
var liuxiang = {};
//定义全局变量
liuxiang.name="刘翔";
//定义函数
liuxiang.app = function(a,b){
return a + b;
}
</script>
把自己的代码全部放入自己定义的唯一空问名字中,降低全局命名冲突的问题。
5.6、局部变量
由于 JavaScript 的变量作用域实际上是函数内部,我们在 for 循环等语句块中是无法定义具有局部作用域的变量的:
<script>
'use strict';
function f1() {
for (var i=0; i<100; i++) {
console.log(i);
}
i += 100; //仍然可以引用变量i
}
</script>
为了解决块级作用域,ES6 引入了新的关键字 let
,用 let
替代 var
可以申明一个块级作用域的变量:
<script>
'use strict';
function f1() {
var sum = 0;
for (let i=0; i<100; i++) {
sum += i;
}
i += 1; //SyntaxError
}
</script>
5.7、常量
由于 var
和 let
申明的是变量,如果要申明一个常量,在 ES6 之前是不行的,我们通常用全部大写的变量来表示”这是一个常量,不要修改它的值“:
var PI = 3.14;
ES6 标准引入了新的关键字 const
来定义常量,const
与 let
都具有块级作用域:
<script>
'use strict';
const PI = 3.14;
PI = 3; // 某些浏览器不报错,但是无效果!
</script>
5.8、方法
在一个对象中绑定函数,称为这个对象的方法。
<script>
'use strict';
var student = {
name: '小明',
birth: 2000,
age: function () {
var now = new Date().getFullYear();
return now - this.birth;
}
}
student.name; // 调用属性
student.age(); // 调用方法必须得带括号
</script>
一个方法内部,this
是一个特殊变量,它始终指向当前对象,也就是 student
这个变量。
<script>
'use strict';
function getAge() {
var now = new Date().getFullYear();
return now - this.birth;
}
var student = {
name: '小明',
birth: 2000,
age: getAge
};
student.age(); // 21, 正常结果
getAge(); // NaN
</script>
如果以对象的方法形式调用,比如 student.age()
,该函数的 this
指向被调用的对象,也就是 student
,这是符合我们预期的。
如果单独调用函数,比如 getAge()
,此时,该函数的 this
指向全局对象,也就是 window
。
在 JS 中可以使用 apply 控制 this
的指向:
<script>
'use strict';
function getAge() {
var now = new Date().getFullYear();
return now - this.birth;
}
var student = {
name: '小明',
birth: 2000,
age: getAge
};
student.age(); // 21
getAge.apply(student, []); // 21, this指向student, 参数为空
</script>
6、内部对象
标准对象
6.1、Date
时间函数
<script>
"use strict"
let date = new Date();
date.getFullYear(); //年份
date.getMonth(); //月份 0-11 代表月
date.getDate(); //日期
date.getDay(); //星期几
date.getHours(); //时
date.getMinutes(); //分
date.getSeconds(); //秒
date.getTime(); //时间戳 全世界统一 1970
console.log(new Date(date.getTime())); //通过时间戳获得日期
</script>
转换:
6.2、JSON
JSON(JavaScript Object Notation, JS 对象标记)是一种轻量级的数据交换格式,目前使用特别广泛。
在 JavaScript 中一切皆为对象、任何 JS 支持的类型都可以用 JSON 来表示。
格式:
- 对象都用:{}
- 数组都用:[]
- 所有的键值对都是:key:value
<script>
"use strict"
//编写一个 javascript 对象
var user= {
name: "秦疆",
age: 3,
sex: "男"
}
//将js对象转换成JSON
var jsonuser = JSON.stringify(user);
//将JSON字符串转换成JS对象
var parse = JSON.parse(jsonuser);
console.log(parse);
</script>
7、面向对象编程
JavaScript 的面向对象编程和大多数其他语言如 Java、C# 的面向对象编程都不太一样。如果你熟悉 Java 或 C#,很好,你一定明白面向对象的两个基本概念:
-
类:类是对象的类型模板,例如,定义 Student 类来表示学生,类本身是一种类型,Student 表示学生类型,但不表示任何具体的某个学生。
-
实例:实例是根据类创建的对象,例如,根据 Student 类可以创建出 xiaoming、xiaohong、xiaojun 等多个实例,每个实例表示一个具体的学生,他们全都属于 Student 类型。
所以,类和实例是大多数面向对象编程语言的基本概念。
不过,在 JavaScript 中,这个概念需要改一改。JavaScript 不区分类和实例的概念,而是通过原型(prototype)来实现面向对象编程。
7.1、原型继承(不推荐使用)
<script>
"use strict"
var Student = {
name: 'kuangshen',
age: 3,
run: function () {
console.log(this.name + ' is running...');
}
};
var xiaoming = {
name: '小明'
};
xiaoming.__proto__ = Student; //原型 小明指向Student
</script>
注意最后一行代码把 xiaoming
的原型指向了对象 Student
,看上去 xiaoming
仿佛是从 Student
继承下来的。xiaoming
有自己的 name
属性,但并没有定义 run()
方法。不过,由于小明是从 Student
继承而来,只要 Student
有 run()
方法,xiaoming
也可以调用。
JavaScript 的原型链和 Java 的 Class 区别就在,它没有 Class 的概念,所有对象都是实例,所谓继承关系不过是把一个对象的原型指向另一个对象而已。
- 原型链
在 JavaScript 中,每个函数都有一个 prototype 属性,这个属性指向函数的原型对象。
当我们用 obj.xxx 访问一个对象的属性时,JavaScript 引擎先在当前对象上查找该属性,如果没有找到,就到其原型对象上找,如果还没有找到,就一直上溯到 Object.prototype 对象,最后,如果还没有找到,就只能返回undefined。
由于 Function.prototype 定义了 apply()
等方法,因此,所有函数都可以调用 apply()
方法。
很容易想到,如果原型链很长,那么访问一个对象的属性就会因为花更多的时间查找而变得更慢,因此要注意不要把原型链搞得太长。
7.2、class继承(推荐使用)
新的关键字 class
从ES6开始正式被引入到 JavaScript 中。class 的目的就是让定义类更简单。
用 class 定义对象的另一个巨大的好处是继承更方便了。现在,原型继承的中间对象,原型对象的构造函数等等都不需要考虑了,直接通过 extends
来实现:
<script>
"use strict"
class Student {
//构造器
constructor(name) {
this.name = name;
}
hello() {
alert('Hello, ' + this.name + '!');
}
}
class xiaoStudent extends Student{
constructor(name,grade){
//必须调用父类的构造器 否则会报错
super(name);
this.grade = grade;
}
MyGrade(){
alert("我是一名"+this.grade+"年级小学生");
}
}
var xiaoming = new Student("xiaoming"); //拥有Student类的方法
var xiaohong = new xiaoStudent("xiaohong",1) //拥有xiaoStudent类与Student类的方法
</script>
8、操作BOM对象(重点)
BOM:浏览器对象模型
JavaScript 诞生就是为了能够让他在浏览器中运行!
- window-浏览器窗口
window
对象不但充当全局作用域,而且表示浏览器窗口。
window
对象有 innerWidth
和 innerHeight
属性,可以获取浏览器窗口的内部宽度和高度。内部宽高是指除去菜单栏、工具栏、边框等占位元素后,用于显示网页的净宽高。还有一个 outerWidth
和 outerHeight
属性,可以获取浏览器窗口的整个宽高。
window.alert(1) //弹窗
window.innerHeight
//210 内部宽度
window.innerWidth
//1280 内部高度
window.outerWidth
//1280
window.outerHeight
//690
//大家可以调整浏览器窗口试试...
- navigator-浏览器信息
navigator
对象表示浏览器的信息,最常用的属性包括:
- navigator.appName:浏览器名称;
- navigator.appVersion:浏览器版本;
- navigator.language:浏览器设置的语言;
- navigator.platform:操作系统类型;
- navigator.userAgent:浏览器设定的 User-Agent 字符串。
大多数时候,我们不会使用 navigator
对象,因为会被人为修改。不建议使用这些属性来判断和修改代码!
- screen-屏幕信息
screen
对象表示屏幕的信息,常用的属性有:
- screen.width:屏幕宽度,以像素为单位
- screen.height:屏幕高度,以像素为单位
- screen.colorDepth:返回颜色位数,如8、16、24。
- location-URL信息
location
对象表示当前页面的URL信息。
要加载一个新页面,可以调用 location.assign()
。如果要刷新当前页面,调用 location.reload()
方法非常方便。
location.reload(); //刷新页面
location.assign('https://www.bilibili.com/video/BV1JJ41177di?p=19'); //加载新页面
- document-文档树
document
对象表示当前页面。由于 HTM L在浏览器中以 DOM 形式表示为树形结构,document
对象就是整个DOM 树的根节点。
获取具体的文档树根节点:
<!DOCTYPE html>
<!-- <html lang="en"> -->
<head>
<meta charset="UTF-8">
<title>HelloScript</title>
</head>
<body>
<dl id="app">
<dt>Java</dt>
<dd>JavaEE</dd>
<dd>JavaSE</dd>
</dl>
<script>
"use strict"
var dl= document.getElementById('app');
</script>
</body>
</html>
JavaScript 可以通过 document.cookie
读取到当前页面的 Cookie
:
document.cookie; //获取cookie
由于 JavaScript 能读取到页面的 Cookie
,而用户的登录信息通常也存在 Cookie
中,这就造成了巨大的安全隐患,这是因为在 HTML 页面中引入第三方的 JavaScript 代码是允许的。
为了解决这个问题,服务器在设置 Cookie
时可以使用 httpOnly
,设定了 httpOnly
的 Cookie
将不能被JavaScript 读取。这个行为由浏览器实现,主流浏览器均支持 httpOnly
选项,IE 从 IE6 SP1 开始支持。
为了确保安全,服务器端在设置 Cookie
时,应该始终坚持使用 httpOnly
。
- history-浏览器历史记录
history 对象保存了浏览器的历史记录,JavaScript 可以调用 history 对象的 back()
或 forward ()
,相当于用户点击了浏览器的后退或前进按钮。
history.back(); //后退
history.forword(); //前进
9、操作DOM对象(重点)
DOM:文档对象模型
由于 HTML 文档被浏览器解析后就是一棵 DOM 树,要改变 HTML 的结构,就需要通过 JavaScript 来操作DOM。
始终记住 DOM 是一个树形结构。操作一个 DOM 节点实际上就是这么几个操作:
-
更新:更新该DOM节点的内容,相当于更新了该DOM节点表示的HTML的内容
-
遍历:遍历该DOM节点下的子节点,以便进行进一步操作
-
添加:在该DOM节点下新增一个子节点,相当于动态增加了一个HTML节点
-
删除:将该节点从HTML中删除,相当于删掉了该DOM节点的内容以及它包含的所有子节点
在操作一个 DOM 节点前,我们需要通过各种方式先拿到这个 DOM 节点。
最常用的方法是 document.getElementById()
和 document.getElementsByTagName()
,以及 CSS 选择器document.getElementsByClassName()
。
9.1、获取DOM节点
<!DOCTYPE html>
<!-- <html lang="en"> -->
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="father">
<h1>标题一</h1>
<p id="p1">p1</p>
<p class="p2">p2</p>
</div>
<script>
"use strict"
//标签选择器
var h1 = document.getElementsByTagName("h1");
//id选择器
var p1 = document.getElementById("p1");
//class选择器
var p2 = document.getElementsByClassName("p2");
var father = document.getElementById("father");
var children = father.children; //获取父节点下的所有子节点
// father.firstChild; 获取第一个节点
// father.lastChild; 获取最后一个节点
</script>
</body>
</html>
9.2、更新节点
<!DOCTYPE html>
<!-- <html lang="en"> -->
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="father"></div>
<script>
"use strict"
var father = document.getElementById("father");
father.innerText="123"; //修改文本的值
father.innerHTML="<a href=\"https://www.baidu.com\">123</a>"; //可以解析HTML文本
//操作CSS
father.style.color = "yellow";
father.style.fontSize="200px";//注意驼峰命名
</script>
</body>
</html>
9.3、删除节点
要删除一个节点,首先要获得该节点本身以及它的父节点,然后,调用父节点的 removeChild
把自己删掉:
<!DOCTYPE html>
<!-- <html lang="en"> -->
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="father">
<h1>标题一</h1>
<p id="p1">p1</p>
<p class="p2">p2</p>
</div>
<script>
"use strict"
//拿到待删除节点
var self = document.getElementById("p1");
//拿到父节点
var father = p1.parentElement;
//删除
father.removeChild(self);
</script>
</body>
</html>
注意到删除后的节点虽然不在文档树中了,但其实它还在内存中,可以随时再次被添加到别的位置。
使用 children
可以获取到父节点下所有子节点,它是一个数组可以通过下标获取节点 father.children[1]
当我们用如下代码删除子节点时:
var father = document.getElementById('father');
father.removeChild(father.children[0]);
father.removeChild(father.children[1]); // <-- 浏览器报错
father.removeChild(father.children[2]); // <-- 浏览器报错
浏览器报错:father.children[1]
不是一个有效的节点。原因就在于,删除多个节点时,children 是在时刻变化的,当 <h1>标题一</h1>
节点被删除后,<p id="p1">p1</p>
节点就变成了 father.children[0]
,此时执行 father.removeChild(father.children[1]);
就会报错。
9.4、插入节点
当我们获得了某个DOM节点,想在这个DOM节点内插入新的DOM,应该如何做?
如果这个 DOM 节点是空的,例如:<div></div>
,那么直接使用 innerHTML
就可以修改 DOM 节点的内容,相当于 插入了新的 DOM 节点。如果这个 DOM 节点不是空的,那就不能这么做,因为 innerHTML 会直接替换掉原来的所有子节点。
有两个办法可以插入新的节点:
- 使用
appendChild
,把一个已有的子节点添加到父节点的最后一个子节点
<!DOCTYPE html>
<!-- <html lang="en"> -->
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<p id="js">JavaScript</p>
<div id="list">
<p id="se">javase</p>
<p id="ee">javaee</p>
<p id="me">javame</p>
</div>
<script>
"use strict"
var js = document.getElementById("js");
var list = document.getElementById("list");
list.appendChild(js); //追加到div最后面
</script>
</body>
</html>
更多的时候我们会从零创建一个新的节点,然后插入到指定位置:
<!DOCTYPE html>
<!-- <html lang="en"> -->
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<p id="js">JavaScript</p>
<div id="list">
<p id="se">javase</p>
<p id="ee">javaee</p>
<p id="me">javame</p>
</div>
<script>
"use strict"
var list = document.getElementById("list");
var newp = document.createElement("p"); //创建一个p标签
newp.id="newp";
newp.innerText="helloword";
list.appendChild(newp); //插入到div最后面
var myscript = document.createElement("script"); //创建一个标签节点
//万能设置值的方式setAttribute
myscript.setAttribute("type","text/javascript");
//插入到div最后面
list.appendChild(myscript);
</script>
</body>
</html>
还可以插入 CSS 样式:
<!DOCTYPE html>
<!-- <html lang="en"> -->
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<p id="js">JavaScript</p>
<div id="list">
<p id="se">javase</p>
<p id="ee">javaee</p>
<p id="me">javame</p>
</div>
<script>
"use strict"
//创建style标签
let mystyle = document.createElement("style");
//设置CSS样式
mystyle.setAttribute("type","text/css");
mystyle.innerText="body{\n" +
" background: steelblue;\n" +
" }";
//获取head标签
var myhead = document.getElementsByTagName("head")[0];
//插入到最后面
myhead.appendChild(mystyle);
</script>
</body>
</html>
- insertBefore
如果我们要把子节点插入到指定的位置怎么办?
可以使用 parentElement.insertBefore(插入节点,在哪个节点前插入);
<!DOCTYPE html>
<!-- <html lang="en"> -->
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<p id="js">JavaScript</p>
<div id="list">
<p id="se">javase</p>
<p id="ee">javaee</p>
<p id="me">javame</p>
</div>
<script>
"use strict"
//获取div节点
var list = document.getElementById("list");
var ee = document.getElementById("ee");
var js = document.getElementById("js");
//要包含的节点,insertBefore(newNode,targetNode)
list.insertBefore(js,ee);
</script>
</body>
</html>
10、操作表单
用 JavaScript 操作表单和操作 DOM 是类似的,因为表单本身也是 DOM 树。
不过表单的输入框、下拉框等可以接收用户输入,所以用 JavaScript 来操作表单,可以获得用户输入的内容,或者对一个输入框设置新的内容。
HTML 表单的输入控件主要有以下几种:
- 文本框,对应的
<input type="text">
,用于输入文本 - 口令框,对应的
<input type="password">
,用于输入口令 - 单选框,对应的
<input type="radio">
,用于选择一项 - 复选框,对应的
<input type="checkbox">
,用于选择多项 - 下拉框,对应的
<select>
,用于选择一项 - 隐藏文本,对应的
<input type="hidden">
, 用户不可见,但表单提交时会把隐藏文本发送到服务器
- 获取值
如果我们获得了一个 <input>
节点的引用,就可以直接调用 value 获得对应的用户输入值。
这种方式可以应用于 text
、password
、hidden
以及 select
。
对于单选框和复选框,value
属性返回的永远是 HTML
预设的值,而我们需要获得的实际是用户是否勾上了选项,所以应该用 checked
判断:
<!DOCTYPE html>
<!-- <html lang="en"> -->
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="post">
<span>用户名:</span> <input type="text" id="username">
<span>性别:</span>
<input type="radio" name="sex" value="man" id="boy">男
<input type="radio" name="sex" value="woman" id="girl">女
</form>
<script>
"use strict"
var input = document.getElementById('username');
input.value; //得到用户输入的值
input.value = "123"; //修改输入的值
var boy = document.getElementById('boy');
var girl = document.getElementById('girl');
boy.value; // man
girl.value; // woman
boy.checked; // true或者false
girl.checked; // true或者false
boy.checked = 'true' //选中男
</script>
</body>
</html>
- 设置值
设置值和获取值类似,对于 text
、password
、hidden
以及 select
,直接设置 value 就可以:
// <input type="text" id="email">
var input = document.getElementById('email');
input.value = '123456789@qq.com'; // 文本框的内容会同步更新
对于单选框和复选框,设置 checked
为 true
或 false
即可。
- 提交表单
提交表单时 MD5对密码加密
<!DOCTYPE html>
<!-- <html lang="en"> -->
<head>
<meta charset="UTF-8">
<title>HelloScript</title>
<!--md5工具类-->
<script src="https://cdn.bootcss.com/blueimp-md5/2.10.0/js/md5.min.js"></script>
</head>
<body>
<!--表单绑定提交事件
onsubmit = 绑定一个提交检测的函数,true,false,将这个结果返回给表单,使用onsubmit接收
-->
<form action="https://www.baidu.com/" method="post" onsubmit="return MD5()">
<p>
<span>用户名:</span><input type="text" name="username" id="username">
</p>
<p>
<span>密码:</span><input type="password" id="input-password">
</p>
<!--隐藏域 隐藏真实密码-->
<input type="hidden" name="password" id="md5-password">
<input type="submit" value="提交">
</form>
<script>
"use strict"
function MD5() {
var name = document.getElementById("username");
var pwd = document.getElementById("input-password");
var md5pwd = document.getElementById("md5-password");
md5pwd.value = md5(pwd.value); //MD5加密
//可以校验表单内容 true就是通过提交 false就是阻止提交
return true;
}
</script>
</body>
</html>
11、jQuery
jQuery 是一个 JavaScript 库,极大地简化了 JavaScript 编程,在 jQuery 中存在大量的 JavaScript 函数。
jQuery API 中文文档:https://jquery.cuishifeng.cn/
- 使用 jQuery
使用 jQuery 只需要在页面的引入 jQuery 文件即可:
<!DOCTYPE html>
<!-- <html lang="en"> -->
<head>
<meta charset="UTF-8">
<title>jQuery</title>
<!-- 本地 -->
<script src="jquery-3.5.1.js"></script>
<!-- 在线CDN -->
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.js"></script>
</head>
<body>
</body>
</html>
$
是著名的 jQuery 符号。实际上,jQuery 把所有功能全部封装在一个全局变量 jQuery 中,而 $
也是一个合法的变量名,它是变量 jQuery 的别名。
<!DOCTYPE html>
<!-- <html lang="en"> -->
<head>
<meta charset="UTF-8">
<title>jQuery</title>
<!-- 本地 -->
<!-- <script src="jquery-3.5.1.js"></script> -->
<!-- 在线CDN -->
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.js"></script>
</head>
<body>
<a href="" id="test-jqury">点我</a>
<!-- jQuery公式: $(selector).action() -->
<script>
"use strict"
//点击事件
$("#test-jqury").click(function () {
alert("hellojQuery")
})
</script>
</body>
</html>
- JQuery 选择器
原生 JS 选择器如下:
//id选择器
document.getElementById();
//类选择器
document.getElementsByClassName();
//标签选择器
document.getElementsByTagName();
原生 JS 代码中,选择器复杂且类型较少,在 jQuery 中 CSS 的选择器它全部都能用。
//在jQuery中CSS的选择器它全部都能用
$('p' ).click(); //标签选择器
$( '#id1' ).click(); //id选择器
$( '.class1 ' ).click(); //cLass选择器
- 事件
因为 JavaScript 在浏览器中以单线程模式运行,页面加载后,一旦页面上所有的 JavaScript 代码被执行完后,就只能依赖触发事件来执行 JavaScript 代码。
浏览器在接收到用户的鼠标或键盘输入后,会自动在对应的 DOM 节点上触发相应的事件。如果该节点已经绑定了对应的 JavaScript 处理函数,该函数就会自动调用。
jQuery常用事件如下:
<!DOCTYPE html>
<!-- <html lang="en"> -->
<head>
<meta charset="UTF-8">
<title>jQuery</title>
<!-- 本地 -->
<!-- <script src="jquery-3.5.1.js"></script> -->
<!-- 在线CDN -->
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.js"></script>
<style>
#divMove{
width: 500px;
height: 500px;
border: 1px solid red;
}
</style>
</head>
<body>
<!-- 要求:获取鼠标当前的一个坐标 -->
mouse : <span id="mouseMove"></span>
<div id="divMove">在这里移动鼠标试试</div>
<!-- jquery公式: $("#test-jqury").click(); -->
<script>
"use strict"
//当网页元素加载完毕之后 响应事件
$(function () {
//获取鼠标移动事件
$("#divMove").mousemove(function (e) {
$("#mouseMove").text("x"+e.pageX+"Y:"+e.pageY);
})
});
</script>
</body>
</html>
- 操作DOM
<!DOCTYPE html>
<!-- <html lang="en"> -->
<head>
<meta charset="UTF-8">
<title>jQuery</title>
<!-- 本地 -->
<!-- <script src="jquery-3.5.1.js"></script> -->
<!-- 在线CDN -->
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.js"></script>
<style>
#divMove{
width: 500px;
height: 500px;
border: 1px solid red;
}
</style>
</head>
<body>
<ul id="test-ul">
<li class="js">JavaScript</li>
<li name="py">python</li>
</ul>
<!-- jquery公式: $("#test-jqury").click(); -->
<script>
"use strict"
/*节点文本操作*/
$(function () {
//操作文本
$("#test-ul li[class=js]").text();//获取值
$("#test-ul li[class=js]").text("JavaSE");//设置值
//操作html
$("#test-ul li[name=py]").html();//获取html
$("#test-ul li[name=py]").css({"color":"red"}); //
$("#test-ul li[name=py]").html("<li name='Java'>Java</li>");//设置值
$("#test-ul li[name=py]").show() //显示
$("#test-ul li[name=py]").hide() //隐藏
$("#test-ul li[name=py]").toggle() //显示与隐藏切换
});
</script>
</body>
</html>