前端总结

一、JavaScript

JS变量

JavaScript 中的变量的申明、初始化和赋值

变量的申明
在 JavaScript 中,申明变量以关键字 var 开头,空一格后再接变量的名字;当然,可以一次申明多个变量。

var myvar1;     //申明变量"myvar1"
var myvar2,myvar3,myvar4;     //一次申明三个变量

注意:一个变量经过多次赋值,它的值为最后一次赋值的值

JavaScript 中的变量的作用域

全局变量
在函数外部申明的变量称为全局变量,全局变量的作用自申明的地方起,到整个 JavaScript 文件的末尾(包括这其中的所有函数的内部)

var wholeVar = 12;     //申明并初始化一个全局变量
function() {  var localVar = 1;     //局部变量  console.log(wholeVar+localVar); //输出13
}
console.log(wholeVar);     //输出12

局部变量

局部变量是指申明在函数内部的变量,其作用域仅是本函数内部,在函数外不可用。
如果局部变量和全局变量的名字相同,那么在函数内部全局变量会被局部变量覆盖。

var myVar = 1;     //全局变量
function scope() {  var myVar = 2;     //局部变量,覆盖了上面的值  console.log(myVar);     //输出2
}

申明提前

var wholeVar = 1;     //全局变量
function myTest() {  console.log(wholeVar);  var wholeVar = 2;  console.log(wholeVar);
}

关于第三行的输出,你的第一反应一定是1吧,正确答案是 undefined。这是因为在函数内部,变量不论在何处申明,都应该看成是在最开始申明(赋值不会看成是在最开始赋值,这就是不输出2的原因),这就是“申明提前”,所以,以上代码等价于:

var wholeVar = 1;
function myTest() {  var wholeVar;     //申明提前了,覆盖了全局变量  console.log(wholeVar);     //上面只申明,没赋值  wholeVar = 2;  console.log(wholeVar);
}

JS数据类型

JavaScript 的数据类型有数字、字符串、布尔型、对象类型、数组、null 和 undefined。

数字类型

JavaScript 中的数字类型指整数和浮点数,不刻意区分二者。

JavaScript 还支持16进制的整数,在所要表示的16进制数前面加上 0x 或者 0X 即可,如 0x1f 表示整数31。

对于较大或较小的数,JavaScript 还支持科学记数法,如 2e3 表示2000。

var number1 = 0X11;     //17
var number2 = 2.01e-2;     //2.01*0.01
var number3 = Math.sqrt(9);     //计算9的平方根
JS数据类型转换
字符串转整数

使用parseInt() 方法,参数为字符串,结果为该字符串转换而来的整数;
转化规则是:如果字符串的首字符不是一个数字,转换失败,返回 NaN;否则,转换到字符串中第一个不是数字的字符止,即,遇到字母、小数点下划线等字符立即停止转换。需要注意的是,16 进制的符号 0x 不会让转换停止。
parseInt() 还可以有第二个参数,表示待转换字符串的进制。下面给一些例子:

parseInt("12");
parseInt("12.2");
parseInt("C",16);
parseInt("12a2");
parseInt("0XC");     //以上均返回数字12
parseInt("a12");     //失败

Number() 进行强制类型转换;

使用 Number() 转换一个字符串,这个字符串必须是只含有数字的字符串,即数字的字符串形式。与上面的 parseInt() 方法对比可知,Number() 实现的是更加严格的字符串转换为数字操作。因为对于 12a3 这种含有字母等非数字字符的字符串,Number() 会报错。下面是一些例子。

Number("12");     //返回12
Number("12a2");   //返回NaN
Number("");       //返回0
字符串转小数

与整数相同,字符串转小数也有两种方式:parseFloat() 和 Number()

数字转字符串

toString() 实现一般的数字转字符串,String() 则是强制类型转换。

toString() 括号内有一个可选的参数,指以几进制的形式转换该字符串,如数字12调用 toString(16) 得到的结果就是 C,即12的16进制表示方式。

String() 可以转换 null 和 undefined,而 toString() 不可以。

var myNum = 15;
console.log(myNum.toString());       //输出"15"
console.log(myNum.toString(16));     //输出"F"
console.log(String(myNum));          //输出"15"
布尔型与其他类型的相互转换

布尔型的值只有两个 true 和 false 。转换规则如下:

  • 布尔型转为字符串直接就是字符串 true 或者 false;

  • 布尔型中的 true 转换为数字 1,布尔型中的 false 转换为数字 0;

  • 数字 0、null、undefined、空字符串转换为布尔型的 false,其他所有都是转换为 true

var myBool = ture;
myBool.toString();     //返回"true"
Number(true);          //返回1
Boolean("js");         //返回true
Boolean("");           //返回false

注意,上面讲的空字符串是"",而不是空格字符串" ",这两个不同,后者双引号之间有一个英文字符的大小的空位,他们转为布尔型的结果不同:

Boolean("");      //返回false
Boolean(" ");     //返回true
隐式转换

JavaScript 是一种弱类型语言,不同类型的变量在运算符的作用下会发生类型转换。这个是编译环境下直接进行的,所以叫隐式类型转换。下面是一些转换规则:

  • +运算的两个操作数是数字和字符串,数字会被转换为字符串;
  • +运算的两个操作数是数字和布尔型,布尔型会被转换为数字;
  • +运算的两个操作数是字符串和布尔型,布尔型会被转换为字符串;
  • 减、乘、除、取余运算会把其他类型转换为数字;
  • if 括号中单独的一个变量会被转换为布尔型。

JS运算符

JavaScript中的算术运算符除了数学中常见的加减乘除外,还有递增、递减和取余等

+ - * / %运算符

JavaScript中的加号除了具有数学中将两个数字相加的作用外,还能对字符串做连接操作。

对两个数字相加和数学中一样,字符串相加就是拼接字符串的意思,比如Java+Script的结果是字符串JavaScript

当字符串和数字相加时需要进行类型转换,数字会先转为字符串,然后再做字符串的拼接。

var resultNumber = 1+1;//结果是2
var resultString1 = "1"+"1";//结果是“11”
var resultString2 = 1+"2";//结果是“12”

减法、乘法、除法以及取余运算符只能用于数字之间的计算,不能做字符串操作。

-和数学中减号用法相同;

*和数学中乘号用法相同;

/表示除以,结果是浮点数,不做四舍五入;

%表示取余数,a%b返回a除以b得到的余数,结果与a的符号相同。

var floatNumber = 3/2;//结果是1.5
var intNumber1 = 3%2;//结果是1
var intNumber2 = -3%2; //结果是-1
递增运算符和递减运算符

递增运算符++表示对一个数做加1的操作,递减运算符--表示对一个数做减1的操作。

++符号在数字的左边还是右边会影响运算的顺序,当++在数的左边时,表示先对数加1,再返回加1后的结果。在右边时,表示先返回该数的值,再加1。下面的例子展示了这种区别。

递减运算符--同理。

递增只对数字有效,不做字符串的拼接。

var i = 1,j = 0;
j = i++;//j为1,i为2
var m = 1,n = 0;
n = ++m;//n为2,m为2
比较运算符

JavaScript中的比较运算符有==,===,>,<,!=,>=等。

  • ==叫做相等,===叫做严格相等。双等号和三等号的区别是:三等号要求数据类型和值都相等,双等号只需要值相等即可,相等包含严格相等。

从比较过程来看,严格相等先比较两个对象的数据类型是否相等,不相等则结束比较,返回false,相等在数据类型不同时,尝试进行数据类型转换,例如,在字符串和数字的比较中,字符串会被转为数字再比较;布尔值true转为数字1,布尔值false转为数字0

如果数据类型相同,相等和严格相等都会直接比较值,值相等返回true

需要特别注意的是,以上仅仅适用于非对象类型。对于对象类型,相等或者严格相等比较的都是对象的引用,而不是具体的值,就是说,一个对象和其他任何对象都是不相等的,即使两者属性、值都相等。下面给出一些例子:

var stringVariable = "2";
var number1 = 2;
var number2 = 2;
console.log(stringVariable == number1);//true
console.log(stringVariable === number1);//false
console.log(number1 === number2);//true
var studentA = {
name:"Bob",
age:22
}
var studentB = {
name:"Bob",
age:22
}
console.log(studentA == studentB);//false,因为不是同一个对象
var studentC = studentA;
console.log(studentA == studentC);//true,因为是同一个对象

studentA赋值给studentC,这时studentCstudentA指向内存中的同一个地址块,视为同一个对象,所以两者相等。

  • 不等
    对应于上面的等号,不等号也有两种:!=!==!===互为相反,==成立,!=一定不成立。
    !=====互为相反,严格相等成立,则严格不相等不成立。

  • 其它
    大于,小于,大于等于,小于等于的比较规则如下:
    比较的两个对象都是数字,按照数学中数字的比较方法。
    数字和字符串比较,字符串转为数字后再比较。
    字符串和字符串比较,从第一个字符开始,逐个比较,发现不相等立即返回。字符按照ASCII编码值的大小比较,一般只要记住:数字<大写字母<小写字母,字母a小于字母zA小于Z, 比较过程中,当一个字符串结束另外一个字符串还有,还没有比较出大小,则长的字符串较大。

var number1 = 1;//定义变量number1
var number2 = 2;//定义变量number2
var string1 = "3";//string1
var lowerLetter = "a";//定义变量lowerLetter
var upperLetter = "A";//定义变量upperLetter
var string1 = "aa";//定义变量string1
var String2 = "ab";//定义变量String2
console.log(number1<number2);//输出true
console.log(number2<string1);//输出true
console.log(upperLetter<lowerLetter);//输出true
console.log(lowerLetter<string1);//输出false
console.log(string1<string2);//输出true
逻辑运算符

在介绍逻辑运算符之前,我们必须明确逻辑运算符的操作数只能是布尔型,其他类型都会被转换为布尔型:除了0nullundefined""外,其他的值转换为布尔值都是true

  • 逻辑与
    逻辑与有两个操作数,中间用&&连接,只有两个操作数都是true结果才是true; 其中一个操作数不是布尔型,当左操作数为真值时,返回右操作数。当左操作数为假值时,返回左操作数。

  • 逻辑或
    逻辑或同样有两个操作数,用||连接,至少有一个操作数为true时结果为true; 其中一个操作数不是布尔型,当左操作数为真值时,返回左操作数。当左操作数为假值时,返回右操作数。

  • 逻辑非
    逻辑非只有一个操作数,!后连接操作数或表达式,意思是将操作数取反;
    如果操作数不是布尔型,编译器首先将其他类型转换为布尔型,然后返回true或者false

    console.log(true&&false);//false
    console.log(true||false);//true
    console.log(!false);//true
    var number1 = 1;
    var number2 = 0;
    var string = "a";
    console.log(number1&&string);//输出字符串"a"
    console.log(number1||string);//输出数字1
    console.log(!number1);//false
    
条件运算符

条件运算符由?:构成,三个操作数分别在?的左边、右边以及:的右边,第一个操作数如果是真值,整个表达式返回第二个操作数的值;第一个操作数如果是假值,返回第三个操作数的值。

一般我们说到JavaScript中的三元运算符,指的就是条件运算符,因为它有三个操作数。条件运算符通常用来简化表达式。

var result1 = a>b?a:b;//result1赋值为a和b中大的那一个
var result2 = (a==5)?(a+1):(a-1);//a为5,返回a+1,否则返回a-1
赋值运算符

赋值运算符就是=号,其实这个在前面出现过很多次了。赋值运算符的左边是一个变量或者对象的属性,右边是这个变量的值,意思是设置左边变量的值为右边的具体值。

除了基本的等号外,赋值运算符还可以和算术运算符结合。例如a+=1表示a=a+1,其中就把相加和赋值结合了起来。同理,相减、相乘、相除、取余、位运算都可以和赋值结合起来。如下:

var b = 1;
b += 1;//等价于b = b+1
b *= 1;//等价于b = b*1
b /= 1;//等价于b = b/1
b %= 1;//等价于b = b%1
b &= 1;//等价于b = b&1
运算符的优先级
分类 运算符 含义 结合性
1 ++ 前后增量 R
1 前后减量 R
1 逻辑非 R
2 * / % 乘 除 求余 L
3 + - 加减 L
4 < <= > >= 比较数字顺序 L
4 in 测试属性是否存在 L
5 == 判断相等 L
5 != 判断不等 L
6 & 按位与 L
7 && 逻辑与 L
8 ?: 条件运算符 R
9 = 赋值 R
9 += -= *= /= %= 运算且赋值 R
10 , 忽略第一个操作数 L

从上到下优先级逐渐降低。第一栏数字相同的运算符优先级相同,对于这个表格,不需要全部记住,但是常见的需要记住,比如加减乘除的优先级高于比较运算符,赋值运算符的优先级几乎是最低的,下面给出例子帮助理解:

var a = 1;  var b = 2;  var c = ++a+b;

因为++的优先级高于+,所以上面的第三个句子等价于:

var c = (++a)+b;
运算符的结合性

运算符的优先级是针对不同优先级的运算符来说的,对于同一级的运算符,运算顺序取决于运算符的结合性,比如加法和减法的优先级相同,而加法和减法都是从左向右结合,所以a+b-c式中,会先计算a+b,即按照阅读的顺序计算。

也有很多运算符是从右到左结合的,比如取反、逻辑非。

上面图中的第三栏就是结合性,R表示从右向左结合,L表示从左到右结合,从左向右的占多数,这和我们在数学中的习惯相同。

var d = a*b/c;//先计算乘法后计算除法
var aa = 2;
var bb = 3;
var cc = aa *= bb;//先计算aa*=bb,再把结果赋值给cc,为6

上面第一个式子是从左到右结合,第四个式子是从右到左结合。
分别等价于:

var d = (a*b)/c;  var cc = (aa *= bb);

JS对象

对象的定义

JavaScript中的一切都是对象,这是该语言的一个很大的特点。像字符串、数组等已经定义的对象叫做内置对象。用户自己也可以定义对象,叫做自定义对象。本实训讲的对象特指自定义对象,自定义对象指数据和函数(又叫方法)的集合。数据指变量名和变量的值构成的组合。如下图所示:

下面介绍五种创建对象的方法,其中通过对象字面量和使用构造函数创建对象最常用。

对象字面量

这是最常用的创建对象的方法,通过新建一个键值对的集合(对象字面量)创建对象,如下:

var song = {  name:"Liekkas",  time:180,  "song language":English,  singer: {  singerName:"Sofia Jannok",  singerAge:30  }
};
通过关键字new创建对象

通过new关键字创建对象也是一个常用的方法。如下:

var Store = new Object();//创建对象的一个实例
Store.name = "lofo Market";
Store.location = "NO.13 Five Avenue";
Store.salesVolume = 1000000;

通过上面的代码,我们就能创建一个名为Store的对象。

通过工厂方法创建对象

工厂方法就是通过函数创建对象,函数封装了创建对象的过程。

这是一种通过函数创建对象的方法,函数封装了对象的创建过程,创建新对象时只需要调用该函数即可。这种方法适合于一次创建多个对象。

//对象的创建函数
function createStoreObject(name,location,salesVolume) {  var store = new Object();  store.name = name;  store.locaion = location;  store.salesVolume = salesVolume;  store.display = function() {  console.log(this.name);  };  return store;
}
//利用该函数创建一个对象
var store1 = createStoreObject("panda express","No.1,People Street",200000);

这样就创建了一个名为store1的对象,注意这个对象除了属性之外还有一个方法display。要创建更多的类似store1的对象,直接调用该函数即可。

使用构造函数创建对象

上面虽然也是通过函数创建对象,但不是构造函数,只是普通函数。构造函数名必须以大写字母开头,函数体内没有返回语句。

//构造函数
function Store(name,location,salesVolume) {  this.name = name;  this.locaion = location;  this.salesVolume = salesVolume;
}
//创建对象的实例
var myStore = new Store("KeyExp","No.1,L.Street",540000);

上面的代码首先是Store对象的构造函数,然后用该构造函数创建了Store对象的一个实例myStore

使用原型(prototype)创建对象

当我们创建一个函数时,函数就会自动拥有一个prototype属性,这个属性的值是一个对象,这个对象被称为该函数的原型对象。也可以叫做原型。

当用new关键字加函数的模式创建一个对象时,这个对象就会有一个默认的不可见的属性[[Prototype]],该属性的值就是上面提到的原型对象。如下图所示:

JavaScript中每个对象都有一个属性[[Prototype]],指向它的原型对象,该原型对象又具有一个自己的[[Prototype]],层层向上直到一个对象的原型为null。根据定义,null 没有原型,并作为这个原型链中的最后一个环节。如下图所示:

这种方法是对使用构造函数创建对象的改进,使用构造函数创建一个对象时,会把构造函数中的方法(上面的构造函数只有属性的键值对,没有方法)都创建一遍,浪费内存,使用原型不存在这个问题。

function Store() {};
Store.prototype.name = "SF Express";
Store.prototype.locaion = "Hong Kong";
Store.prototype.salesVolume = 1200000000;
//创建对象
var myStore = new Store();
//创建一个新的对象
var hisStore = new Store();
hisStore.name = "STO Express";//覆盖了原来的name属性

这种方法的好处是,创建一个新的对象时,可以更改部分属性的值。

属性的增删改查

属性的获取
方式一

属性的获取有两种方式,一种是使用.符号,符号左侧是对象的名字,符号右侧是属性的名字,如下:

var student = {name:"Alice",gender:"girl"};
console.log(student.name);//输出Alice

这种情况下属性名必须是静态的字符串,即不能是通过计算或者字符串的拼接形成的字符串。

方式二

另外一种是使用[""]符号,符号的左边是对象的名字,双引号中间是属性的名字,这种情况下属性名可以是一个表达式,只要表达式的值是一个字符串即可。如下:

var student = {name:"Alice",gender:"girl"};
console.log(student["name"]);//输出Alice

有两种情况必须使用第二种方式:

  • 属性名含有空格字符,如student["first name"],这时不能用student.first name代替,编译器无法解释后者;
  • 属性名动态生成,比如用for循环获取前端连续id的值,这种id名之间一般有特定关系。如下面的例子:
for(int i = 0;i < 5;i ++) {  console.log(student["id"+i]);
}
属性的修改与新增

属性的修改指修改已有属性的值,这个直接用赋值符号即可。

属性的新增与修改在形式上完全相同,区别仅在于编译器会根据属性的名字判断是否有该属性,有则修改,没有则新增。

var student = {  name:"Kim",  age:21
};
student.age = 20;//修改属性,覆盖了原来的值21
student.gender = "female";//新增属性gender
删除属性

JavaScript中的属性还可以删除,这在其他的面向对象语言如Java或者C++中是无法想象的,删除通过delete运算符实现。删除成功返回布尔型true,删除失败也是返回true,所以在删除之前需要判断一个属性是否存在,这个内容将在下一关讲解。

需要注意的是,对象只能删除自己特有的属性,而不能删除继承自原型对象的属性。同时,对象在删除属性时,要防止删除被其他对象继承的属性,因为这样会导致程序出错。

var Store = new Object();
Store.name = "lofo Market";
Store.location = "NO.13 Five Avenue";
console.log(delete Store.name);//删除成功,输出true
console.log(Store.name);//已删除,返回undefined
delete Store.prototype;//删除失败,非自有属性
属性的检测

属性的检测指检查对象是否有某个属性或者方法,需要使用运算符inin的左侧是属性或者方法名,右侧是检查对象,对象有该属性或者方法则返回true,否则返回false,如下

var school = {  name:"SJTU",  location:"ShangHai",  studentNum:40000,  display:function() {  console.log(this.name);  }
};
//检测属性
console.log("name" in school);//输出true
console.log("sales" in school);//输出false
//检测方法
console.log("display" in school);//输出true

这里的属性名是字符串,必须用双引号包含在内。

还可以用hasOwnProperty()检测对象是否具有某个自有属性或方法。括号内的参数是属性或者方法的名字。

所谓自有属性或者方法,是指对象自己定义的属性或者方法,而不是从原型链上继承来的。

  1. var school = {  name:"SJTU",  location:"ShangHai",  studentNum:40000,  display:function() {  console.log(this.name);  }
    };
    console.log(school.hasOwnProperty("studentNum"));//true
    console.log(school.hasOwnProperty("hasOwnProperty"));//false
    

    枚举属性有三种方法:

    for…in…循环;

    可以枚举所有可枚举的属性,包括继承的属性。如下:

    //首先定义一个school对象,它从原型链上继承的属性都是不可枚举的,而下面自定义的四个属性或者方法都是可枚举的
    var school = {  name:"SJTU",  location:"ShangHai",  studentNum:40000,  display:function() {  console.log(this.name);  }
    };
    //枚举school的属性
    //下面的圆括号中的att表示对象的属性,school表示对象
    for(var att in school) {  //依次输出name,location,studentNum,display  console.log(att);
    }
    

    Object.getOwnPropertyNames()

    //定义一个school对象
    var school = {  name:"SJTU",  location:"ShangHai",  studentNum:40000,  display:function() {  console.log(this.name);  }
    };
    //为school对象增加一个不可枚举的属性range
    Object.defineProperty(school, "range", {  value: 4,//设置range属性的值  enumerable: false//设置range属性为不可枚举
    });
    //输出["name","location","studentNum","display","range"]
    console.log(Object.getOwnPropertyNames(school));
    

    Object.keys()

    var school = {  name:"SJTU",  location:"ShangHai",  studentNum:40000,  display:function() {  console.log(this.name);  }
    };
    //为school对象增加一个不可枚举的属性range
    Object.defineProperty(school, "range", {  value: 4,//设置range属性的值  enumerable: false//设置range属性为不可枚举
    });
    //输出["name","location","studentNum","display"]
    console.log(Object.keys(school));
    

    JS数组

    数组的创建

    创建数组有两种方法,一是使用数组字面量,简单来说就是在[]之中列出数组的所有元素:

    var numberArray = [1,2,3,4,5];//数字数组
    var stringArray = ["java","script","edu","coder"];//字符串数组
    var mixArray = [1,2,"java",true,6.6];//混合数组
    

    创建数组的第二种方法是新建一个Array对象的实例,如:

    var myArray = new Array();//创建一个初始为空的数组
    var lengthMixArray = new Array(6);//创建一个长为6的数组
    

    这种情况下可以设置数组的长度(即数组中元素的个数),也可以不设置

    数组元素的读取和写入

    数组元素的读取和写入在形式上相似,都是用赋值符号连接的两个表达式。

    读取时,存放读入值的变量在左边,数组元素在右边:

    var readArray = [1,3,"js",true,2.22];
    var read = readArray[0];//读取第一个元素到变量read中
    
    数组元素的增加

    JavaScript中,为数组增加元素可以在数组头部(索引最小处)或者尾部进行,可以使用数组的方法或者直接使用运算符。

    在尾部添加元素

    最直观的方法是直接给当前尾部元素的后一个位置赋值。

    var numberArray = [12,23,34,45];
    numberArray[numberArray.length] = 56;
    console.log(numberArray);//输出[12,23,34,45,56]
    

    第二种方法是使用push()函数,往数组的末尾添加一个或多个元素,参数是要添加的元素,返回数组长度。

    //利用push()方法在数组尾部添加元素
    var numberArray = [12,23,34,45];
    var newLength = numberArray.push(56);
    console.log(newLength);//输出5
    console.log(numberArray);//输出[12,23,34,45,56]
    
    在头部添加元素

    unshift()方法在数组的头部添加元素,并返回数组新的长度,其余元素自动向索引大的方向移动。

    var sArray = ["ja","va","script"];
    var newLength = sArray.unshift("he","llo");
    console.log(newLength)//输出5
    console.log(sArray);//输出["he","llo","ja","va","script"];
    
    数组元素的删除

    删除也能在数组头部(索引值小)或者尾部进行。

    在尾部删除元素

    上一关介绍过一种方法:直接修改数组长度为更小的值。

    var array = [1,2,true,"hello"];
    array.length = 3;//索引最大的元素被删除
    console.log(array);//输出[1,2,true]  var array = [1,2,true,"hello"];  array.length = 3;//索引最大的元素被删除
    console.log(array);//输出[1,2,true]
    

    第二种方法是使用delete运算符。delete运算符后接要删除的元素,但是删除后,会有一个空占位符,所以数据的长度保持不变。如:

    var dArray = [11,22,33,44,55];
    delete dArray[4];//删除索引最大的元素
    console.log(dArray);//输出[11,22,33,44]
    console.log(dArray.length); //长度为5
    

    第三种方法是使用pop(),一次删除一个,并返回被删除的元素。

    //利用pop()方法在数组尾部删除元素
    var numberArray = [3,4,5,6,7];
    var deletedNumber = numberArray.pop();
    console.log(deletedNumber);//输出被删除的元素7
    console.log(numberArray);//删除后的数组为[3,4,5,6]
    
    在头部删除元素

    unshift(),自然有shift()shift()的作用是删除数组头部一个元素并返回该元素,然后所有元素往索引值小的方向移动一位。

    初学者很容易混淆这两个方法,建议记住shift单词的意思是:删除,去掉。

    var dArray = [11,22,33,44,55];
    console.log(dArray.shift());//输出11,11被从数组中删除
    console.log(dArray);//输出[22,33,44,55]
    
数组的遍历

数组的遍历指按顺序访问你数组的每一个元素。有两种方法:

  • 使用for循环
//依次在浏览器的控制台输出one,two,three,four
var stringArray = ["one","two","three","four"];
for(var i=0,sLength=stringArray.length;i<sLength;i++) {  console.log(stringArray[i]);
}
  • 使用forEach()方法
    forEach()方法的参数是一个无名字的函数,函数有三个参数,第一个参数是当前的数组元素,第二个参数是当前的索引,第三个参数是数组对象的索引。与for循环的区别是无法用break中断循环。
var numArr = [10,11,12,13,14];
numArr.forEach(function(mem,i,arr) {  mem *= 10;  arr[i] = mem;
});
console.log(numArr);//输出[100,110,120,130,140]
多维数组的实现

多维数组实际上就是数组的数组,指数组的每一个元素也是一个数组,这里仅讨论二维数组。

JavaScript中二维数组的列的长度不唯一,第一列可以有4个元素,第二列可以有5个元素,等等。

  • 二维数组的创建
    创建已知的二维数组:
var multiArr = [[1,2,3,4],[5,6,7],[8,9]];

创建仅知道长度的二维数组

//创建一个4行6列的二维数组
var muArr = new Array(4);
for(var i = 0;i <4;i++)
{      muArr[i] = new Array(6);  }
  • 二维数组的读写
    二维数组的读写用数组名[][]的方式,第一个中括号内为行数,从0计数,第二个中括号内为列数,也从0计数。
    以上面的数组multiArr为例:
var multiArr = [[1,2,3,4],[5,6,7],[8,9]];
console.log(multiArr[1][1]);//读元素,输出6
multiArr[0][0] = 0;//写元素
查找元素的位置

根据值查找元素的位置,有两个方法:indexOf()lastIndexOf(),前者从索引小处往大搜索,后者相反。都返回第一次遇到该元素时的索引。

两者都有两个参数,第一个参数为要查找的元素,第二个参数可选,为搜索的起点索引。如:

var search = ["a","b","a","b","c","d","a","a","b","a"];
console.log(search.indexOf("a"));//输出0
console.log(search.lastIndexOf("a"));//输出9
console.log(search.indexOf("a",2));//输出2,从索引为2处开始搜索

第二个参数可以是负数,-1表示倒数第一个元素,-2表示倒数第二个元素,依次类推。如:

var search = ["a","b","a","b"];
console.log(search.indexOf("a",-3));//输出2
console.log(search.lastIndexOf("a",-3));//输出0
数组的合并

concat()实现数组合并,其形式是数组a.concat(数组b),合并之后返回新数组,新数组为数组a后面连接数组b,但是数组ab不变。

var a = [1,2,3];
var b = [4,5,6];
var c = a.concat(b);//合并后返回新数组
console.log(c);//输出[1,2,3,4,5,6]
数组倒置

reverse()实现数组倒置,无参数,返回倒置后的数组,同时调用该方法的数组也会被倒置。称为就地逆置。

var a = [1,2,3,4];
var b = a.reverse();
console.log(a);//输出[4,3,2,1]
console.log(b);//输出[4,3,2,1]
元素合并

join()将数组的所有元素连接起来组成字符串,参数为元素之间的分隔符,默认逗号。

var sArray = ["June","July","August"];
console.log(sArray.join());//输出June,July,August
console.log(sArray.join("+"));//输出June+July+August
元素排序

sort()实现数据元素排序,不带该参数表示元素按照ASCII表从小到大排序。如:

var stringArray = ["a","ab","b","aa"];
stringArray.sort();
console.log(stringArray);//输出["a","aa","ab","b"]

需要注意的是数字的排序,例子如下:

var arr = [1,2,10,5,12];
arr.sort();
console.log(arr);//输出[1,10,12,2,5];

带参数的格式如下:

arr.sort(function(a,b){  return a-b;  //升序排列
})

或者:

arr.sort(function(a,b){  return b-a;  //降序排列
})

说明:

  • arr是要排序的数组;
  • ab是两个参数,返回a-b,升序排列,返回b-a,降序排列。

对于数字的排序,sort()带参数和不带参数是不一样的,例子如下:

var arr = [1,2,10,5,12];
arr.sort();
console.log(arr);//输出[1,10,12,2,5]
arr.sort(function(a,b){  return a-b;
});
console.log(arr);//输出[1,2,5,10,12]
提取子数组

slice()返回切割出的子数组,不修改原来的数组。

它有两个整数参数aba表示切割的起点,该点属于子数组;b可选,表示切割的终点,该点不属于子数组。

ab都可以为负数,如-1表示倒数第一个位置,依次类推。

var arr = ["a","b","c","d","e"];
console.log(arr.slice(0,3));//["a","b","c"]
console.log(arr.slice(0,-2));//["a","b","c"]
console.log(arr.slice(4));//["e"]
console.log(arr.slice(-4));//["b","c","d","e"]

JS函数

函数的定义是指用一段代码实现函数的功能,通常的定义方式以关键字function开头。

用函数语句定义

先给一个例子,该函数的功能是返回数组元素的和;

function sumArray(arr) {  var sum = 0;  for(var i = 0,aLength = arr.length;i < aLength;i++) {  sum += arr[i];  }  return sum;
用表达式定义

用表达式的方式定义函数,就是用赋值表达式把函数赋值给一个变量,这其实就是把函数看成一个变量。这个时候函数可以有名字,也可以没有名字,没有名字的函数叫做匿名函数

  • 带名字的;
var funct = function getMax(a,b) {  return a>b?a:b;
};//注意这后面的分号不能少,因为我们定义的是一个变量!

和前一关不同的是,只能在函数定义语句之后调用该函数,且调用的时候只能用变量名funct,不能用函数名getMax,如:

var funct = function getMax(a,b) {  return a>b?a:b;
};
console.log(funct(1,2));//输出2
  • 匿名函数;
    所谓匿名函数就是关键字function之后直接是参数列表:
var funct = function(a,b) {  return a>b?a:b;
};

这个函数没有名字,它被赋值给了变量funct,所以叫匿名函数。同样,也只能在这一语句之后调用该函数。

var funct = function(a,b) {  return a>b?a:b;
};
console.log(funct(1,2));//输出2
函数的调用

在实训四中,我们曾经介绍过对象可以有自己的方法,当然这也是函数。这种函数的调用和前面两关定义的函数有细小的区别。

//函数的定义:求三个数的最大值
function max(a,b,c) {  if(a > b) {  if(a > c)  return a;  else   return c;  }  else {  if(b > c)  return b;  else   return c;  }
}
//调用该函数
var result = max(1,2,3);//result为3
console.log(result);//输出3

调用函数的时候,需要传入和形参相同个数的的具体值,上面的函数有3个参数,所以下面调用的时候传入3个具体的值,1传给参数a2传给参数b3传给参数c。函数的返回值通过赋值符号=传给了变量result。如果函数体内没有return关键字,将返回undefined

再来看一下对象里定义的函数的调用:

var ob = {  id:1,  getMax:function(a,b) {  return a>b?a:b;  }
};
var result = ob.getMax(2,1);//result值为2
var result1 = ob["getMax"](2,1);//result1的值也是2

与上面的区别是,这里要定位到函数,需要使用对象名.函数名或者对象名["函数名"],其它相同。

函数的基本功能是对函数内的参数进行操作,其中,函数定义时的参数被称为形式参数,函数被调用时传入的参数被称为实际参数

未定义的实参

在大部分的编程语言里面,都会对调用函数时传入的实参个数和类型进行检查,而JavaScript不检查实参的类型,也不检查实参的个数。
JavaScript中的实参会按照顺序从左到右依次匹配上形参,例如:

function myFunction(a,b,c) {  console.log(a);  console.log(b);  console.log(c);
}
myFunction(1,2,3);

实参1传入形参a,实参2传入形参b,实参3传入形参c。 当实参个数少于形参时,靠右的形参会被传入值undefined。如:

function myFunction(a,b,c) {  console.log(a);  console.log(b);  console.log(c);
}
myFunction(1,2);

实参1传入形参a,实参2传入形参bundefined传入形参c。 如果只想给右侧的参数传入数据,可以给前几个实参传入undefined。如:

function myFunction(a,b,c){
console.log(a);
console.log(b);
console.log(c);
}
myFunction(undefined,1,2);

上面这两种做法不够严谨,最佳实践是给可能被传入undefined值的形参设定一个默认值。如:

function getSum(a,b,c) {  if(c === undefined)   c = 0;  console.log(a+b+c);
}
myFunction(1,2);
实参对象

JavaScript一切都是对象,实参也是一个对象,有一个专门的名字arguments,这个对象可以看成一个数组(类数组,不是真的数组),实参从左到右分别是arguments[0]、arguments[1]...arguments.length表示实参的个数。

实参对象一个最重要的应用是可变长参数列表,想象一下求一组数的和,如果这组数不在一个数组里面,使用函数来求则无法定义函数体,因为不知道形参的个数。这个时候就可以用arguments来解决问题。如:

//求参数的和
function getSum() {  var aLength = arguments.length;  var sum = 0;  for(var i = 0;i < aLength;i++) {  sum += arguments[i];  }  return sum;
}
console.log(getSum(1,2,3,4,5))//输出15

这里的形参直接省略,使用arguments[i]表示。

对象作为参数

复杂的函数通常多达十几个参数,尽管JavaScript不做参数个数和类型的检查,但是调用时实参的顺序不能乱。开发人员需要检查每一个实参和形参的对应关系,这样效率很低。一种很好的解决方案是使用对象作为参数,函数会根据对象的属性名操作参数。

function myFunction(obj) {  console.log(obj.name);  obj.number++;  return obj.number;
}
myObj = {name:"myObj",number:34};
myFunction(myObj);//输出myObj
console.log(myObj.number);//输出35

这种情况下开发人员不需要记住或查阅形式参数的顺序。

函数对象作为另一个函数的参数

一个函数(为方便行文,称为a函数)可以作为另外一个函数(称为b函数)的参数b函数最终可以返回一个具体的值。

从原理上来说,b函数在自己的函数体内调用了a函数,所以需要把a函数的名字作为实际参数传递给b函数。如下:

//求最大值
function getMax(a,b) {  return a>b?a:b;
}
//求最小值
function getMin(a,b) {  return a<b?a:b;
}
//下面这个函数以函数作为参数,并最终返回一个值
function getM(func,num1,num2) {  return func(num1,num2);
}
getM(getMax,1,2);//返回2
getM(getMin,1,2);//返回1

我们把a函数的名字(getMax或者getMin)传给b函数(getM()),然后在b函数内部调用闯入的a函数,得到相关的结果。

JS字符串

indexOf()

子字符串指一个字符串中连续的一部分。

上图中有两个字符串aaabcabc,将abc从左往右在aaabc中查找,第一次出现的位置是在aaabc的索引2处。

indexOf()就是返回某个字符串在字符串中首次出现的位置。如果搜索到尾部还没有找到,返回-1

indexOf(a,b)中参数a是字符串;b是开始查找的位置,即从调用者(一个字符串)的第几个位置开始查找,可选。

var short = "ab";
var long = "aabccccaab";
var mix = "cdef";
long.indexOf(short);//返回1
long.indexOf(short,4);//返回8
long.indexOf(mix);//返回-1

可以看到第4行与第5行的结果不同,因为第5行是从long的第5个字符开始查找的。

lastIndexOf()

从名字上就可以知道,这个函数与indexOf()功能很相似,区别是搜素方向是从后往前搜索。

lastIndexOf()也有两个参数,含义同indexOf()

var short = "ab";
var long = "aabccccaab";
var mix = "cdef";
long.lastIndexOf(short);//返回8
long.lastIndexOf(short,4);//返回1
long.lastIndexOf(mix);//返回-1
charAt()

charAt()作用是返回调用者指定位置的字符,位置从0计数:

var str = "abcdefg";
console.log(str.charAt(0));//输出a
console.log(str.charAt(str.length-1));//输出g

字符串的截取是指获得原来字符串连续的一部分。这种操作很常见,比如现在各大互联网企业的招聘官网上,应聘者在填入身份证号码后,页面会自动截取中间6位生成出生日期,这是一种提高页面体验的方式。

slice()

slice(a,b)的作用是截取a位置()到b位置(不含)之间的字符串,被截取的字符串不变,返回截取后获得的子字符串。比如a0b3,则截取0、1、2三个位置的字符。

b为可选参数,不填则表示截取到字符串的尾部ab为负数时表示从右往左排,即-1表示最右侧的位置,依次类推。

var str = "0123456789";
console.log(str.slice(0,4));//输出0123
console.log(str.slice(3));//输出3456789
console.log(str.slice(-3));//输出789
console.log(str.slice(-5,-2));//输出567
substring()

substring(a,b)slice(a,b)功能相同,参数的意义也相同:ab都是表示位置的数字。只是参数的处理有些不同:

  • ab为负数时,自动转换为0
  • a大于b时,编译器会自动对调两者。
var str = "0123456789";
console.log(str.substring(0,4));//输出0123
console.log(str.substring(-1,4));//输出0123
console.log(str.substring(4,0));//输出0123
substr()

与上面的两个函数不同,substr(a,b)指定开始位置和要截取的长度

a表示开始位置(包含);b表示截取的长度,可选。不填则截取到字符串结尾处;

a为负数时表示从右往左排,即-1表示最右侧的位置,依次类推。

var myStr = "0123456789";
console.log(myStr.substr(3));//输出3456789
console.log(myStr.substr(3,2));//输出34
console.log(myStr.substr(-2));//输出89
toLowerCase()

toLowerCase()把字符串中的所有大写英文字母转为小写,返回转换后的字符串,但是操作该函数的字符串不变。

var upperStr = "aBCd";
var lowerStr = upperStr.toLowerCase();
console.log(upperStr);//输出aBCd
console.log(lowerStr);//输出abcd
toUpperCase()

与上面的函数相反,toUpperCase()把字符串中的所有小写英文字母转为大写,返回转换后的字符串,但是操作该函数的字符串不变。

var str = "asdf";
var strin = str.toUpperCase();
console.log(str);//输出asdf
console.log(strin);//输出ASDF
split()

还记得数组对象的方法join()吗?

字符串的方法split()join()正好相反,split()以指定的字符分割字符串,从而得到一个子字符串数组。

比如字符串a,b,c,d,e,f以逗号为分隔符,就可以得到数组["a","b","c","d","e","f"]

split(a,b)中的参数a是分割符,它的含义是:原来的字符串以该分隔符为边界,分为若干个子字符串(不含该分隔符)。b表示返回的数组的最大长度,不填表示返回所有子字符串组成的数组。

如果要实现分割每一个字符,需要用空字符串""作为分隔符。

var str = "012304560789";
var arr1 = str.split("");//返回["0","1","2","3","4","5","6","7","8","9]
var arr1 = str.split("0");//返回["123","456","789"];
var arr2 = str.split("0",2);//返回["123","456"];

后面的学习中我们将看到,a也可以是一个正则表达式,表示以该正则表达式匹配到的字符串为分隔符。

JS正则表达式

在开发中,经常会有这样的需求:用一个式子表达有相同规律的一类字符串,比如国内的手机号码:必须是11位数字,而且只能以1315171819开头。

这样的式子在JavaScript中称为正则表达式。

正则表达式的定义

正则表达式:能够定义一类具有相同特征的字符串的式子。

JavaScript中,正则表达式有两种定义方法:

创建RegExp对象的实例

创建RegExp对象的一个实例:

var myFirstPattern = new RegExp("s$");

上面等号右侧定义了一个正则表达式,并赋值给变量myFirstPattern

使用//符号定义

更简单的方法是直接用//符号创建正则表达式:

var easyPattern = /s$/;

这里的easyPattern和上面的myFirstPattern含义一模一样。

匹配

上面的正则表达式表示的是这样一类字符串:以字母s结尾的字符串,比如字符串apples就符合这个要求,我们把符合要求叫做匹配

需要注意的是,字符串和正则表达式匹配,指的是字符串的一部分(子串)和正则表达式匹配即可,比如/s/匹配的是字母s,一个单词如果含有字母s,那么就和这个正则表达式匹配。

通过方法test()可以检测字符串和正则表达式是否匹配,括号内的参数是待匹配的字符串,如果匹配返回true,否则返回false,如下:

var pattern = /s/;
pattern.test("s");//匹配,返回true
pattern.test("asa");//匹配,返回true
pattern.test("aa");//不匹配,返回false

使用正则表达式的关键是正确编写表达式,一个设计良好的正则表达式可以表示一类非常复杂的字符串。为了实现这种复杂的表示,正则表达式提供了字符串字面量、字符类、重复、选择、分组、引用、指定匹配位置、修饰符等功能,这些将在本实训的剩余部分逐条讲述。

字符串字面量

正则表达式里面的数字和英文字母都是按照本来的含义匹配的,即aa匹配的就是字符串aa123匹配的就是数字123。这些数字和字母称为字符串字面量。

var pattern = /aa/;
pattern.test("aa");//true
pattern.test("aabb");//true
pattern.test("abb")//false

一些特殊的符号,比如换行符、换页符,需要用斜杠加字母表示。比如\n匹配的是换行符,下面列出一些常用的特殊符号:

字符 匹配
\n 换行符
\f 换页符
\t 制表符
\v 垂直制表符
\r 回车符
字符类第一种表示

前一关阐述了字符串字面量的使用,一种更常见的需求是匹配一类字符:比如选择题的答案只能是ABCD中的任何一个。正则表达式用字符类来实现,字符类被放在中括号之内。[ABCD]表示一个字符类,它和A、B、C、D中的任何一个都匹配。

还可以用[A-D]表示上面的字符类,中间的-表示AD之间的所有字符。

var lowerCharPattern = /[a-z]/;//匹配任意小写字母
var upperCharPattern = /[A-Z]/;//匹配任意大写字母
var numberPattern = /[0-9]/;//匹配任意数字
var mixPattern = /[a-zA-Z0-9]/;//匹配大小写字母,数字

在中括号最前面加上^符号表示反向匹配:匹配和中括号内的正则表达式不匹配的所有字符,比如:

var notNumberPattern = /[^0-9]/;
notNumberPattern.test("123");//false
notNumberPattern.test("ahc");//true

[^0-9]匹配的是任意非数字的字符。其中^符号一定要在中括号内排在最前面。

字符类第二种表示

字符类还有一种较为简单的表示方法,比如\d[0-9]的含义相同:表示任意的数字。下面用表格列出:

字符 匹配 等价于
\w 大小写字符或数字 [a-zA-Z0-9]
\W 非字母,非数字 [^a-zA-Z0-9]
\d 数字 [0-9]
\D 非数字 [^0-9]

上面的内容都是单独的一个字面量或者字符类,对于稍微复杂一点的,比如一个数字后面紧跟着一个小写字母,该怎么表示呢?来看一个例子:

//表示数字后面紧跟着一个小写字母
var pattern = /[0-9][a-z]/;
pattern.test("1a");//true
pattern.test("11a");//true
pattern.test("a1");//false

从上面可以看出,表示数字的正则表达式[0-9]和表示小写字母的正则表达式[a-z]直接连接起来,即表示数字后面紧跟小写字母。其他的字符串字面量或者字符类也可以用这种方式。

通过前面的学习,我们可以编写一些简单正则表达式,比如两个连续的数字可以表示为[0-9][0-9],但是,这种方法有一个问题:比如手机号码的正则表达式,用11个连续的[0-9]写起来容易错,且不具有可读性。正则表达式用重复解决了这个问题。

重复

重复表示指定的字符或者字符串(本关可以简单理解为前面紧邻的字符)可以连续出现多次。比如匹配含有100个字母a的字符串,在这个字符串中,a连续出现100次,用正则表达式表示为:

var pattern = /a{100}/;//匹配100个连续的字母a组成的字符串

有多种表示重复的方法:

  • {a,b}中的ab都是数字,表示前面的字符至少出现a次,最多出现b次;
var pattern = /at{1,2}/;//表示a后面最少一个t,最多两个t
pattern.test("at");//true
pattern.test("att");//true
pattern.test("am");//false
  • {a,}表示前面的字符至少出现a次,最多不限制;
var pattern = /[0-9]{4,}/;//匹配最少四个数字
pattern.test("1234");//true
pattern.test("1");//false
  • {a}表示前面的字符出现a次;
var pattern = /[a-z]{1}/;//匹配单个小写字母
pattern.test("r");//true
pattern.test("12R");//false
  • ?,表示前面的字符出现一次或者不出现,等价于{0,1}
var pattern = /A[0-9]?A/;//匹配两个A之间有0或1个数字
pattern.test("AA");//true
pattern.test("A0A");//true
pattern.test("A01A");//false
  • +,表示前面的字符至少出现一次,等价于{1,}
var pattern = /js+/;//匹配j后面最少一个s
pattern.test("jsjs");//true
pattern.test("java");//false
  • *,表示前面的字符至少出现0次,等价于{0,}
var pattern = /A[0-9]*B/;//匹配A和B之间为空或者只有数字
pattern.test("AB");//true
pattern.test("A1B");//true
pattern.test("AaB");//false

需要特别注意的是,至少出现0次,就是说这个字符可以不出现,比如正则表达式[0-9]{0,}和字符串hello是匹配的,这里特别容易出错。

总结一下重复匹配的用法,如下:

表达式 匹配 等价于
{a,b} 至少出现a次,最多出现b次
{a,} 至少出现a次
{a} 出现a次 {a,a}
+ 最少出现一次 {1,}
? 出现一次或者不出现 {0,1}
* 至少出现0次 {0,}
转义

相信细心的读者已经发现了一个问题:对于?+等表示特殊含义的字符,如何实现字面量匹配呢?就是说如何匹配他们本来的含义呢?

JavaScript中,使用\实现特殊符号的普通化,又叫做转义:

var pattern1 = new RegExp("\?");//匹配一个问号
var pattern2 = /\+{4}/;//匹配四个加号

考虑一下身份证号码的匹配:最后一位是数字或者X,即二选一的关系,正则表达式使用选择实现这种匹配。

选择

选择使用符号|来实现,比如0|1表示匹配0或者1\d|a表示匹配数字或者字母a

第一关我们讲过:所谓匹配指的是匹配字符串的某一个子串。但是涉及到选择的时候情况就会有些复杂,比如正则表达式[0-9]|[a-z]匹配的是字符串1ABCa中的子串1还是a

看起来似乎都可以。实际上JavaScript会先挑选左边的子正则表达式[0-9]进行匹配,匹配成功后立即结束,所以匹配上的子串是1

下面是一些选择的例子:

var pattern1 = /[ABCD]|[abcd]/;//匹配ABCD中任意一个或者abcd中任意一个
pattern1.test("A");//true
pattern1.test("Ab");//true,且匹配的是A
var pattern2 = /\d|\+/;//匹配数字或者+号
pattern2.test("1");//true
pattern2.test("+");//true
选择符号作用范围

关于选择,还有一个问题需要明确:|作用到整个式子中的,即整个的正则表达式会被选择符号一分为二,一个字符串和该表达式匹配,要么匹配左侧的整个子正则表达式,要么匹配右侧的整个子正则表达式,如:

var pattern = /0|1ABC/;
pattern.test("0");//true,和左侧的整个子正则表达式0匹配
pattern.test("1");//false,和右侧的整个子正则表达式1ABC不匹配

如果想要限制|符号的作用范围,需要将目标作用范围用圆括号包含在内,如:

var pattern = /(0|1)ABC/;//选择符号仅仅作用在0和1上,而不是像上面的例子一样作用在整个正则表达式中
pattern.test("0");//false,注意这里和上面的例子不同
pattern.test("0ABC");//true
pattern.test("1ABC");//true
子表达式

我们把一个完整的正则表达式的一部分叫做子正则表达式,或者子表达式,比如:

var pattern = /[0-9]?a{12,15}/;  var subPattern = /[0-9]?/;

subPatternpattern的前面一部分,称为pattern的子表达式。

通过分组获得子表达式,通过引用操作子表达式。

分组

前面讲过的正则表达式的写法中,重复只能作用在紧邻符号的前面一个字符上,比如:

var pattern = /hello{3}/;

pattern表达的意思是字母o必须重复三遍,而不是单词hello必须重复三遍。

如果要表达单词hello必须重复三遍的意思,我们需要用到分组。

同样,要改变选择符号|的作用范围,也必须用到分组。

分组的符号是(),括号中是单独的项构成的子表达式,将这些子表达式看成一个整体进行操作,比如:

var pattern = /(hello){2}/;//匹配字符串hellohello
pattern.test("shellohellos");//返回true
pattern.test("helloo");//返回false

用括号分组后,可以像使用单独的项一样使用子表达式,即可以+?等符号操作它:

var pattern1 = /((hello){2})+/;//hellohello至少出现一次
var pattern2 = /(he|she)?/;//he或者she出现一次或不出现
console.log(pattern1.test("hello"));//输出false
console.log(pattern1.test("hellohello"));//输出true
console.log(pattern2.test("he"));//输出true
console.log(pattern2.test("it"));//输出true
编号

什么时候需要用到引用?

比如需要匹配这样一类字符串:以数字开头,中间是若干个字母,以数字结尾,并且开头的数字和结尾的数字相同,这个时候用前面所有介绍过的方法都不可行,无法确保开头的数字和结尾的数字相同。

所谓引用,就是在后面使用和前面完全相同的子表达式。我们把所有的带圆括号的子表达式编个号,从1开始。比如:

var pattern = /(A|B)(\d{5})not([0-9])/;//匹配字母A或者B后面紧跟5个数字,后面再紧跟字符串not,后面再紧跟1个数字

其中(A|B)编号为1(\d{5})编号为2([0-9])编号为3,中间的not不在圆括号内,不参与编号。

一个小问题:如果子表达式里面嵌套另外一层子表达式,引用时如何编号?简单来说,以子表达式前面的左括号的个数为准:

var pattern = /(play(ed|ing)){1,}/;

ed|ing前面有两个左括号,所以编号为2

引用

后面可以用\1引用编号为1的子表达式,依次类推,比如:

var pattern = /(A|B)(\d{5})not([0-9])\1\2/;

pattern在最后引用了第一个和第二个子表达式。

注意:* 这里的引用是对与子表达式匹配的字符串的引用*,而不是简单的对子表达式的引用。例如:

var pattern = /([0-9])AA\1/;

pattern不等价于正则表达式([0-9])AA[0-9],而是指字符串AA后面的数字必须和前面的相同,即形如1AA1这样的字符串。

再看一个例子:

var pattern = /([a-z])([a-z])([a-z])\3\2\1/;
pattern.test("abccba");//返回true
pattern.test("123eduude456");//返回true
pattern.test("abcdefg");//返回false

在上面的正则表达式pattern里面,我们先引用第三个子表达式,表示此处的字符串必须和第三个子表达式相同,然后引用第二个子表达式,最后引用的是第一个子表达式,所以pattern匹配一个回文串,即顺序读取和倒序读取结果相同的字符串。

考虑这样一种情况,判断一个字符串是否为合法的JavaScript变量名,变量名必须以字母、$或者_开头,这个时候要用到正则表达式中指定匹配位置的功能。

指定匹配位置

^用来匹配字符串的开头,比如:

var startPattern = /^[0-9]/;//匹配以数字开头的字符串  console.log(startPattern.test("1aa"));//true  console.log(startPattern.test("a11"));//false

注意,^符号在中括号的外面!不要与[^0-9]混淆,后者匹配非数字字符。

$用来匹配字符串的结尾,比如:

var endPattern = /ing$/;//匹配以ing结尾的字符串
console.log(endPattern.test("playing"));//true
console.log(endPattern.test("going first"));//false

\b用来匹配单词的边界,那么什么是单词的边界?

图片中蓝色的线指示了单词的边界,实际上就是英文字母和非英文字母(如空格符)之间的界限。

var boundaryPattern = /\bOK\b/;//匹配单词OK
console.log(boundaryPattern.test("OK"));//true
console.log(boundaryPattern.test("rewa OK de"));//true
console.log(boundaryPattern.test("sa,OK"));//true
console.log(boundaryPattern.test("OKNot"));//false

简单来说,\b表示英文字母非英文字母之间的界限,这个界限不占用字符的位置。

\B用来匹配非单词的边界,与上面的\b相反。

var pattern = /\Bed/;//ed左侧不能是单词的边界
console.log(pattern.test("played"));//true
console.log(pattern.test("edu"));//false
修饰符

修饰符用来描述一些整体的匹配规则,有igm三种。

修饰符需要放在//符号之后,如果通过新建RegExp对象定义正则表达式,则修饰符作为第二个参数。比如:

var pattern1 = /^java/m;  var pattern2 = new RegExp("^java","m");
不区分大小写

i表示整个的匹配过程中不考虑单词的大小写。如:

var pattern = /^edU/i;
console.log(pattern.test("edu"));//输出true
console.log(pattern.test("Edu"));//输出true
console.log(pattern.test("EDUCoder"));//输出true
全局匹配

g表示执行全局匹配,即找出所有满足匹配的子字符串。比如,已知match()函数返回由匹配结果组成的数组,如果没有匹配到返回null

不用g修饰时:

var pattern = /[a-z]/;//匹配小写字母
console.log("a1b2c3".match(pattern));//输出["a", index: 0, input: "a1b2c3"]

显然,只匹配到了第一个小写字母a

g修饰时:

var pattern = /[a-z]/g;//全局匹配小写字母
console.log("a1b2c3".match(pattern));//输出["a", "b", "c"]

三个小写字母都被匹配到了,这就是所谓的全局匹配。

多行模式

有的时候,需要匹配的字符串很长,分为很多行(即中间有换行符号)。

m在多行模式中执行匹配,即:符号^不仅匹配整个字符串的开头,还匹配每一行的开头,&不仅匹配整个字符串的结尾,还匹配每一行的结尾。

var pattern = /[0-9]$/m;//多行匹配以数字结尾的字符串
var string = "1\nhello";//字符串在两行,中间的\n是换行符
console.log(pattern.test(string));//输出true

如果没有m修饰,将会输出false,因为这种情况下$不会和换行符\n匹配。

字符串比较常用的方法有search()replace()match()split(),这些方法的调用者都是字符串对象,参数中都有正则表达式。

search(a)方法

参数:a为正则表达式。
功能:返回字符串中与该正则表达式匹配的第一个子串的起始位置,无匹配返回-1

var pattern = /[0-9]/;  var string = "a3b2c1";  console.log(string.search(pattern));//输出1
split(a)方法

参数:a是字符串或者正则表达式;
功能:以a为分隔符分隔原来的字符串;
返回值:分割后形成的子字符串数组。

console.log("a1b2c3d".split(/[0-9]/));//以数字为分隔符,输出["a", "b", "c", "d"]
replace(a,b)方法

参数:a是正则表达式,b是字符串;
功能:用b替换掉第一个a匹配的子串,如果a中有修饰符g,替换掉所有子串;
返回值:被替换后的字符串。

var pattern1 = /[0-9]/;
var pattern2 = /[0-9]/g;
var string = "a1b2c3";
console.log(string.replace(pattern1,"A"));//部分替换,输出aAb2c3
console.log(string.replace(pattern2,"A"));//全部数字被替换,输出aAbAcA

复杂的情况:b还可以是子表达式$1$2等,$1等会先被替换为与它匹配的子串。比如:

var pattern = /([0-9])[A-Z]/g;
var string = "1A,2B,3C,1";
console.log(string.replace(pattern,"$1"));//输出1,2,3,1

上面的$1指的是与子表达式[0-9]匹配的子字符串,比如第一个匹配1A$1指的是1,第二个匹配2B$1指的是2,依次类推。

string中与pattern匹配的有1A2B3C,这其中与子表达式$1匹配的分别是123,所以$1会相继被替换为123,然后再用123去分别替换1A2B3C

JS JSON

JSON既然是用来传递数据的,必然要先存储数据,存储数据需要采用一定的数据格式,JSON常用的数据格式有JSON对象、JSON数组和JSON字符串。

什么是JSON对象

JSON对象(通常叫JSON)是一种文本数据的交换格式,用于存储和传输数据。示例如下:

{"name":"Jerry", "age":15}

这就是一个简单的json对象,它的规则是:

  • 数据以键/值对的形式存在;
  • 数据之间用逗号间隔;
  • 大括号表示保存对象;
  • 方括号表示保存数组。
JSON对象与Javascript对象的区别

在实训四里面,我们讲过JavaScript中的自定义对象。而JSON是基于JavaScript语法的,所以JSON中也有对象的概念,但是和JavaScript中的对象有一些小的区别。

定义一个JavaScript对象:

var myObject = {  id:1,  name:"Peter Bruce",  "first name":"Bruce",  display:function() {  console.log(this.name);  }
}

定义一个JSON对象:

{"id":1,"name":"Peter Bruce","first name":"Bruce"}

三点区别:

  • JSON对象的属性名(key)必须被包含在双引号之中,而JavaScript对象除了有空格的属性名、中间有连字符-的属性名必须在双引号之中外,其它随意;
  • 不能在JSON对象中定义方法,而在JavaScript对象中可以;
  • JSON对象可以被很多语言操作,而JavaScript对象只有JS自己可以识别。

定义JSON对象的方法如下:用一个{}包含在内,内部是若干个属性名和属性值构成的键值对,键值对之间用,隔开,属性名和属性值之间用:隔开,属性值可以是以下任意一种数据类型的数据:数字、字符串、JSON数组、JSON对象、null。如:

 {"a":1,"b":2.12,"c":true,"d":"string","e":null};

属性值是JSON数组或者JSON对象的情况稍复杂,后面的关卡将介绍。

在JavaScript中使用JSON对象

支持JSON的语言都能够使用JSON对象,这里仅介绍在JavaScript中如何使用JSON对象。

  • JavaScript中定义一个JSON对象:
var jsonObject = {"name":"js","number":2};
  • 操作属性,使用.或者[]

    console.log(jsonObject.name);//读属性,输出js
    console.log(jsonObject["name"]);//读属性,输出js
    jsonObject.name = "javascript";//写属性,给name属性赋值javascript
    
  • 删除属性,使用delete

    var jsonObject = {"name":"js","number":2};
    delete jsonObject.name;//删除name属性
    
  • 遍历属性,使用for-in循环:

    var jsonObject = {"name":"js","number":2};
    for(att in jsonObject) {  console.log(jsonObject[att]);//依次输出js、2
    }
    
    JSON属性对应的值(value)是一个数组

    JSON键值对中的值(value)可以是一个数组,比如:

    {
    "country":"China",
    "population":"1.3billion",
    "bigCity":["Peking","Shanghai","ShenZhen","HongKong"]
    }
    

    属性bigCity的值有多个,放在一个数组里面。

    上面例子里面,数组的每一个元素都是字符串。其实,数组的每一个元素还可以是另外一个json对象。比如:

    {
    "class":"高三一班",
    "studentNumber":70,
    "score":[  {"name":"LiMing","score":128},  {"name":"ZhangHua","score":134},  {"name":"ShenLu","score":112}
    ]
    }
    

    上面的score属性的值是一个数组,这个数组的每一个元素是一个json对象。

    数组的一些操作

    读写元素:

    var myJson = {
    "country":"China",
    "population":"1.3billion",
    "bigCity":["Peking","Shanghai","ShenZhen","HongKong"]
    }
    console.log(myJson.bigCity[1]);//打印出Shanghai
    myJson.bigCity[0] = "GuangZhou";//第一个元素被赋值为GuangZhou
    

    遍历:

    var myJson = {
    "country":"China",
    "population":"1.3billion",
    "bigCity":["Peking","Shanghai","ShenZhen","HongKong"]
    }
    for(var i = 0;i < myJson.bigCity.length;i++) {  console.log(myJson.bigCity[i]);//依次输出Peking,Shanghai,ShenZhen,HongKong
    }
    

    在前端和后台之间传递数据可以使用JSON,但是实际上传递的是JSON字符串,而JSON对象是不可以直接进行传递的。

    JSON字符串

    JSON字符串就是在JSON对象两边套上'形成的字符串,如:

    var JSONObject  = {"k1":"v1","k2":"v2"};//JSON对象
    var JSONString1 = '{"k1":"v1","k2":"v2"}';//JSON字符串
    

    上面的JSONSring1就是JSON字符串,可以直接从前端传到后台或者后台传到前端。

    JavaScript收到从后台传来的JSON字符串后,怎么把它变成JSON对象方便处理呢?

    JSON字符串到JavaScript对象

    JSON.parse(a,b)方法将JSON字符串a转换为JavaScript对象。b是一个可选的函数参数。

    var JSONString1 = '{"k1":"v1","k2":"v2"}';
    console.log(JSON.parse(JSONString1));//输出Object {k1: "v1", k2: "v2"}
    

    函数参数b按从里到外的顺序作用在对象的所有属性上,最后一个作用的是对象本身:

    //对象的每一个属性的值加1
    var text = '{ "key1":1, "key2":2, "key3":2.2}';
    var obj = JSON.parse(text, function (key, value) {  if(key === '')//当遇到对象本身时,不进行加1操作  return value;  return value+1;//对属性值加1
    });
    console.log(obj);//输出Object {key1: 2, key2: 3, key3: 3.2}
    

    如上面所示,函数的参数有两个,其中key表示属性的名字,value表示属性的值,当遇到对象本身时,key的值为'',即空字符串。

    JSON对象转换为JSON字符串

    JSON.stringify(a,b,c)a是待转换的JSON对象,bc为可选参数。

    var JSONObject = {"k1":"v1","k2":"v2"};
    JSON.stringify(JSONObject);//JSON对象转换为JSON字符串
    

    参数b为函数时,该函数按照从里到外的顺序处理JSON对象的每一个属性,最后一个处理的是JSON对象本身,处理完后再转为JSON字符串:

    //对象的所有属性值加1,再转为字符串
    var JSONObject = {"k1":1,"k2":2.2};
    var JSONString = JSON.stringify(JSONObject,function(k,v){  if(k === '')//处理到了JSON对象本身  return v;  return v+1;//所有的属性的值加1
    });
    console.log(JSONString);//输出{"k1":2,"k2":3.2}
    

    参数b还可以是数组,数组存储的是属性的名字,用来指定只转换哪些属性:

    //转换对象中特定的属性
    var JSONObject = {"k1":1,"k2":2.2,"k3":3};
    var JSONString = JSON.stringify(JSONObject,["k1","k2"]);
    console.log(JSONString);//输出{"k1":1,"k2":2.2}
    

    这里简单介绍一下c

    var str = ["name":"Tom","age":16];
    var obj1 = JSON.stringify(str);
    var obj2 = JSON.stringify(str,null,4);
    console.log(obj1);  //输出{"name":"Tom","age":16}
    console.log(obj2); //输出
    //{
    //    "name": "Tom",
    //    "age": 16
    //}
    

    参数c:文本添加缩进、空格和换行符,如果 c 是一个数字,则返回值文本在每个级别缩进指定数目的空格,如果 c 大于 10,则文本缩进 10 个空格。

JS日期、math、异常处理

Math用于进行数学计算,没有构造函数。

常用的数

一些常用的无理数的表示方法:

表示 意义 约等于
Math.E e 2.718
Math.PI 圆周率 3.14
Math.SQRT2 2的平方根 1.414
Math.LN2 2的自然对数 0.693
console.log(Math.PI);//输出3.141592653589793
console.log(Math.pi == 3.141592653589793);//true
console.log(Math.pi == 3.14);//false
向上取整

Math.ceil(x)实现向上取整,返回大于等于x且离x最近的整数。

console.log(Math.ceil(1.1));//输出2
console.log(Math.ceil(1));//输出1
console.log(Math.ceil(-1.1));//输出-1
向下取整

Math.floor(x)返回小于等于x且离x最近的整数。

console.log(Math.floor(1.1));//输出1
console.log(Math.floor(1));//输出1
console.log(Math.floor(-1.1));//输出-2
随机数

Math.random()返回01之间的一个随机数,包含0不包含1

console.log(Math.random());//输出0.1493135392665863
平方根

Math.sqrt(x)返回x的平方根。

Math.sqrt(4);//2  Math.sqrt(2);//1.4142135623730951
四舍五入

Math.round(x)返回x四舍五入后的整数。

Math.round(1.5);//2  Math.round(1.49);//1
求最值

Math.max(x1,x2,....xn)返回参数的最大值,参数个数不限;

Math.max(1,2,34,100);//100  Math.max(-1,-2,-34,-100);//-1

Math.min(x1,x2,....xn)返回参数的最小值;

Math.min(1,2,34,100);//1  Math.min(-1,-2,-34,-100);//-100
其余方法

Math还有很多的方法,如下面的表格所示:

方法 作用
Math.abs(x) 求x的绝对值
Math.pow(x,y) 计算x的y次幂
Math.exp(x) e的x次方
Math.log(x) x以e为底的对数
Math.sin(x) x的正弦
Math.cos(x) x的余弦
Math.tan(x) x的正切
Math.asin(x) x的反正弦
Math.acos(x) x的反余弦
Math.atan(x) x的反正切(-PI/2到PI/2之间)
Math.atan2(y,x) x轴到点(x,y)的角度 (-PI/2到PI/2之间

Date类用于处理日期和时间。

构造函数

Date(x)的参数x可以有四种情况:

new Date();//无参数,返回当前时间  new Date(millseconds);//参数为距离1970年1月1日8时0分0秒0毫秒的毫秒数,返回对应的时间  new Date(timeString);//参数为时间字符串,返回这个时间字符串对应的时间  new Date(year,month,day,hour,minute,second,millsecond);//参数为整数序列,分别表示年、月、日、时、分、秒、毫秒,返回这些时间参数对应的一个特定的时间

以上均返回一个对应的时间对象,如下:

console.log(new Date());//输出Sat Apr 07 2018 18:56:00   console.log(new Date(1000));//输出Thu Jan 01 1970 08:00:01  console.log(new Date("April 7, 2018 18:00:00"));//输出Sat Apr 07 2018 18:00:00  console.log(new Date(2018,4,7,18,0,0,0));//输出Mon May 07 2018 18:00:00

时间对象有一系列获取和设置年、月、日、时、分、秒、毫秒的函数。

获取和设置年月日

为举例方便,我们先定义一个时间。

//1970年1月1日上午8点0分0秒  var date = new Date(0);
  • x.getFullYear()返回x对应的四位数年份,xDate类对象;
console.log(date.getFullYear());//输出1970
  • setFullYear(year,month,day)用于设置年份,monthday可选;
date.setFullYear(2017);
console.log(date);//输出Sun Jan 01 2017 08:00:00
  • x.getMonth()返回x中的月份,结果在0(一月)到11(十二月)之间;
console.log(date.getMonth());//输出0
  • setMonth(month,day)作用是设置月份,0表示一月。1表示二月,依次类推,day可选;
date.setMonth(2);
console.log(date);//输出Sun Mar 01 1970 08:00:00
  • x.getDate()返回x对象在一个月的第几天(131),x.getDay()返回x对象在一个星期的第几天(06,0为周日);
console.log(date.getDate());//输出1
console.log(date.getDay());//输出4

由此可见,197011号那个美好的新年第一天是个周四。

  • setDate(day)设置日期对象在一个月的第几天(131);
date.setDate(31);
console.log(date.getDate());//输出31
获取和设置时分秒

获取和设置时、分、秒、毫秒的函数在形式上与上面的函数几乎相同,这里以表格的形式列出:

方法 作用 参数或者结果的范围
getHours() 获取小时 0~23
setHours(hour,min,sec,millsec) 设置小时 023、059、059、0999
getMinutes() 获取分钟 0~59
setMinutes(min,sec,millsec) 设置分钟 059、059、0~999
getSeconds() 获取秒钟 0~59
setSeconds(sec,millsec) 设置秒钟 059、0999
getMillSeconds() 获取毫秒数 0~999
setMillSeconds(millsec) 设置毫秒数 0~999
getTime() 获取距1970年1月1日8时0分0秒的毫秒数 大于等于0
setTime(millsec) 设置距1970年1月1日8时0分0秒的毫秒数 大于等于0

注:以上多个参数的函数,除了第一个参数外的参数都为可选。

日期转字符串

toString()将日期转字符串,结果类似于Sun Jan 07 2018 20:01:14 GMT+0800 (中国标准时间),分别表示星期、月份、天数、年份、小时、分钟、秒和时区(中国采用东八区时间)。

console.log(new Date().toString());//输出Sat Apr 07 2018 20:40:14 GMT+0800 (中国标准时间)

JavaScript代码在运行过程中,可能会因为很多原因出现错误,如:语法错误、用户输入错误等。发生错误后不能置之不理,要进行处理。为可能的错误提前在代码中制定解决方案。

try-catch捕获和处理错误

用法如下:

try {  //运行时可能出错的代码
}catch(err) {  //处理出现的错误
}

JavaScript在运行时抛出的错误或者异常,会被catch语句捕捉到,其中的变量err包含了错误的有关信息。

try {  console.lo("JavaScript");
}catch(err) {  window.alert("发生错误,错误信息:"+err.message);
}
console.log("错误已处理完毕。");

第二句有语法错误,被catch捕捉到,错误信息console.lo is not a function通过弹窗告知用户。用户点击弹窗上的确定后,程序继续往下运行。

如果没有进行错误处理,程序在第二句停止运行,这样后面的所有代码都不再执行。

创建自定义错误

console.lo is not a function显然是一个系统内置的错误类型,如果用户觉得这样的错误类型不够具体,可以自定义错误类型:

throw exception;

exception可以是字符串、数字、逻辑值或者对象,这样的错误也会被catch语句块捕获。

//求开方的值
function mySqrt(a) {  try {  if(a < 0)  throw new Error("错误!负数不能开平方");  if(a > 10000)  throw new Error("错误!不支持大数开平方");  return Math.sqrt(a);  } catch(err) {  console.log("发生错误,错误信息:"+err.message);  }  return "error";  }

new Error()是一个Error对象,括号内的参数是err.message属性的值。比如a-2,抛出第一个自定义异常,并在catch块中输出异常的信息,函数最后返回error

JS文档元素的操作(一)

什么是DOM

Document Object Module,简称DOM,中文名文档对象模型。在网页上,组成页面(又叫文档)的一个个对象被组织在树形结构中,用这种结构表示它们之间的层次关系,表示文档中对象的标准模型就称为DOM

DOM的作用是给HTML文档提供一个标准的树状模型,这样开发人员就能够通过DOM提供的接口去操作HTML里面的元素。

文档元素

先看一段网页代码:

<html>  <head>  <title>这里是标题</title>  </head>  <body>  <p>这是我学习JavaScript的网址:</p>  <a href="https://www.educoder.net/paths">JavaScript学习手册</a>  </body>
</html>

执行后效果如下:

文档元素:指的就是<html><head>等等这样一个个的标签和里面的内容

比如文档元素<title>就是这样:

<title>这里是标题</title>

JavaScript中,元素<title>对应一个对象,这个对象有一个属性的值是“这里是标题”。

所以,用JS操作这些文档元素,操作的就是它们对应的JS对象。

节点树

从代码的缩进可以知道,文档元素之间有层次关系,如下:

上面的图和数据结构中树的概念类似,被称为节点树<html>是根节点,网页的所有文档元素都在里面,<head><body>是两个子节点,分别存储网页标题有关内容和网页的主体部分。

JavaScript要操作这些元素,第一步自然是获得这些元素对应的JavaScript对象,那么,怎么获取呢?

通过id获取文档元素

文档元素一般都有一个id属性,它的值在本文档中唯一,如下:

<p id="myId">这是我学习JavaScript的网址:</p>

用这个id获取<p>元素的方法如下:

var pElement = document.getElementById("myId");

其中document表示整个文档,getElementById()document对象的一个方法,参数是id属性的值myId

获取的pElement就代表了<p>标签以及里面的内容,接下来,可以通过pElement操作这个元素。比如可以用弹框展示一下<p>标签里面的内容:

window.alert(pElement.innerText);

效果如下:

除了id以外,文档元素另外一个常见的属性是类名。

通过类名获取文档元素

文档元素的类名不唯一(存在多个文档元素的类名相同的情况),如下:

<p class="myName">段落</p>
<a class="myName" href="https://www.educoder.net">这是一个链接</a>

documentgetElementsByClassName()方法用来获取指定类名的文档元素数组(NodeList,一般叫节点列表),如下:

var myNodeList = document.getElementsByClassName("myName");

这样,myNodeList[0]就是<p>元素,而myNodeList[1]就是<a>元素,通过这个方法的名字我们也可以知道获取的标签不唯一。

我们以弹框的形式查看一下<p>里面的内容:

window.alert(myNodeList[0].innerText);

效果如下:

通过前面的多个例子,我们可以看到,文档无非是由几个特定的标签组成,比如<p><a><img>等,由此可以想到,我们能不能通过标签的名字获取特定的文档元素呢?

通过标签的名字获取文档元素

标签名指的是<>里面的字符串,document对象的getElementsByTagName()获取整个文档中指定名字的所有标签,显然,结果是一个文档元素数组(节点列表),方法的名字也暗示了这一点。

<div id="div1">  <p id="p1">文本1</p>  <p id="p2">文本2</p>  <a name="a1">链接</a>
</div>
<div id="div2">  <p id="p3" name="a1">文本3</p>
</div>

获取所有的<div>元素,如下:

var allDiv = document.getElementsByTagName("div");

为了显示效果,我们以页面弹框的形式展示第一个<div>里面的内容:

window.alert(allDiv[0]);

效果如下:

这个弹框表明,我们试图弹出的内容是一个div元素。

获取标签内部的子元素

我们获取到的文档元素,也有getElementsByTagName()方法,作用是获取该元素内部指定名字的所有子元素。比如,要获取第一个<div>里面所有的<a>元素,代码如下:

//变量allDiv上面有,这里不再重复!
var allLink = allDiv[0].getElementsByTagName("a");

这样就获取了第一个<div>里面的所有超链接元素。

html5中获取文档元素还有两种方法,querySelectorquerySelectorAll

css选择器

css选择器是干什么用的?简单来说,选择你想要的元素的样式。

这一块的内容对于没有学习过css的同学来说比较难,我们分三步来理解:

第一步:先看一段html代码:

<body>  <p>  CSS选择器  </p>
</body>

运行的效果如下:

第二步:我们想把字体改为红色,需要使用css来处理,假设我们已经有了一段css代码:

.css1
{  color:red;
}
#css2
{  color:blue;
}

前四行是一个名字为css1选择器,它是一种类选择器;后四行是一个名字为css2选择器,它是一种id选择器

第三步:有了css,我们还要把它和html关联起来,比如我们想在p元素上使用第一个选择器,反过来说:就是第一个选择器选择p元素(将它的样式应用在p元素上)。那么给p元素新增一个class属性,它的值是css1

<body>  <p class="css1">  CSS选择器  </p>
</body>

再来看一下效果:

这样p元素就选择了名字为css1的样式(反过来说也行),如果p元素要选择名为css2的样式,因为css2id选择器,需要设置id属性的值为css2

<body>  <p id="css2">  CSS选择器  </p>
</body>

效果如下:

querySelector的用法

querySelector返回匹配指定css选择器的第一个元素。

以上面的html代码作为例子,比如我们想要获得class="css1"的元素:

css1是一个类选择器,在css代码里面的完整表示为.css1,那么这个.css1直接作为querySelector的参数,即可获得class="css1"的元素:

var myElement =  document.querySelector(".css1");
console.log(myElement.innerText);//输出“CSS选择器”

总结一下就是:用css选择器的完整名字作为querySelector的参数,就可以获取该选择器控制的所有元素。

需要注意的是,querySelector只返回一个元素,如果指定的选择器控制的元素有多个,返回第一个,下面是一个例子:

先看一段html代码:

<body>  <div class="myClass">文本1</div>  <div class="myClass">文本2</div>  <div class="myClass">文本3</div>
</body>

显然,类名为myClassdiv元素有3个,使用querySelector在控制台输出类名为myClass的元素,看能输出几个:

var myClassElement = document.querySelector(".myClass");
console.log(myClassElement);

F12查看一下浏览器的控制台,效果如下:

很明显,querySelector方法只能获得第一个类名为myClass的元素。

querySelector只能获得第一个符合要求的文档元素,显然,我们需要一个能获取所有符合要求的文档元素的方法,querySelectorAll就是负责这项工作的。

querySelectorAll的用法

如果一个选择器控制的元素有多个,而且我们需要取到这些元素,就需要用querySelectorAll方法,该方法返回指定的选择器对应的多个元素。

比如对于下面一段html代码:

<p class="pClass">我是第一个元素</p>
<p class="pClass">我是第二个元素</p>
<p class="pClass">我是第三个元素</p>

我们分别调用querySelectorquerySelectorAll方法:

var p1 = document.querySelector(".pClass");
var allP = document.querySelectorAll(".pClass");
console.log(p1);
console.log("=====我是巨大的分割线=====");
console.log(allP);

打开浏览器,摁下F12,查看效果:

浏览器告诉我们:querySelectorAll获得的是一个NodeList(一个有顺序的节点列表),它有三个元素,所以,很显然,querySelectorAll捕获了所有符合要求的元素。

为了使接下来的HTML DOM内容的讲解更加易懂,我们不得不完整的认识一下节点树的有关概念。

节点树的有关概念

回顾一下前面出现过的这张图:

定义几个概念:

  • 父节点:逆着箭头方向的第一个节点,即为该节点的父节点;
  • 子节点:顺着箭头方向第一层的若干个结点,一般不唯一,即为该节点的的子节点;
  • 兄弟节点:父节点相同的节点互为兄弟节点;
  • 第一个子节点:子节点中最左边的节点;
  • 最后一个子节点:子节点中最右侧的节点;
  • 节点类型,值为9时表示Document节点(整个文档);值为1表示Element节点,即前面反复提到的文档元素;值为3表示Text节点,即节点里面的内容为纯文本,如<p>节点。
顺着节点树获取文档元素

先给一段html代码:

<body>
<div id="div1">  <div class="cl1">  <p>文本</p>  <a>超链接</a>  </div>  <div class="cl2">  <select id="se">  <option>红</option>  <option>黄</option>  <option>蓝</option>  </select>  </div>
</div>
</body>

首先,我们获取最外层的div节点:

var div1 = document.getElementById("div1");

然后获取它的第一个子节点(class值为cl1的节点):

//firstElementChild表示第一个子节点
var cl1 = div1.firstElementChild;

再获取cl1的最后一个子节点,即a节点:

//lastElementChild表示最后一个子节点
var aElement = cl1.lastElementChild;

在控制台打印出获取到的节点里面的文本:

console.log(aElement.innerText);

效果如下(用浏览器打开这段代码,然后按下F12键即可):

通过这个例子可以看出,我们的思路是顺着这个节点树从根部一直往下找,即顺着箭头的方向直到目标节点。

其他一些属性

篇幅有限,这里简单说明一下其它属性,用法和上面的相同。

前一个兄弟节点

previousElementSibling表示前一个兄弟节点。比如获取上面的超链接的前一个节点p,然后在控制台打印p的内容,代码以及效果如下:

var left = aElement.previousElementSibling;
console.log(left.innerText);

后一个兄弟节点

nextElementSibling表示后一个兄弟节点。显然,上面的p元素的后一个兄弟节点是a元素,打印一下a的内容:

var right = left.nextElementSibling;
console.log(right.innerText);

子节点列表: children

children[0]表示第一个子节点,以此类推。比如依次在控制台打印出上面的select标签的三个子节点:

var selectTag = document.getElementById("se");
console.log(selectTag.children[0].innerText);
console.log(selectTag.children[1].innerText);
console.log(selectTag.children[2].innerText);

效果如下:

children.length:子节点的个数;

console.log(selectTag.children.length);//输出3
获取文档元素的属性

通过前面的学习,我们可以发现,文档元素后面都会跟着相应的属性,比如<a>后面都会有一个href属性,用来表示超链接的地址,即点击这个超链接后跳往的目标页的地址。

怎么获取属性值呢?先看一段html代码:

<a href="https://www.educoder.net" target="_blank">EduCoder</a>

先获取文档元素:

var aElement = document.getElementsByTagName("a").[0];

然后通过获取到的元素的属性名来得到属性值:

var hrefValue = aElement.href;
console.log(hrefValue);//输出https://www.educoder.net

从上面可以看出,文档元素.属性名即获得了该属性的值。

getAttribute()

getAttribute(a)用来获取属性值,a表示属性的名字。比如上面获取超链接的href属性值,也可以这样写:

console.log(aElement.getAttribute("href"));//输出https://www.educoder.net

区别是:getAttribute()返回的值统一为字符串,而第一种方法会返回属性实际的值,比如<input>maxLength属性的值应该是数值,第一种方法就会返回数值,getAttribute()返回了字符串:

<input type="name" maxLength=16 id="inputId"/>
//typeof检测变量的类型
var result1 = document.getElementById("inputId").maxLength;//返回16
var result2 = document.getElementById("inputId").getAttribute("maxLength");//返回"16"
console.log(typeof(result1));//输出number
console.log(typeof(result2));//输出string
特别提醒

class等文档元素的属性,不能直接用文档元素对象.class来获取,因为classJavaScript中的关键字,需要用className来代替。

但是,如果用的是getAttribute(),直接用class作为参数就行。

<a class="aClass" id="aId">超链接</a>
document.getElementById("aId").className;//返回"aClass"
document.getElementById("aId").getAttribute("class");//返回"aClass"

文档元素属性的值,除了可以获取外,当然也可以设置。设置属性的值也有两种方法。

直接设置

=连接,左边是你要设置的属性变量,右边是你要赋予这个属性的具体的值。比如:

<a id="a1" href="https://www.google.com">EduCoder</a>

设置超链接的href属性的值的表达式如下:

document.getElementById("a1").href="https://www.educoder.net";

这样,a标签的href属性的值就变成了https://www.educoder.net

需要注意:标签之间的文本用innerText属性表示,要修改上面超链接里面的文本,需要这样:

document.getElementById("a1").innerText="educoder";
setAttribute()

没错,setAttribute(a,b)就是一个与getAttribute()对应的方法,参数a是你要设置的属性的名字,参数b是你要设置的属性的值。

所以上面的操作(设置href)也可以这样写:

document.getElementById("a1").setAttribute("href","https://www.educoder.net");

JS文档元素的操作(二)

创建节点

document.createElement("tagName")用来创建一个新的Element节点(即文档元素),返回值是新创建的节点,tagName是标签的名字,比如apimg等,需要注意的是它不区分大小写。

比如创建一个新的超链接节点:

var newA = document.createElement("a");//创建超链接节点
newA.src = "https://www.educoder.net";//设置超链接的目的地址

编程要求

本关的编程任务是补全右侧代码片段中BeginEnd中间的代码,具体要求如下:

  • 创建一个form节点,然后赋值给变量newNode,设置节点的id值为myFormmethod值为post,如下所示:
<form id="myForm" method="post"></form>
  • 字符串类型参数用""包含在内。
插入节点

插入节点:插入一个新的文档元素。

最常见的应用就是在<select>标签里插入一个新的<option>标签。

两种可选的方法:
######方法1appendChild()
node1.appendChild(node2)表示将node2插入到node1的最后一个子节点中。

比如原来的选择框是这样:

<select id="s1">  <option id="cs">长沙</option>  <option id="zz">株洲</option>
</select>

要把它变成这样:

<select id="s1">  <option id="cs">长沙</option>  <option id="zz">株洲</option>  <option >湘潭</option>
</select>

实现代码如下:

var node1 = document.getElementById("s1");
var node2 = document.createElement("option");
node2.innerText = "湘潭";
node1.appendChild(node2);

方法2insertBefore()

pNode.insertBefore(node1,node2):将node1插入作为pNode的子节点,并且node1排在已存在的子节点node2前面

比如要把最开始的复选框变成这样:

<select id="s1">  <option id="cs">长沙</option>  <option>湘潭</option>  <option id="zz">株洲</option>
</select>

我们需要像下面这样操作节点:

var pNode = document.getElementById("s1");
var node1 = document.createElement("option");
node1.innerText = "湘潭";
var node2 = document.getElementById("zz");
//将内容为"湘潭"的节点插入到内容为"株洲"的节点前面
pNode.insertBefore(node1,node2);
删除节点

删除节点的方法是removeChild(),调用者是父节点,参数是子节点,作用是删除这个子节点。

下面是一个无序列表的代码:

<ul id="parent">  <li>提子</li>  <li>车厘子</li>  <li id="child3">荔枝</li>
</ul>

用浏览器打开这个文件,效果如下:

如果我们要删除第三行,可以这样操作:

第一步:获取父节点,即ul节点:

var parentNode = document.getElementById("parent");

第二步:获取待删除的子结点:

var childNode = document.getElementById("child3");

第三步:父节点调用removeChild()方法删除子节点:

parentNode.removeChild(childNode);

执行完这三个js语句后,再次用浏览器打开,结果为:

替换节点

一般来说,替换节点=删除节点+新增节点,可以用前面介绍的知识结合起来实现,当然,js提供了replaceChild()方法一次完成替换。

replaceChild(a,b)的调用者是要被替换的节点的父节点,a是新的节点,b是被替换的节点。

以无序列表为例:

<ul id="parent">  <li id="child1">黄山</li>  <li id="child2">庐山</li>  <li id="child3">泰山</li>
</ul>

要替换掉第三个子节点,过程如下:

第一步,获取父节点:

var parNode = document.getElementById("parent");

第二步,获取要被替换的子节点:

var oldNode = document.getElementById("child3");

第三步,创建新节点:

var newChild = document.createElement("li");  newChild.innerText = "武夷山";

第四步,替换:

parNode.replaceChild(newChild,oldNode);

替换前后的效果对比:

下拉列表的级联

相信大家都见过这样的下拉框:

它有三列,每一列都会根据前一列的结果动态的改变自己的可选项,称为下拉框的级联,那么如何实现呢?

第一步:静态的HTML的代码如下(简单起见,只考虑前两列):

<select id="province" onChange="changeCity()">  <option value="BeiJing" id="bj">北京</option>  <option value="AnHui" id="ah">安徽</option>
</select>
<select id="city">  <option value="BeiJingCity">北京市</option>
</select>

select表示选择框,option表示选择框里面的每一个选项,onChange="changeCity()"表示一旦改变当前的选项,就会触发JavaScript函数changeCity()的执行。

对于这个静态的HTML页面,不论你在第一列选择的是北京还是安徽,第二列的选项都只有北京市一项。

第二步:获取两个选择框对应的节点元素
(以下的所有代码都是changeCity()函数里面的内容):

var province = document.getElementById("province");
var city = document.getElementById("city");

province变量代表第一列的选择框,city变量代表第二列的选择框。

第三步:清空第二列中的所有内容;
根据第一列的选择结果改变第二列的内容,就是根据父节点的不同替换不同的子节点,我们采用先删除后新增的方法实现替换:

var length = city.children.length;
for(var i = length-1;i >= 0;i--) {  city.removeChild(city.children[i]);
}

for循环里面,依次删除city节点下面的所有子节点。

需要注意的是,每删除一个子节点后,表示子节点个数的属性city.children.length都会自动减1,所以我们需要在for循环开始之前索取子节点的长度。

第四步:根据父节点的不同新增不同的子节点:

if(province.value == "BeiJing") {  var child1 = document.createElement("option");  child1.value ="BeiJingCity";  child1.innerText="北京市"  city.appendChild(child1);
} else {  var child1 = document.createElement("option");  child1.value="HuangShanCity";  child1.innerText="黄山市";  city.appendChild(child1);
}

province.value表示第一列选中的选项的值,即选中的option标签的value的值。

如果第一列选择的是北京,我们需要增加一个选项为北京市option节点,这个节点将作为city的子节点。如果第一列选择的是安徽,我们需要增加一个选项为黄山市option节点,这个节点将作为city的子节点。

value属性表示option标签的值,innerText表示option标签呈现出来的值。

JS事件处理

页面上的每一个元素,会发生几种不同的事件,比如表单元素,可能会发生提交事件,也可能发生重置事件,我们需要为每一个事件绑定一个事件处理程序,也叫为事件注册事件处理程序。

下面是三种注册事件处理程序的方法。

为JavaScript对象设置一个函数

页面上的元素对应一个JavaScript对象,元素的每一个事件对应对象的一个属性,比如:

<form id="myForm"></form>
var myForm = document.getElementById("myForm");

myForm对应页面中id值为myForm的表单,myForm.onsubmit对应表单提交事件,myForm.onreset对应表单重置事件。通过为这些属性设置一个函数类型的值,实现事件处理程序的注册:

//为表单提交事件注册处理程序
myForm.onsubmit = function() {  console.log("表单提交的事件处理程序");
}
//为表单重置事件注册处理程序
myForm.onreset = function() {  console.log("表单重置的事件处理程序");
}
设置HTML标签属性的值为事件处理程序

比如,设置form标签的onsubmit属性的值为事件处理程序:

<form onsubmit="submitForm()"></form>
function submitForm() {  console.log("表单提交的事件处理程序");
}

这样提交表单时,就会触发submitForm()函数。

调用addEventListener()函数

页面元素对应的JS对象,通过调用addEventListener()函数也可以注册事件处理程序,函数的第一个参数是事件的类型,第二个参数是事件处理程序:

<form id="myForm"></form>
var myForm = document.getElementById("myForm");
myForm.addEventListener("submit",function() {  console.log("表单提交中");
});

submit表示这是一个表单提交事件,后面的匿名函数即表单提交的事件处理程序。

文档加载事件

文档,指的是网页上的所有元素构成的一种格式化文本。文档加载事件指浏览器从服务器下载并渲染完文档后发生的事件。

文档加载事件名字为load

当文档加载完成后,就会触发文档加载事件监听程序(即上一关所说的事件处理程序),一般我们会在这个时候监测用户浏览器的类型、版本,从而加载不同的脚本。

在大多数情况下,文档记载事件绑定在body元素上,表示网页主体加载完成后触发该事件,也有少数情况下绑定在image等元素上,表示相关的元素加载完成后触发该事件。

文档加载完成后监测用户的浏览器类型并在控制台打印:

<body onload="detectBrowser()"></body>
function detectBrowser(){  var userAgent = navigator.userAgent; //取得浏览器的userAgent字符串  if (userAgent.indexOf("Opera") > -1) {//判断是否是Opera浏览器  console.log("Opera");  };  if (userAgent.indexOf("Firefox") > -1) { //判断是否是Firefox浏览器  console.log("Firefox");  }  if (userAgent.indexOf("Chrome") > -1) { //判断是否是Chrome浏览器  console.log("Chrome");  }  if (userAgent.indexOf("Safari") > -1) {//判断是否是Safari浏览器  console.log("Safari");  }  if (userAgent.indexOf("compatible") > -1 && userAgent.indexOf("MSIE") > -1) {  console.log("IE");  };
}
常见的鼠标事件

说到鼠标事件,最常见的无非是鼠标单击事件click,很多按钮都会绑定一个onclick()函数,表示当用户单击鼠标后会执行的函数。其实还有很多鼠标事件,比如双击鼠标、按下鼠标等,下面是一个总结:

类型 事件处理函数 触发条件
click onclick 按下并且释放鼠标
dbclick ondbclick 双击鼠标
mousedown onmousedown 按下鼠标按键
mouseup onmouseup 释放鼠标按键
mousemove onmousemove 移动鼠标
mouseover onmouseover 鼠标进入元素
mouseout onmouseout 鼠标离开元素

为页面上的某一个元素绑定一个鼠标事件,当用户在该元素上用鼠标执行了指定的动作后,就会触发指定的鼠标事件处理程序,开始执行函数。

鼠标的按下和释放

mousedowm表示鼠标按下的事件,onmousedown是用户按下鼠标后会触发的事件处理程序;mouseup表示鼠标按键释放的事件,onmouseup表示用户释放鼠标按键后会触发的事件处理函数。

下面是一个例子,页面上有一行文字“点我”,用户在文字上按下鼠标按键后,文字会变成“鼠标已经按下”,而用户释放鼠标后,文字会变成“鼠标已经释放”。

html代码如下:

<body>  <p id="p" onmousedown="downfunc()" onmouseup="upfunc()">  点我  </p>
</body>

页面的效果如下:

事件处理函数的代码如下:

function downfunc() {  document.getElementById("p").innerText = "鼠标已经按下";
}
function upfunc() {  document.getElementById("p").innerText = "鼠标已经释放";
}

用户在文字上面先按下鼠标,再释放鼠标,效果如下:

键盘事件,是指用户敲击键盘上的按键所代表的事件。

键盘事件有三种:

  • 按键按下:keydown,用户按下键盘上的键;
  • 按键释放:keyup,用户释放按键;
  • 点击按键:keypress,用户按下并且释放了按键。
点击按键

keypress表示用户点击某个按键的事件,该事件会触发onkeypress()事件处理程序,onkeypress()有一个event参数,其中event.which表示点击的按键的编码。这个编码是该按键的unicode编码。

需要注意的是,按下键盘上的A时,keyCode值是a的编码,只有同时按下shiftAkeyCode的值才是A的编码。

下面是一个例子,当用户点击键盘上的按键时,会打印出该按键的编码值:

<body onkeypress="keyEvent(event)">  <p>  keypress event  </p>
</body>
<script>  function keyEvent(event) {  console.log("编码是:"+event.which);  }
</script>

比如我们按下B时,控制台如下:

同时按下Bshift,控制台如下:

按下按键

keydown表示用户按下按键,同上面一样,它也有一个event.which表示按下的按键的编码。

<body onkeydown="downEvent(event)">
</body>
<script>  function downEvent(event) {  console.log("编码是:"+event.which);  }
</script>

如果你按下按键后没有释放,控制台将会一直进行打印,比如按下的是1,控制台的打印结果如下:

灰色的数字20表示这条信息已经打印了20次。

释放按键

keyup表示用户释放按键,可以有一个参数eventevent.which表示释放的按键的编码。

<body onkeyup="upEvent(event)">
</body>
<script>  function upEvent(event) {  console.log("编码是:"+event.which);  }
</script>

比如,当用户按下1时,控制台没有输出,释放1后,控制台输出如下:

.

表单

表单,即form,是页面最基本的元素之一,通常,用户的输入会放置在表单里面,然后提交给后台。

form有很多子元素,分别表示不同类型的用户输入:例如input表示文本等类型;select表示下拉列表;button表示按钮。

这些子元素可以被绑定一些事件,比如change表示用户的输入发生了改变。这些事件是表单元素特有的。

change事件

change事件表示当前元素失去焦点并且元素的内容发生了改变。失去焦点,指光标不在该元素上,比如光标本来在文本输入框里面,当用户在页面的其他地方点击了鼠标,文本框就会失去焦点。

下面是一个例子:当用户输入文本,并且鼠标点击页面上的其他地方后,我们将在控制台打印出用户的输入。

<body>  <form>  <input id="t1" type="text" onchange="changeEve()"/>  </form>  <script>  function changeEve() {  var e = document.getElementById("t1");  console.log(e.value);  }  </script>
</body>

比如当用户输入I changed后,在页面的其他地方点击鼠标,控制台如下:

select事件

select事件:文本框中的文本被用户选中时发生。

只能作用在<input type="text">的文本框里面,可以用window.getSelection().toString()获取选择的文本。

下面的例子:当用户选择了一段文本后,我们在控制台打印出用户选择的文本:

<body>  <input type="text" value="赵钱孙李,周吴郑王" onselect="selectEve()"/>  <script>  function selectEve() {   console.log(window.getSelection().toString());  }  </script>
</body>

比如我们选择了郑王,然后松开鼠标,控制台的输出如下:

submit事件

表单的提交事件。

表单里面包含了用户输入的信息,最终要传到后台的服务器进行处理,这样就有一个表单的提交过程,submit即表单提交事件。

通常情况下,在submit的事件处理函数中,校验用户的输入是否符合要求,比如密码的长度够不够。

下面的例子,用户提交表单时,用js校验用户输入的密码长度是否达到6位。

<body>      <form onsubmit="subEve()">          <input type="password" id="pw"/>          <input type="submit" value="提交" />      </form>      <script>      function subEve() {          var content = document.getElementById("pw").value;          if (content.length < 6) {              window.alert("密码不可以小于6位");          }      }      </script>  </body>

这时,用户在密码输入框输入123,点击提交,页面会弹出一个警告框如下:

元素的拖放

鼠标指向元素,按下鼠标,然后移动鼠标到另一个地方释放,即拖动元素。

相比html4以及之前的版本,html5增加了一个全新的属性draggable,表示元素是否支持拖动,默认的情况下,图片和超链接元素是支持拖动的,其他元素不支持。

将元素的draggable属性设置为true,即表示元素支持拖动。如:下面设置了p元素支持拖动:

<p id="p1" draggable="true">      元素支持鼠标的拖动  </p>

也可以用下面的JavaScript代码设置p为可拖动的:

document.getElementById("p1").draggable = true;
ondrag

ondrag()是元素正在拖动时触发的事件处理程序。如果元素一直在拖动的过程中,ondrag()会每隔350ms被触发一次,比如,在下面的例子中,我们一直在拖动p元素,控制台会一直打印拖动的信息:

<body>  <div>  <p ondrag="dragging(event)" draggable="true">拖动我!</p>  </div>  <script>  function dragging(event) {  console.log("正在拖动");  }  </script>
</body>

拖动不放下,控制台如下:

111表示这条信息已经打印了111次。

ondragstart

用户开始拖动元素时触发,可以带有一个event参数,其中的event.target表示拖动的元素,比如,下面的例子中,用户开始拖动元素时,触发了ondragstart程序,我们尝试打印一下event.target的内容:

<body>  <p ondragstart="dragStart(event)"  draggable="true">拖动我!</p>  <script>  function dragStart(event) {  console.log(event.target);  console.log("你要拖动的文本的内容是:"+event.target.innerText);  }  </script>
</body>

拖动文本,效果如下:

第一行是要拖动的文本元素,第二行显示了文本里面的内容。

事件冒泡
文档树

在前面的学习中,我们说过,文档元素之间有层次关系,比如:

<body>  <div onclick="clickParent()">  <p onclick="clickChild()">点我</p>  <p id="p">content</p>  </div>  <script>  function clickChild() {  console.log("子");  }  function clickParent() {  console.log("父");  }
</script>
</body>

对应这样一个模型:

其中,箭头的方向是子节点的方向,而反过来则是父节点的方向。比如,两个p元素的父节点(父元素)都是div元素。

事件冒泡

在上面的例子中,点击p元素里面的内容,显然会触发p元素的事件处理程序clickChild。然后,因为p元素是放在div里面的,点击p元素相当于点击了div元素,会触发div的事件处理程序clickParent,这个过程被称为事件冒泡

事件冒泡是指,某个事件触发了某个元素的事件处理程序,接下来,就会自动沿着节点树往根节点的方向依次触发经过的路径上的所有元素的某个事件的处理程序。

比如上面的例子中,用鼠标点击p标签里面的文字点我,控制台的打印结果如下:

第一行是子元素的click事件的事件处理程序的输出,第二行是父元素的,这里有一个先后的顺序,即从子元素一直到根节点。

事件冒泡的控制

事件冒泡不是所有的时候都受到欢迎,有的时候需要控制它的发生,使用event.stopPropagation()即可。

比如,对于上面的例子,我们在子元素的事件处理程序clickChild()的最后一行添加一行代码:

function clickChild() {  console.log("子");  window.event?window.event.cancelBubble=true:event.stopPropagation();
}

这个时候再次点击p里面的内容,控制台的输出如下:

可以看出,事件没有冒泡到父元素上面。所以,想要在哪里停止事件的冒泡,就在它的子元素的事件处理程序的最后调用event.stopPropagation()即可。

JS浏览器对象模型

JavaScript中定时器的作用是在指定的时间或者时间间隔之后执行函数,可以用来对网页上的数据做实时的更新,比如网页上的北京时间的更新:

这个可以通过每隔一秒执行一次时间更新函数来实现。

定时器的实现有两种方式,一个是window.setTimeout()函数,一个是window.setInterval()函数,本关讲解第一种。

设置定时器

window.setTimeout(a,b)用来指定函数a在延迟b毫秒时间后执行,即在window.setTimeout(a,b)这句话开始执行的b毫秒之后,再执行a函数。

比如下面的例子中,我们点击页面上的文字,经过4秒的延迟后弹出警告框:

<body>  <p onclick="al()">  单击此处4秒后弹出警告框  </p>  <script>  var id;  function al() {  id = window.setTimeout(showAlert,4000);  }  function showAlert() {  window.alert("警告框");  }
</script>
</body>

点击文字会触发函数a1(),在这个函数里面设置了一个定时任务showAlert(),定时的时间为4秒之后。即:点击文字4秒之后会执行showAlert()函数。

效果如下所示:

取消定时器

上面代码里的变量id是用来唯一标识这个定时任务的,它的作用是,作为函数window.clearTimeout(id)的参数,而这个函数是用来在定时任务发生之前关闭定时任务,这好比,早晨我们在闹钟响之前关掉闹钟。

对上面的例子稍微改造,在定时任务开启之前,通过调用window.clearTimeout(id)关闭定时任务,代码如下:

<body>  <p onclick="al()">  单击此处4秒后弹出警告框  </p>  <p onclick="a2()">  单击此处取消警告框的弹出  </p>  <script>  var id;  function al() {  id = window.setTimeout(showAlert,4000);  }  function showAlert() {  window.alert("警告框");  }  function a2() {  window.clearTimeout(id);  }
</script>
</body>

效果如下:

先点击上面一行文字,触发了函数a1(),预定了一个4秒之后执行的定时任务showAlert()。再点击下面一行文字,执行了函数a2(),取消了这个定时任务,这样在4秒之后不会弹出警告框,这是它和上面的例子的区别。

定时

setInterval(a,b):每隔b毫秒,执行一次a函数。

下面的例子,每隔一秒钟,更新一下网页上的时间:

<body>  <p onclick="updateTime()">  开始更新时间  </p>  <p id="timeContainer">  </p>  <script>  var id;  function updateTime() {  id = window.setInterval(showTime,1000);  }  function showTime(){        document.getElementById("timeContainer").innerText = new Date();  }
</script>
</body>

点击“开始更新时间”,触发updateTime()函数,该函数开启一个循环定时任务showTime()showTime()的作用是更新网页上显示的时间。

效果如下所示:

取消定时

与上一关类似,window.clearInterval(id)也是停止定时任务,其中的id就是上面例子里面的变量id,这个是为了唯一标识某一个定时任务,防止错误的取消了别的定时任务。

修改一下上面的代码,增加停止时间更新的功能:

<body>  <p onclick="updateTime()">  开始更新时间  </p>  <p onclick="stopTime()">  停止更新时间  </p>  <p id="timeContainer">  </p>  <script>  var id;  function updateTime() {  id = window.setInterval(showTime,1000);  }  function showTime() {  document.getElementById("timeContainer").innerText = new Date();  }  function stopTime() {  window.clearInterval(id);  }
</script>
</body>

点击“停止更新时间”,触发stopTime()函数,在函数里面会取消定时任务showTime()

其效果如下:

location对象

location对象就是window.location,记载了浏览器当前所在的窗口的URL(统一资源定位符)信息,它常常被用来实现网页的跳转。

页面的跳转

location.href属性表示当前窗口所在页面的地址,比如,如果我们在本网站的首页(https://www.educoder.net/),打印window.location.href

<body>  <script>  console.log(window.location.href);  </script>
</body>

打印出来的结果是:

这个结果应当和浏览器地址栏上内容一样。

window.location.href还是可写的,如果把它设置为一个新的地址,当前窗口将立即打开这个新的地址,这是实现页面跳转的一种方式。比如下面的例子:

<body>  <p onclick="toNew()">  点我调到EduCoder首页  </p>  <script>  function toNew() {  window.location.href = "https://www.educoder.net";  }  </script>
</body>

点击页面上的文字,效果如下:

location对象的其它属性

假设,当前浏览器地址栏的内容是:https://www.educoder.net:8080/shixun.html?tab=1。我们用一个表格看一下location对象的所有属性:

属性名 解释 属性的值
host 主机名和端口号 www.educoder.net:8080
hostname 主机名 www.educoder.net
pathname 路径部分 /shixun.html
port 端口号 8080
protocal 协议 https
search 问号开始的查询部分 ?tab=1

以下用一个例子打印所有这些属性,假设我们在https://www.educoder.net:8080/shixun.html?tab=1这个页面:

<body onload="printInfo()">  location attribute  <script>  function printInfo() {  var loc = window.location;  console.log("host:"+loc.host);  console.log("hostname:"+loc.hostname);  console.log("pathname:"+loc.pathname);  console.log("port:"+loc.port);  console.log("protocal:"+loc.protocal);  console.log("search:"+loc.search);  }  </script>
</body>

载入页面后,打印结果如下:

对话框

对话框就是在浏览器页面上再弹出一个小的窗口,用于和用户直接互动,window对象的alert()confirm()prompt()三个方法都是用来显示对话框的。

警告框

window.alert(msg)弹出一个警告框,表示向用户发送一条通知。前面的相关知识已经多次讲过alert()的用法。

需要注意的是,alert()方法弹出警告框后,其后的代码会暂停执行,直到警告框被关闭,即:alert()方法是阻塞的。

下面的例子中,关闭警告框后,控制台才会打印信息:

<body>  <p onclick="showAlert()">点我弹出警告框</p>  <script>  function showAlert() {  window.alert("先弹出一个警告框");  console.log("然后才能在控制台打印信息");  }  </script>
</body>

其效果如下:

确认框

window.confirm(msg)弹出一个确认框,msg是需要用户确认的信息,用户在弹出的框里面选择确认或者取消后,会返回true或者false

<body>  <p onclick="showConfirm()">点我弹出确认框</p>  <script>  function showConfirm() {  var result = window.confirm("确定今天是周五?");  console.log(result);  }  </script>
</body>

效果如下:

可以看到,点击确认,返回true;点击取消,返回false

输入框

window.prompt(a,b)弹出一个输入框,供用户输入关键信息。其中a是输入框的提示语,b是输入框里面默认的内容。

如果用户不输入,直接点击确认,返回b。如果点击取消,返回null

下面是一个例子:

<body>  <p onclick="showPrompt()">点我弹出输入框</p>  <script>  function showPrompt() {  var result = window.prompt("请输入姓名:", "Jack");  console.log(result);  }  </script>
</body>

效果如下所示:

如动态图所示,不输入点确定返回默认的内容;直接点取消返回null

窗口

浏览器的一个标签页叫做一个窗口,window对象可以操作浏览器窗口的打开与关闭。

打开浏览器的窗口

window.open(url,name,specs,replace)用来打开一个浏览器的窗口,它有四个参数:

url表示窗口里面的文档的地址;

name有两种情况。

如果name里面是窗口的名字。浏览器会先判断这个窗口是否已经打开。已经打开则用新的url里面的文档替换这个窗口里面原来的文档,反映到浏览器上是不会有新的标签页打开,但是一个已存在的标签页会刷新。没有打开则打开一个新的窗口,并且载入url里面的文档。

如果name_blank_self里面中的任何一个,它的含义如下:

含义
_blank 打开新的窗口,载入地址为url的文档
_self 不打开新的窗口,用地址为url的文档替换掉当前的文档

specs是用来控制新窗口的尺寸等特征,比如值为width=200,height=100时,表示新窗口宽度为200px,高度为100px

replace用来控制新的窗口在浏览器的浏览历史里面如何显示。为true表示装载到窗口的url替换掉浏览历史中的当前条目;为false表示装载到窗口的url创建一个新的条目。

下面的例子展示了这些参数的具体用法:

<body>  <p onclick="openNewWindow()">name是一个窗口的名字,打开一个新的窗口,载入新的文档</p>  <p onclick="openOldWindow()">name是一个窗口的名字,打开已存在的窗口,替换掉里面的文档</p>  <p onclick="blankWindow()">name是一个target属性,值为_blank</p>  <p onclick="selfWindow()">name是一个target属性,值为_self</p>  <script>  function openNewWindow() {  window.open("Demo1.html", "windowName");  }  function openOldWindow() {  window.open("Demo2.html", "windowName");  }  function blankWindow() {  window.open("Demo1.html", "_blank");  }  function selfWindow() {  window.open("Demo1.html", "_self");  }  </script>
</body>

效果如下所示:

如上所示,第一种情况,name是一个窗口的名字,因为这个窗口还没有打开,所以会打开一个新的窗口,并载入url文档。

第二种情况,name是一个窗口的名字,因为这个窗口已打开,所以新的文档会替换掉原来的文档。

第三种情况,name值为_blank,会直接打开一个新的窗口。

第四种情况,name值为_self,会用url里面的文档替换掉当前窗口的文档,不会打开新的窗口。

关闭窗口

上面的window.open()会有一个返回值resultresult.close()用来关闭打开的窗口。比如下面的例子:

<body>  <p onclick="openNewWindow()">打开新窗口</p>  <p onclick="closeNewWindow()">关闭新窗口</p>  <script>  var w;  function openNewWindow() {  w = window.open("Demo1.html", "windowName");  }  function closeNewWindow() {  w.close();  }  </script>
</body>

效果如下:

JS-jQuery制作验证码

生成随机字符串
基本结构

基本的html结构如下:

<div class="vertify-code"></div>
<span class="change">换一张</span>

效果如下:

先生成随机数,基本定义如下:

//存放所有可取值的数组
var arr_code = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'];
//生成随机数的字符串
var str = '';
//数组的下标;
var num;

这里主要是让数组的下标随机,获取到数组里随机下标的值,从而达到获取随机数的目的。这里限定字符串的个数为6个。 代码如下:

for(var i =0; i<6; i++){  num = Math.floor((arr_code.length-1) * Math.random());  str = str + arr_code[num];
}

这样就可以获得一个随机的字符串,这里主要说明两个方法:

  • Math.random():可返回介于 0 ~ 1 之间的一个随机数;
  • Math.floor():对一个数进行向下取整计算,得到的是一个整数;

现在把这个字符串放到类为.vertify-codediv里,代码如下:

$(".vertify-code").text(str);

效果如下:

基本效果已经实现了,怎么让背景色也随机变呢?这里定义一个背景色的数组,随机取值就可以了。代码如下:

//存放背景色的数组
var arr_back = ['#fffff0', '#f0ffff', '#f0fff0', '#fff0f0'];
//随机下标
var index_back= Math.floor(Math.random()* 3);

现在就可以改变背景色了。代码如下:

$(".vertify-code").css({  background: arr_back[index_back]  })

效果如下:

换一张的功能无非是重新获取一个随机字符串,把上面代码封装成一个函数,再执行一遍就可以了。

检验验证码

效果如下:

相关知识

你需要掌握:1.val(),2.toLowerCase()

这里实现的功能是: 点击【确定】按钮,判断输入的验证码是否正确,如果正确,弹出验证通过,否则,弹出验证失败

基本结构如下:

<div class="vertify-code"></div>
<div class="vertify-area">
<input type="text" class="check"/>
<span class="change">换一张</span>
</div>
<button class="btn">确定</button>

效果如下:

这里只需要分别获取input输入框的值和上面验证码的值,比对一下,判断是否一致就可以了。

需要注意的是: 一般在校验验证码时,不区分大小写,所以要把字符串转化成大写或小写。 代码如下:

$(".btn").click(function(){  var input_val = $(".check").val().toLowerCase();  var vertify_text = $(".vertify-code").text().toLowerCase();  if(input_val === vertify_text) {  alert("验证通过");  }else{  alert("验证失败");  }  })

这里效果就已经实现了。

注意: 获取input输入框的值用val()

滑动验证码

本关任务: 实现滑动验证码。

效果如下:

相关知识

为了完成本关任务,你需要掌握:1.event.pageX,2.offset(),3.Math.max(),4.Math.min()

基本结构

基本的html结构如下:

<div class="container">  <span>向右移动完成验证</span>  <div class="bar">  <span class="content"></span>  <div class="slide"><i class="iconfont icon-xiangyou"></i>  </div>
</div>

效果如下:

这里先说明两个值:

  • event.pageX:显示鼠标指针的位置,相对于文档的左边缘;
  • offset():返回或设置匹配元素相对于文档的偏移;

当鼠标按下的时候,获取.slide元素到鼠标位置的偏移值和离文档最左边的距离。代码如下:

$('.slide').mousedown(function(e){  //兼容IE  var e = e || window.event;  //.slide元素左边和鼠标之间的偏移值  var offset = $(".slide").offset().left;  //.slide元素到文档最左边的距离  var distance = e.pageX - offset;
})

当鼠标向右移动时,获取鼠标移动的距离,也就是滑块移动的距离。代码如下:

$(document).mousemove(function(e){  var e = e || window.event;  //滑块移动的距离x  var x = parseInt(e.pageX- offset - distance);  //把x赋值给.slide元素的left值,就能移动了  $(".slide").css('left', x + 'px' );

效果如下:

这里用示意图说明如下:

可以发现:滑块可以滑动了,但是有超出界限的情况,所以要控制滑块滑动的范围。滑动的最小距离是0,最大距离是.container元素的宽度减去.slide元素自身的宽度。代码如下:

//全局定义变量moveX,maxRange
var moveX, maxRange;
//滑动的最大距离
maxRange = parseInt($(".container").width() - $(".slide").width());
//滑动的距离
moveX = Math.min(Math.max(0,x),maxRange);
//把x赋值给.slide元素的left值,就能移动了
$(".slide").css('left', moveX + 'px' );

效果如下:

这里说明一下取最大、最小值的方法:

  • Math.max():返回指定的数中带有较大的值的那个数。
  • Math.min():返回指定的数中带有较小的值的那个数。

怎么体现滑动的过程呢? 这里给.bar元素添加效果,让宽度随着滑块的移动而变化。代码如下:

$(".bar").css({  width: moveX + 'px',  background: 'lightgreen'
});

效果如下:

这里需要判断一下什么情况下就验证成功了呢? 当滑动的距离等于最大能滑动的距离,然后.content元素会添加内容验证成功,滑动的图标也变了。代码如下:

if(moveX === maxRange){  $(".content").text('验证成功');  $(".slide").html('<i class="iconfont icon-duihao"></i>')
}

效果如下:

上面为了效果图的展示,已经包括了鼠标抬起的情况。

现在就缺鼠标抬起的情况了,按照编程要求的提示进行代码的编写。

二 、css

字体颜色、类型、大小

字体颜色 color
字体类型 font-family
字体大小 font-size

字体的粗细与风格

字体粗细 font-weight
字体风格 font-style

我们在设置斜体文本时使用 font-style 属性。

它属性有三个值:

  • normal - 文本正常显示;
  • italic - 文本斜体显示;
  • oblique - 文本倾斜显示。

文本装饰和文本布局

文本装饰 text-decoration

我们使用text-decoration设置字体上的文本装饰。

其可用值有:

  • none: 取消已经存在的任何文本装饰;
  • underline: 文本下划线;
  • overline: 文本上划线;
  • line-through: 贯穿文本的线;
  • blink:闪烁文本。

使用时,可以根据需求,同时添加多个装饰值。

文本布局

接下来,我们来看用来影响文本布局的属性。

文本对齐 text-align

我们使用text-align来改变一个元素中的文本行互相之间的对齐方式。

其可用值有:

  • left: 左对齐文本;
  • right: 右对齐文本;
  • center: 居中文字;
  • justify: 水平对齐,改变单词之间的差距,使所有文本行的宽度相同。
行高 line-height

我们使用 line-height 属性来设置行高。

注意:行高属性值不能为负。

其可用值有:

  • normal:默认。设置合理的行间距;

  • number:设置数字,此数字会与当前的字体尺寸相乘来设置行间距;

  • length:设置固定的行间距;

  • %:基于当前字体尺寸的百分比行间距;

  • inherit:从父元素继承 line-height 属性的值。

css元素选择器

<body>  <h1>标题1</h1>  <h2>标题2</h2>  <p>普通段落中<a href="#">删除的引用。</a></p>
</body>
<style type="text/css">  html {color:black;}  h1 {color:darkcyan;}  h2 {color:lightSeaGreen;}  p {color:grey;}  a {text-decoration:line-through;}  </style>

显示效果如下:

类选择器
body>
<div class="main">  <article class="news">  <h1>地震自救指南</h1>  <p>大的晃动时间约为1分钟左右。这时首先应顾及的是您自己与家人的人身安全。首先,在重心较低、且结实牢固的桌子下面躲避,并紧紧抓牢桌子腿。在没有桌子等可供藏身的场合,无论如何,也要用坐垫等物保护好头部。</p>  </article>
</div>
</body>
<style type="text/css">  .main {  background-color: ivory;  margin: 10px;  }.news {  padding: 10px;  color: black;  font-weight: bold;  }p {  color: grey;  }  </style>

与元素选择器不同,对同一类的元素应用样式,需要在类名前加上一个点号(.),然后书写相应的样式声明。

最后,样式应用的效果如图:

id选择器
<body>  <h1 class="important">温馨提示</h1>  <p>少一串脚印,多一份绿意。</p>
</body>
<body>  <h1 id="important">温馨提示</h1>  <p>少一串脚印,多一份绿意。</p>
</body>
#important {  color: red;  font-weight: bold;
}

样式应用效果如图:

类选择器与id选择器的区别

类选择器与id选择器类似,那么它们的区别是什么呢?什么情况下应该使用哪一种选择器呢?

它们最大的区别在于,在一个 HTML 文档中,可以为任意多个元素指定类,但id选择器只能使用一次,一个id只能运用于一个元素。

一般情况下,都推荐使用类选择器。而在一些特定情况下,我们才会建议使用id选择器。例如,通过id选择器在页面中定义锚,在编写 JavaScript 为指定的页面元素应用特殊行为时。

lide元素左边和鼠标之间的偏移值
var offset = $(“.slide”).offset().left;
//.slide元素到文档最左边的距离
var distance = e.pageX - offset;
})


当鼠标向右移动时,获取鼠标移动的距离,也就是滑块移动的距离。代码如下:```js
$(document).mousemove(function(e){  var e = e || window.event;  //滑块移动的距离x  var x = parseInt(e.pageX- offset - distance);  //把x赋值给.slide元素的left值,就能移动了  $(".slide").css('left', x + 'px' );

效果如下:

[外链图片转存中…(img-VkwQMmIo-1652194809039)]

这里用示意图说明如下:

[外链图片转存中…(img-gKUlK8r8-1652194809039)]

可以发现:滑块可以滑动了,但是有超出界限的情况,所以要控制滑块滑动的范围。滑动的最小距离是0,最大距离是.container元素的宽度减去.slide元素自身的宽度。代码如下:

//全局定义变量moveX,maxRange
var moveX, maxRange;
//滑动的最大距离
maxRange = parseInt($(".container").width() - $(".slide").width());
//滑动的距离
moveX = Math.min(Math.max(0,x),maxRange);
//把x赋值给.slide元素的left值,就能移动了
$(".slide").css('left', moveX + 'px' );

效果如下:

[外链图片转存中…(img-zq7mUsjS-1652194809039)]

这里说明一下取最大、最小值的方法:

  • Math.max():返回指定的数中带有较大的值的那个数。
  • Math.min():返回指定的数中带有较小的值的那个数。

怎么体现滑动的过程呢? 这里给.bar元素添加效果,让宽度随着滑块的移动而变化。代码如下:

$(".bar").css({  width: moveX + 'px',  background: 'lightgreen'
});

效果如下:

[外链图片转存中…(img-NdfFm1mX-1652194809040)]

这里需要判断一下什么情况下就验证成功了呢? 当滑动的距离等于最大能滑动的距离,然后.content元素会添加内容验证成功,滑动的图标也变了。代码如下:

if(moveX === maxRange){  $(".content").text('验证成功');  $(".slide").html('<i class="iconfont icon-duihao"></i>')
}

效果如下:

[外链图片转存中…(img-2edtwTOj-1652194809040)]

上面为了效果图的展示,已经包括了鼠标抬起的情况。

现在就缺鼠标抬起的情况了,按照编程要求的提示进行代码的编写。

二 、css

字体颜色、类型、大小

字体颜色 color
字体类型 font-family
字体大小 font-size

字体的粗细与风格

字体粗细 font-weight
字体风格 font-style

我们在设置斜体文本时使用 font-style 属性。

它属性有三个值:

  • normal - 文本正常显示;
  • italic - 文本斜体显示;
  • oblique - 文本倾斜显示。

文本装饰和文本布局

文本装饰 text-decoration

我们使用text-decoration设置字体上的文本装饰。

其可用值有:

  • none: 取消已经存在的任何文本装饰;
  • underline: 文本下划线;
  • overline: 文本上划线;
  • line-through: 贯穿文本的线;
  • blink:闪烁文本。

使用时,可以根据需求,同时添加多个装饰值。

文本布局

接下来,我们来看用来影响文本布局的属性。

文本对齐 text-align

我们使用text-align来改变一个元素中的文本行互相之间的对齐方式。

其可用值有:

  • left: 左对齐文本;
  • right: 右对齐文本;
  • center: 居中文字;
  • justify: 水平对齐,改变单词之间的差距,使所有文本行的宽度相同。
行高 line-height

我们使用 line-height 属性来设置行高。

注意:行高属性值不能为负。

其可用值有:

  • normal:默认。设置合理的行间距;

  • number:设置数字,此数字会与当前的字体尺寸相乘来设置行间距;

  • length:设置固定的行间距;

  • %:基于当前字体尺寸的百分比行间距;

  • inherit:从父元素继承 line-height 属性的值。

css元素选择器

<body>  <h1>标题1</h1>  <h2>标题2</h2>  <p>普通段落中<a href="#">删除的引用。</a></p>
</body>
<style type="text/css">  html {color:black;}  h1 {color:darkcyan;}  h2 {color:lightSeaGreen;}  p {color:grey;}  a {text-decoration:line-through;}  </style>

显示效果如下:

[外链图片转存中…(img-zWPEqPm3-1652194809041)]

类选择器
body>
<div class="main">  <article class="news">  <h1>地震自救指南</h1>  <p>大的晃动时间约为1分钟左右。这时首先应顾及的是您自己与家人的人身安全。首先,在重心较低、且结实牢固的桌子下面躲避,并紧紧抓牢桌子腿。在没有桌子等可供藏身的场合,无论如何,也要用坐垫等物保护好头部。</p>  </article>
</div>
</body>
<style type="text/css">  .main {  background-color: ivory;  margin: 10px;  }.news {  padding: 10px;  color: black;  font-weight: bold;  }p {  color: grey;  }  </style>

与元素选择器不同,对同一类的元素应用样式,需要在类名前加上一个点号(.),然后书写相应的样式声明。

最后,样式应用的效果如图:

[外链图片转存中…(img-tveUu9D2-1652194809041)]

id选择器
<body>  <h1 class="important">温馨提示</h1>  <p>少一串脚印,多一份绿意。</p>
</body>
<body>  <h1 id="important">温馨提示</h1>  <p>少一串脚印,多一份绿意。</p>
</body>
#important {  color: red;  font-weight: bold;
}

样式应用效果如图:

[外链图片转存中…(img-sG4MbOR7-1652194809042)]

类选择器与id选择器的区别

类选择器与id选择器类似,那么它们的区别是什么呢?什么情况下应该使用哪一种选择器呢?

它们最大的区别在于,在一个 HTML 文档中,可以为任意多个元素指定类,但id选择器只能使用一次,一个id只能运用于一个元素。

一般情况下,都推荐使用类选择器。而在一些特定情况下,我们才会建议使用id选择器。例如,通过id选择器在页面中定义锚,在编写 JavaScript 为指定的页面元素应用特殊行为时。

js、css基础总结相关推荐

  1. HTML+CSS+JS的基础知识笔记

    语义化:通俗来说就是明白每个标签的用途(在什么情况下使用此标签合理) 语义化的好处:1更容易被搜索引擎收录.2. 更容易让屏幕阅读器读出网页内容. html文件基本结构: <html>   ...

  2. 和前端撕出逼格,撕的硬气 - 产品应该懂的html/css基础知识

    之前和前端交流页面的实现方案时,经常被告知:这个效果实现不了:那个东西兼容性不好:这个做不了...明明这些效果别人家已经实现出来了,哎,奈何不懂前端相关,没辙! 最近花了点时间看了些前端相关的博客.论 ...

  3. HTML/CSS基础知识(四)

    WEB标准和W3C的理解与认识 Web标准是一系列标准的集合. 网页主要由三部分组成:结构(Structure).表现(Presentation)和行为(Behavior). 对应的标准也分三方面:结 ...

  4. html css基础知识

    1 这是自己学习html时候做的一些记录,供大家参考 <!-- 2 块和内联3 块元素:独占一行的元素4 div p h ul5 div没有任何语义,就是一个纯粹的快元素6 就是为了方便布局7 ...

  5. 诺禾致源css客户端,诺禾,诺禾致源:CSS 基础教学

    伦多星报 2020-07-06 17:14:58 Applying CSS and JavaScript to HTML 当今您将使用的几乎所有网站都将使用CSS使它们看起来很酷,并使用JavaScr ...

  6. html js css如何关联_会html+css+js就能把前端项目发布到多个平台

    在这篇文章中,小编将给大家分享如何让自己的前端代码发布到多个常用的平台. 看完这篇文章以后,你就知道了如何让你的前端代码发布到多个平台,如:安卓应用程序,小程序,iOS应用程序,Windows,Mac ...

  7. CSS基础「六」元素的显示与隐藏

    本篇文章为 CSS 基础系列笔记第六篇,参考 黑马程序员pink老师前端入门教程 其他CSS基础相关文章: CSS基础「一」基础选择器 / 字体属性 / 文本属性 / 三种样式表 CSS基础「二」复合 ...

  8. CSS基础「五」定位

    本篇文章为 CSS 基础系列笔记第五篇,参考 黑马程序员pink老师前端入门教程 其他CSS基础相关文章: CSS基础「一」基础选择器 / 字体属性 / 文本属性 / 三种样式表 CSS基础「二」复合 ...

  9. 前端面试题汇总(css基础篇)

    前端面试题汇总(css基础篇) 1 css sprite是什么,有什么优缺点 概念:将多个⼩图⽚拼接到⼀个图⽚中. 通过 background-position 和元素尺⼨调节需 要显示的背景图案. ...

  10. CSS基础汇总——点击标题跳转详细博客【学习笔记】

    CSS 网页的美容师 理想中的结果: 结构(html)与样式(css)相分离 1.引入CSS样式表(书写位置) 行内式(内联样式) : <标签名 style="属性1:属性值1; 属性 ...

最新文章

  1. golang 使用 protobuf 的教程
  2. 使用字符串定界符(标准C ++)在C ++中解析(拆分)字符串
  3. 移动端https抓包那些事--进阶篇
  4. 牛客OI周赛10-提高组:B-Taeyeon的困惑(值域线段树)
  5. JAVA程序通过JNI调用C/C++库
  6. Linux下编译安装Apache httpd 2.4
  7. Spring Boot JWT 快速入门
  8. 计算机不能上网 检查路线,腾达(Tenda)路由器不能上网的解决方法
  9. 8.数据结构 --- 动态存储管理
  10. AI2(App Inventor 2)离线版服务器单机版
  11. pytorch入门——构建神经网络
  12. Filenet 周报(12.02-12.08)
  13. sql问题导致CPU使用率100%
  14. “采菊东篱下,悠然见南山”----南山路上品太古 总结
  15. Excel 插入图表后图标属性一直闪烁
  16. VS错误代码列表之美
  17. 网络协议篇之SNMP协议(二)—— 管理信息库MIB及OID
  18. 色相、色彩、色度和色调
  19. 偏光显微镜研究聚合物的球晶形态
  20. 超声波测距传感器模块在畜牧业的应用

热门文章

  1. 【渝粤题库】陕西师范大学200531 英语测试 作业(高起本、专升本)
  2. 盘点 6 个开源的音乐播放器!
  3. Doris export任务概率性cancelled第二种情况
  4. java运行内存不足_Java运行过程出现的内存不足问题
  5. 淘宝店铺上传成人用品类宝贝的错误解决
  6. 微软输入法繁体简体切换
  7. 《笨办法学python》第39课—— 列表的操作
  8. 计算机证件照无法上传,计算机IE浏览器为什么不能上传照片
  9. 当《流浪地球》冲过2亿:中国的科幻大片为什么来得这么晚?
  10. antd系列之Select