JavaScript基础篇
JavaScript简介
JavaScript(简称“JS”) 是一种具有函数优先的轻量级,解释型或即时编译型的编程语言。虽然它是作为开发Web页面的脚本语言而出名,但是它也被用到了很多非浏览器环境中,JavaScript 基于原型编程、多范式的动态脚本语言,并且支持面向对象、命令式、声明式、函数式编程范式。
JS 编写位置
- 写在标签属性中
- 如 button 中的 onclick 和 a 中的 href
<button onclick="alert('我是弹窗')">点击出现弹窗</button>
<a href="javascript:alert('我是弹窗')">点击出现弹窗</a>
- 如 button 中的 onclick 和 a 中的 href
- 页面中的位置
- javascript是可以放在任何位置
- 放在head部分
- 最长用的方法是在head部分位置放置script元素,浏览器解析head就会执行这个代码
- 放置body部分
- javascript代码在页面读取到该语句时就会被执行
- 注意:浏览器解析HTML是按先后顺序的,前面的javascript会被先执行
<head>
<script type="text/javascript">alert('我是在head里的弹窗')</script>
</head>
<body>
<script type="text/javascript">alert('我是在body里的弹窗')</script>
</body> - 引用外部js文件
- javascript 有专门的文件夹放置,后缀为js
- 像link应用css差不多的方式 引用就可以了
<script src="./js/index.js"></script>
- 但最好就是写在外部文件中
- 以为前两中结构和行为耦合了,不方便后期维护
JS的HelloWorld
- alert(); 弹出警告框
- document.write(); 向body输出一个内容
- console.log(); 向控制台输出一个内容
alert("alert HelloWorld");
document.write("write HelloWorld");
console.log("console HelloWorld");
基本语法
- 1、注释
- /* 多行注释 */
- // 单行注释
- 2、js中严格区分大小写
- 3、js中没一条语句以分号(;)结尾
- 如果不写分号,浏览器会自动添加,但会增加一些系统资源
- 4、js会自动忽略多个空格和换行,所以我们可以利用空格和换行对代码进行格式化
字面量和变量
- 字面量,一些不可改变的值
- 如: 1 2 3 4 5 6 7
- 字面量都可以直接使用,但我们一般不会直接使用
- 变量,变量可以用来保存字面量,而且变量的值是可以该改变的
- 变量更加方便使用,一般用变量来做开发
// 声明变量
var x;
// 为变量赋值
x = 2123123123123123; - 声明和赋值可以同时进行
var x = 2123123123123123;
- 变量更加方便使用,一般用变量来做开发
标识符
- 在js中 所有可以由我们自足命名的都可以成为标识符
- 如: 变量名、函数名、属性名都是标识符
- 命名一个标识符需要遵守一下规则
- 表示符中可以含有字母、数字、_、$
- 标识符不能以数字开头
- 标识符不能是ES中的关键字或保留字
- 标识符一般用采用驼峰命名法
- 首字母小写,每个单词的抬头字母大写,其余字母小写
- js底层保存标识符时实际上采用的Unicode编码
- 也就是理论上 UTF-8 中所有内容都可以作为标识符
数据类型(六种)
String 字符串
Number 数值
Boolean 布尔值
Null 空值
Undefined 未定义
Object 对象
- 其中String Number Boolean Null Undefined 属于基本数据类型
- 而 Object 属于引用数据类型
- 用运算符 typeof() 可以检查出变量的数据类型
String 字符串
- 在js中 字符串 需要用引号引起来
- 使用双引号或单引号都可以但不能用混着用
- 单引号里面可以放双引号,双引号可以用单引号
- 用反斜杠(\) 可以转义
- \“ 输出 “
- \‘ 输出 ‘
- \n 输出 换行
- \t 输出 制表符
Number 数值
- 在js中所有的数值都是Number类型
- 包括整数和浮点数
- js中可以表示数字的最大
- Number.MAX_VALUE
- 如果使用Number 表示的数字超过了最大值,则会显示无穷大(Infinity)
- Number.MAX_VALUE * Number.MAX_VALUE
- Infinity 是 字面量 可以直接表示 正无穷
- -Infinity 是 字面量 可以直接表示 负无穷
- NaN 表示不是数字 但是属于Number 的数据类型
- 在js中的整数的运算基本可以保证精确 但浮点数不能保证
- 如果使用js进行浮点元素,可能得到一个不精确的结果
- 所以千万不能使用js进行精确度要求比较高的运算
- 如果使用js进行浮点元素,可能得到一个不精确的结果
Boolean 布尔值
- true
- 表示逻辑上的真
- false
- 表示逻辑上的假
Null 和 Undefined
- Null 类型的值 只有一个 表示为 空 的对象
- 使用 typeof 检查一个 null 时,会返回 object
- Undefined 类型的值 只有一个 表示为 未定义
强制类型转换
- 强制将数据类型转换成别的数据类型
- 主要是将 其他的数据类型 转为 String Number Boolean
其他数据类型转换为 String
- 方法一:
- 调用被 转换数据类型的 toString()方法
- 这个方法不会影响到原变量,它只会将转换的结果返回
- 调用变量a的toString()方法
a.toString();
- 调用变量a的toString()方法
- 利用这个方法,也可以直接改变原变量
a = a.toString();
- 但是要注意: null 和 undefined 没有toString()方法,会报错
a = null;
a = a.toString; //报错
a = undefined;
a = a.toString; //报错
- 但是要注意: null 和 undefined 没有toString()方法,会报错
- 方法二:
- 调用String()函数,将变量转换成字符串
- 使用方法和toString一样
- 用来转换Number 和 Boolean 时,实际上还是用 toString
- 但null 和 undefined 是直接加上引号 转换为 “null” 和 “undefined”
- 调用String()函数,将变量转换成字符串
其他数据类型转换为 Number
方法一:
- 调用Number()函数,来将变量转换成Number类型
a = "123";
a = Number(a);
- 纯数字的字符串则直接转为 数字
- 如果字符串中有非数字的内容(如字母),则转为 NaN
- 如果字符串是一个空格或全是空格,则转为 0
- 如果是 Boolean 中的 true , 则转为 1
- 如果是 Boolean 中的 false , 则转为 0
- 如果是 NaN 则转为 0
- 如果是 undefined 则转为数字 NaN
序号 原值 Number()转换后 1 “123” 123 2 “123asd” NaN 3 “ “或” “ 0 4 true 1 5 false 0 6 NaN 0 7 undefined NaN - 调用Number()函数,来将变量转换成Number类型
方法二:针对字符串
- 调用 parseInt() 函数将变量转换为Number
- parseInt() 可以将一个字符串中的有效整数内容取出来转为 Number
- parseFloat() 可以将一个字符串的有效小数的内容取出来 转为 Number
- 如果是对 非 String 类型 使用 parseInt() 或 parseFloat()
- 则会先转为 String 类型 再 使用 parseInt() 或 parseFloat()
原值 parseInt()转换后 parseFloat()转换后 “123px” 123 123 “a123px” NaN NaN “123abc123px” 123 123 “123.123px” 123 123.123 “123a.123px” 123 123 “123.123” 123 123.123 “010” 10 10 010有些浏览器直接当成八进制去转换了,需要在parseInt()或parseFloat()中添加第二个参数来表示进制(比较少用)
a = "010"
a = parseInt(a,10);
其他数据类型转换为 Boolean
- Number类型转Boolean
转换前 | 转换后 |
---|---|
-123 | true |
0 | false |
123 | true |
“ “ | false |
‘123’ | true |
NaN | false |
undefined | false |
对象 | true |
其他进制的数字
- 数据前添加开头来表示各个进制
进制 | 转换 | 结果 |
---|---|---|
十六进制(0x) | 0x10 | 16 |
八进制(0) | 010 | 8 |
二进制(0b) | 0b10 | 2 |
算数运算符
对非Number进行运算时,会先转成Number再做运算
对两个字符串进行运算时,会直接连接起来
任何值和字符串进行加法运算,都会先转换成字符串再做运算
任意数据类型 + 一个’’(空字符串)即可转换到string
任何值和null运算都得null(0)
任何值做 + - * / 运算都会先转成Number
运算符 | 作用 |
---|---|
+(加) | 加法 |
-(减) | 减法 |
*(乘) | 乘法 |
/(除) | 除法 |
%(求余) | 求余数 |
总结: 就加法(+)特殊点:与字符串进行运算连接,全部转为字符串(String)
一元运算符
- 加号(+)
- 加在数据类型的前面可转成Number类型
- 减号(-)
- 减在数据类型的前面可取反,和数学一样
自增和自减
表达式 | 解释 |
---|---|
++x | 先自增,再把值给别人或运算 |
x++ | 先把原来的值给别人或者运算后,再自增 |
--x | 先自减,再把值给别人或运算 |
x-- | 先把原来的值给别人或者运算后,再自减 |
逻辑运算符
符号 | 逻辑 |
---|---|
&& | 与 |
|| | 或 |
! | 非 |
- 逻辑运算符真值表
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 |
&&(与)
&& 与运算符
- 在传统的编程中,只有两个操作数都是真值时,与运算返回true,否则返回false
- 有一个false 则返回 false
alert( true && true); //true
alert( true && false); //false
alert( false && true); //false
alert( false && false); //false如果操作数不是布尔值的情况下,会先转成布尔值,在运算
&& 与运算寻找第一个假值
result = value1 && value2 && value3;
- 与运算**&&**做了以下的事:
- 从左往右依次计算操作数
- 在处理每个操作数时,都将其转化为布尔值,如果结果是false,就停止计算,并返回这个操作数的初始值
- 如果说操作数都被计算过(如果都是真值),则返回最后一个操作数
- 换句话说,就是
- 如果是假值,就停止运算,并返回这个假值的初始值,
- 如果是真值,就返回最后的那个真值的初始值
- 与运算**&&**做了以下的事:
&& 运算(返回假值):
- 如果第一个值为
true
,则必然返回第二个值
- 如果第一个值为
false
,则直接返回第一个值
- 如果第一个值为
||(或)
&& 与运算符
- 在传统的编程中,有一个 true 则返回 true,否则返回 false
- 有一个true 则返回 true
alert( true || true); //true
alert( true || false); //true
alert( false || true); //true
alert( false || false); //false如果操作数不是布尔值的情况下,会先转成布尔值,在运算
或运算寻找第一个真值
result = value1 || value2 || value3;
- 与运算**||**做了以下的事:
- 从左往右依次计算操作数
- 在处理每个操作数时,都将其转化为布尔值,如果结果是true,就停止计算,并返回这个操作数的初始值
- 如果说操作数都被计算过(如果都是false),则返回最后一个操作数
- 换句话说,就是
- 如果是真值,就停止运算,并返回这个真值的初始值,
- 如果是假值,就返回最后的那个假值的初始值
- 与运算**||**做了以下的事:
||或运算(返回真值):
- 如果第一个值
true
,则返回第一个值
- 如果第一个值为
false
,则返回第二个值
- 如果第一个值
!(非)
result = !value; |
- 逻辑非运算符接受一个参数,并按如下运作:
- 将操作数转化为布尔类型:true/false。
- 返回相反的值
alert( !true ); // false |
登录页面小练习
实现使用 prompt
进行登陆校验的代码。
如果访问者输入 "Admin"
,那么使用 prompt
引导获取密码,如果输入的用户名为空或者按下了 Esc 键 —— 显示 “Canceled”,如果是其他字符串 —— 显示 “I don’t know you”。
密码的校验规则如下:
- 如果输入的是 “TheMaster”,显示 “Welcome!”,
- 其他字符串 —— 显示 “Wrong password”,
- 空字符串或取消了输入,显示 “Canceled.”。
流程图:
|
关系运算符
运算符 | 用法 | 功能 |
---|---|---|
> | a > b | 如果成立,结果为 true,否则为 false |
>= | a >= b | 如果成立,结果为 true,否则为 false |
< | a < b | 如果成立,结果为 true,否则为 false |
<= | a <= b | 如果成立,结果为 true,否则为 false |
== | a == b | 如果成立,结果为 true,否则为 false |
!= | a != b | 如果成立,结果为 true,否则为 false |
对非数值的比较运算,会先把非数值先转为数字,在做比较
任何值和null比较都得false
如果两边都是字符串,则用两边的unicode编码进行比较(一位一位按顺序比较)
相等运算符
运算符 | 解释 | 区别 |
---|---|---|
== | 相等 | 先转成同类型,再比较 |
=== | 全等 | 不转类型,直接比较,不同类型直接先false |
条件运算符
条件运算符也叫三元运算符
语法:条件表达式?语法1:语法2;
条件表达式在执行时,首先对条件表达式进行求值
如果值为true
,则执行语句1
,并返回结果
如果值为false
,则执行语句2
,并返回结果
if语句
if(….) 语句计算括号里的表达式,如果计算结果是true
, 就会执行对应的代码块
如:
if(true){ |
else 语句
如果条件不成立时,执行的语句
if(false){ |
else if 语句
如果有多个条件
则可以使用else if
如:
let a = 1; |
练习1
从键盘输入小明的期末成绩:
当成绩为100时,‘奖励一辆BMW’
当成绩为[80-99]时,’奖励一台iphone15s’
当成绩为[60-80]时,’奖励一本参考书’
其他时,什么奖励也没有
|
练习2
大家都知道,男大当婚,女大当嫁。那么女方家长要嫁女儿,当然要提出一定的条件:
高:180cm以上;富:1000万以上;帅:500以上;
如果这三个条件同时满足,则:’我一定要嫁给他’
如果三个条件有为真的情况,则:’嫁吧,比上不足,比下有余。’
如果三个条件都不满足,则:’不嫁!’
|
switch
switch
语句有至少
一个 case
代码块和一个可选
的 default
代码块。
语法:
switch (a) { |
- 比较
x
值与第一个case
(也就是value1
)是否严格相等,然后比较第二个case
(value2
)以此类推。 - 如果相等,
switch
语句就执行相应case
下的代码块,直到遇到最靠近的break
语句(或者直到switch
语句末尾)。 - 如果没有符合的
case
,则执行default
代码块(如果default
存在)。
while
while
循环的语法如下:
let i = 0; |
do…while
使用 do..while
语法可以将条件检查移至循环体下面
let i = 0; |
相比无do
的while 至少会执行一次
,当i < 0 时,会输出0
for
for
循环看起来就像这样:
for (begin; condition; step) { |
for (let i = 0; i < 3; i++) { // 结果为 0、1、2 |
新声明了一个初始值i,表达式为i<3,执行循环体,每次循环后运行i++
break和continue
|
break
跳出离最近的循环体
|
continue
跳出本次循环
|
对象 Object
对象属于一种复合属性的类型,在对象中可以保存多个不同的数据类型的属性
类型
内建对象
- 由ES标准中定义的对象,在如何的ES的实现中都可以使用
- 如:
Math String Number Boolean Function Object
宿主对象
- 由JS的运行环境提供的对象,目前来将主要指由浏览器提供的对象
- 比如 BOM DOM
自建对象
- 由开发人员直接创建
基本操作
- 创建对象
- 使用
new
关键字调用的函数,是构造函数constructor
构造函数
是专门用来创建对象的函数
let obj = new Object();
console.log(typeof obj); // Object
- 使用
- 向对象添加属性
- 在对象中的值称为属性
- 语法:
对象
.属性名
=属性值
;对象
[属性名
] =属性值
;obj.name = "a dian";
obj.age = 20;
obj['sex'] = '男'
- 读取对象中的属性
- 语法:
对象
.属性名
;对象
[属性名
]; 注意
:读取未定义的属性,不会报错,而是返回undefined
console.log(obj.name); //a dian
console.log(obj.age); //20
console.log(obj.sex); //男
console.log(obj.hello); //undefined
- 语法:
- 修改对象的属性值
- 语法:
对象
.属性名
=新属性值
;obj.name = "chen a dian";
- 语法:
- 删除对象的属性
- 语法:
delete
对象
.属性名
delete obj.name;
console.log(obj.name); //undefined
- 语法:
属性名与属性值
- 属性名
对象的属性名不强制
要求遵守标识符的规范
但最好还是用规范命名
特殊命名:obj.123 = 123; //报错
obj["123"] = 123; //正确
console.log(obj.123); //报错
console.log(obj["123"]); //正确 - 属性值
属性值可以是任意的数据类型:number string null undefined boolean
也可以是对象object
in
运算符检查属性
是否 在指定的对象中有则true
,无则false
语法:“属性名” in 对象
obj.name = "我是名字";
console.log("name" in obj);
枚举对象中的属性
当不知道对象中有什么属性时
可以用 for…in
来输出对象的各个属性
语法:for(var 变量 in 对象){ console.log(n); }
let obj={ |
基本数据类型和引用数据类型
- 基本数据类型:
Number、String、Boolean、Null、Undefined
- 引用数据类型:
Object
JS中的变量
都是保存到栈内存
中的基本数据类型的值
直接在栈内存
中储存对象
是保存在堆内存
中,每新建一个对象,就在堆内存中开辟一个新的空间- 而变量保存的是
对象的内存地址(对象的引用)
,如果两个变量保存的是同一个对象引用, - 当一个变量改变时,另一个变量会受到影响。
- 而变量保存的是
- 比较
两个基本数据类型
时,就是在比较两数据的值
,而比较两对象
时,比较的是两对象的内存地址
,两对象
的内容一样
,地址不一样
会返回false
对象字面量
使用字面量创建一个对象
let obj = {}; |
使用对象字面量,可以在创建对象时,直接指定对象中的属性
语法 : 声明 对象名 = {属性名:属性值,属性名:属性值…}
let obj = { |
函数
简介
函数也是对象
声明
语法:
function 函数名([形参1,形参2]){
语句。。。。
}
function showOne(){ |
调用
刚刚创建了一个函数showOne(),现在就可以调用,如:
showOne(); //警告框:声明了一个函数 |
参数
参数可称为函数参数
,可以使用参数来将任意数据传递给函数
形参 (形式参数)
- 可以在函数的
括号()
中来指定一个或多个形参(形式参数) - 多个形参之间使用(
,
)隔开,声明形参
就是相当于在函数内部声明对应的变量
,但不赋值
function sum(x,y){
alert(x+y);
}
sum(); //Null
- 可以在函数的
实参 (实际参数)
- 在
调用函数
时,可以在函数的括号()
里来指定实参(实际参数) 实参
将会赋值
给函数对应的形参
形参
和实参
不会检查类型和数量- 当
形参
过多时,建议把形参
封装进对象
function sum(x,y){
alert(x+y);
}
sum(1,"asd"); //1asd
- 在
返回值
return
返回函数的值return
后,无法运行后面的代码
return
可以返回任何数据类型
单写一个return
返回undefined
不写return
返回undefined
return
可以加上判断 来结束整个函数运行
function sum(x,y){ |
局部变量
在调用
时,创建
在结束
时,销毁
每次调用,都是独立
的,
调用三次,便是创建销毁三次
在函数中声明的变量
,只能在函数内部中使用
function showOne(){ |
全局变量
在页面打开时创建
,在页面关闭时销毁
全局变量
都会作为window的属性
保存
在函数内
可以调用外部的变量
let a = "外部的变量"; |
函数即可以调用函数外部的变量
,也可以改变外部的变量
let a = "外部的变量"; |
只有在没有局部变量
的情况下,才会去调用外部变量
,
如果局部变量
和外部变量同名
,局部变量优先级
比外部变量高
,会使用局部变量
let a = "我是外部变量"; |
全局变量
任何
函数外
声明的变量,都称为全局变量
任何全局变量
在任何函数
中都是可见
的,除非局部变量
和全局变量同名
,全局
就会被遮蔽
减少全局变量的使用是一种很好的做法。现在的代码有很少甚至没有全局变量。大多数变量存在于它们的函数中。但是有时候,全局变量能够用于存储项目级别的数据。
默认值
参数是可以带有默认值
的
当没有给值传递给参数时,则得到undefined
如上一个函数 showSum(); 来举例
function showSum(oneSrting,towString){ |
所以可以给参数给个默认值
,当没值传递给参数
时,便使用默认值
function showSum(oneSrting,towString = "我是第二个参数"){ |
值是从左到右
按顺序传递的
function showSum(oneSrting = "我是第二个参数",towString){ |
后备的默认参数
当没给参数默认值,又不想它返回undefined
为了判断参数
是否没用传递到值,我们可以拿它和undefined
做比较
立即执行函数
function(){ |
创建一个匿名函数
,但是这样报错
因为没有函数名
,不知道它是什么
所以可以加个括号()
,让程序知道是一个整体
(function(){ |
而执行只需要在后面再加一个括号()
即可
(function(){ |
这样就会被立即执行
,且只运行一次
,不占内存
方法
函数
也可以称为对象的属性
如果一个函数
作为一个对象
的属性
保存
那么就称这个对象的方法
调用函数
就是调用对象的方法
调用方法和调用函数只是名字的区别
let obj = new Object(); |
声明提前
变量声明提前
var
关键字声明的变量,会在代码执行之前被声明
,
如果不用var
声明,则变量不会被声明提前
alert(a); // undefined |
实际上,是这样运行的
var a |
函数声明提前
function
声明的函数会在代码执行之前
,就先创建
fun(); |
实际上,是这样运行的
function fun(){ |
this
对象方法
需要访问对象中存储的信息
才能完成其工作。
如:
user.sayHi(); |
而 this的值
便是 user
this.sayHi(); |
user
会变,而this
则不会变
总结:
- 当以
函数
的形式调用时,this
是window
- 当以
方法
的形式调用时,谁调用方法,this就是谁,即当前对象
- 当以
构造函数
的形式调用时,this就是新创建的那个对象
构造函数
构造函数
就是普通的函数
,就是调用方式不同
不同的是构造函数
习惯上首字母大写
普通函数
是直接调用
,而构造函数
需要使用new关键字来调用
执行流程
- 1.立即创建一个
新的对象
- 2.将
新建的对象
设置为函数中this
,在构造函数
中可以使用this
来引用新建的对象
- 3.
逐行
执行函数中的代码
- 4.将
新建的对象
作为返回值
function Person(name , age ,gender){ |
per
为 Person构造函数
的实例
per
为实例
Person
为类/构造函数
使用同一个构造函数创建的对象,我们称为一类对象,也将一个构造函数
称为一个类
。
我们将通过一个构造函数创建的对象
,称为是该类的实例
构造函数
就是类
,实例
通过构造函数
创建的对象
instanceof
检查一个对象
是否是一个类的实例
语法: 对象 instanceof 构造函数
如果是
,则ture
,否则false
console.log(per instanceof Person); //true |
所有对象
都是 object
的对象
即 返回 true
原型对象 prototype
创建的每一个函数,解析器都会向函数中添加一个属性prototype
,这个属性
对应着一个对象
,这个对象
就是我们所谓的原型对象
如果函数作为普通函数
调用prototype
没有任何作用,
当函数以构造函数
的形式调用时,它所创建的对象
中都会有一个隐含的属性
, 指向该构造函数
的原型对象
,我们可以通过__proto__
来访问该属性
原型对象prototype
就相当于类
的公共的区域
,所有同一个类
的实例
都可以访问到这个原型对象
, 我们可以将对象中共有
的内容,统一设置到原型对象
中。
function Person(name , age ,gender){ |
执行过程
访问对象
的一个属性
或方法
时
先在对象自身
里找属性
, 有则直接使用
无则去prototype
里面找
原理
每个构造函数
,即类
在创建时,都有一个隐藏属性prototype
,这个空间为堆内存
而通过这个类/构造函数
,创建的实例
中的栈内存
都有去指向这个堆内存
即 类与实例 公用的 堆内存
便是 prototype
在实例自身
属性中没找到的属性
,就会去prototype
里面找
in
检查对象
中的属性
使用in
检查对象中是否含有某个属性时,有
则true
无
则false
但 prototype
里面的也会被检查
hasOwnProperty
检查对象自身
中的属性
使用hasOwnProperty
检查对象中是否含有某个属性时,有
则true
无
则false
不过是在自身
中找,不包括原型(prototype
)
原型对象 里面的 原型对象
原型对象
也是对象
,所以也有原型
当使用一个对象的属性
或方法
时,会在自身
中寻找
如果没有
则去原型对象
中寻找,原型对象
中没有,则去原型对象
中的原型对象
中找
直到找到object对象
的原型
,Object对象
的原型
没有原型
,如果在Object原型中依然没有找到,属性
则返回undefinedObject对象
的原型
的值是null
一般
就 嵌套两个
toString
当在页面打印一个对象
时,实际上是输出对象
的toString
的返回值
function Person(name , age ,gender){ |
per
和per.toString()
是一样的
而要输出属性,则修改toString
Person.prototype.toString = function(){ |
垃圾回收(GC)
程序运行
就会产生垃圾
,
占用大量内存空间
,程序运行变慢
内存溢出
,程序瘫痪
而js
有自动回收机制
我们只需要断开``栈内存
与堆内存
的连接
让浏览器等编译器知道可以回收即可,对象
设置null
function Person(name , age ,gender){ |
数组(Array)
数组也是对象
,但不是普通对象
,普通对象
使用字符
作为属性名
数组对象
使用数字
作为索引(下标)
数组
可以放任何数据类型
,
包括对象 、函数、数组
数组
索引(下标)
从0开始的整数
就是索引
存储
数组的存储性能
比普通对象要好
创建数组
- 构造函数创建
let arr = new Array();
console.log(typeof arr); //object - 字面量创建
let arr = [];
console.log(typeof arr); //object - 数组添加元素
语法:数组[索引] = 值
读取不存在
的索引
,返回undefined
let arr = new Array();
arr[0] = 123;
arr[1] = 456;
arr[2] = 789;
console.log(arr[0]); //123
console.log(arr[1]); //456
console.log(arr[2]); //789
console.log(arr[3]); //undefined - 数组长度(length)
使用length
属性获取数组的长度(元素的个数)
语法:数组.length
对let arr = new Array();
arr[0] = 123;
arr[1] = 456;
arr[2] = 789;
console.log(arr.length); // 3非连续
的数组
,使用length
会获取到最大索引+1
let arr = new Array();
arr[0] = 123;
arr[1] = 456;
arr[2] = 789;
arr[10] = 789;
console.log(arr.length); // 11 - 修改length
length 大于
原长度,则多出部分
会空出来
length 小于
原长度,则多出部分
会被删除
- 向数组的最后一个位置添加元素
语法:数组[数组.length] = 值
let arr = new Array();
arr[0] = 123;
arr[1] = 456;
arr[2] = 789;
arr[arr.length]=10;
arr[arr.length]=20;
console.log(arr[3]); // 10
console.log(arr[4]); // 20 - 创建时添加元素
- 构造函数创建
let arr = new Array(10,20,30);
console.log(arr[0],arr[1],arr[2],arr.length); //10 20 30 3 - 字面量创建
let arr = [10,20,30];
console.log(arr[0],arr[1],arr[2],arr.length); //10 20 30 3
- 构造函数创建
- 创建只添加一个元素,即数组长度
- 构造函数创建
let arr = new Array(10);
console.log(arr[0],arr[1],arr[2],arr.length); //undefined undefined undefined 10 - 字面量创建
let arr = [10];
console.log(arr[0],arr[1],arr[2],arr.length); //undefined undefined undefined 10
- 构造函数创建
数组的方法
push()
向数组的末尾
添加一个
或多个
元素,并把数组的新长度
作为返回值
let arr = [10,20,30]; |
pop()
删除
数组最后一个元素
,并把删除的元素
作为返回值
let arr = [10,20,30]; |
unshift()
向数组的开头
添加一个
或多个
元素,并把数组的新长度
作为返回值
let arr = [10,20,30]; |
shift()
删除
数组开头一个
元素,并把删除的元素
作为返回值
let arr = [10,20,30]; |
slice()
提取``数组``指定
的元素
,不影响原数组
,将截取到的元素封装到一个新数组
中返回
- 参数:
- 1.截取开始位置的索引,
包含
开始的索引 - 2.截取结束位置的索引,
不包含
结束的索引第二个参数
可以省略不写
,此时会截取从开始索引往后的所有元素
第二个参数
是负值
则从最后往前算
-1
则是倒数第一个
-2
则是倒数第二个
- 1.截取开始位置的索引,
let perArr = ["孙悟空","红孩儿","猪八戒","二郎神","唐三藏","沙悟净"]; |
splice()
删除
数组指定
的元素,会影响原数组
, 将被删除的元素
作为返回值
返回
- 参数:
- 第一个,表示开始位置的索引,
包含
开始的索引 - 第二个, 表示
删除的数量
- 第三个及以后,可以传递
新的元素
,这些元素将会自动插入到开始位置索引前边
- 第一个,表示开始位置的索引,
let perArr = ["孙悟空","红孩儿","猪八戒","二郎神","唐三藏","沙悟净"]; |
concat()
连接两个
或多个数组
或元素
,并将新的数组返回
,不会影响原数组
let perArr1 = ["孙悟空","红孩儿"]; |
join()
将数组
转换成一个字符串
,不会影响原数组
,返回字符串
let perArr1 = ["孙悟空","红孩儿"]; |
实参默认为逗号(,)
,可进行更改
let perArr1 = ["孙悟空","红孩儿"]; |
reverse()
颠倒
原数组
,影响原数组
,返回颠倒后的数组
let perArr1 = ["孙悟空","沙悟净","猪八戒","唐三藏"]; |
sort()
对数组的元素进行排序
,影响原数组
,返回排序后的数组
默认对 Unicode
编码进行排序
let str1 = [e,c,b,f,a,d]; |
对数字的排序也是按照 Unicode
排序
let str1 = [1,2,5,4,8,11]; |
所以可以在sort()添加回调函数
,来指定排序规则
回调函数中需要定义两个形参
,
浏览器将会分别使用数组中的元素作为实参去调用回调函数第一个形参
数所代表的实参
始终在第二个形参
代表的实参
的后面
let str1 = [1,3,2]; |
浏览器会根据回调函数的返回值
来决定元素
的顺序
,
如果返回一个大于0
的值,则元素会交换位置
如果返回一个小于0
的值,则元素位置不变
如果返回一个0
,则认为两个元素相等
,也不交换位置
如果需要升序
排列,则返回a-b
如果需要降序
排列,则返回b-a
let str1 = [1,5,2,8,4,9,11,0,3,6,7,10]; |
数组的遍历
运用for循环
遍历数组就是把数组的所有元素
都取出来
let arr = [10,20,30]; |
forEach()
ie8
以上支持forEach()
方法需要一个函数
作参数
let perArr = ["孙悟空","红孩儿","猪八戒","二郎神","唐三藏","沙悟净"]; |
但一般直接写函数
let perArr.forEach(function(){ |
而这种函数是由开发人员创建
,但却不是由开发人员调用
的,称为回调函数
并且数组中有几个元素,该函数就执行多少遍
如果有添加上形参
let perArr = ["孙悟空","红孩儿","猪八戒","二郎神","唐三藏","沙悟净"]; |
函数的方法
call()和apply()
call()
和apply()
用来改变this
的指向
function fun(){ |
call()
和apply()
的区别在于所传的实参类型
call()
是一个一个
实参传递到形参,和普通传递一样;apply()
是一个整体数组
实参传递到形参,实参也得加上中括号([])
;
function fun(a, b) { |
arguments
- 在调用函数时,浏览器每次都会传递进两个隐含的参数:
- 函数的上下文对象 this
- 封装实参的对象 arguments
- arguments是一个类数组对象,它也可以通过索引来操作数据,也可以获取长度
- 在调用函数时,我们所传递的实参都会在arguments中保存
- arguments.length可以用来获取实参的长度
- 我们即使不定义形参,也可以通过arguments来使用实参, 只不过比较麻烦
- arguments[0] 表示第一个实参
- [1] 表示第二个实参 。。。
- 它里边有一个属性叫做callee,
- 这个属性对应一个函数对象,就是当前正在指向的函数的对象
function fun(a,b){
//console.log(arguments instanceof Array);
//console.log(Array.isArray(arguments));
//console.log(arguments[1]);
//console.log(arguments.length);
console.log(arguments.callee == fun);
}
fun("hello",true);
- 这个属性对应一个函数对象,就是当前正在指向的函数的对象
Date对象
- 在JS中使用Date对象来表示一个时间
- new Date()
- getDate()
- 获取当前日期对象是几日
- getDay()
- 获取当前日期对象时周几
- 会返回一个0-6的值,0 表示周日 、1表示周一
- getMonth()
- 获取当前时间对象的月份
- 会返回一个0-11的值, 0 表示1月、11 表示12月
- getFullYear()
- 获取当前日期对象的年份
- getTime()
- 获取当前日期对象的时间戳
- 时间戳,指的是从格林威治标准时间的1970年1月1日,0时0分0秒,到当前日期所花费的毫秒数(1秒 = 1000毫秒)
- 计算机底层在保存时间时使用都是时间戳
- Date.now()
- 利用时间戳来测试代码的执行的性能
- 获取当前的时间戳
//创建一个Date对象 |
Math
- Math和其他的对象不同,它不是一个构造函数,它属于一个工具类不用创建对象,它里边封装了数学运算相关的属性和方法
- Math.PI
- 表示的圆周率
- Math.abs()
- 可以用来计算一个数的绝对值
- Math.ceil()
- 可以对一个数进行向上取整,小数位只有有值就自动进1
- Math.floor()
- 可以对一个数进行向下取整,小数部分会被舍掉
- Math.round()
- 可以对一个数进行四舍五入取整
- Math.random()
- 可以用来生成一个0-1之间的随机数
- 生成一个0-x之间的随机数:Math.round(Math.random()*x)
- Math.max()、Math.min()
- 可以获取多个数中的最大值、最小值
- Math.pow(x,y)
- 返回x的y次幂
- Math.sqrt()
- 用于对一个数进行开方运算
/* |
包装类
- 基本数据类型:String Number Boolean Null Undefined
- 引用数据类型:Object
- 在JS中为我们提供了三个包装类,通过这三个包装类可以将基本数据类型的数据转换为对象
- String()
- 可以将基本数据类型字符串转换为String对象
- Number()
- 可以将基本数据类型的数字转换为Number对象
- Boolean()
- 可以将基本数据类型的布尔值转换为Boolean对象
- String()
- 但是注意:我们在实际应用中不会使用基本数据类型的对象,如果使用基本数据类型的对象,在做一些比较时可能会带来一些不可预期的结果
//创建一个Number类型的对象
//num = 3;
var num = new Number(3);
var num2 = new Number(3);
var str = new String("hello");
var str2 = new String("hello");
var bool = new Boolean(true);
var bool2 = true;
//向num中添加一个属性
num.hello = "abcdefg";
//console.log(str === str2);
var b = new Boolean(false);
/*if(b){
alert("我运行了~~~");
}*/
/*
* 方法和属性之能添加给对象,不能添加给基本数据类型
* 当我们对一些基本数据类型的值去调用属性和方法时,
* 浏览器会临时使用包装类将其转换为对象,然后在调用对象的属性和方法
* 调用完以后,在将其转换为基本数据类型
*/
var s = 123;
s = s.toString();
s.hello = "你好";
console.log(s.hello);
//console.log(typeof s);
字符串方法
- length属性
- 可以用来获取字符串的长度
- concat()
- 可以用来连接两个或多个字符串,作用和+一样
- indexof()
- 该方法可以检索一个字符串中是否含有指定内容
- 如果字符串中含有该内容,则会返回其第一次出现的索引;如果没有找到指定的内容,则返回-1
- 可以指定一个第二个参数,指定开始查找的位置
- lastIndexOf()
- 该方法的用法和indexOf()一样
- 不同的是indexOf是从前往后找,而lastIndexOf是从后往前找
- 也可以指定开始查找的位置
- slice()
- 可以从字符串中截取指定的内容
- 不会影响原字符串,而是将截取到内容返回
- 参数:
- 第一个,开始位置的索引(包括开始位置)
- 第二个,结束位置的索引(不包括结束位置),如果省略第二个参数,则会截取到后边所有的
- 也可以传递一个负数作为参数,负数的话将会从后边计算
- substring()
- 可以用来截取一个字符串,可以slice()类似
- 参数:
- 第一个:开始截取位置的索引(包括开始位置)
- 第二个:结束位置的索引(不包括结束位置)
- 不同的是这个方法不能接受负值作为参数, 如果传递了一个负值,则默认使用0
- 而且他还自动调整参数的位置,如果第二个参数小于第一个,则自动交换
- substr()
- 用来截取字符串
- 参数:
- 截取开始位置的索引
- 截取的长度
- split()
- 可以将一个字符串拆分为一个数组
- 参数:
- 需要一个字符串作为参数,将会根据该字符串去拆分数组
- 如果传递一个空串作为参数,则会将每个字符都拆分为数组中的一个元素
- toUpperCase()
- 将一个字符串转换为大写并返回
- toLowerCase()
- 将一个字符串转换为小写并返回
- charAt()
- 可以返回字符串中指定位置的字符,根据索引获取指定的字符
- charCodeAt()
- 获取指定位置字符的字符编码(Unicode编码)
- String.formCharCode()
- 可以根据字符编码去获取字符
//创建一个字符串 |
正则表达式
创建正则表达式的对象
- 正则表达式用于定义一些字符串的规则,计算机可以根据正则表达式,来检查一个字符串是否符合规则,获取将字符串中符合规则的内容提取出来
- 语法:var 变量 = new RegExp(“正则表达式”,”匹配模式”);
- new RegExp(“a”); 这个正则表达式可以来检查一个字符串中是否含有a
- 在构造函数中可以传递一个匹配模式作为第二个参数,
- i 忽略大小写
- g 全局匹配模式
- 使用typeof检查正则对象,会返回object
- 正则表达式的方法:
- test():使用这个方法可以用来检查一个字符串是否符合正则表达式的规则,如果符合则返回true,否则返回false
// 检查一个字符串中是否含有ab
var reg = new RegExp('ab', i)
var str = 'a'
var result = reg.test(str) // false
- test():使用这个方法可以用来检查一个字符串是否符合正则表达式的规则,如果符合则返回true,否则返回false
字面量创建正则表达式
语法:var 变量 = /正则表达式/匹配模式
使用字面量的方式创建更加简单,使用构造函数创建更加灵活
var reg = /a/i
reg.test('Abc') // true使用 | 表示或者的意思
//创建一个正则表达式,检查一个字符串中是否有a或b或c
var reg = /a|b|c/[]里的内容也是或的关系, [ab] == a|b
- [a-z] 任意小写字母
- [A-Z] 任意大写字母
- [A-z] 任意字母
- [0-9] 任意数字
//检查一个字符串中是否含有 abc 或 adc 或 aec
var reg = /a[bde]c/;
[^ ] 除了(只有除了的内容显示false,其他情况显示true)
var reg = /[^0-9]/;
reg.test("a") // true
reg.test("12a3456") // true
reg.test("12") // false
字符串和正则相关的方法
- split()
- 可以将一个字符串拆分为一个数组
- 方法中可以传递一个正则表达式作为参数,这样方法将会根据正则表达式去拆分字符串
- 这个方法即使不指定全局匹配,也会全都插分
// 根据任意字母来将字符串拆分
var str = "1a2b3c4d5e6f7";
var result = str.split(/[A-z]/);
console.log(result) // ['1', '2', '3', '4', '5', '6', '7']
- search()
- 可以搜索字符串中是否含有指定内容
- 如果搜索到指定内容,则会返回第一次出现的索引,如果没有搜索到返回-1
- 它可以接受一个正则表达式作为参数,然后会根据正则表达式去检索字符串
- serach()只会查找第一个,即使设置全局匹配也没用
// 搜索字符串中是否含有abc 或 aec 或 afc
var str = "hello abc hello aec afc";
var result = str.search(/a[bef]c/)
console.log(result); // 6
- 可以搜索字符串中是否含有指定内容
- match()
- 可以根据正则表达式,从一个字符串中将符合条件的内容提取出来
- 默认情况下match只会找到第一个符合要求的内容,找到以后就停止检索
- 可以设置正则表达式为全局匹配模式,这样就会匹配到所有的内容
- 可以为一个正则表达式设置多个匹配模式,且顺序无所谓
- match()会将匹配到的内容封装到一个数组中返回,即使只查询到一个结果
var str = "1a2a3a4a5e6f7A8B9C";
var result = str.match(/[a-z]/ig);
console.log(result) // ['a', 'a', 'a', 'a', 'e', 'f', 'A', 'B', 'C']
- replace()
- 可以将字符串中指定内容替换为新的内容
- 参数:
- 被替换的内容,可以接受一个正则表达式作为参数
- 新的内容
- 默认只会替换第一个
var str = "1a2a3a4a5e6f7A8B9C";
var result = str.replace(/[a-z]/gi , "@_@"); // 1@_@2@_@3@_@4@_@5@_@6@_@7@_@8@_@9@_@
result = str.replace(/[a-z]/gi , ""); // 123456789
简写正则
量词
- 通过量词可以设置一个内容出现的次数
- 量词只对它前边的一个内容起作用
- {n} 正好出现连续n个
- {m,n} 匹配包含最少 m 个、最多 n 个
- {m,} 至少m个
- + 至少一个,相当于{1,}
- * 0个或多个,相当于{0,}
- ? 0个或1个,相当于{0,1}
var reg = /a{3}/;
reg = /(ab){3}/;
reg = /b{3}/;
reg = /ab{1,3}c/;
reg = /ab{3,}c/;
reg = /ab+c/;
reg = /ab*c/;
reg = /ab?c/;
检查一个字符串开头或结尾
- ^ 表示开头
- $ 表示结尾
如果在正则表达式中同时使用^ $
则要求字符串必须完全
符合正则表达式var reg = /^a/; //匹配开头的a
reg = /a$/; //匹配结尾的a
reg = /^a$/;
console.log(reg.test("bbca")) // false
// 创建一个正则表达式,用来检查一个字符串是否是一个合法手机号
/**
* 手机号的规则:
* 1 3 567890123 (11位)
*
* 1. 以1开头
* 2. 第二位3-9任意数字
* 3. 三位以后任意数字9个
* ^1 [3-9] [0-9]{9}$
*/
var phoneReg = /^1[3-9][0-9]{9}$/;
检查一个字符串中是否含有 .
- . 表示任意字符
- 在正则表达式中使用\作为转义字符
- \. 表示.
- \\ 表示
使用构造函数
时,由于它的参数是一个字符串,而\是字符串中转义字符,使用\则需要使用\\来代替var reg = /\./;
reg = /\\/;
reg = new RegExp("\\.");
reg = new RegExp("\\\\");
\w
- 任意字母、数字、_ [A-z0-9_]
\W
- 除了字母、数字、_ [^A-z0-9_]
\d
- 任意的数字 [0-9]
\D
- 除了数字 [^0-9]
\s
- 空格
\S
- 除了空格
\b
- 单词边界
\B
- 除了单词边界
// 创建一个正则表达式检查一个字符串中是否含有单词child
var reg = /\bchild\b/;
console.log(reg.test("hello child ")); // true
console.log(reg.test("hellochild ")); // false
// /^\s*|\s*$/g 匹配开头和结尾的空格
var str = str.replace(/^\s*|\s*$/g,"");邮箱正则
/**
* 电子邮箱
* hello .nihao @ abc .com.cn
* 任意字母数字下划线 .任意字母数字下划线 @ 任意字母数字 .任意字母(2-5位) .任意字母(1-2位)
* \w{3,} (\.\w+)* @ [A-z0-9]+ (\.[A-z]{2, 5}){1, 2}
*/
var emailReg = /^\w{3,}(\.\w+)*@[A-z0-9]+(\.[A-z]{2,5}){1,2}$/;
var email = "abc.hello@163.com";
console.log(emailReg.test(email)); // true
- 除了单词边界
DOM
document
- 浏览器已经为我们提供 文档节点,这个对象是window属性
- 可以在页面中直接使用,文档节点代表的是整个网页
console.log(document);
//获取到button对象
var btn = document.getElementById("btn");
//修改按钮的文字
btn.innerHTML = "I'm Button";
事件
- 用户和浏览器之间的交互行为,比如:点击按钮,鼠标移动、关闭窗口…
<body>
<!--
我们可以在事件对应的属性中设置一些js代码,
这样当事件被触发时,这些代码将会执行
这种写法我们称为结构和行为耦合,不方便维护,不推荐使用
<button id="btn1" onmousemove="alert('讨厌,你点我干嘛!');">我是一个按钮</button>
-->
<button id="btn">我是一个按钮</button>
<script type="text/javascript">
//获取按钮对象
var btn = document.getElementById("btn");
/**
* 可以为按钮的对应事件绑定处理函数的形式来响应事件
* 这样当事件被触发时,其对应的函数将会被调用
*/
//绑定一个单击事件
//像这种为单击事件绑定的函数,我们称为单击响应函数
btn.onclick = function(){
alert("你还点~~~");
};
</script>
</body>
文档加载
- 浏览器在加载一个页面时,是按照自上向下的顺序加载的
- 读取到一行就运行一行,如果将script标签写到页面的上边,
- 在代码执行时,页面还没有加载,页面DOM对象也没有加载,
- 会导致无法获取到DOM对象
- onload事件会在整个页面加载完成之后才触发,为window绑定一个onload事件
- 该事件对应的响应函数将会在页面加载完成之后执行,
- 这样可以确保我们的代码执行时所有的DOM对象已经加载完毕了
<head>
<meta charset="UTF-8">
// 第一种
<script type="text/javascript">
window.onload = function(){
//获取id为btn的按钮
var btn = document.getElementById("btn");
//为按钮绑定一个单击响应函数
btn.onclick = function(){
alert("hello");
};
};
</script>
</head>
<body>
<button id="btn">点我一下</button>
// 第二种
<script type="text/javascript">
/**
* 将js代码编写到页面的下部就是为了可以在页面加载完毕以后再执行js代码
*/
//获取id为btn的按钮
var btn = document.getElementById("btn");
//为按钮绑定一个单击响应函数
btn.onclick = function(){
alert("hello");
};
</script>
</body>
dom查询
- innerHTML 通过这个属性可以获取到元素内部的html代码,对于自结束标签,这个属性没有意义
- innerText 该属性可以获取到元素内部的文本内容, 它和innerHTML类似,不同的是它会自动将html去除
- getElementById()可以根据id来获取元素节点对象
- getElementsByTagName()可以根据标签名来获取一组元素节点对象
- 这个方法会给我们返回一个类数组对象,所有查询到的元素都会封装到对象中
- 即使查询到的元素只有一个,也会封装到数组中返回
var lis = document.getElementsByTagName("li");
- getElementsByName()查找指定name的所有节点
- 需要读取元素节点属性,直接使用 元素.属性名
- 例子:元素.id 元素.name 元素.value
- 注意:class属性不能采用这种方式,读取class属性时需要使用 元素.className
var inputs = document.getElementsByName("gender");
var um = document.getElementById("username");
//读取um的value属性值,文本框的value属性值,就是文本框中填写的内容
console.log(um.value);
// 设置#username的value属性值
um.value = "今天天气真不错~~~";
- 需要读取元素节点属性,直接使用 元素.属性名
- childNodes属性会获取包括文本节点在内的所有节点
- 根据DOM标签标签间空白也会当成文本节点
- 注意:在IE8及以下的浏览器中,不会将空白文本当成子节点,所以该属性在IE8中会返回4个子元素而其他浏览器是9个
var city = document.getElementById("city");
//返回#city的所有子节点
var cns = city.childNodes;
- firstChild可以获取到当前元素的第一个子节点(包括空白文本节点)
var phone = document.getElementById("phone");
//返回#phone的第一个子节点
//phone.childNodes[0];
var fir = phone.firstChild; - parentNode获取父节点
var bj = document.getElementById("bj");
//返回#bj的父节点
var pn = bj.parentNode; - previousSibling前一个兄弟节点(也可能获取到空白的文本)
var and = document.getElementById("android");
//返回#android的前一个兄弟节点(也可能获取到空白的文本)
var ps = and.previousSibling; - 在document中有一个属性body,它保存的是body的引用
var body = document.body;
//获取body标签
//var body1 = document.getElementsByTagName("body")[0]; - document.documentElement保存的是html根标签
var html = document.documentElement;
- document.all代表页面中所有的元素或document.getElementsByTagName(“*”)
var all = document.all;
all = document.getElementsByTagName("*"); - getElementsByClassName()可以根据class属性值获取一组元素节点对象,但是该方法不支持IE8及以下的浏览器
var box1 = document.getElementsByClassName("box1");
- document.querySelector()
- 需要一个选择器的字符串作为参数,可以根据一个CSS选择器来查询一个元素节点对象
- 虽然IE8中没有getElementsByClassName()但是可以使用querySelector()代替
- 使用该方法总会返回唯一的一个元素,如果满足条件的元素有多个,那么它只会返回第一个
var div = document.querySelector(".box1 div");
// 通过 Id 选择器选取元素
var element1 = document.querySelector("#myDiv");
console.log(element1); // 输出:<div id="myDiv">...</div>
// 通过类选择器选取元素
var element2 = document.querySelector(".selected");
console.log(element2); // 输出:<p class="selected">Hello World!</p>
// 通过标签选择器选取元素
var element3 = document.querySelector("p");
console.log(element3); // 输出:<p class="selected">Hello World!</p>
// 当没有匹配的元素时返回 null
var element4 = document.querySelector(".nonexistent");
console.log(element4); // 输出:null
- document.querySelectorAll()
- 该方法和querySelector()用法类似,不同的是它会将符合条件的元素封装到一个数组中返回
- 即使符合条件的元素只有一个,它也会返回数组
var box = document.querySelectorAll("#box");
var elements = document.querySelectorAll(".selected");
console.log(elements); // 输出:NodeList [ <p class="selected">Hello World!</p> ]
dom增删改
dom增
document.createElement()
- 可以用于创建一个元素节点对象,
- 它需要一个标签名作为参数,将会根据该标签名创建元素节点对象,
- 并将创建好的对象作为返回值返回
//创建li元素节点
var li = document.createElement("li");
document.createTextNode()
- 可以用来创建一个文本节点对象
- 需要一个文本内容作为参数,将会根据该内容创建文本节点,并将新的节点返回
//创建广州文本节点
var gzText = document.createTextNode("广州");
appendChild()
- 向一个父节点中添加一个新的子节点
- 用法:父节点.appendChild(子节点);
//创建广州节点 <li>广州</li>
//将gzText设置li的子节点
var li = document.createElement("li");
var gzText = document.createTextNode("广州");
li.appendChild(gzText);
//获取id为city的节点
var city = document.getElementById("city");
//将广州添加到city下
city.appendChild(li);
dom插入
- insertBefore()
- 可以在指定的子节点前插入新的子节点
- 语法:父节点.insertBefore(新节点,旧节点);
//将"广州"节点插入到#bj前面
//创建一个广州
var li = document.createElement("li");
var gzText = document.createTextNode("广州");
li.appendChild(gzText);
//获取id为bj的节点
var bj = document.getElementById("bj");
//获取city
var city = document.getElementById("city");
/*
* insertBefore()
* - 可以在指定的子节点前插入新的子节点
* - 语法:
* 父节点.insertBefore(新节点,旧节点);
*/
city.insertBefore(li , bj);
dom修改
- replaceChild()
- 可以使用指定的子节点替换已有的子节点
- 语法:父节点.replaceChild(新节点,旧节点);
//使用"广州"节点替换#bj节点
//创建一个广州
var li = document.createElement("li");
var gzText = document.createTextNode("广州");
li.appendChild(gzText);
//获取id为bj的节点
var bj = document.getElementById("bj");
//获取city
var city = document.getElementById("city");
city.replaceChild(li , bj);
dom删除
- removeChild()
- 可以删除一个子节点
- 语法:父节点.removeChild(子节点) 或 子节点.parentNode.removeChild(子节点);
//获取id为bj的节点
var bj = document.getElementById("bj");
//获取city
var city = document.getElementById("city");
city.removeChild(bj)
// 或 bj.parentNode.removeChild(bj);
HTML代码
读取HTML代码
- 节点.innerHTML
//读取#city内的HTML代码
var city = document.getElementById("city");
alert(city.innerHTML);
- 节点.innerHTML
设置HTML代码
- 节点.innerHTML = 设置内容
//设置#bj内的HTML代码
var bj = document.getElementById("bj");
bj.innerHTML = "昌平";
- 节点.innerHTML = 设置内容
使用innerHTML也可以完成DOM的增删改的相关操作,一般两种方式结合使用
//向city中添加广州
var city = document.getElementById("city");
//创建一个li
var li = document.createElement("li");
//向li中设置文本
li.innerHTML = "广州";
//将li添加到city中
city.appendChild(li);
// 或 city.innerHTML += "<li>广州</li>"额外知识
confirm()用于弹出一个带有确认和取消按钮的提示框
- 需要一个字符串作为参数,该字符串将会作为提示文字显示出来
- 如果用户点击确认则会返回true,如果点击取消则返回false
//删除之前弹出一个提示框
var flag = confirm("确认删除"+name+"吗?");
//如果用户点击确认
if(flag){
// ...
}
DOM操作CSS
修改元素样式
通过JS修改元素的样式
- 语法:元素.style.样式名 = 样式值
- 注意:如果CSS的样式名中含有-,这种名称在JS中是不合法的比如background-color,需要将这种样式名修改为驼峰命名法,去掉-,然后将-后的字母大写
- 通过style属性设置的样式都是内联样式,而内联样式有较高的优先级,所以通过JS修改的样式往往会立即显示
- 如果在样式中写了!important,则此时样式会有最高的优先级,即使通过JS也不能覆盖该样式,此时将会导致JS修改样式失效,所以尽量不要为样式添加!important
// 点击按钮以后,修改box1的大小
var box1 = document.getElementById("box1");
//为按钮绑定单击响应函数
var btn01 = document.getElementById("btn01");
btn01.onclick = function(){
box1.style.width = "300px";
box1.style.height = "300px";
box1.style.backgroundColor = "yellow";
}
读取元素样式
- 语法:元素.style.样式名
- 通过style属性设置和读取的都是内联样式,无法读取样式表中的样式
读取元素样式
- currentStyle
- 语法:元素.currentStyle.样式名
- 可以用来读取当前元素正在显示的样式,如果当前元素没有设置该样式,则获取它的默认值
- currentStyle只有IE浏览器支持,其他的浏览器都不支持
- getComputedStyle()
- 获取元素当前的样式,这个方法是window的方法,可以直接使用
- 两个参数
- 第一个:要获取样式的元素
- 第二个:可以传递一个伪元素,一般都传null
- 该方法会返回一个对象,对象中封装了当前元素对应的样式,可以通过对象.样式名来读取样式
- 如果获取的样式没有设置,则会获取到真实的值,而不是默认值,比如:没有设置width,它不会获取到auto,而是一个长度
- 该方法不支持IE8及以下的浏览器
- 通过currentStyle和getComputedStyle()读取到的样式都是只读的,不能修改,如果要修改必须通过style属性
//读取box1的宽度
var obj = getComputedStyle(box1,null);
console.log(getComputedStyle(box1,null).width)
console.log(getComputedStyle(box1,null).backgroundColor)
var w = getStyle(box1,"width");
function getStyle(obj , name){
if(window.getComputedStyle){
//正常浏览器的方式,具有getComputedStyle()方法
return getComputedStyle(obj , null)[name];
}else{
//IE8的方式,没有getComputedStyle()方法
return obj.currentStyle[name];
}
//return window.getComputedStyle?getComputedStyle(obj , null)[name]:obj.currentStyle[name];
}
其他样式操作属性
- clientWidth、clientHeight
- 这两个属性可以获取元素的可见宽度和高度
- 这些属性都是不带px的,返回都是一个数字,可以直接进行计算
- 会获取元素宽度和高度,包括内容区和内边距
- 这些属性都是只读的,不能修改
var box1 = document.getElementById("box1");
console.log(box1.clientWidth)
- offsetWidth、offsetHeight
- 获取元素的整个的宽度和高度,包括内容区、内边距和边框
var box1 = document.getElementById("box1");
console.log(box1.offsetWidth)
- 获取元素的整个的宽度和高度,包括内容区、内边距和边框
- offsetParent
- 可以用来获取当前元素的定位父元素
- 会获取到离当前元素最近的开启了定位的祖先元素(position不是static), 如果所有的祖先元素都没有开启定位,则返回body
var box1 = document.getElementById("box1");
var op = box1.offsetParent;
console.log(op.id)
- offsetLeft
- 当前元素相对于其定位父元素的水平偏移量
- offsetTop
- 当前元素相对于其定位父元素的垂直偏移量
- scrollWidth、scrollHeight
- 可以获取元素整个滚动区域的宽度和高度
- scrollLeft
- 可以获取水平滚动条滚动的距离
- scrollTop
- 可以获取垂直滚动条滚动的距离
- scrollHeight - scrollTop == clientHeight
- 说明垂直滚动条滚动到底了
- scrollWidth - scrollLeft == clientWidth
- 说明水平滚动条滚动到底
- onscroll
- 该事件会在元素的滚动条滚动时触发
var info = document.getElementById("info");
info.onscroll = function(){
// ...
}
- 该事件会在元素的滚动条滚动时触发
事件
事件对象
- 事件对象
- 当事件的响应函数被触发时,浏览器每次都会将一个事件对象作为实参传递进响应函数,
- 在事件对象中封装了当前事件相关的一切信息,比如:鼠标的坐标 键盘哪个按键被按下 鼠标滚轮滚动的方向…
- onmousemove
- 该事件将会在鼠标在元素中移动时被触发
// 当鼠标在areaDiv中移动时,在showMsg中来显示鼠标的坐标
var areaDiv = document.getElementById("areaDiv");
var showMsg = document.getElementById("showMsg");
areaDiv.onmousemove = function(event){
/*
* 在IE8中,响应函数被触发时,浏览器不会传递事件对象,
* 在IE8及以下的浏览器中,是将事件对象作为window对象的属性保存的
*/
/*if(!event){
event = window.event;
}*/
//解决事件对象的兼容性问题
event = event || window.event;
/*
* clientX可以获取鼠标指针的水平坐标
* cilentY可以获取鼠标指针的垂直坐标
*/
var x = event.clientX;
var y = event.clientY;
//alert("x = "+x + " , y = "+y);
//在showMsg中显示鼠标的坐标
showMsg.innerHTML = "x = "+x + " , y = "+y;
};
- 该事件将会在鼠标在元素中移动时被触发
- clientX、clientY
- 用于获取鼠标在当前的可见窗口的坐标
- pageX、pageY
- 可以获取鼠标相对于当前页面的坐标
// 使div可以跟随鼠标移动
//获取box1
var box1 = document.getElementById("box1");
//绑定鼠标移动事件
document.onmousemove = function(event){
//解决兼容问题
event = event || window.event;
//获取滚动条滚动的距离
var st = document.body.scrollTop || document.documentElement.scrollTop;
var sl = document.body.scrollLeft || document.documentElement.scrollLeft;
var left = event.clientX;
var top = event.clientY;
//设置div的偏移量
box1.style.left = left + sl + "px";
box1.style.top = top + st + "px";
}
// 移动时box2中不可以进入
var box2 = document.getElementById("box2");
box2.onmousemove = function(event){
event = event || window.event;
event.cancelBubble = true;
};
- 可以获取鼠标相对于当前页面的坐标
冒泡
- 事件的冒泡(Bubble)
- 所谓的冒泡指的就是事件的向上传导,当后代元素上的事件被触发时,其祖先元素的相同事件也会被触发
- 在开发中大部分情况冒泡都是有用的,如果不希望发生事件冒泡可以通过事件对象来取消冒泡
//为s1绑定一个单击响应函数
var s1 = document.getElementById("s1");
s1.onclick = function(event){
event = event || window.event;
alert("我是span的单击响应函数");
//取消冒泡
//可以将事件对象的cancelBubble设置为true,即可取消冒泡
//影响box1
event.cancelBubble = true;
};
//为box1绑定一个单击响应函数
var box1 = document.getElementById("box1");
box1.onclick = function(event){
event = event || window.event;
alert("我是div的单击响应函数");
//影响body
event.cancelBubble = true;
};
//为body绑定一个单击响应函数
document.body.onclick = function(){
alert("我是body的单击响应函数");
};
事件冒泡与事件捕获
提示:下面提到的目标节点
指的是触发事件的节点
事件捕获
- 由微软公司提出,事件从文档根节点(Document 对象)流向目标节点,途中会经过目标节点的各个父级节点,并在这些节点上触发捕获事件,直至到达事件的目标节点
- 在事件捕获阶段,事件会从 DOM 树的最外层开始,依次经过目标节点的各个父节点,并触发父节点上的事件,直至到达事件的目标节点。以上图中的代码为例,如果单击其中标签,则该事件将通过document->div->p->a的顺序传递到标签
<!--单击最内层的<a>标签--> |
事件冒泡
- 由网景公司提出,与事件捕获相反,事件会从目标节点流向文档根节点,途中会经过目标节点的各个父级节点,并在这些节点上触发捕获事件,直至到达文档的根节点。整个过程就像水中的气泡一样,从水底向上运动
<!--单击最内层的<a>标签-->
<div onclick="alert('事件冒泡: ' + this.tagName)">DIV
<p onclick="alert('事件冒泡: ' + this.tagName)">P
<a href="#" onclick="alert('事件冒泡: ' + this.tagName)">A</a>
</p>
</div>
阻止事件捕获和冒泡
- stopPropagation() 方法来阻止事件捕获和事件冒泡的发生
- stopPropagation() 会阻止事件捕获和事件冒泡,但是无法阻止标签的默认行为,例如点击链接任然可以打开对应网页
<body>
<div id="wrap">DIV
<p class="hint">P
<a href="#">A</a>
</p>
</div>
<script>
function showAlert(event) {
alert("您点击了 "+ this.tagName + " 标签");
event.stopPropagation();
}
var elems = document.querySelectorAll("div, p, a");
for(let elem of elems) {
elem.addEventListener("click", showAlert);
}
</script>
</body> - stopImmediatePropagation()方法来阻止同一节点的同一事件的其它事件处理程序,例如为某个节点定义了多个点击事件,当事件触发时,这些事件会按定义顺序依次执行,如果其中一个事件处理程序中使用了 stopImmediatePropagation() 方法,那么剩下的事件处理程序将不再执行。
<body>
<div onclick="alert('您点击了 ' + this.tagName + ' 标签')">DIV
<p onclick="alert('您点击了 ' + this.tagName + ' 标签')">P
<a href="#" id="link">A</a>
</p>
</div>
<script>
function sayHi() {
alert("事件处理程序 1");
event.stopImmediatePropagation();
}
function sayHello() {
alert("事件处理程序 2");
}
// 为 id 为 link 的标签定义多个点击事件
var link = document.getElementById("link");
link.addEventListener("click", sayHi);
link.addEventListener("click", sayHello);
</script>
</body>
阻止默认操作
- event.preventDefault();
- event.returnValue = false; IE9 及以下的浏览器
<body>
<a href="http://c.biancheng.net/" id="link">链接</a>
<script>
var link = document.getElementById("link");
link.addEventListener('click', function(event){
event.preventDefault(); // 阻止链接跳转
});
</script>
</body>
事件委派
- 事件的委派
- 指将事件统一绑定给元素的共同的祖先元素,这样当后代元素上的事件触发时,会一直冒泡到祖先元素,从而通过祖先元素的响应函数来处理事件。
- 事件委派是利用了冒泡,通过委派可以减少事件绑定的次数,提高程序的性能
// 只绑定一次事件,即可应用到多个的元素上,即使元素是后添加的
// 可以尝试将其绑定给元素的共同的祖先元素
// 为ul绑定一个单击响应函数
<ul id="u1" style="background-color: #bfa;">
<li>
<p>我是p元素</p>
</li>
<li><a href="javascript:;" class="link">超链接一</a></li>
<li><a href="javascript:;" class="link">超链接二</a></li>
<li><a href="javascript:;" class="link">超链接三</a></li>
</ul>
var u1 = document.getElementById("u1");
u1.onclick = function(event){
event = event || window.event;
/*
* target
* - event中的target表示的触发事件的对象
*/
//alert(event.target);
//如果触发事件的对象是我们期望的元素,则执行否则不执行
if(event.target.className == "link"){
alert("我是ul的单击响应函数");
}
};
事件绑定
语法:元素对象.事件 = 函数的形式绑定响应函数,
只能同时为一个元素的一个事件绑定一个响应函数,不能绑定多个,如果绑定了多个,则后边会覆盖掉前边的
//为btn01绑定一个单击响应函数
btn01.onclick = function(){
alert(1);
};
//为btn01绑定第二个响应函数
btn01.onclick = function(){
alert(2);
};addEventListener()
- 通过这个方法也可以为元素绑定响应函数
- 参数:
- 1.事件的字符串,不要on
- 2.回调函数,当事件触发时该函数会被调用
- 3.是否在捕获阶段触发事件,需要一个布尔值,一般都传false
- 使用addEventListener()可以同时为一个元素的相同事件同时绑定多个响应函数,这样当事件被触发时,响应函数将会按照函数的
绑定顺序执行
- 这个方法不支持IE8及以下的浏览器
btn01.addEventListener("click",function(){
alert(1);
},false);
btn01.addEventListener("click",function(){
alert(2);
},false);
btn01.addEventListener("click",function(){
alert(3);
},false);
attachEvent()
- 在IE8中可以使用attachEvent()来绑定事件
- 这个方法也可以同时为一个事件绑定多个处理函数,不同的是它是
后绑定先执行
,执行顺序和addEventListener()相反 - 参数:
- 1.事件的字符串,要on
- 2.回调函数
btn01.attachEvent("onclick",function(){
alert(1);
});
btn01.attachEvent("onclick",function(){
alert(2);
});
btn01.attachEvent("onclick",function(){
alert(3);
});
事件传播
- W3C将事件传播分成了三个阶段
- 1.捕获阶段
- 在捕获阶段时从最外层的祖先元素,向目标元素进行事件的捕获,但是默认此时不会触发事件
- 2.目标阶段
- 事件捕获到目标元素,捕获结束后开始在目标元素上触发事件
- 3.冒泡阶段
- 事件从目标元素向他的祖先元素传递,依次触发祖先元素上的事件
- 1.捕获阶段
- 如果希望在捕获阶段就触发事件,可以将addEventListener()的第三个参数设置为true
- 一般情况下我们不会希望在捕获阶段触发事件,所以这个参数一般都是false
- IE8及以下的浏览器中没有捕获阶段
window.onload = function(){
/*
* 分别为三个div绑定单击响应函数
*/
var box1 = document.getElementById("box1");
var box2 = document.getElementById("box2");
var box3 = document.getElementById("box3");
bind(box1,"click",function(){
alert("我是box1的响应函数")
});
bind(box2,"click",function(){
alert("我是box2的响应函数")
});
bind(box3,"click",function(){
alert("我是box3的响应函数")
});
};
//定义一个函数,用来为指定元素绑定响应函数
/*
* addEventListener()中的this,是绑定事件的对象
* attachEvent()中的this,是window
* 需要统一两个方法this
*/
/*
* 参数:
* obj 要绑定事件的对象
* eventStr 事件的字符串(不要on)
* callback 回调函数
*/
function bind(obj , eventStr , callback){
if(obj.addEventListener){
//大部分浏览器兼容的方式
obj.addEventListener(eventStr , callback , true);
}else{
/*
* this是谁由调用方式决定
* callback.call(obj)
*/
//IE8及以下
obj.attachEvent("on"+eventStr , function(){
//在匿名函数中调用回调函数
callback.call(obj);
});
}
}
键盘事件
- onkeydown
- 按键被按下
- 对于onkeydown来说如果一直按着某个按键不松手,则事件会一直触发
- 当onkeydown连续触发时,第一次和第二次之间会间隔稍微长一点,其他的会非常的快,这种设计是为了防止误操作的发生。
- onkeyup
- 按键被松开
- 键盘事件一般都会绑定给一些可以获取到焦点的对象或者是document
BOM
BOM介绍
浏览器对象模型
BOM可以通过JS来操作浏览器
在BOM中提供了一组对象,用来完成对浏览器的操作
BOM对象
Window
- 代表的是整个浏览器的窗口,同时window也是网页中的全局对象
Navigator
- 代表的当前浏览器的信息,通过该对象可以来识别不同的浏览器
Location
- 代表当前浏览器的地址栏信息,通过Location可以获取地址栏信息,或者操作浏览器跳转页面
History
- 代表浏览器的历史记录,可以通过该对象来操作浏览器的历史记录
- 由于隐私原因,该对象不能获取到具体的历史记录,只能操作浏览器向前或向后翻页
- 而且该操作只在当次访问时有效
- 代表浏览器的历史记录,可以通过该对象来操作浏览器的历史记录
Screen
- 代表用户的屏幕的信息,通过该对象可以获取到用户的显示器的相关的信息
这些BOM对象在浏览器中都是作为window对象的属性保存的,可以通过window对象来使用,也可以直接使用
Navigator
- 由于历史原因,Navigator对象中的大部分属性都已经不能帮助我们识别浏览器了
- 一般我们只会使用userAgent来判断浏览器的信息,
- userAgent是一个字符串,这个字符串中包含有用来描述浏览器信息的内容,
- 不同的浏览器会有不同的userAgent
var ua = navigator.userAgent;
if(/firefox/i.test(ua)){
alert("你是火狐!!!");
}else if(/chrome/i.test(ua)){
alert("你是Chrome");
}else if(/msie/i.test(ua)){
alert("你是IE浏览器~~~");
}else if("ActiveXObject" in window){
// 如果通过UserAgent不能判断,还可以通过一些浏览器中特有的对象,来判断浏览器的信息
alert("你是IE11,枪毙了你~~~");
}
History
- 对象可以用来操作浏览器向前或向后翻页
- length
- 属性,可以获取到当成访问的链接数量
- back()
- 可以用来回退到上一个页面,作用和浏览器的回退按钮一样
- forward()
- 可以跳转下一个页面,作用和浏览器的前进按钮一样
- go()
- 可以用来跳转到指定的页面
- 它需要一个整数作为参数
- 1:表示向前跳转一个页面 相当于forward()
- 2:表示向前跳转两个页面
- -1:表示向后跳转一个页面
- -2:表示向后跳转两个页面
window.onload = function(){
//获取按钮对象
var btn = document.getElementById("btn");
btn.onclick = function(){
alert(history.length);
history.back();
history.forward();
history.go(-2);
};
};
Location
- 该对象中封装了浏览器的地址栏的信息(当前页面的完整路径)
- 直接将location属性修改为一个完整的路径,或相对路径,则我们页面会自动跳转到该路径,并且会生成相应的历史记录
location = "http://www.baidu.com";
location = "01.BOM.html"; - assign()
- 用来跳转到其他的页面,作用和直接修改location一样
location.assign("http://www.baidu.com");
- 用来跳转到其他的页面,作用和直接修改location一样
- reload()
- 用于重新加载当前页面,作用和刷新按钮一样
- 如果在方法中传递一个true,作为参数,则会强制清空缓存刷新页面
location.reload(true);
- replace()
- 可以使用一个新的页面替换当前页面,调用完毕也会跳转页面
- 不会生成历史记录,不能使用回退按钮回退
location.replace("01.BOM.html");
定时器
setInterval()
- 定时调用
- 可以将一个函数,每隔一段时间执行一次
- 参数:
- 1.回调函数,该函数会每隔一段时间被调用一次
- 2.每次调用间隔的时间,单位是毫秒
- 返回值:
- 返回一个Number类型的数据
- 这个数字作为定时器的唯一标识
// 使count中的内容,自动切换
var num = 1
var count = document.getElementById('count')
var timer = setInterval(function (){
count.innerHTML = num ++ // 第一次的值是1,赋值结束后,num值加1
if(num == 11){
// 关闭定时器
clearInterval(timer)
}
}, 1000)
- 工作原理:
- 每隔一段时间(如每隔2s)就会执行,一直重复
- 这样会存在问题:
- 设定一个定时器a每隔2s执行一次,如果a执行的时间(executiveTime)大于时间间隔(2s),那么,第一次执行的a还没执行完第一次,就开始执行第二次了。
- js巧妙地避免了这个问题——即等到定时器的代码执行完后,再去将定时器的代码加入到队列中,所以定时器代码加入队列的最小时间间隔即指定间隔(因为理想状态是是定时器的执行时间在时间间隔内完成)
- 使用setInterval的问题
- 某些间隔会被跳过——如设置每个1s执行一次,如果该定时器代码执行时间大于1s,或者正好等于1s,那么,相当于代码执行时间正好等于间隔时间,那么,这个间隔时间就被执行时间占据了,所以就没有了间隔时间即没有间隔。
- 多个定时器代码执行之间的间隔可能会比预期小
- 因为代码执行时间会长一点(因为队列并不总是空闲的,所以要排队等待执行),导致时间间隔变小。
- 以上两个问题的解决方法:使用递归调用延时器,代替定时器
setTimeout(function () {
//执行代码
//xxxx
//xxxx
setTimeout(arguments.callee,1000) // arguments.callee的作用是获取当前执行函数的引用,并设置新的相同的延时器再来一次,即递归调用
},1000)clearInterval()
- 关闭一个定时器
- 方法中需要定时器的标识作为参数,这样将关闭标识对应的定时器
setTimeout()
- 延时调用
- 延时调用函数不会马上执行,而是间隔一段时间再执行,而且只执行一次
- 延时调用和定时调用的区别
- 定时调用会执行多次,
- 延时调用只会执行一次
- 延时调用和定时调用是可以相互替代的,在开发中可以根据自己需要去选择
- 工作原理
- 当设定一个延时器是5s后进行时,并不代表它5s后就立即执行,只是代表它5s后会被加入队列,如果5s后,队列没有其他东西,那么延时器的代码会立即执行,否则会延迟执行。
- 指定的时间间隔(如设置5s后执行),表示何时将延时器加入到队列,而不是何时真正执行代码。
var num = 1
var timer = setTimeout(function (){
console.log(num++)
}, 3000)
clearTimeout()
- 关闭一个延时调用
- 方法中需要延时调用的标识作为参数,这样将关闭标识对应的延时调用
JSON
- JavaScript Object Notation JS对象表示法
- JS中的对象只有JS自己认识,其他的语言都不认识
- JSON就是一个特殊格式的字符串,这个字符串可以被任意的语言所识别,并且可以转换为任意语言中的对象,JSON在开发中主要用来数据的交互
- JSON和JS对象的格式一样,只不过JSON字符串中的
属性名必须加双引号
,其他的和JS语法一致 - 文件扩展名为.json
- JSON分类:
- 对象{}
- 数组[]
- JSON中允许的值:
- 1.字符串
- 2.数值
- 3.布尔值
- 4.null
- 5.对象
- 6.数组
// JSON字符串
var str = '{"name":"孙悟空","age":18,"gender":"男"}';
- 在JS中,为我们提供了一个工具类,就叫JSON, 这个对象可以帮助我们将一个JSON转换为JS对象,也可以将一个JS对象转换为JSON
- 将JSON字符串转换为JS中的对象
- JSON.parse()
- 可以将以JSON字符串转换为js对象
- 它需要一个JSON字符串作为参数,会将该字符串转换为JS对象并返回
- JSON.parse()
- 将JS对象转换为JSON字符串
- JSON.stringify()
- 可以将一个JS对象转换为JSON字符串
- 需要一个js对象作为参数,会返回一个JSON字符串
- JSON.stringify()
- eval()
- 这个函数可以用来执行一段字符串形式的JS代码,并将执行结果返回
- 如果使用eval()执行的字符串中含有{},它会将{}当成是代码块
- 如果不希望将其当成代码块解析,则需要在字符串前后各加一个()
- eval()这个函数的功能很强大,可以直接执行一个字符串中的js代码,但是在开发中尽量不要使用,首先它的执行性能比较差,然后它还具有安全隐患
var str = '{"name":"孙悟空","age":18,"gender":"男"}';
var obj = eval("("+str+")"); // 同JSON.parse()
异常处理
- 错误和异常的区别
- 错误(Error)是在代码运行之前出现的,在运行 JavaScript 程序之前,JavaScript 解释器会先对代码进行检查,如果代码有误,例如某些语法错误,浏览器就会报出相应的错误,只有将错误修正后,代码才能运行。
- 异常(Exception)是在代码运行中出现的,例如调用某个未定义的方法、读取不存在的文件等。在出现异常之前,代码的运行并不受影响,当出现异常时,会在浏览器控制台输出错误信息,并终止程序的运行。
try catch 语句
- 捕获异常,并做出相应处理
- 当 try 语句块中的代码出现异常时,会创建并抛出一个 Error 对象(例如上面代码内catch(error)中的 error),对象中包含两个属性,如下所示:
- name:错误的类型;
- message:对错误的描述信息。
// 语法格式
try {
// 可能会发生异常的代码
} catch(error) {
// 发生异常时要执行的操作
}
// 例子
try {
var title = "JavaScript";
document.write(title);
// 调用一个未定义的变量
document.write(str);
// 若发生错误,则不会执行以下行
alert("所有语句都已成功执行。");
} catch(error) {
// 处理错误
alert("错误信息: " + error.message);
}
// 继续执行下面的代码
document.write("<p>Hello World!</p>");try catch finally 语句
- 无论 try 语句块中的代码是否发生错误,finally 语句中的代码都会执行
// 接收用户输入的参数
var num = prompt("输入一个 0 到 100 的数字");
// 获取当前时间
var start = Date.now();
try {
if(num > 0 && num <= 100) {
console.log(Math.pow(num, num)); // 指数幂的基
} else {
console.log("输入的值无效!");
}
} catch(e) {
console.log(e.message);
} finally {
// 显示执行代码所用的时间
console.log("代码执行花费了:" + (Date.now() - start) + "ms");
}
// 输入123
// 代码执行花费了:0ms抛出错误
- throw expression
- 其中 expression 为要抛出的异常,可以是任何类型的值,例如对象、字符串、数组等,推荐使用对象类型
- JavaScript 中内置了一个 Error() 函数来创建要抛出的错误对象
function squareRoot(number) {
// 如果数字为负数,则抛出错误
if(number < 0) {
throw new Error("抱歉,无法计算负数的平方根!");
} else {
console.log(Math.sqrt(number));
}
}
try {
squareRoot(16);
squareRoot(625);
squareRoot(-9);
squareRoot(100);
// 若抛出错误,则不会执行下面的行
console.log("所有函数都执行成功。");
} catch(e) {
// 处理错误
console.log(e.message);
}
/* 运行结果如下:
* 4
* 25
* 抱歉,无法计算负数的平方根!
* */错误类型
错误类型 说明 EvalError 使用 eval() 函数时发出错误,会抛出该错误 InternalError 由 JavaScript 引擎内部错误导致的异常,会抛出该错误 RangeError 范围错误,当使用了超出允许范围的数值时,会抛出该错误 SyntaxError 语法错误,当代码中存在任何语法错误时,会抛出该错误 TypeError 类型错误,当使用的值不是预期类型时,会抛出该错误,例如对数字调用字符串方法,对字符串调用数组方法等 URIError URI 错误,当使用 URI 相关函数但传入 URI 参数时,会抛出该错误 ReferenceError 参数错误,当尝试使用未定义的变量、函数、对象时,会抛出该错误
闭包
认识闭包
闭包,指的就是一个
函数
。当两个函数彼此嵌套
时,内部的函数
就是闭包
因为在 JavaScript 中,函数属于对象,对象又是属性的集合,而属性的值又可以是对象,所以我们可以在函数内部再定义函数。例如在函数 A 中定义了函数 B,然后在函数外部调用函数 B,这个过程就是闭包。
闭包的形成条件
内部函数需要通过外部函数 return 给返回出来
// 以下代码就构成了一个闭包,其实就是函数 fun。
function funOne(){ // 外部函数
var num = 0; // 局部变量
function funTwo(){ // 内部函数
num++;
return num;
}
return funTwo;
}
var fun = funOne(); // 返回函数 funTwo闭包的用途
当需要在函数中定义一些变量,并且希望这些变量能够一直保存在内存中,同时不影响函数外的全局变量时,就可以使用闭包
function funOne(){
var num = 0;
console.log(num)
function funTwo(){
num++;
console.log(num);
}
return funTwo;
}
var fun = funOne();
fun(); // 输出:0 输出:1
fun(); // 输出:2
fun(); // 输出:3
fun(); // 输出:4
// num 是外部函数 funOne() 中的一个变量,它的值在内部函数 funTwo() 中被修改,函数 funTwo() 每执行一次就会将 num 加 1。根据闭包的特点,函数 funOne() 中的变量 num 会一直保存在内存中GC(垃圾回收)机制
- 在 JavaScript 中,如果一个对象不再被引用,那么这个对象就会被 GC 回收,否则这个对象会一直保存在内存中。在上面的例子中,内部函数 funTwo() 定义在外部函数 funOne() 中,因此 funTwo() 依赖于 funOne(),而全局变量 fun 又引用了 funTwo(),所以 funOne() 间接的被 fun 引用。因此 funOne() 不会被 GC 回收,会一直保存在内存中
闭包的高级用法
- 闭包与匿名函数结合使用
var funOne = (function(){
var num = 0;
return function(){
num++;
return num;
}
})();
console.log(funOne()); // 输出:1
console.log(funOne()); // 输出:2
console.log(funOne()); // 输出:3 - 同一个闭包机制可以创建多个闭包函数出来,它们彼此没有联系,都是独立的
function funOne(i){
function funTwo(){
console.log('数字:' + i);
}
return funTwo;
};
var fa = funOne(110);
var fb = funOne(111);
var fc = funOne(112);
fa(); // 输出:数字:110
fb(); // 输出:数字:111
fc(); // 输出:数字:112
严格模式
启用严格模式
- 将
"use strict";
指令添加到 JavaScript 程序的第一行,则表示整个脚本都会处于严格模式。如果在函数的第一行代码中添加"use strict";
,则表示只在该函数中启用严格模式 - 注意:
"use strict";
或'use strict';
指令只有在整个脚本第一行
或者函数第一行
时才能被识别
严格模式中的变化
- 不允许使用未声明的变量
- 普通模式下,如果一个变量还没有声明,就直接拿来赋值,JavaScript 解释器会自动为您创建这个变量。而在严格模式下,则不允许这么做,所有变量在使用前必须显式的声明,否则将会抛出一个 ReferenceError 错误。
;
v = 1; // 此处报错:Uncaught ReferenceError: v is not defined
for(i = 0; i < 2; i++) { // 此处报错:Uncaught ReferenceError: i is not defined
}
- 普通模式下,如果一个变量还没有声明,就直接拿来赋值,JavaScript 解释器会自动为您创建这个变量。而在严格模式下,则不允许这么做,所有变量在使用前必须显式的声明,否则将会抛出一个 ReferenceError 错误。
- 不允许删除变量或函数
- 在严格模式下,如果您尝试删除一个变量或函数,则会抛出语法错误。而在普通模式下,虽然不会成功,但并不报错。
;
var person = {name: "Peter", age: 28};
delete person; // 此处报错:Uncaught SyntaxError: Delete of an unqualified identifier in strict mode.
function sum(a, b) {
return a + b;
}
delete sum; // 此处报错:Uncaught SyntaxError: Delete of an unqualified identifier in strict mode.
- 在严格模式下,如果您尝试删除一个变量或函数,则会抛出语法错误。而在普通模式下,虽然不会成功,但并不报错。
- 函数中不允许有同名的参数
- 在严格模式下,如果函数中有两个或多个同名参数,则会抛出语法错误,而在普通模式下则不会。
;
function square(a, a) { // 此处报错:Uncaught SyntaxError: Duplicate parameter name not allowed in this context
return a * a;
}
- 在严格模式下,如果函数中有两个或多个同名参数,则会抛出语法错误,而在普通模式下则不会。
- eval 语句的作用域是独立的
- 普通模式下,eval 语句的作用域取决于它所在的位置,而在严格模式下,eval 语句本身就是一个局部作用域,通过 eval 语句生成的变量只能在 eval 语句内使用。
;
eval("var x = 5; console.log(x);");
console.log(x); // 此处报错:Uncaught ReferenceError: x is not defined
- 普通模式下,eval 语句的作用域取决于它所在的位置,而在严格模式下,eval 语句本身就是一个局部作用域,通过 eval 语句生成的变量只能在 eval 语句内使用。
- 不允许使用 with 语句
- 在严格模式下,不允许使用 with 语句
;
var radius1 = 5;
var area1 = Math.PI * radius1 * radius1;
var radius2 = 5;
with(Math) { // 此处报错:Uncaught SyntaxError: Strict mode code may not include a with statement
var area2 = PI * radius2 * radius2;
}
- 在严格模式下,不允许使用 with 语句
- 不允许写入只读属性
- 在严格模式下,不允许为只读或不存在的属性赋值,否则会造成语法错误,而在普通模式下,虽然不会成功,但并不会报错。
;
var person = {name: "Peter", age: 28};
Object.defineProperty(person, "gender", {value: "male", writable: false});
person.gender = "female"; // 此处报错:Uncaught TypeError: Cannot assign to read only property 'gender' of object '#<Object>'
- 在严格模式下,不允许为只读或不存在的属性赋值,否则会造成语法错误,而在普通模式下,虽然不会成功,但并不会报错。
- 不允许使用八进制数
- 在严格模式下,不允许使用八进制数(以零为前缀的数字,例如 010、0377),而在普通模式下则可以。
;
var x = 010; // 此处报错:Uncaught SyntaxError: Octal literals are not allowed in strict mode.
console.log(parseInt(x));
- 在严格模式下,不允许使用八进制数(以零为前缀的数字,例如 010、0377),而在普通模式下则可以。
- 不能在 if 语句中声明函数
;
//如果在if语句中声明函数,则会产生语法错误
if (true) {
function demo() { // 此处报错:Uncaught ReferenceError: demo is not defined
console.log("http://c.biancheng.net/");
}
}
demo(); - 禁止使用 this 表示全局对象
- 在普通模式下,this 关键字表示全局对象 window,而在严格模式下,this关键字则表示 undefined。
;
var name = "http://c.biancheng.net/";
function demoTest() {
console.log(this);
}
demoTest();
- 在普通模式下,this 关键字表示全局对象 window,而在严格模式下,this关键字则表示 undefined。