JavaScript浏览器发展史
1.浏览器组成
- shell部分
- 内核部分
2.1 渲染引擎(语法规则和渲染)
2.2 js引擎 - 其他模块(异步…)
主流浏览器 | 内核 |
---|---|
IE | trident |
Chrome | webkit/blink |
firefox | Gecko |
Opera | presto |
Safari | webkit |
2.js的特点
- 解释性语言——不需要编译成文件、跨平台
编译性语言(c、c++、R) | 解释性语言 <>(php、js、python) | |
---|---|---|
特点 | 通篇翻译,生成特定的文件 | 逐行翻译,不生成特定文件 |
优点 | 快 | 跨平台(直接翻译成机器码) |
缺点 | 移植性不好(不跨平台) | 慢 |
注意:JAVA既不是编译性语言也不是解释性语言,可跨平台(可在linux、unix运行)
.java --> 通过javac指令编译 --> .class -->通过jvm虚拟机解释执行
-
(js引擎)是单线程
同步:多线程(同时做多件事:实质是电脑同时只做一件事)
异步:单线程(同时只能做一件事:实质是电脑同时做多件事,例如html里link标签的加载) -
ECMA标准
3.js执行队列
把任务分成多个时间片,争抢时间片
A,1ms
–>B,1ms
–>B,1ms
–>A,1ms
–>B,1ms
–>B,1ms
–>A,1ms
–…
轮转时间片:象棋车轮战
JavaScript基本语法
1.js执行队列
不可更改的原始值(stack:FILO) | 引用值(heap) | |
---|---|---|
种类 | Number String Boolean undefined null | Array Object function…date RegExp |
区别 | ② = ①,值②不会因值①改变而改变 | ② = ①,值②会因值①改变而改变 |
原理 | 用栈存储①的值,②拷贝的是①的值 | 用栈存储①的地址,用堆存储①的值,②拷贝的是①的地址 |
//原始值:Number String Boolean undefined null
var a = 10;
var b = a;//赋的是a的值
a = 20;
document.write(b);
//结果->10
//引用值:Array Object function...date RegExp
var a = [1];
var b = a;//赋的是a的地址
a.push(2);//在a的地址增加一个值
document.write(b);
//结果->1,2
注意以下情况
var a = [1,2];
var b = a;//赋的是a的地址
a = [1,3];//创建一个新数组,并更改a的地址
document.write(b);
//结果->1,2,思考为什么结构不是1,3?
//因为a = [1,3];不是更改原来的数据,而是在堆里开辟了 新的空间用于存储新的数组[1,3]
2.操作运算符
- "+"
var a = 1 + 1 + "a" + (1 + 2);
document.write(a);
//结果->2a3
var a = "a" + true + 1;
document.write(a);
//结果->atrue1
- "-","*","/","%","=","()"
优先级"=“最弱,”()"优先级较高 - "–","++","-=","+=","*=","/=","%="
a++ | ++a |
---|---|
先执行语句后自加 | 先自加后执行语句 |
var a = 10;
document.write(a++);//先执行语句后自加,10
document.write(a);//11
3.比较运算符
- 比较运算符:">","<","==",">=","<=","!="
- 逻辑运算符:"&&","||","!"
表达式①&& 表达式② | 表达式①|| 表达式② |
---|---|
表达式①为真,则返回表达式②的值;表达式①为假,则返回表达式①的值 | 表达式①为真,则返回表达式①的值;表达式①为假,则返回表达式②的值 |
var a = 1 && 2;
document.write(a);//结果->2
//解决浏览器兼容性问题,既能在非IE浏览器又能在IE浏览器正常使用
//e在非IE浏览器中有值,window.event在IE浏览器中有值,e 等于 window.event
var event = e || window.event;
- 被认定为false的值:undefined,null,NaN, “”,0,false
4.条件语句
- if <=> && 转换
if(1 < 2){
document.write("a")
}
1 < 2 && document.write("a")
//aa
- for和while
var i = 11;
//i--语句相当于if(i),即当i自减到0时,循环结束
for(; --i; ){
document.write(i+' ');
}
var i = 10;
while(i){
document.write(i+' ');
i--;
}
//结果->10 9 8 7 6 5 4 3 2 1 10 9 8 7 6 5 4 3 2 1
5.类型转换
- 显式类型转换
- Number(mix)把内容转换成数
var demo = "123";//=>> number:123
var demo = false;//=>> number:0
var demo = null;//=>> number:0
var demo = undefined;//=>> number:NaN
var demo = "a";//=>> number:NaN
var sth = Number(demo);
console.log(typeof sth + ":" + sth);
Number(undefined)``Number("a") | Number(null) |
---|---|
NaN | 0 |
2. parseInt(string,radix)把内容转换成整型
var demo = "123";//=>> number:123
从第一个数字位开始看,一直看到非数字为止
var demo = "123abc";//=>> number:123
var demo = "10.9"; var radix = 16;//=>> number:16
var demo = "b"; var radix = 16;//=>> number:11
var sth = parseInt(demo,radix);
radix是进制的意思,如果radix=16,则证明用16进制转换结果
console.log(typeof sth + ":" + sth);
parseInt("123abc") | parseInt("10.9" , 16) | parseInt("b" , 16) |
---|---|---|
123 | 16 | 11 |
3. parseFloat(string)把内容转换成浮点型
var demo = "123abc";//=>> number:123
var demo = "123.9";//=>> number:123.9
var demo = "123.9.2";//=>> number:123.9
var sth = parseFloat(demo);
console.log(typeof sth + ":" + sth);
4. String(mix)把内容转换成字符串
var demo = 123.9;//=>> string:123.9
var sth = String(demo);
console.log(typeof sth + ":" + sth);
5. Boolean(mix)把内容转换成字符串
var demo = 123.9;//=>> boolean:true
var demo = "";//=>> boolean:false
var sth = Boolean(demo);
console.log(typeof sth + ":" + sth);
6. toString(mix)把内容转换成字符串
var demo = 123.9;//=>> string:123.9
var demo = undefined;//=>> Uncaught TypeError
var demo = null;//= >> Uncaught TypeError
var demo = {};//=>> string:[object Object]
var sth = demo.toString();
console.log(typeof sth + ":" + sth);
undefined.toString() 、 null.toString() | Uncaught TypeError |
---|
实现将2进制转换成16进制
var demo = 10101010;
var demo2 = parseInt(demo,2);//将2进制转成10进制
console.log(typeof demo2 + ":" + demo2);//=>> number:170
var sth = demo2.toString(16);//将10进制转成16进制
console.log(typeof sth + ":" + sth);//=>> string:aa
parseInt(radix) | toString(radix) |
---|---|
将radix进制转换成10进制 | 将10进制转换成radix进制 |
- 隐式类型转换
1.isNaN()-->Number()//判断这个数是否是NaN
console.log(isNaN("123"));//=>>false
console.log(isNaN("NaN"));//=>>true
console.log(isNaN("abc"));//=>>true
原理:先把变量放入Number("abc"),转换结果与NaN比对
2.++/-- +/-(一元正负)
var a = "123.4";a++;//=>>number:124.4
var a = -"123.4";//=>>number:-123.4
console.log(typeof a + ":" + a);
3.+(加号)有一个值是字符串结果都会变成字符串
var a = "123.4" + 1;//=>>number:string:123.41
console.log(typeof a + ":" + a);
4.-(减)*(乘)/(除)%(模)-->Number()
var a = "123.4" * 1;//=>>number:number:123.4
console.log(typeof a + ":" + a);
5.&& || !先转换乘boolean值
6.< > <= >=
var a = 1 > "2"; //有数字将所有值转化成数字进行比较
var a = "3" > "2"; //全是字符串转化成ascii码进行比较
7.== !=
var a = 1 == "1"; //=>>boolean:true
var a = 1 == true; //=>>boolean:true
console.log(typeof a + ":" + a);
undefined == null | NaN == NaN |
---|---|
true | false |
- 不发生类型转换
- ===(绝对相等)
- !==(绝对相等)
var a = 1 === "1"; //=>>boolean:false
var a = 1 !== "1"; //=>>boolean:true
console.log(typeof a + ":" + a);
- 特殊
console.log(typeof(typeof(a)));//=>>string
var a = 123123.245454
alert(a.toFixed(3));//123123.245
函数
- 形参和实参数量无需对应
- arguments ———— 实参列表
- arguments.length实参长度;函数名.length型参长度
//形式参数————形参
function test(x){
console.log(x);
//arguments ———— 实参列表
console.log(arguments);
//Arguments(3) [10, 3, 4]
}
//实际参数————实参
test(10,3,4);
//任意个数求和————不定参
function sum(){
var result = 0;
for(i = 0; i < arguments.length; i++){
result += arguments[i];
}
console.log(result);
}
sum(1,2,3,4,5,6,7,7);
- 当形参数量 = 实参数量,形成映射关系:修改形参能改变实参
function sum(a,b) {
a = 2;
console.log(arguments[0]);//->>2
arguments[1] = 3;
console.log(arguments[1]);//->>3
}
sum(1,2);
- 当形参数量 != 实参数量,不形成映射关系:修改形参不能改变实参
function sum(a,b) {
b = 2;
console.log(arguments[1]);//->>undefined
}
sum(1);
用递归解决两大经典问题
- 递归方式解决斐波那契额问题
//普通方法
function fbnq(f,s,num){
if(num == 0){
return;
}
var t = f + s;
document.write(t + " ")
f = s;
s = t;
fbnq(f,s,num-1)
}
var num = parseInt(window.prompt("input"));
var f = 0;
var s = 1;
if(num){
document.write(1 + " ");
fbnq(f,s,num-1);
}
//快捷方法
function fb(n){
//结束条件
if(n == 1 || n == 2){
return 1;
}
//运算规律
return fb(n - 1) + fb(n - 2);
}
var n = window.prompt("input");
fb(n);
- 递归方式解决阶乘
//快捷方法
function mul(n){
//结束条件
if(n == 1){
return 1;
}
//运算规律
return n * mul(n - 1);
}
var n = window.prompt("input");
mul(n);
JS运行三部曲
- 语法分析(通篇扫描)
- 预编译
2.1 imply global暗示全局变量:任何没有声明的变量直接赋值,此变量就为全局对象所有。a = 10;相当于window.a = 10;
,
2.2 全局上的变量即使声明了,也是全局变量(window就是全局的域)。在全局上var a = 10;相当于window.a = 10;
function test(){
var a = b = 10;
document.write(window.a + "----");
document.write(window.b);
}
test();//undefined----10
结论:b是全局变量,而a是局部变量
2.3 四部曲:
- 创建AO对象(Activition Object)(执行期上下文)
- 找形参和变量声明,将变量和形参名名作为AO对象的属性名,值是undefined
- 将实参和形参统一
- 在函数体里面找函数声明,值赋予函数体
作用域
- [[scope]]
1.2 每个JavaScript函数都是一个对象,对象中有些属性我们可以访问(eg:name\prototype
),但有些不可以(eg:[[scope]]
),这些属性仅供JavaScript引擎存取。
1.3 [[scope]]指的就是我们所说的作用域,其中存储了运行期上下文的集合。 - 作用域链
2.1 [[scope]]中所存储的执行期上下文对象的集合,这个集合呈链式连接,我们把这种链式链接叫做作用域链 - 执行期上下文
3.1 当函数预编译时,会创建一个成为执行器上下文的内部对象。一个执行期上下文定义一个函数执行时的环境
3.2 函数每次执行时对应的执行上下文都是独一无二的,所以多次调用一个函数会导致创建多个执行上下文,当函数执行完毕,它所产生的执行上下文被销毁。 - 查找变量:从作用域链顶端一次向下查找
立即执行函数
- 立即执行函数的形式
// 基本形式(标准)
(function(){
}())
// 基本形式(非标准)
(function(){
})()
- 只有表达式才能被执行
function test(){ //函数声明
console.log ("a");
}
var demo = function(){ //函数表达式
console.log ("a");
}
//函数表达式+执行符号()
test();
//函数表达式+执行符号()
var demo = function(){ //函数表达式
console.log ("a");
}()
// 一旦函数表达式成为立即执行函数,该表达式就会
// 失去对原来函数的索引。即在上述这个例子中:
// 在控制台输入demo,不会返回方法,而是返回undefined
function test(){ //函数声明 + 执行符号()
console.log ("a");
}()
//Uncaught SyntaxError
//+(正)-(负)!(非)或者()括号都可以使函数声明变成函数表达式
-function test(){ //函数表达式 + 执行符号()
console.log ("a");
}()
//a
(function test(){ //函数表达式 + 执行符号()
console.log ("a");
})()
//a
- 针对初始化功能的函数——只执行一次就立即销毁
- 可传参,可以有返回值
- 内部有预编译,有执行期上下文
var num = (function (a, b, c){
return a + b + c;//6
}(1, 2, 3));
闭包
闭包是什么:当内部函数被保存到外部时,将会生成闭包。
闭包的危害:闭包会导致原有作用域链不释放,造成内存泄露(内存占用)
闭包的作用:(例子见闭包例题集)
- 实现公有变量(
eg:函数累加器
) - 可以做缓存(存储结构)(
eg:eater
) - 可以实现封装,属性私有化(
eg:Deng
) - 模块化开发,防止污染全局变量
对象
对象的创建方法
- var obj = {} plainObject 对象字面量/对象直接量
- 构造函数
2.1 系统自带的构造函数 new Object()
2.2 自定义
//2.1 系统自带的构造函数 new Object()
// 双胞胎:两个结果一模一样的独立个体
var obj = {};
console.log(obj);// {}
var obj = new Object();
console.log(obj);// {}
//2.2 自定义
// 构造函数用大驼峰式命名规则
function Person(){}//与函数声明不一样的地方就是名字用大驼峰式命名规则
var person = new Person(); //new Person()会返回一个对象给person1
console.log(person);//Person {}
构造函数的内部原理
- 在函数体最前面隐式的加上this = {}
- 执行this.xxx = xxx;
- 隐式的返回this
function Student(name,age,sex){
// var this = {
// name:
// age:
// .....
// }
this.name = name;
this.age = age;
this.sex = sex;
this.grade = 2019;
// return this;
}
var car = new Student();
//当你new一个sutdent,会发生如下变化:
//①自动生成隐式的this结构体
//②根据你的执行语句生成属性
//③return this;最终生成
function Person(name,height){
this.name = name;
this.height = height;
// return {};//{}
return 123;//Person {name: "aaa", height: 180}
}
var person = new Person('aaa',180);
console.log(person);
//所以如果没有return语句,会执行隐式的return this;
//如果有自定义return{}语句,会执行自定义的return {},并返回{};
//如果自定义返回语句不是return {},会执行隐式的return this,因为有new;
包装类
把原始值(没有属性和方法)转换成对象值(有属性和方法)
- new String();
- new Boolean();
- new Number();
// undefined和NaN都不能有属性;数字\字符串\布尔有原始值和对象值
var num = 123;//原始值
num.len = 1;//不报错,能执行以下语句,进行隐式的中间环节——// 包装类
//new Number(123).len = 1; delete
console.log(num.len);//undefined
//new Number(123).len = 没有值
var num = new Number(123);//Number {123}//对象值
num.len = 1;
console.log(num.len);//1
// 一旦数字的对象能参与运算,返回的是原始值数字。
console.log(typeof num);//object
console.log(typeof (num * 2));//number
//易错点!!!
var arr = [1,2,3,4,5];
console.log(arr.length);//5
arr.length = 2;//length是数组本身的属性,可以进行截断操作
console.log(arr);//[1, 2]
// 基于这个理论问:字符串也有length属性,同样操作为什么没有被截断?
var str = '12345';
console.log(str.length);//5
str.length = 2;
//new String('abcd').length = 2; delete 销毁新建出来的String('abcd'),并不会影响str
console.log(str);//12345
console.log(str.length);//12345 str自身没有属性length,但其包装类new String()有,所以调用了其包装类
//这里str.length返回的是对象字符串自带的属性length的值,不会人为的更改而变化
原型
- 定义:原型是function对象的一个属性,它定义了构造函数制造出的对象的公共祖先。通过该构造函数产生的对象,可以继承该原型的属性和方法。原型也是对象
//Person.prototype = {} --原型(是函数Person的祖先)
Person.prototype.name = 'abc';
// 构造函数必须大驼峰式命名
// 构造函数就像一个生产车间,生产的每个产品都有相同的基本属性
function Person(){}
var person = new Person();
console.log(person);//Person {}//函数Person并没有name属性
console.log(person.name);//abc//获得了函数Person祖先name属性的值
var person1 = new Person();
console.log(person1.name);//abc//原型是其构造函数制造出的对象的公共祖先
- 利用原型特点和概念,可以提取共有属性,减少代码冗余
//固定的值,无论构造几个对象都执行1次,减少代码耦合
Car.prototype = {
height : 1400,
lang : 4900,
carName : 'BMW'
}
function Car (color,owner){
this.owner = owner;
//固定的值构造几个对象就执行几次,造成代码冗余、耦合
// this.carName = 'BMW';
// this.height = 1400;
// this.lang = 4900;
this.color = color;
}
var car = new Car('red','Mr.Zhang');
var car1 = new Car('green','Mis.Liu');
Person.prototype.lastName = 'Deng';
function Person(){}
var person =new Person();
person.lastName = 'Liu';//没有改变原型的属性,而是增加了构造函数的属性
//无法通过后代改变(增/删/改)祖先的属性,只能通过Person.prototype.lastName来修改原型的属性
3. 对象如何查看原型–> 隐式属性_proto_
4. 对象如何查看对象的构造函数–> constructor
//问题①:为什么明明function A中没有__proto__属性,
//但当在控制台打印a.__proto__的值为原型?
function A (){
//答①:当执行new A()时会隐式在function A中创建this,
//this中有系统自带属性__proto__,这是它能找到原型的根本原因
// var this = {
// __proto__:A.prototype,
// }
}
var a = new A();
A.prototype = {
name = 'abc';
};
var obj = {
name:'sunny';
}
function A (){
}
var a = new A();
a.__proto__ = obj;
原型链
绝大多数对象
的最终都会继承自Object.prototype
Grand.prototype.lastName = 'Deng';
function Grand(){}
var grand = new Grand();
Father.prototype = grand;
function Father(){
this.name = 'xuming';
}
var father = new Father();
Son.prototype = father;
function Son(){
this.hobby = 'smoke';
}
var son = new Son();
2. 后辈无法改变(增删改)前辈的属性
情况一:
- 但如果是获取了引用在修改引用能修改前辈的属性
function Father(){
this.fortune = {
card1:'visa',
}
}
var father = new Father();
Son.prototype = father;
function Son(){}
var son = new Son();
情况二:
function Father(){
this.num = 100;
}
var father = new Father();
Son.prototype = father;
function Son(){}
var son = new Son();
son.num = son.num + 1;所以father.num并不受影响
- Object.create(原型)
// 方式一:
Person.prototype.name = "a";
function Person(){}//若是在这里改变name的值,Object.create(原型)执行的效果会就做不到一样了
var person = new Person();
console.log(person.name);//a
// 方式二:var obj = Object.create(原型);
Person.prototype.name = "a";
function Person(){}
var person = Object.create(Person.prototype);
console.log(person.name);//a
Object.create(prototype(原型),defindProperty(特性))
四大属性之一:不可配置属性
- 一旦经历了var操作所得出来的属性,这种属性叫做不可配置属性
eg.1:
console.log(Object.create());// Object prototype may only be an Object or null
eg.2:
console.log(Object.create(null));//{No properties}
//返回一个没有__proto__属性的object
eg.3:
var obj = Object.create(null);
obj.__proto__ = {name:'sunday'};
console.log(obj.name);//undefined
//不是系统定义的__proto__ 无法正常使用
- undefined和null没有原型
number、boolean、String能通过包装类转换成对象,并 继承自Object.prototype
console.log(undefined.toString());//Cannot read property 'toString' of undefined
console.log(null.toString());//Cannot read property 'toString' of null
call/apply
- 作用:改变this指向
- 区别:后面传的参数形式不同
2.1 call 需要把实参按照形参的个数传进去
2.2 apply 需要传一个arguments
继承发展史
- 传统形式——原型链
1.1 缺点:过多的继承了没用的属性 - 借用构造函数(call/apply)
2.1 不能继承借用构造函数的原型
2.2 每次构造函数都要多走一个函数(低效率) - 共享原型
3.1 无法私人订制属性 - 圣杯模式(你能在继承别人的基础上改你自己的而且不影响别人)
命名空间
- 用闭包将变量私有化解决命名冲突问题
var deng = {
smoke : function(){
console.log('smoking....');
return this;//可以实现执行函数后返回自身
},
drink : function (){
console.log('drinking....');
return this;
},
perm : function(){
console.log('perming....');
return this;
}
}
deng.smoke().drink().perm().drink();
属性的表示方法
obj.prop === obj["prop"]
var deng = {
wife1 : {name : 'lucy'},
wife2 : {name : 'sophy'},
wife3 : {name : 'icy'},
sayWife : function(num){
//方式一:
// switch(num){
// case 1:
// return this.wife1
// }
//方式二:可实现字符串拼接
return this['wife' + num].name;
//相当于this.wife2.name
}
}
console.log(deng.sayWife(2));//sophy
对象的枚举
- for in
1.1 hasOwnProperty
1.2 in
1.3 instanceof
this
- 函数预编译过程this ==> window
- 全局作用域里this ==> window
- call/apply 可以改变函数运行时this指向
- obj.func(); (fun()里面的this指向调用this的对象)
arguments
- arguments.callee
- 实参列表arguments,callee是其属性,callee指向函数自身的引用(即返回函数自己)
- 解决立即执行函数没有函数名而无法递归的问题
// 初始化一个复杂的数据:3的阶乘
var num = (function(n){
if(1 == n){
return 1;
}
return n * arguments.callee(n-1);
//arguments.callee实现调用自身,解决立即函数没有函数名而无法递归的问题
}(3))
- func.caller
- 返回调用这个函数的调用者
三目运算符
条件判断 ? 是 : 否
并且会返回值
数组
- 数组的2种定义方式(全来源于Array.prototype)
1.1 数组字面量
1.2 new Array[]
- 两种方式的区别如下:
- 数组的读和写
2.1 arr[num] //溢出读为undefined
2.2 arr[num] = xxx;//可以溢出写
- 数组常用的方法
3.1 改变原数组
push
:在数组最后一位开始添加
pop
:移除数组最后一位
shift
: 移除数组第一位
unshift
:在数组第一位开始添加
reverse
:逆转数组
splice
:截取数组
sort
:数组排序
3.2 不改变原数组
concat
:拼接数组
join
:数组元素连接符
类数组
- 可以利用属性名模拟数组的特性
- 可以动态的增长length属性
- 如果强行让类数组调用push方法,则会根据length属性值的位置进行属性的扩充
try…catch…finally
- 当try里面代码发生错误,不执行try里的后续代码,不终止执行,会抛出catch里的异常,然后继续执行。finally子句在try块和catch块之后执行但是在下一个try声明之前执行。无论是否有异常抛出或捕获它总是执行。
ES5 严格模式
"use strict"
- 不再兼容es3的一些不规则语法。使用全新的es5规范。
- 两种用法:
2.1 全局严格模式(在全局/局部的最顶端)
2.2 局部函数内严格模式(推荐)
- 使用一行字符串而不是一个函数来开启严格模式的原因是不产生异常导致后续代码无法执行,即不会对不兼容严格模式的浏览器产生影响
- 有哪些冲突?
4.1 不支持with
,arguments.callee
,func.caller
- with(通过改变顶端作用域来改变this的指向)
- 解决命名空间问题
- with修改作用域会降低代码运行效率,所以es5.0严格模式不允许使用
4.2 变量赋值前必须声明
4.3 局部的this必须被赋值,全局的this指向window
4.4 拒绝重复属性和参数
es3.0中this未被赋值,this指向window | es5.0中this未被赋值,this为undefined |
---|
es3.0中this是原始值时,自动转化为包装类 | es3.0中this是原始值时,不转化 |
---|
DOM基本操作
- DOM——Document Object Model文档对象模型
- DOM是一套操作HTML的方法集合
- 趣味小例子
对节点的增删改查
- 查看元素节点
1.2document
代表整个文档
1.3document.getElementById()
元素id在IE8以下浏览器,不区分id的大小写,而且也返回匹配name属性的元素
1.4document.getElementByTagName()
选择特定标签名的元素,返回类数组
1.5document.getElementByClassName()
选择特定类名的元素,IE8及其以下版本没有,可以多个class一起,返回类数组
1.6document.getElementByName()
选择特定数据名的元素
1.7querySelector()
css选择器 ,返回一个 在IE7及其以下版本没有 非实时性
1.8querySelectorAll()
css选择器,返回一组 在IE7及其以下版本没有 非实时性 - 遍历节点数
2.1parentNode
->父节点(最顶端的parentNode为#document)
2.2childNodes
->子节点们
2.3firstChild
->第一个子节点
2.4lastChild
->最后一个子节点
2.5nextSibling
->后一个兄弟节点&previousSibling
->前一个兄弟节点 - 基于元素节点数的遍历
3.1parentElement
->返回当前父元素节点
3.2children
->只返回当前元素的元素子节点
3.3firstElementChild
->第一个元素节点(IE不兼容)
3.4lastElementChild
->最后一个元素节点(IE不兼容)
3.5node.childElementCount === node.children.length
- 节点类型:
- 元素节点——1
- 属性节点——2
- 文本节点——3
- 注释节点——8
- document——9
- DocumentFragment——11
- 节点的四个属性:
4.1 nodeName 元素的标签名,以大写形式表示,只读
4.2 nodeValue Text节点或Comment节点的文本内容,可读写
4.3 nodeTyped 节点类型,只读
4.4 Element 节点的属性集合 - 节点的一个方法 Node.hasChildNodes();
DOM继承树
继承关系document —> HTMLDocument.prototype —> Document.prototype
继承关系HTMLHeadElement/HTMLBodyElement/HTMLTitleElement/HTMLParagraphElement/HTMLInputElement/HTMLTableElement/…etc—> HTMLElement—>Element
document的继承树:
Date
setInterval
var minutesNode = document.getElementsByTagName('input')[0];
var secondsNode = document.getElementsByTagName('input')[1];
var minutes = seconds = 0;
var timer = setInterval(function(){
seconds ++;
if(seconds == 60){
seconds = 0;
minutes ++;
if(minutes == 3){
clearInterval(timer);
}
}
minutesNode.value = minutes;
secondsNode.value = seconds;
},100)
Bom基本操作
- 查看滚动条的滚动距离
1.1window.pageXOffset
/window.pageYOffset
(ie8及其以下不兼容)
1.2document.body.scrollTop + document.documentElement.scrollTop
(ie8及其以下兼容)
1.3 封装兼容性方法,求滚动轮滚动距离getScrollOffset()
function getScrollOffset(){
if(window.pageXOffset){
return{
x:window.pageXOffset,
y:window.pageYOffset
}
}else{
return{
x:document.body.scrollLeft + document.documentElement.scrollLeft,
y:document.body.scrollTop + document.documentElement.scrollTop
}
}
}
- 查看可视区窗口的尺寸
2.1window.innerWidth
/window.innerHeight
(ie8及其以下不兼容)
2.2document.documentElement.clientWidth
/document.documentElement.clientHeight
(标准模式下任何浏览器都兼容)
2.3document.body.clientHeight
/document.body.clientWidth
(怪异模式下任何浏览器都兼容)
2.4document.compatMode
的值:"CSS1Compat"——标准模式
/"BackCompat"——怪异模式
2.5 封装兼容性方法,求浏览器视口尺寸getViewportOffset()
function getViewportOffset(){
if(window.innerWidth){
return{
width:window.innerWidth,
height:window.innerWidth
}
}else{
if(document.compatMode == "CSS1Compat"){//标准模式,有<!DOCTYPE html>
return{
width:document.documentElement.clientWidth,
heighty:document.documentElement.clientHeight
}
}else{//怪异模式,无<!DOCTYPE html>
return{
width:document.body.clientWidth,
height:document.body.clientHeight
}
}
}
}
- 查看元素的几何尺寸
3.1dom.offsetWidth
/dom.offsetHeight
(视觉上的尺寸:包括padding,包括margin) - 查看元素的几何尺寸
4.1dom.offsetLeft
/dom.offsetTop
(距离与无定位的父级元素,返回相对文档的坐标。距离与已定位的父级元素,返回相对于最近的有定位的父级的坐标)
4.2dom.offsetParent
(返回最近有定位的父级,如无返回body,body.offsetParent,返回null)
4.3 求元素相对于文档的坐标getElementsPosition() - 让滚动条滚动
5.1window.scroll(x,y)
/window.scrollTo(x,y)
/window.scrollBy(x,y)
脚本化CSS
- 读写元素css属性——dom.style.prop
1.1 可读写行间样式,没有兼容性问题,碰到float这样的保留字属性,前面应加css
eg:float-->cssFloat
1.2 复合属性必须拆解,组合单词变成小驼峰式写法eg:background-color-->backgroundColor
- 查询计算样式——window.getComputedStyle(ele,null)
2.1 计算样式只读(返回最终显示效果的值以及默认值)
2.2 返回的计算样式的值都是绝对值,没有相对单位(em是相对单位转化成px,颜色red转换成rgb形式)
2.3 IE8及其以下不兼容——ele.currentStyle
3.封装兼容性方法getStyle(elem,prop)
//封装查看样式的方法getStyle(elem,prop)
function getStyle(elem,prop){
if(window.getComputedStyle(elem,null)[prop]){//不能用.prop,必须[prop]
return window.getComputedStyle(elem,null)[prop];
}else{
return elem.currentStyle[prop];
}
}
如何绑定事件处理函数
- ele.onxxx = fuction(){}
1.1 兼容性好,但一个元素的同一个事件只能绑定一个处理函数
1.2 句柄绑定方式:基本等于写在行间上
<div style = "width:80px;height:80px;background-color:red;"
onclick = "console.log('a')"></div>
- obj.addEventListener(type,fn,false)
2.1 IE9以下不兼容,可以为一个事件绑定多个处理程序
注意一下两张图片的区别:
- obj.attachEvent(
on
+ type,fn)
3.1 IE独有,一个事件同样可以绑定多个处理程序 - 绑定事件一旦发生在循环里,要考虑是否产生闭包
事件处理函数的运行环境
- ele.onxxx = fuction(){}
1.1 程序this指向dom元素本身 - obj.addEventListener(type,fn,false)
2.1 程序this指向dom元素本身 - obj.attachEvent(
on
+ type,fn)
3.1 程序this指向window
var div = document.getElementsByTagName('div')[0];
div.attachEvent('onclick',function(){
handle.call(div);//让this指向元素本身
});
function handle(){
console.log(this);//div
}
- 封装兼容性addEvent(elem,type,handle)方法——给一个dom对象添加一个该事件类型的处理函数
function addEvent(elem,type,handle){
if(elem.addEventListener){
elem.addEventListener(type,handle,false);
}else if(elem.attachEvent){
elem.attachEvent('on' + type,function(){
handle.call(elem);
})
}else{
elem['on'+type] = handle;
}
}
解除事件处理程序
ele.onclick = false/''/null;
ele.removeEventListener(type , fn , false);
ele.detachEvent('on' + type , fn)
- 若绑定匿名函数,则无法解除
事件处理模型——事件冒泡、捕获
- 事件冒泡
- 结构上(非视觉)嵌套关系的元素,会存在事件冒泡功能,即同一事件,自子元素冒泡向父元素。(自底向上)
var wrapper = document.getElementsByClassName('wrapper')[0];
var content = document.getElementsByClassName('content')[0];
var box = document.getElementsByClassName('box')[0];
wrapper.addEventListener('click',function(){
console.log('wrapper');
},false);
content.addEventListener('click',function(){
console.log('content')
},false);
box.addEventListener('click',function(){
console.log('box')
},false);
- 事件捕获
- 结构上(非视觉上)嵌套关系的元素,会存在事件捕获的功能,即同一事件,自父元素捕获至子元素,子元素进行事件执行(事件源元素)(自顶向下)
- IE没有捕获事件
var wrapper = document.getElementsByClassName('wrapper')[0];
var content = document.getElementsByClassName('content')[0];
var box = document.getElementsByClassName('box')[0];
//事件捕获实现:addEventListener(type,fn,ture)
wrapper.addEventListener('click',function(){
console.log( 'wrapper');
},true);
content.addEventListener('click',function(){
console.log('content')
},true);
box.addEventListener('click',function(){
console.log('box')
},true);
思考题:我们知道同一对象的同一个事件类型上绑定了两个对象处理函数,一个是事件冒泡,另一个是事件捕获,问这两个对象处理函数执行顺序如何?
先捕获后冒泡,其中注意捕获至子元素的时候是子元素执行,优先级和冒泡一样,是否先执行取决于代码位置
var wrapper = document.getElementsByClassName('wrapper')[0];
var content = document.getElementsByClassName('content')[0];
var box = document.getElementsByClassName('box')[0];
//冒泡事件
content.addEventListener('click',function(){
console.log('contentBubble')
},false);
wrapper.addEventListener('click',function(){
console.log('wrapperBubble')
},false);
box.addEventListener('click',function(){
console.log('boxBubble')
},false);
//捕获事件
wrapper.addEventListener('click',function(){
console.log( 'wrapper');
},true);
content.addEventListener('click',function(){
console.log('content')
},true);
box.addEventListener('click',function(){
console.log('box')
},true);
- focous(聚焦)、blur、change、submit、reset、select等事件不冒泡
取消冒泡和阻止
- 取消冒泡
1.1 W3C标准event.stopPropagation();但不支持ie9以下版本
1.2 IE、Google独有event.cancelBubble = true;
1.3 封装取消冒泡的函数stopBubble(event)
function stopBubble(event){
if(event.stopPropagation()){
event.stopPropagation();
}else{
event.cancelBubble = true;
}
}
- 阻止默认事件
默认事件——表单提交、a标签自动跳转只顶端、右键自动弹出菜单等
2.1 return false; 以对象属性的方式注册的事件才生效
2.2 event.preventDefault();W3C标注,IE9以下不兼容
2.3 event.returnValue = false;兼容IE
封装阻止默认事件的函数cancelHandler(event)
function cancelHandler(event){
if(event.preventDefault){
event.preventDefault();
}else{
event.returnValue = false;
}
//虽然还有一种return false,但封装进来只会返回false
}
取消a标签的默认事件
方法1:
<a href="javascript:void(false)"> baidu.com </a>
方法2:
a.onclick = function(){
return false;
}
事件对象
- event || window.event(用于IE)
- 事件源对象srcElement:(chrome都有)
2.1 event.target 火狐只有这个
2.2 event.srcElement IE只有这个 - 兼容性写法:
var wrapper = document.getElementsByClassName('wrapper')[0];
var box = document.getElementsByClassName('box')[0];
wrapper.onclick = function(e){
var event = e || window.event;//兼容性设计
var target = event.target || event.srcElement;//兼容性设计
console.log(target);
}
事件委托
- 利用事件冒泡,和事件源对象进行处理
- 优点:
2.1 性能高 不需要循环所有的元素一个个绑定事件
2.2 灵活 当有新的子元素时不需要重新绑定事件
var ul = document.getElementsByTagName('ul')[0];
ul.onclick = function(e){
var event = e || window.event;
var target = event.target ||event.srcElement;
console.log(target.innerText);
}
事件分类
- 鼠标事件
click
mousedown
mousemove
mouseup
contextmenu用于右键取消菜单
mouseover
mouseout
mouseenter
mouseleave
1.1. 用e.button来区分鼠标的按键,0(左键)/1(滚轮)/2 (右键)
1.2. DOM3标准规定:click事件只能监听左键,只能通过mousedown
和mouseup
触发右键
实现小方块随鼠标移动
var div = document.getElementsByTagName('div')[0];
var disX,disY;
div.onmousedown = function(e){//先按下去
disX = e.pageX - parseInt(div.style.left);
disY = e.pageY - parseInt(div.style.top);
//鼠标挪动的频次太快,系统的监听事件频次太慢
document.onmousemove = function(e){//再跟随移动
var event = e || window.event;
div.style.left = e.pageX - disX + 'px';
div.style.top = e.pageY - disY + 'px';
}
document.onmouseup = function(){//最后放开鼠标
document.onmousemove = null;
}
}
实现按钮点击跳转界面,长按拖动
//如何实现区分点击事件和拖拽事件
var a = document.getElementsByTagName('a')[0];
var firstTime = 0;
var lastTime = 0;
var key = false;
var disX,disY;
a.onmousedown = function(e){
firstTime = new Date().getTime();
disX = e.pageX - parseInt(a.style.left);
disY = e.pageY - parseInt(a.style.top);
document.onmousemove = function(e){
var event = e || window.event;
a.style.left = e.pageX - disX + 'px';
a.style.top = e.pageY - disY + 'px';
}
}
document.onmouseup = function(){
lastTime = new Date().getTime();
if(lastTime - firstTime < 300){//点击事件时间小于0.3秒
key = true;
}
document.onmousemove = null;
}
document.onclick = function(){
if(key){//是拖拽的话就不能执行click
console.log('click~');
window.location.href = 'https://www.baidu.com';
key = false;//执行了click后key返回原状态
}
}
- 键盘事件
2.1keydown
keyup``keypress
2.2keydown > keypress > keyup
2.3keydown
&keypress
的区别
- keypress返回ASCII码,可以转换成相应字符
- keydown可以响应任意键盘按键,keypress只可以响应字符类键盘按钮并区分大小写
打印出按下的键盘:
document.onkeypress = function(e){
console.log('onkeypress');
console.log(String.fromCharCode(e.charCode));
}
- 文本操作事件
3.1input
change
focus
blur
<input
style = 'width:100px;height:20px;border:purple solid 1px;color:#999'
value = '请输入用户名'
onfocus = "if(this.value == '请输入用户名')this.value = '';this.style.color = '#424242'"
onblur = "if(this.value == '')this.value = '请输入用户名';this.style.color = '#999'">
- 窗体操作类(window上的时间)
3.1 scroll load
json
JSON是一种传输数据的格式(以对象为样板,本质就是对象,但用途有别,对象就是本地用的,json用于传输)
JSON.parse(); string --> json
JSON.stringify(); json – >string
异步加载js
- js加载的缺点:加载工具方法没有必要阻塞文档,过多js加载影响页面效率,一旦网速不好,那么整个网站将等待js加载而不进行后续渲染等工作
- 有些工具方法需要按需加载,用到在加载,不用不加载
- javascript 异步加载的三种方案
3.1 defer 异步加载——等到dom文档全部解析完才会被执行。只有IE能用,也可以将代码写到内部。
3.2 async 异步加载——加载完就执行,async只能加载外部脚本(不能把js写在script标签里)
- 执行时也不阻塞页面
3.3 创建script,插入到DOM中,加载完毕后callBack(异步加载+按需加载+最常用)
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = "todo.js"
//附加到documen内才能被执行
document.head.appendChild(script);
js加载时间线(创建–解析–加载)
- 创建document对象,开始解析web页面。这个阶段document。readyState = ‘loading’
- 遇到link外部css,创建线程加载,并继续解析文本
- 遇到lscript外部js,并且没有设置async、defer,浏览器加载,并阻塞,等待js加载完成并执行该脚本,并继续解析文本
- 遇到lscript外部js,并且有设置async、defer,浏览器创建线程加载,并继续解析文档。对于async属性的脚本,脚本加载完后立即执行(异步禁止使用document.write())
- 遇到img等,先正常解析dom结构,然后浏览器异步加载src,并继续
- 当文档解析完成,document.readyState = ‘interactive’
- 文档解析完成后,所有设置有defer的脚本会按照顺序执行。
- document对象触发DOMContentLoaded事件,这也标志着程序执行从同步脚本执行阶段,转化为事件驱动阶段
- 当所有async的脚本加载完成并执行后,img等加载完成后,document.readyState = 'complete’事件,windo对象触发load事件
正则表达式(RegExp)
课前补充:
- 转义字符"" (document.write()无法识别)
- 多行字符串
- reg.test();——boolean
- str.match();———字符数组
i | g | m |
---|---|---|
不区分大小写 | 全局匹配 | 多行匹配 |