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)是否严格相等,然后比较第二个 casevalue2)以此类推。
  • 如果相等,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则不会变

总结:

  • 当以函数的形式调用时,thiswindow
  • 当以方法的形式调用时,谁调用方法,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,"男");

perPerson构造函数实例
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

perper.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();