JavaScript简介

JavaScript(简称“JS”) 是一种具有函数优先的轻量级,解释型或即时编译型的编程语言。虽然它是作为开发Web页面的脚本语言而出名,但是它也被用到了很多非浏览器环境中,JavaScript 基于原型编程、多范式的动态脚本语言,并且支持面向对象、命令式、声明式、函数式编程范式。


JS 编写位置

  • 写在标签属性中
    • 如 button 中的 onclick 和 a 中的 href
      <button onclick="alert('我是弹窗')">点击出现弹窗</button>
      <a href="javascript:alert('我是弹窗')">点击出现弹窗</a>
  • 页面中的位置
    • 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进行精确度要求比较高的运算

Boolean 布尔值

  • true
    • 表示逻辑上的真
  • false
    • 表示逻辑上的假

Null 和 Undefined

  • Null 类型的值 只有一个 表示为 空 的对象
    • 使用 typeof 检查一个 null 时,会返回 object
  • Undefined 类型的值 只有一个 表示为 未定义

强制类型转换

  • 强制将数据类型转换成别的数据类型
  • 主要是将 其他的数据类型 转为 String Number Boolean

其他数据类型转换为 String

  • 方法一:
    • 调用被 转换数据类型的 toString()方法
    • 这个方法不会影响到原变量,它只会将转换的结果返回
      • 调用变量a的toString()方法
        a.toString();
    • 利用这个方法,也可以直接改变原变量
      a = a.toString();
      • 但是要注意: null 和 undefined 没有toString()方法,会报错
        a = null;
        a = a.toString; //报错
        a = undefined;
        a = a.toString; //报错
  • 方法二:
    • 调用String()函数,将变量转换成字符串
      • 使用方法和toString一样
      • 用来转换Number 和 Boolean 时,实际上还是用 toString
      • 但null 和 undefined 是直接加上引号 转换为 “null” 和 “undefined”

其他数据类型转换为 Number

  • 方法一:

    • 调用Number()函数,来将变量转换成Number类型
      a = "123";
      a = Number(a);
    1. 纯数字的字符串则直接转为 数字
    2. 如果字符串中有非数字的内容(如字母),则转为 NaN
    3. 如果字符串是一个空格或全是空格,则转为 0
    4. 如果是 Boolean 中的 true , 则转为 1
    5. 如果是 Boolean 中的 false , 则转为 0
    6. 如果是 NaN 则转为 0
    7. 如果是 undefined 则转为数字 NaN
    序号 原值 Number()转换后
    1 “123” 123
    2 “123asd” NaN
    3 “ “或” “ 0
    4 true 1
    5 false 0
    6 NaN 0
    7 undefined NaN
  • 方法二:针对字符串

    • 调用 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
alert( !0 ); // true

登录页面小练习

实现使用 prompt 进行登陆校验的代码。
如果访问者输入 "Admin",那么使用 prompt 引导获取密码,如果输入的用户名为空或者按下了 Esc 键 —— 显示 “Canceled”,如果是其他字符串 —— 显示 “I don’t know you”。

密码的校验规则如下:

  • 如果输入的是 “TheMaster”,显示 “Welcome!”,
  • 其他字符串 —— 显示 “Wrong password”,
  • 空字符串或取消了输入,显示 “Canceled.”。

流程图:

<!DOCTYPE html>
<script>
"use strict";
let userNume = prompt("Who's there?", '');
if (userNume === 'Admin') {
let pass = prompt('Password', '');
if (pass === 'TheMaster') {
alert('Welcome');
} else if (pass === '' || pass === null) {
alert('Canceled');
} else {
alert('Wrong password');
}
} else if (userNume === '' || userNume === null) {
alert('canceled');
} else {
alert('other');
}
</script>

关系运算符

运算符 用法 功能
> 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){
alert('判断为真时执行');
}

else 语句

如果条件不成立时,执行的语句

if(false){
alert('不执行');
}else{
alert('执行的语句');
}

else if 语句

如果有多个条件 则可以使用else if
如:

let a = 1;
if(a=1){
alert('a='1);
}else if(a=2){
alert('a='2);
}else if(a=3){
alert('a='3);
}

练习1

从键盘输入小明的期末成绩:
当成绩为100时,‘奖励一辆BMW’
当成绩为[80-99]时,’奖励一台iphone15s’
当成绩为[60-80]时,’奖励一本参考书’
其他时,什么奖励也没有

<!DOCTYPE html>
<script>
"use strict";
let score = prompt("小明的期末成绩", '')
if (score == 100) {
alert('奖励一辆BMW');
} else if (score >= 80 && score <= 99) {
alert('奖励一台iphone15s');
} else if (score >= 60 && score < 80) {
alert('奖励一本参考书');
} else {
alert('什么奖励也没有');
}
</script>

练习2

大家都知道,男大当婚,女大当嫁。那么女方家长要嫁女儿,当然要提出一定的条件:
高:180cm以上;富:1000万以上;帅:500以上;
如果这三个条件同时满足,则:’我一定要嫁给他’
如果三个条件有为真的情况,则:’嫁吧,比上不足,比下有余。’
如果三个条件都不满足,则:’不嫁!’

<!DOCTYPE html>
<script>
"use strict";
let height = prompt("你的身高长度(cm)", '');
let money = prompt("你的有钱程度(w)", '');
let cool = prompt("你的帅气程度(分)", '');
if (height > 180 && money > 1000 && cool > 500) {
alert('我一定要嫁给他');
} else if (height > 180 || money > 1000 || cool > 500) {
alert('嫁把,比上不足比下有余');
} else {
alert('不嫁');
}
</script>


switch

switch 语句有至少一个 case 代码块和一个可选的 default 代码块。
语法:

switch (a) {
case 1:
alert( 'Too small' );
break;
case 2:
alert( 'Exactly!' );
break;
case 3:
alert( 'Too large' );
break;
default:
alert( "I don't know such values" );
}
  • 比较x值与第一个case(也就是 value1)是否严格相等,然后比较第二个 case(value2)以此类推。
  • 如果相等,switch 语句就执行相应 case 下的代码块,直到遇到最靠近的 break 语句(或者直到 switch 语句末尾)。
  • 如果没有符合的 case,则执行 default 代码块(如果 default 存在)。

while

while 循环的语法如下:

let i = 0;
while (i < 3) { // 依次显示 0、1 和 2
alert( i );
i++;
}

do…while

使用 do..while 语法可以将条件检查移至循环体下面

let i = 0;
do {
alert( i ); // 依次显示 0、1 和 2
i++;
} while (i < 3);

相比无do的while 至少会执行一次,当i < 0 时,会输出0


for

for 循环看起来就像这样:

for (begin; condition; step) {
// ……循环体……
}
for (let i = 0; i < 3; i++) { // 结果为 0、1、2
alert(i);
}

新声明了一个初始值i,表达式为i<3,执行循环体,每次循环后运行i++


break和continue

<!DOCTYPE html>
<script>
"use strict";
for (let i = 1; i <= 5; i++) {
document.writeln(i); // 输出 1、2、 3、4、5
}
</script>

break

跳出离最近的循环体

<!DOCTYPE html>
<script>
"use strict";
for (let i = 1; i <= 5; i++) {
if (i == 2) {
break;
}
document.writeln(i); //输出 1
}
</script>

continue

跳出本次循环

<!DOCTYPE html>
<script>
"use strict";
for (let i = 1; i <= 5; i++) {
if (i == 2) {
continue;
}
document.writeln(i); // 输出 1、3、4、5
}
</script>

对象 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={
name:"chenadian",
age:18,
gender:"男"
}
for(var n in obj){
console.log("属性名:"+n); //name、age、gender
console.log("属性值:"+obj[n]); //chenadian、18、男
}

基本数据类型和引用数据类型

  • 基本数据类型:Number、String、Boolean、Null、Undefined
  • 引用数据类型: Object
  • JS中的变量都是保存到栈内存中的
  • 基本数据类型的值直接在栈内存中储存
  • 对象是保存在堆内存中,每新建一个对象,就在堆内存中开辟一个新的空间
    • 而变量保存的是对象的内存地址(对象的引用),如果两个变量保存的是同一个对象引用,
    • 当一个变量改变时,另一个变量会受到影响。
  • 比较两个基本数据类型时,就是在比较两数据的值 ,而比较两对象时,比较的是两对象的内存地址,两对象的内容一样,地址不一样会返回false

对象字面量

使用字面量创建一个对象

let obj = {};
console.log(typeof obj); //object

使用对象字面量,可以在创建对象时,直接指定对象中的属性
语法 : 声明 对象名 = {属性名:属性值,属性名:属性值…}

let obj = {
name:"chenadian",
age:18
};
console.log(obj.name); //chenadian
console.log(obj.age); //18

函数

简介

函数也是对象

声明

语法:
function 函数名([形参1,形参2]){
语句。。。。
}

function showOne(){
alert('声明了一个函数');
}

调用

刚刚创建了一个函数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){
return x+y;
}
sum(1,"asd"); // 1asd
sum(1,2); // 3
function sum(x,y){
return;
}
sum(1,2); // undefined
function sum(x,y){
let a = x + y;
}
sum(1,2); // undefined

局部变量

在调用时,创建
在结束时,销毁
每次调用,都是独立的,
调用三次,便是创建销毁三次
在函数中声明的变量,只能在函数内部中使用

function showOne(){
let a = "内部的变量";
alert(a);
}
showOne(); //警告框:内部的变量
alert(a); //出错,无法调用局部的变量

全局变量

在页面打开时创建,在页面关闭时销毁
全局变量都会作为window的属性保存
在函数内可以调用外部的变量

let a = "外部的变量";
function showOne(){
alert(a);
}
showOne(); //警告框:外部的变量

函数即可以调用函数外部的变量,也可以改变外部的变量

let a = "外部的变量";
function showOne(){
a = "被修改后的外部变量"
}
alert(a); // a变量被改变前 警告窗:外部的变量
showOne(); // 调用函数改变外部的变量
alert(a); // a变量被改变后 警告窗:被改变后的外部变量

只有在没有局部变量的情况下,才会去调用外部变量,
如果局部变量和外部变量同名,局部变量优先级比外部变量高,会使用局部变量

let a = "我是外部变量";
function showOne(){
let a = "我是内部变量";
alert(a);
a = "我是被修改后的局部变量";
}
showOne(); // 我是内部变量
alert(a); //警告框:我是外部变量

全局变量

任何函数外声明的变量,都称为全局变量
任何全局变量在任何函数中都是可见的,除非局部变量和全局变量同名,全局就会被遮蔽

减少全局变量的使用是一种很好的做法。现在的代码有很少甚至没有全局变量。大多数变量存在于它们的函数中。但是有时候,全局变量能够用于存储项目级别的数据。

默认值

参数是可以带有默认值的
当没有给值传递给参数时,则得到undefined
如上一个函数 showSum(); 来举例

function showSum(oneSrting,towString){
alert(oneSrting + "," + towString);
}
showSum(); //警告窗: undefined,undefined

所以可以给参数给个默认值,当没值传递给参数时,便使用默认值

function showSum(oneSrting,towString = "我是第二个参数"){
alert(oneSrting + "," + towString);
}
showSum("我是第一个参数"); // 警告窗: 我是第一个参数,我是第二个参数

值是从左到右按顺序传递的

function showSum(oneSrting = "我是第二个参数",towString){
alert(oneSrting + "," + towString);
}
showSum("我是第一个参数"); // 警告窗: 我是第一个参数,undefined

后备的默认参数

当没给参数默认值,又不想它返回undefined
为了判断参数是否没用传递到值,我们可以拿它和undefined做比较


立即执行函数

function(){
alert("我是一个匿名函数");
}

创建一个匿名函数,但是这样报错
因为没有函数名,不知道它是什么
所以可以加个括号() ,让程序知道是一个整体

(function(){
alert("我是一个匿名函数");
})

而执行只需要在后面再加一个括号()即可

(function(){
alert("我是一个匿名函数");
})();

这样就会被立即执行,且只运行一次,不占内存


方法

函数也可以称为对象的属性
如果一个函数作为一个对象的属性保存
那么就称这个对象的方法
调用函数就是调用对象的方法
调用方法和调用函数只是名字的区别

let obj = new Object();
obj.name = "chenadian";
obj.fun = function(){
alert(obj.name);
}
function fun2(){
alert(obj.name);
}
obj.fun(); //调用方法
fun2(); //调用函数

声明提前

变量声明提前

var关键字声明的变量,会在代码执行之前被声明,
如果不用var声明,则变量不会被声明提前

alert(a); // undefined
var a = "提前声明";

实际上,是这样运行的

var a 
alert(a);
a = "提前声明";

函数声明提前

function声明的函数会在代码执行之前,就先创建

fun();
function fun(){
alert("提前声明");
}

实际上,是这样运行的

function fun(){
alert("提前声明");
}
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){
this.name = name;
this.name = age;
this.gender = gender;
}
let per = new Person("孙悟空",18,"男");

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){
this.name = name;
this.age = age;
this.gender = gender;
}

let per1 = new Person("a1",18,"男");
let per2 = new Person("a2",19,"女");
//向Person的原型中添加属性id
Person.prototype.id = "我是公共空间里的";
console.log(per1.id); //我是公共空间里的
console.log(per2.id); //我是公共空间里的
console.log(per1.__proto__ == Person.prototype);

执行过程

访问对象的一个属性或方法时
先在对象自身里找属性, 有则直接使用
无则去prototype里面找

原理

每个构造函数,即类在创建时,都有一个隐藏属性prototype,这个空间为堆内存
而通过这个类/构造函数,创建的实例中的栈内存都有去指向这个堆内存
即 类与实例 公用的 堆内存 便是 prototype
在实例自身属性中没找到的属性,就会去prototype里面找

in

检查对象中的属性
使用in检查对象中是否含有某个属性时,有则true 无则false
但 prototype 里面的也会被检查

hasOwnProperty

检查对象自身中的属性
使用hasOwnProperty检查对象中是否含有某个属性时,有则true 无则false
不过是在自身中找,不包括原型(prototype)

原型对象 里面的 原型对象

原型对象也是对象,所以也有原型
当使用一个对象的属性或方法时,会在自身中寻找
如果没有则去原型对象中寻找,
原型对象中没有,则去原型对象中的原型对象中找
直到找到object对象的原型,
Object对象的原型没有原型,如果在Object原型中依然没有找到,属性则返回undefined
Object对象的原型的值是null
一般就 嵌套两个


toString

当在页面打印一个对象时,实际上是输出对象的toString的返回值

function Person(name , age ,gender){
this.name = name;
this.age = age;
this.gender = gender;
}
let per = new Person("chenadian",20,"男");
console.log(per); // object
console.log( per.toString() ); // object

per和per.toString()是一样的
而要输出属性,则修改toString

Person.prototype.toString = function(){
return "Person[name="+this.name+",age="+this.age+",gender="+this.gender+"]"
}
console.log(per); // Person[name=chenadian,age=20,gender=男]
console.log( per.toString() ); // Person[name=chenadian,age=20,gender=男]

垃圾回收(GC)

程序运行就会产生垃圾,
占用大量内存空间,程序运行变慢
内存溢出,程序瘫痪
而js有自动回收机制
我们只需要断开``栈内存与堆内存的连接
让浏览器等编译器知道可以回收即可,对象设置null

function Person(name , age ,gender){
this.name = name;
this.age = age;
this.gender = gender;
}
let per = new Person("chenadian",20,"男");
per = null;

数组(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];
arr.push(40,50,60);
console.log(arr); // 10,20,30,40,50,60
let result = arr.push(70,80,90);
console.log(resule); // 9

pop()

删除数组最后一个元素,并把删除的元素作为返回值

let arr = [10,20,30];
arr.pop();
console.log(arr); // 10,20
let result = arr.pop();
console.log(resule); // 20

unshift()

向数组的开头添加一个或多个元素,并把数组的新长度作为返回值

let arr = [10,20,30];
arr.unshift(4,5,6);
console.log(arr); // 4,5,6,10,20,30
let result = arr.unshift(1,2,3);
console.log(resule); // 9

shift()

删除数组开头一个元素,并把删除的元素作为返回值

let arr = [10,20,30];
arr.shift();
console.log(arr); // 20,30
let result = arr.shift();
console.log(resule); // 20

slice()

提取``数组``指定的元素,不影响原数组,将截取到的元素封装到一个新数组中返回

  • 参数:
    • 1.截取开始位置的索引,包含开始的索引
    • 2.截取结束位置的索引,不包含结束的索引
      • 第二个参数可以省略不写,此时会截取从开始索引往后的所有元素
      • 第二个参数是负值则从最后往前算
        • -1 则是 倒数第一个
        • -2 则是 倒数第二个
let perArr = ["孙悟空","红孩儿","猪八戒","二郎神","唐三藏","沙悟净"];
console.log( perArr.slice(1, 4) ); // 红孩儿 猪八戒 二郎神

let perArr = ["孙悟空","红孩儿","猪八戒","二郎神","唐三藏","沙悟净"];
console.log( perArr.slice(2) ); // 猪八戒 二郎神 唐三藏 沙悟净

let perArr = ["孙悟空","红孩儿","猪八戒","二郎神","唐三藏","沙悟净"];
console.log( perArr.slice(1,-2) ); // 红孩儿 猪八戒 二郎神

splice()

删除数组指定的元素,会影响原数组, 将被删除的元素作为返回值返回

  • 参数:
    • 第一个,表示开始位置的索引,包含开始的索引
    • 第二个, 表示删除的数量
    • 第三个及以后,可以传递新的元素,这些元素将会自动插入到开始位置索引前边
let perArr = ["孙悟空","红孩儿","猪八戒","二郎神","唐三藏","沙悟净"];
console.log( perArr.splice(1,4) ); // 红孩儿 猪八戒 二郎神 唐三藏
console.log( perArr ); // 孙悟空 沙悟净
console.log( perArr.splice(1,0,"红孩儿","猪八戒","二郎神","唐三藏") ); // 红孩儿 猪八戒 二郎神 唐三藏
console.log( perArr ); // 孙悟空 红孩儿 猪八戒 二郎神 唐三藏 沙悟净

concat()

连接两个或多个数组或元素,并将新的数组返回,不会影响原数组

let perArr1 = ["孙悟空","红孩儿"];
let perArr2 = ["宋江","李逵"];
let perArr4 = perArr1.concat(perArr2,"林黛玉","包青天");
console.log(perArr4); // 孙悟空 红孩儿 宋江 李逵 林黛玉 包青天

join()

将数组转换成一个字符串,不会影响原数组,返回字符串

let perArr1 = ["孙悟空","红孩儿"];
let perArr2 = perArr1.join();
console.log(perArr2); //孙悟空,红孩儿
console.log(typeof perArr2); //string

实参默认为逗号(,),可进行更改

let perArr1 = ["孙悟空","红孩儿"];
let perArr2 = perArr1.join("-我是实参-");
console.log(perArr2); //孙悟空-我是实参-红孩儿
console.log(typeof perArr2); //string

reverse()

颠倒 原数组,影响原数组,返回颠倒后的数组

let perArr1 = ["孙悟空","沙悟净","猪八戒","唐三藏"];
let perArr2 = perArr1.reverse();
console.log(perArr2); // [ '唐三藏', '猪八戒', '沙悟净', '孙悟空' ]
console.log(perArr1); // [ '唐三藏', '猪八戒', '沙悟净', '孙悟空' ]

sort()

对数组的元素进行排序,影响原数组,返回排序后的数组
默认对 Unicode 编码进行排序

let str1 = [e,c,b,f,a,d];
let str2 = str1.sort();
console.log(str2); // [ 'a', 'b', 'c', 'd', 'e', 'f' ]
console.log(str1); // [ 'a', 'b', 'c', 'd', 'e', 'f' ]

对数字的排序也是按照 Unicode 排序

let str1 = [1,2,5,4,8,11];
str1.sort();
console.log(str1); // [ 1, 11, 2, 4, 5, 8 ]

所以可以在sort()添加回调函数,来指定排序规则
回调函数中需要定义两个形参,
浏览器将会分别使用数组中的元素作为实参去调用回调函数
第一个形参数所代表的实参始终在第二个形参代表的实参的后面

let str1 = [1,3,2];
str1.sort(function(a,b){
console.log(a); // 3,2
console.log(b); // 1,3
});

浏览器会根据回调函数的返回值来决定元素的顺序,
如果返回一个大于0的值,则元素会交换位置
如果返回一个小于0的值,则元素位置不变
如果返回一个0,则认为两个元素相等,也不交换位置
如果需要升序排列,则返回a-b
如果需要降序排列,则返回b-a

let str1 = [1,5,2,8,4,9,11,0,3,6,7,10];
str1.sort(function(a,b){
if(a > b){
return -1;
}else if(a < b ){
return 1;
}else{
return 0;
}
}); // 升序
str1.sort(function(a,b){
return a - b // 升序
return b - a // 降序
});
console.log(str1); // [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 ]

数组的遍历

运用for循环

遍历数组就是把数组的所有元素都取出来

let arr = [10,20,30];
for(let i = 0 ; i < arr.length ; i++){
console.log(arr[i]); // 10 20 30
}

forEach()

ie8以上支持
forEach() 方法需要一个函数作参数

let perArr = ["孙悟空","红孩儿","猪八戒","二郎神","唐三藏","沙悟净"];
function fun (){
console.log("我有多少个"); // 执行6遍,因为有perArr 有6个元素
}
perArr.forEach(fun);

但一般直接写函数

let perArr.forEach(function(){
console.log("我有多少个"); // 执行6遍,因为有perArr 有6个元素
});

而这种函数是由开发人员创建,但却不是由开发人员调用的,称为回调函数
并且数组中有几个元素,该函数就执行多少遍
如果有添加上形参

let perArr = ["孙悟空","红孩儿","猪八戒","二郎神","唐三藏","沙悟净"];
perArr.forEach(function(value,index,object){
console.log(value); // 遍历的元素 // 孙悟空 红孩儿 猪八戒 二郎神 唐三藏 沙悟净
console.log(index); // 遍历元素的索引(下标) // 0 1 2 3 4 5
console.log(object); // 正在遍历的数组对象
});

函数的方法

call()和apply()

call()和apply()用来改变this的指向

function fun(){
console.log(this);
}
let obj1 = {name:"1"};
let obj2 = {name:"2"};
fun(); // undefined
fun.call(obj1); // { name: '1' } 也就是obj1
fun.apply(obj1); // { name: '1' } 也就是obj1
fun.call(obj2); // { name: '2' } 也就是obj2
fun.apply(obj2); // { name: '2' } 也就是obj2

call()和apply()的区别在于所传的实参类型
call()是一个一个实参传递到形参,和普通传递一样;
apply()是一个整体数组实参传递到形参,实参也得加上中括号([]);

function fun(a, b) {
console.log(this.name);
console.log(a);
console.log(b);
}
let obj1 = {
name: "obj1"
};
fun.call(obj1,1,2); //obj1, 1, 2
fun.apply(obj1,[1,2]); //obj1, 1, 2

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对象
//如果直接使用构造函数创建一个Date对象,则会封装为当前代码执行的时间
var d = new Date();

//创建一个指定的时间对象
//需要在构造函数中传递一个表示时间的字符串作为参数
//日期的格式 月份/日/年 时:分:秒
var d2 = new Date("2/18/2011 11:10:30");

/*
* getDate()
* - 获取当前日期对象是几日
*/
var date = d2.getDate();
/*
* getDay()
* - 获取当前日期对象时周几
* - 会返回一个0-6的值
* 0 表示周日
* 1表示周一
* 。。。
*/
var day = d2.getDay();

/*
* getMonth()
* d2 = new Date("12/18/2011 11:10:30");
* - 获取当前时间对象的月份
* - 会返回一个0-11的值
* 0 表示1月
* 1 表示2月
* 11 表示12月
*/
var month = d2.getMonth();

/*
* getFullYear()
* - 获取当前日期对象的年份
*/
var year = d2.getFullYear();

//console.log(d2);
//console.log("date = "+date);
//console.log("day = "+day);
//console.log("month = "+month);
//console.log(year);

/*
* getTime()
* - 获取当前日期对象的时间戳
* - 时间戳,指的是从格林威治标准时间的1970年1月1日,0时0分0秒
* 到当前日期所花费的毫秒数(1秒 = 1000毫秒)
* - 计算机底层在保存时间时使用都是时间戳
*/

var time = d2.getTime();

//console.log(time/1000/60/60/24/365);

/*var d3 = new Date("1/1/1970 0:0:0");
time = d3.getTime(); // 中国时间,有时区限制,差8小时
console.log(time); // -28800000
*/

//利用时间戳来测试代码的执行的性能
//获取当前的时间戳
var start = Date.now();

for(var i=0 ; i<100 ; i++){
console.log(i);
}

var end = Date.now();


console.log("执行了:"+(end - start)+"毫秒");

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()
    • 用于对一个数进行开方运算
/*
* Math.PI 表示的圆周率
*/
//console.log(Math.PI);

/*
* abs()可以用来计算一个数的绝对值
*/
//console.log(Math.abs(-1));

/*
* Math.ceil()
* - 可以对一个数进行向上取整,小数位只有有值就自动进1
* Math.floor()
* - 可以对一个数进行向下取整,小数部分会被舍掉
* Math.round()
* - 可以对一个数进行四舍五入取整
*/
//console.log(Math.ceil(1.1));
//console.log(Math.floor(1.99));
//console.log(Math.round(1.4));

/*
* Math.random()
* - 可以用来生成一个0-1之间的随机数
* - 生成一个0-10的随机数
* - 生成一个0-x之间的随机数
* Math.round(Math.random()*x)
*
* - 生成一个1-10
* - 生成一个x-y之间的随机数
* Math.round(Math.random()*(y-x)+x)
*/
/*for(var i=0 ; i<100 ; i++){
//console.log(Math.round(Math.random()*10));
//console.log(Math.round(Math.random()*20));

//console.log(Math.round(Math.random()*9)+1);
//console.log(Math.round(Math.random()*8)+2);

//生成1-6之间的随机数
console.log(Math.round(Math.random()*5+1));
}*/

/*
* max() 可以获取多个数中的最大值
* min() 可以获取多个数中的最小值
*/

var max = Math.max(10,45,30,100);
var min = Math.min(10,45,30,100);
//console.log(min);

/*
* Math.pow(x,y)
* 返回x的y次幂
*/

//console.log(Math.pow(12,3));

/*
* Math.sqrt()
* 用于对一个数进行开方运算
*/
console.log(Math.sqrt(2));

包装类

  • 基本数据类型:String Number Boolean Null Undefined
  • 引用数据类型:Object
  • 在JS中为我们提供了三个包装类,通过这三个包装类可以将基本数据类型的数据转换为对象
    • String()
      • 可以将基本数据类型字符串转换为String对象
    • Number()
      • 可以将基本数据类型的数字转换为Number对象
    • Boolean()
      • 可以将基本数据类型的布尔值转换为Boolean对象
  • 但是注意:我们在实际应用中不会使用基本数据类型的对象,如果使用基本数据类型的对象,在做一些比较时可能会带来一些不可预期的结果
    //创建一个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 str = "Hello Atguigu";

/*
* 在底层字符串是以字符数组的形式保存的
* ["H","e","l"]
*/

/*
* length属性
* - 可以用来获取字符串的长度
*/
//console.log(str.length);
//console.log(str[5]);

/*
* charAt()
* - 可以返回字符串中指定位置的字符
* - 根据索引获取指定的字符
*/
str = "中Hello Atguigu";

var result = str.charAt(6);

/*
* charCodeAt()
* - 获取指定位置字符的字符编码(Unicode编码)
*/

result = str.charCodeAt(0);

/*
* String.formCharCode()
* - 可以根据字符编码去获取字符
*/
result = String.fromCharCode(0x2692);

/*
* concat()
* - 可以用来连接两个或多个字符串
* - 作用和+一样
*/
result = str.concat("你好","再见");

/*
* indexof()
* - 该方法可以检索一个字符串中是否含有指定内容
* - 如果字符串中含有该内容,则会返回其第一次出现的索引
* 如果没有找到指定的内容,则返回-1
* - 可以指定一个第二个参数,指定开始查找的位置
*
* lastIndexOf();
* - 该方法的用法和indexOf()一样,
* 不同的是indexOf是从前往后找,
* 而lastIndexOf是从后往前找
* - 也可以指定开始查找的位置
*/

str = "hello hatguigu";

result = str.indexOf("h",1);

result = str.lastIndexOf("h",5);

/*
* slice()
* - 可以从字符串中截取指定的内容
* - 不会影响原字符串,而是将截取到内容返回
* - 参数:
* 第一个,开始位置的索引(包括开始位置)
* 第二个,结束位置的索引(不包括结束位置)
* - 如果省略第二个参数,则会截取到后边所有的
* - 也可以传递一个负数作为参数,负数的话将会从后边计算
*/
str = "abcdefghijk";

result = str.slice(1,4);
result = str.slice(1,-1);

/*
* substring()
* - 可以用来截取一个字符串,可以slice()类似
* - 参数:
* - 第一个:开始截取位置的索引(包括开始位置)
* - 第二个:结束位置的索引(不包括结束位置)
* - 不同的是这个方法不能接受负值作为参数,
* 如果传递了一个负值,则默认使用0
* - 而且他还自动调整参数的位置,如果第二个参数小于第一个,则自动交换
*/

result = str.substring(0,1);

/*
* substr()
* - 用来截取字符串
* - 参数:
* 1.截取开始位置的索引
* 2.截取的长度
*/

str = "abcdefg";

result = str.substr(3,2);

/*
* split()
* - 可以将一个字符串拆分为一个数组
* - 参数:
* -需要一个字符串作为参数,将会根据该字符串去拆分数组
*/
str = "abcbcdefghij";

result = str.split("d");

/*
* 如果传递一个空串作为参数,则会将每个字符都拆分为数组中的一个元素
*/
result = str.split("");

//console.log(Array.isArray(result));
//console.log(result[0]);
console.log(result);


str = "abcdefg";

/*
* toUpperCase()
* - 将一个字符串转换为大写并返回
*/
result = str.toUpperCase();

str = "ABCDEFG";

/*
* toLowerCase()
* -将一个字符串转换为小写并返回
*/
result = str.toLowerCase();

//console.log(result);

正则表达式

创建正则表达式的对象

  • 正则表达式用于定义一些字符串的规则,计算机可以根据正则表达式,来检查一个字符串是否符合规则,获取将字符串中符合规则的内容提取出来
  • 语法: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

字面量创建正则表达式

  • 语法: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);
  • 设置HTML代码

    • 节点.innerHTML = 设置内容
      //设置#bj内的HTML代码
      var bj = document.getElementById("bj");
      bj.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的单击响应函数");
      };

事件冒泡与事件捕获

Image text
提示:下面提到的目标节点指的是触发事件的节点

事件捕获

  • 由微软公司提出,事件从文档根节点(Document 对象)流向目标节点,途中会经过目标节点的各个父级节点,并在这些节点上触发捕获事件,直至到达事件的目标节点
  • 在事件捕获阶段,事件会从 DOM 树的最外层开始,依次经过目标节点的各个父节点,并触发父节点上的事件,直至到达事件的目标节点。以上图中的代码为例,如果单击其中标签,则该事件将通过document->div->p->a的顺序传递到标签
<!--单击最内层的<a>标签-->
<body>
<div id="wrap">DIV
<p class="hint">P
<a href="#">A</a>
</p>
</div>
<script>
function showTagName() {
alert("事件捕获: " + this.tagName);
}
var elems = document.querySelectorAll("div, p, a");
for (let elem of elems) {
elem.addEventListener("click", showTagName, true);
}
</script>
</body>

Image text

事件冒泡

  • 由网景公司提出,与事件捕获相反,事件会从目标节点流向文档根节点,途中会经过目标节点的各个父级节点,并在这些节点上触发捕获事件,直至到达文档的根节点。整个过程就像水中的气泡一样,从水底向上运动
    <!--单击最内层的<a>标签-->
    <div onclick="alert('事件冒泡: ' + this.tagName)">DIV
    <p onclick="alert('事件冒泡: ' + this.tagName)">P
    <a href="#" onclick="alert('事件冒泡: ' + this.tagName)">A</a>
    </p>
    </div>
    Image text

阻止事件捕获和冒泡

  • 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.冒泡阶段
      • 事件从目标元素向他的祖先元素传递,依次触发祖先元素上的事件
  • 如果希望在捕获阶段就触发事件,可以将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对象中的大部分属性都已经不能帮助我们识别浏览器了
  • 一般我们只会使用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");
  • 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对象并返回
  • 将JS对象转换为JSON字符串
    • JSON.stringify()
      • 可以将一个JS对象转换为JSON字符串
      • 需要一个js对象作为参数,会返回一个JSON字符串
  • 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 错误。
      "use strict";
      v = 1; // 此处报错:Uncaught ReferenceError: v is not defined
      for(i = 0; i < 2; i++) { // 此处报错:Uncaught ReferenceError: i is not defined
      }
  • 不允许删除变量或函数
    • 在严格模式下,如果您尝试删除一个变量或函数,则会抛出语法错误。而在普通模式下,虽然不会成功,但并不报错。
      "use strict";
      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.
  • 函数中不允许有同名的参数
    • 在严格模式下,如果函数中有两个或多个同名参数,则会抛出语法错误,而在普通模式下则不会。
      "use strict";
      function square(a, a) { // 此处报错:Uncaught SyntaxError: Duplicate parameter name not allowed in this context
      return a * a;
      }
  • eval 语句的作用域是独立的
    • 普通模式下,eval 语句的作用域取决于它所在的位置,而在严格模式下,eval 语句本身就是一个局部作用域,通过 eval 语句生成的变量只能在 eval 语句内使用。
      "use strict";
      eval("var x = 5; console.log(x);");
      console.log(x); // 此处报错:Uncaught ReferenceError: x is not defined
  • 不允许使用 with 语句
    • 在严格模式下,不允许使用 with 语句
      "use strict";
      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;
      }
  • 不允许写入只读属性
    • 在严格模式下,不允许为只读或不存在的属性赋值,否则会造成语法错误,而在普通模式下,虽然不会成功,但并不会报错。
      "use strict";
      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),而在普通模式下则可以。
      "use strict";
      var x = 010; // 此处报错:Uncaught SyntaxError: Octal literals are not allowed in strict mode.
      console.log(parseInt(x));
  • 不能在 if 语句中声明函数
    "use strict";
    //如果在if语句中声明函数,则会产生语法错误
    if (true) {
    function demo() { // 此处报错:Uncaught ReferenceError: demo is not defined
    console.log("http://c.biancheng.net/");
    }
    }
    demo();
  • 禁止使用 this 表示全局对象
    • 在普通模式下,this 关键字表示全局对象 window,而在严格模式下,this关键字则表示 undefined。
      "use strict";
      var name = "http://c.biancheng.net/";
      function demoTest() {
      console.log(this);
      }
      demoTest();