主流浏览器的内核
IE:trident
Chrome:webkit/blink
firefox:Gecko
Opera:最初是presto,现在用的是blink
Safari:webkit
引入JS的方式
页面内嵌标签(可在head内或body内)
外部引入(常用方法)
JS基本语法
变量:
变量声明:声明,赋值分解。单一var。 (var a=100)
命名规则:变量名必须以英文字母,*,$开头*
变量名可以包括英文字母,,$,数字
不可以用系统的关键字,保留字作为变量名
值类型(数据类型):
不可改变的原始值(栈数据):Number,Boolean,String,undefined,null
栈内存和栈内存之间赋值是拷贝(互不影响)
引用值(堆数据):array,Object,function,..data,RegExp
typeof()可查看数据类型(返回number,string,boolean,object,undefined,function)
类型转换:显式类型转换:Number(mix) 转换不了的就显示为NaN
parseInt(string,radix) string以radix进制为基底转换为10进制
parseFloat(string)
num.toString(radix) num转为radix进制
String(mix)
Boolean()
隐式类型转换:isNaN() 先转为number,再与NaN比较
++/-- +/-(正负)转为数字类型
+ 转为字符串
- * / % 转为数字类型 '1'*1=1
&& || !
< > <= >= 字符串与数字比较,转为数字再比较。字符串与字符串比较,比较ascii码
== != 转为数字再比较
不发生类型转换:===,!==
语句的基本规则:
每一句后面要以分号结束(除函数,for语句,if语句...)
js语法错误(低级错误,逻辑错误)会引发后续代码终止,但不会影响其他js代码块
书写格式要规范,'=+/-'两边要有空格
运算操作符:
'+':数学运算,字符串连接。
任何数据类型加字符串都等于字符串
'-','*','/','%','=','()':'()'优先级最高,'='优先级最低
'++','--','+=','-=','/=','*=','%='
e.g. var a = 10 ; var b = ++a - 1 + a++ (此时b等于21,a等于12)
var a = 1 ; var b = a-- + --a (此时b等于0(先算--a,然后b=0+0=0),a等于-1)
比较运算符:'>','<','==','>=','<=','!='(比较结果为boolean值) NaN!=NaN
逻辑运算符:'&&'(碰到假就停),'||'(碰到真就停),'!'(运算结果为真实的值)
e.g. var a = 1 && 2+2 (a为4)
var a = 0 && 2+2 (a为0)
var a = 2+1 && 0 (a为0)
var a = 1 || 3 (a为1)
var a = 0 || 2 (a为2)
(window.foo || (window.foo='bar')) (window.foo的值为bar(先看括号的))
被认定为false的值:undefined,null,NaN,'',0,false
条件语句:if,if else if,switch case,break,continue
循环语句:for,while,do while
函数
定义:函数声明(function xxx(){})
函数表达式(命名函数表达式:var test=function xxx(){}, 匿名函数表达式(常用):var test=function (){})
组成形式:函数名称(小驼峰原则 如theFirstName)
参数:形参(获取形参长度:函数名.length),实参(arguments表示的是实参列表,获取实参长度:arguments.length)
返回值
e.g.1 求5的阶乘(递归)
function mul(n){
if(n==1 || n==0)
{
return 1
}
return n*mul(n-1);
}
mul(5);
作用域
作用域定义:变量和函数生效的区域(存储了运行期上下文的集合)
作用域的访问顺序:函数里面的可以访问外面的值(里面的可以访问外面的,外面的不能访问里面的)
函数被定义时,会在作用域链中生成一个GO对象
函数被执行时,会在作用域链中多生成一个AO对象,此时0号位置是AO,1号位置是GO
当多个函数为包含关系时,各函数作用域链的变化:
e.g. function a(){
function b(){
function c(){
}
c();
}
b();
}
a();
a defined a.[[scope]] --> 0:GO
a doing a.[[scope]] --> 0:aAO 1:GO
b defined b.[[scope]] --> 0:aAO 1:GO
b doing b.[[scope]] --> 0:bAO 1:aAO 2:GO
c defined c.[[scope]] --> 0:bAO 1:aAO 2:GO
c doing c.[[scope]] --> 0:cAO 1:bAO 2:aAO 3:GO
with会改变作用域链:with(访问的对象){执行的代码}
若访问的对象没有对应的变量,则在原本执行的这个函数体中找变量。
e.g.
var obj={name:'obj'};
function test(){
var age=123;
var name='scope';
with(obj){
console.log(name); //输出obj
console.log(age); //输出123
}
}
JS三部曲(预编译重点)
语法分析
预编译(发生在函数执行的前一刻)
解释执行
预编译:函数声明整体提升
变量声明提升
预编译前奏:imply global暗示全局变量:即任何变量,如果变量未经声明就赋值,此变量为全局对象(window)所有(var a=b=123(b为经声明就赋值,归window所有。所以window.a为undefined,window.b为123))
一切声明的全局变量,全是window的属性(var a=123 => window.a=123)
预编译过程四部曲:创建AO对象
找形参和变量声明,将变量和形参名作为AO属性名,值为undefined
将实参值和形参统一
在函数体里面找函数声明,值赋予函数体
e.g. function fn(a){ 1.创建AO对象(执行期上下文)
console.log(a); 2.找形参和变量声明
var a=123; AO{a:undefined,b:undefined}
console.log(a); 3.将实参和形参统一
function a(){}; AO{a:1,b:undefined}
console.log(a); 4. 找函数声明
var b=function(){} AO{a:function a(){},b:undefined,d:function d(){}}
console.log(b);
function d(){}
}
fn(1)
所以第一个console.log(a)打印的是function a(){}
此时执行var a=123,所以此时AO{a:123,b:undefined,d:function d(){}}
第二个console.log(a)打印的是123
此时不用再看function a(){}(因为预编译已经看过了)
第三个console.log(a)打印的是123
此时执行var b=function (){},所以此时AO{a:123,b:function (){},d:function d(){}}
第四个console.log(b)打印的是function (){}
若一个变量未经声明就赋值,会把此变量放到GO对象(window)中
e.g.1 console.log(test);
function test(test){
console.log(test);
var test=234;
console.log(test);
function test(){};
}
test(1);
var test=123;
先创建GO对象:GO{test:function (){...}}
再创建AO对象:AO{test:function (){}}
所以第一个console.log(test)为function(){}
第二个为function(){}
执行var test=234,所以AO{test:234}
第三个为234
e.g.2 global=100;
function fn(){
console.log(global);
global=200;
console.log(global);
var global=300;
}
fn();
var global;
先创建GO:{global:100}
再创建AO:{global:undefined}
此时第一个输出为undefined(AO中有的就先从AO中找)
再执行global=200;此时AO:{global:200}
所以第二个输出为200
e.g.3 function test(){
console.log(b);
if(a){
var b=100;
}
console.log(b);
c=234;
console.log(c);
}
var a;
test();
a=10;
console.log(c);
先创建GO:{a:undefined}
再创建AO:{b:undefined}
第一个输出为undefined
此时a为undefined,不能执行var b=100
所以第二个输出为undefined
执行c=234,AO里没有c,则把变量c添加到GO中,此时GO{a:undefined,c:234}
第三个输出为234
再执行a=10,此时GO{a:10,c:234}
第四个输出为234
e.g.4 var x=1;
if(function f(){}){
x+=typeof f;
}
console.log(x);
因为(function f(){})为表达式,所以f是undefined
所以x输出为1undefined
(未经声明的变量放在typeof中不会报错,为undefined)
关于GO和AO对象,若AO上没有相应变量,则再去GO上找
预编译时会忽略掉判断的语句
GO和AO对象实则是执行期上下文
立即执行函数
立即执行函数主要针对初始化功能的函数(执行完就被销毁)
(function (形参(可有可无)){}(实参(可有可无)))
只有表达式才能被执行符号执行
如:var test=function (){}();
+function test(){}();
(function test(){})();
闭包(重点)
闭包的生成:当内部函数被保存到外部的时候
闭包的防范:闭包会导致多个执行函数共用一个公有变量,如果不是特殊需要,应尽量防止这种情况发生
闭包的作用:实现公有变量(函数累加器)
可以做缓存(存储结构)
可以实现封装,属性私有化
模块化开发,防止污染全局变量
可用立即执行函数解决闭包
e.g. 给4个li绑定点击事件
var li=document.getElementByTagName('li');
for(var i=0;i<li.length;i++){
(function (j){
li[j].onclick=function(){
console.log(j)
}(i))
}
对象
属性的增,删,改,查:
增:对象名.属性名=属性值
删:delete 对象名.要删除的属性名
改:对象名.要修改的属性名=新的属性值
查:对象名.要查看的属性名
对象的创建方法:
1. 对象字面量/对象直接量 var obj={}
2. 构造函数(大驼峰命名规则:如TheFirstName):系统自带的构造函数Object():var obj=new Object()
自定义:function 函数名(){this.属性名=属性值...}
var 对象名= new 函数名()
3. var obj=Object.create(原型/null)(创建对象的同时可自定义原型)
改造函数的内部原理:1.在函数体最前面隐式的加上this={}(AO:{this:{xxx}})
2.执行this.xxx=xxx
3.隐式的返回this(构造函数中返回值不能返回原始值,会忽略掉,自动返回this)
包装类:new String()
new Boolean()
new Number()
e.g. var str='abc';
str+=1;
var test=typeof(str);
if(test.length==6){
test.sign='typeof的返回结果可能为String';
(原始值赋属性值要调用包装类,赋和没赋一样)
//new String(test).sign='xxx' 随后delete
}
//new String(test).sign(为undefined)
cosnole.log(test.sign);
输出的结果为undefined
原型
原型是function对象的一个属性,它定义了构造函数制造出来的对象的公共祖先
通过该构造函数产生的对象,可以继承该原型的属性和方法
原型也是对象
利用原型可以提取公有的属性
对象查看原型:隐式属性__proto__ (函数名.prototype)
对象查看对象的构造函数:constructor
判断是否为自身的属性:对象名.hasOwnProperty(属性值)
e.g. Person.prototype.name='sunny'
function Preson(){}
var person=new Preson();
Preson.prototype.name='cherry';
此时 person.name='cherry'
Person.prototype.name='sunny'
function Preson(){}
var person=new Preson();
Preson.prototype.name={name:'cherry'}
类似于
var obj={name:'a'};
var obj1=obj;
obj={name:'b'};(新开的空间)
所以此时 person.name还是等于'sunny'(此时的__proto__还是指向原来的Person.prototype)
原型链中绝大多数对象最终都会继承自Object.prototype
undefined和null没有原型
改变this指向:call,apply
call:原来的函数名.call(this要指向的函数名,参数)
apply:原来的函数名.apply(this要指向的函数名,[参数])
call和apply的区别:传参数的形式不同
call是要把实参按照形参的个数传进去
apply是传一个arguments
继承模式
1. 传统形式(原型链):过多的继承了没用的属性
2. 借用构造函数:不能继承借用构造函数的原型
每次构造函数都要多走一个函数
3. 共享原型:不能随便改动自己的原型
4. 圣杯模式
第一种写法:
function inherit(Target,Origin){
function F(){};
F.prototype=Origin.prototype;
Target.prototype=F.prototype;
Target.prototype.constructor=Target;
Target.prototype.uber=Origin.prototype;
}
第二种写法:
var inherit=(function (){
var F=function (){};
return function(Target,Origin){
F.prototype=Origin.prototype;
Target.prototype=F.prototype;
Target.prototype.constructor=Target;
Target.prototype.uber=Origin.prototype;
}
}())
命名空间
管理变量,防止污染全局,适用于模块化开发
可使用闭包的方法实现变量私有化,从而防止变量污染
var name='bcd';
var init=(function (){
var name='abc';
function callName(){
console.log(name);
}
return function(){
callName();
}
}())
init();
此时打印出来的name还是abc
还可使用Webpack等
JS实现链式调用模式(仿JQ)
如:obj.eat().smoke().drink()
在每个方法后面return this
对象枚举
数组遍历:for循环数组长度
对象遍历:for in循环
for(var xxx(自定义) in 对象名){console.log(对象名[xxx])}
区分数组,对象的三种方法
方法一:instanceof
判断A对象是不是B构造函数构造出来的(判断A对象的原型链上有没有B的原型):A instanceof B
数组:[] instanceof Array --> true
对象:{} instanceof Object --> true
方法二:constructor
对象:obj={},obj.constructor --> function Object()
数组:arr=[],arr.constructor --> function Array()
方法三:toString()
数组:Object.prototype.toString.call([]) --> [object Array]
对象:Object.prototype.toString.call([]) --> [object Object]
关于this
函数预编译过程 this -> window
全局作用域里 this -> window
call/apply可改变函数运行时的指向
obj.func(); func()里面的this指向obj(谁调用this指向谁)
e.g.1 var name='222';
var a={
name:'111',
say:function(){
console.log(this.name);
}
}
var fun=a.say;
fun(); //全局调用
a.say(); //a调用
var b={
name:'333',
say:function(fun){
fun();
}
}
b.say(a.say); //没有说明是this调用,只是把a.say这个方法传进来执行(相当于借用这个方法在b里面执行),此时还是全局调用
b.say=a.say;
b.say(); //此时为b调用
依次的输出结果为:222,111,222,333
e.g.2 var foo=123;
function print(){
this.foo=234;
console.log(foo);
}
print();
执行this.foo=234时,此时this指向window,改变GO中foo的值为234
所以输出结果为234
若把上题的print()改为new print():由于此时的this=Object.create(print.prototype),this不指向window了
所以此时输出结果为123
e.g.3 var a=5;
function test(){
a=0;
alert(a);
alert(this.a);
var a;
alert(a);
}
test(); 依次输出0 5 0
new test(); 依次输出0 undefined(this上面没有a) 0
e.g.4 var bar={a:'002'};
function print(){
bar.a='a';
Object.prototype.b='b';
return function inner(){
console.log(bar.a);
console.log(bar.b);
}
}
print()();(相当于先返回一个函数,再进行函数执行) 依次输出为a,b
arguments.callee:会指向自己的引用。引用该函数的函数体内的当前正在执行的函数。当函数名称未知时,例如在没有名称的函数表达式中(也称为“匿名函数”),此功能很有用。
e.g. var num=(function(n){
if(n==1){
return 1
}
return n * arguments.callee(n-1)
}(100))
func.caller:返回调用指定函数的函数
注意:arguments.callee和func.caller在严格模式下都不能使用
克隆
浅层克隆和深层克隆的区别:
浅层克隆:当克隆引用值时,两个对象会共用一个引用地址,造成相互的干扰,即我改,它也改
深层克隆:克隆出来的对象和原来的对象是相互独立的,互不影响。也就是对新对象的修改都不会反映到原对象中
浅层克隆:
function clone(origin,target){
var target=target || {};
for(var prop in origin){
target[prop]=origin[prop];
}
return target;
}
深层克隆:
实现步骤:
1. 判断是不是原始值 (typeof)
2. 判断是数组还是对象(instanceof,toString(推荐使用),constructor)
3. 建立相应的数组或对象
4. 递归
function deepClone(origin,target){
var target=target || {};
var toStr=Object.prototype.toString;
var arrStr="[object Array]";
for(var prop in origin){
if(origin.hasOwnProperty(prop)){ //要克隆自身的属性
if(origin[prop] !== "null" && typeof(origin[prop])=='object'){
if(toStr.call(origin[prop])==arrStr){ //克隆的属性是个数组
target[prop]=[]; //新建一个数组
}
else{ //克隆的属性是个对象
target[prop]={}; //新建一个对象
}
deepClone(origin[prop],target[prop])
}
else{ //原始值直接拷贝
target[prop]=origin[prop];
}
}
}
数组和类数组
创建数组的方法:
var arr=[];
var arr=new Array(1,2,3,4);
var arr=new Array(10);
new Array():当为一个参数n时表示新开一个长度为n的数组空间
当为多个参数时表示数组里的值
数组的读和写:
arr[n]:读,但不可以溢出读,要不然结果为undefined
arr[n]=xxx:写,可以溢出写
数组常用的方法:
会改变原数组的:push(在数组末尾添加数据)
pop(剪切数组末尾的数据)
shift(剪切数组前面的数据)
unshift(在数组前面添加数据)
sort(数据排序,默认升序):arr.sort(function(a,b){return x(若x为负数时,那么前面的数放在前面。若x为正数,那么后面的数放在前面。若x为0,则不动。可直接利用return a-b进行升序,return b-a进行降序)})
可利用sort生成一个乱序数组:xxx.sort(function(){ return Math.random()-0.5 })
reverse(数据反转倒序)
splice(切片):splice(从第几位开始,截取多少长度,在切口处添加新的数据)
不会改变原数组的:concat(数组拼接)
join(连接):join('x')按照'x'来连接数组里的数据
split(拆分):solit('x')按照'x'来拆分数组里的数据,拆分为字符串
toString(变成字符串)
slice(截取):slice(从该位开始截取,截取到该位)
类数组:
可以利用属性名模拟数组的特性
可以动态的增长length属性
如果强行让类数组调用push方法,则会根据length属性值的位置进行属性的扩充
属性要为索引(数字)属性,必须有length属性,最好加上push
e.g. var obj={
"2" : "a",
"3" : "b",
"length" : 2,
"push" : Array.prototype.push
}
obj.push('c');
obj.push('d');
输出结果obj
push的原理是:
Array.prototype.push=function(target){
this[this.length]=target;
this.length++;
}
所以obj.push('c'),在这个obj中相当于obj[obj.length(即为2)]='c',然后再length++
随后obj.push('d'),相当于obj[3]='d'
所以输出的obj为{
"2" : "c",
"3" : "d",
"length" : 2,
"push" : Array.prototype.push
}
实现数组的去重(在原型链上实现):
Array.prototype.unique=function(){
var temp={};
var arr=[]; //放置去重完后的数组
var len=this.length;
for(var i=0;i<len;i++){
if(!temp[this[i]]){ //若没有对应的属性值
temp[this[i]]='abc';
arr.push(this[i]);
}
}
return arr;
}
Try…catch
在try里面的发生错误,不会执行错误后的try里面的代码,但还会依然执行外面的代码
try{
}catch(e){
}
常见的错误信息(error.name的六种对应信息):
1. EvalError:eval()的使用与定义不一致
2. RangeError:数值越界
3. ReferenceError:非法或不能识别的引用数值
4. SyntaxError:发生语法解析错误
5. TypeError:操作数类型错误
6. URLError:URL处理函数使用不当
es5严格模式
"use strict"(要写在页面逻辑的最顶端)
不再兼容es3的一些不规则语法,使用全新的es5规范。
两种用法:全局严格模式
局部函数内严格模式(推荐)
"use strict"就是一行字符串,不会对不兼容严格模式的浏览器产生影响。
不支持with,arguments,callee,func,caller,变量赋值前必须声明,局部this必须被赋值(Person.call(null/undefined)赋值什么就是什么),拒绝重复属性和参数
DOM操作
DOM定义了表示和修改文档所需的方法。DOM对象即为宿主对象,由浏览器厂商定义。用来操作html和xml功能的一类对象的集合。
对节点的增删改查
查: (除了getElementsById匹配出来是一个,其他匹配出来都是类数组(要在末尾加上[n(0,1,2,...)]))
document.getElementById('id名') 根据元素id匹配相应的元素(在IE8以下的浏览器不区分id名大小写)
document.getElementsByTagName('标签名') 根据元素标签名匹配相应的元素
document.getElementsByName('name名') 根据元素的标签name匹配相应的元素
document.getElementsByClassName('类名') 根据元素标签名匹配相应的元素(在IE8和IE8以下的浏览器没有,可以多个class一起)
document.querySelector('CSS选择器字符串') 匹配指定 CSS 选择器的一个元素(在IE7和IE7以下的版本没有)不能实时更新
document.querySelectorAll('CSS选择器字符串') 匹配指定 CSS 选择器的一组元素(在IE7和IE7以下的版本没有)不能实时更新
增:
document.createElement():创建元素节点
document.createTextNode():创建文本节点
document.createComment():创建注释节点
document.createDocumentFragment():创建虚拟的节点对象(可在其中添加DOM节点以构建屏幕外DOM树)
插:
要添加到的区域.appendChild(要添加的节点):从后面添加一个子节点到页面(是一种剪切操作)
要添加到的区域.insertBefore(要添加的节点,要在此节点前添加的节点):从前面添加一个子节点到页面
封装函数insertAfter():
Element.prototype.insertAfter(targetNode,afterNode){
var beforeNode=afterNode.nextElementSibling;
if(beforeNode==null){ //若是最后一个节点,则直接在其后面插入
this.appendChild(targetNode);
}
else{ //在要插的下一个节点前插入(相当于插在要插节点的后面)
this.insertBefort(targetNode,beforeNode);
}
}
删:
父节点.removeChild():父节点移除子节点(剪切操作)
节点.remove():节点自己移除自己(删除操作)
替换:
父节点.replaceChild(new,origin):替换子节点
遍历节点树:
parentNode:父节点(最顶端的parentNode为#document)
childNodes:子节点们
firstChild:第一个子节点
lastChild:最后一个子节点
nextSibling:后一个兄弟节点
previousSibling:前一个兄弟节点
节点的类型:元素节点(1)
属性节点(2)
文本节点(3)
注释节点(8)
document(9)
DocumentFragment(11)
基于元素节点树的遍历:
parentElement:返回当前元素的父元素节点(IE不兼容)
children:只返回当前元素的元素子节点
node.childElementCount(相当于node.children.length):返回当前元素节点的子元素个数
firstElementChild:返回第一个元素节点(IE不兼容)
lastElementChild:返回最后一个元素节点(IE不兼容)
nextElementSibling:返回后一个兄弟元素节点
previousElementSibling:返回前一个兄弟元素节点
(IE不兼容都是指IE9和IE9以下浏览器不兼容)
节点的四个属性:
nodeName:元素的标签名,以大写形式表示,只读
nodeValue:Text节点或Comment节点的文本内容,可读写
nodeType:该节点的类型,只读
attribute:Element节点的属性集合
节点的一个方法:Node.hasChildNodes()(判断是否有子节点)
Element节点的一些属性:
innerHTML:添加HTML结构内容
innerText/textContent:添加文本结构内容(innerText 火狐不兼容,textContent IE老版本不好使)
Element节点的一些方法:
ele.setAttribute(属性名,属性值):设置节点属性
ele.getAttribute(属性名):获取节点属性
date(日期)对象
系统提供的
var date=new Date();
详细功能方法可参考:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date
定时器 , 延时器
设置定时器:
定时器名=setInterval(function(){
.....
},1000)
清除定时器:
clearInterval(定时器名)
设置延时器:
延时器名=setTimeout(function(){
.....
},1000) //时间间隔在window系统里面最小是15ms,在MAC系统里面最小是10ms(即使设置为0,也会按照最小的时间间隔执行)
清除延时器:
clearInterval(延时器名)
关于setTimeout和setInterval的最小值,详细可参考网址:https://www.cnblogs.com/daysme/p/6207495.html
获取窗口属性,dom尺寸
查看滚动条的滚动距离:
window.pageXOffset/pageYOffset(横向/纵向) (IE8/IE8以下不兼容)
document.body/documentElement.scrollLeft/scrollTop (兼容性混乱)
(使用的时候可两者相融合(相加),适应兼容性)
如:document.body.scrollLeft+document.documentElement.scrollLeft
封装兼容性方法:getScrollOffset(){
if(0 && 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
}
}
}
查看视口的尺寸:
window.innerWidth/innerHeight(查看窗口的宽高)(IE8/IE8以下不兼容)
document.documentElement.clientWidth/clientHeight(标准模式下任何浏览器兼容)
document.body.clientWidth/clientHeight(适用于怪异模式下的浏览器)
判断是否为怪异模式:document.compatMode:CSS1Compat(正常模式),backCompat(怪异模式)
封装兼容性方法:getViewportOffset(){
if(0 && window.innerWidth){
return {
w:window.innerWidth,
h:window.innerHeight
}
}
else{
if(document.compatMode==='BackCompat'){ //怪异模式下
return {
w:document.body.clientWidth,
h:document.body.clientHeight
}
}
else{
return {
w:document.documentElement.clientWidth,
h:document.documentElement.clientHeight,
}
}
}
}
查看元素的几何尺寸:domEle.getBoundingClientRect()
返回一个对象,包含left,top,right,bottom,width,height等属性(但width,height在IE老版本中没有显示)
返回结果不实时
查看元素的尺寸:dom.offsetWidth,dom.offsetHeight
查看元素的位置:dom.offsetLeft,dom.offsetTop
dom.offsetParent(返回最近的有定位的父级,若无则返回body。body.offsetParent返回null)
封装函数:getElementPosition(求相对于文档的坐标)
使滚动条滚动的三个方法:scroll(x,y):滚动到...
scrollTo(x,y):滚动到...
scrollBy(x,y):滚动了...(会累加)
脚本化CSS
读写元素css属性:
dom.style.prop
可读写行间样式,没有兼容性问题,碰到float这样的保留字属性,前面应加css(即float --> cssFloat)
复合属性尽量拆解
组合单词变成小驼峰式写法
查询计算样式:
window.getComputedStyle(ele,null)[prop]
计算样式只读
返回的计算样式的值都是绝对值,没有相对单位
IE8及IE8以下不兼容
null用在获取伪元素时,改变null(如:window.getComputedStyle(ele,'after').width)
查询样式(IE8及IE8以下可用,IE独有):ele.currentStyle.prop
计算样式只读
返回的计算样式的值不是经过转换的绝对值
封装兼容性方法:getStyle(ele,prop){
if(window.getComputedStyle){
return window.getComputedStyle(ele,null)[prop];
}
else{
return ele.currentStyle.prop;
}
}
事件
绑定事处理函数:
ele.onxxx=function(e){} 兼容性很好,但是一个元素的同一个时间上只能绑定一个处理程序 this指向元素本身
obj.addEventListener(type,fn,false){} IE9以下不兼容,可为一个事件绑定多个处理程序 this指向元素本身
obj.attachEvent('on'+type,fn){} IE独有,一个事件同样可以绑定多个处理程序 this指向window
封装兼容性的函数方法:addEvent(ele,type,handle){
if(ele.addEventListener){
ele.addEventListener(type,handle,false);
}
else if(ele.attachEvent){
ele.attachEvent('on'+type,function(){
handle.call(ele);
})
}
else{
ele['on'+type]=handle;
}
}
解除事件处理函数:
ele.onclick=false/null/''
ele.removeEventListener(type,fn,false)
ele.detachEvent('on'+type,fn)
事件处理模型:
事件冒泡:结构上(非视觉上)嵌套关系的元素,会存在事件冒泡的功能,即同一事件,自子元素冒泡向父元素(自底向上)
(focus,blur,change,submit,reset,selectd等事件不冒泡)
取消冒泡事件发生:e.stopPropagation()(不支持IE9以下的版本)
e.cancelBubble=true(IE独有)
事件捕获:结构上(非视觉上)嵌套的关系的元素,会存在事件捕获的功能,即同一事件,自父元素捕获至子元素(事件源元素) (自顶向下) IE没有捕获事件
把addEventlistener中第三个参数改为true可触发事件捕获
触发顺序:先捕获后冒泡
阻止默认事件(表单提交,a标签跳转,右键菜单等):
return false:以对象属性的方式注册的事件才生效
e.preventDefault() (IE9以下不兼容)
e.returnValue=false (兼容IE9)
事件对象:
event(非IE浏览器都有) || window.event(用于IE浏览器)
事件源对象:
event.target (火狐只有这个)
event.srcElement (IE只有这个)
上面两种chrome都有
事件委托:
利用事件冒泡和事件源对象进行处理
优点:性能不需要循环所有的元素一个个绑定事件
灵活,当有新的元素时不需要重新绑定事件
事件分类:
鼠标事件:click,mousedown,mousemove,mouseup,contextmenu,mouseover,mouseout,mouseenter,mouseleave
event中的button属性可区分鼠标按键(左键为0,右键为2)
键盘事件:keydown,keyup,keypress(keydown > keypress > keyup)
keydown和keypress的区别:keydown可响应任意键盘按键
keypress只响应字符类键盘按键,可返回ASCII码并转换成相应字符
文本操作事件:input(实时获取文本的值),focus,blur,change(失焦后才获取文本的值)
窗体操作类(window上的事件):scroll,load
JSON
一种传输数据的格式(以对象为样板,本质上是对象,但用途有区别,对象就是本地用的,json是用来传输的)
JSON中,属性名必须带双引号
JSON.parse():string -> json
JSON.stringfy():json -> string
异步加载js
js加载的缺点:加载工具方法没必要阻塞文档,使得js加载会影响页面效率,一旦网速不好,那么整个网站将等待js加载而不进行后续渲染等工作
js异步加载的三种方案:
1. defer异步加载,等到dom文档全部解析完才会被执行。只有IE能用,也可将代码写到内部。
2. async异步加载,加载完就执行,async只能加载外部脚本,不能把js写在script标签里
3. 创建script,插入到DOM中,加载完毕后callback
(其中第一和第二个方法执行时也不阻塞页面)
js加载的时间线
1. 创建Document对象,开始解析web页面。解析HTML元素和他们的文本内容后添加Element对象和Text节点到文档中。 (document.readyState='loading')
2. 遇到link外部css,创建线程加载,并继续解析文档
3. 遇到script外部js,并且没有设置async,defer,浏览器加载,并阻塞,等待js加载完成并执行该脚本,然后继续解析文档
4. 遇到script外部js,并且设置有async,defer,浏览器创建线程加载,并继续解析文档。对于async属性的脚本,脚本加载完成后立即执行。(异步禁止使用document.write())
5. 遇到img等,先正常解析dom结构,然后浏览器异步加载src,并继续解析文档
6. 当文档解析完成,document.readyState='interactive'
7. 文档解析完成后,所有设置有defer的脚本会按照顺序执行
8. document对象触发DOMContentLoaded事件,标志着程序执行从同步脚本执行阶段,转化为事件驱动阶段
9. 当所有async的脚本加载完成并执行后,img等加载完成后,document.readyState='complete',window对象触发load
正则表达式
转义字符:'\'
/^开头,结尾$/
正则表达式作用:匹配特殊字符或有特殊搭配原则的字符的最佳选择
两种创建方式:直接量(推荐):var reg=/.../(后面可加i(忽略大小写)/g(全局匹配)/m(多行匹配))
new RegExp():var reg=new RegExp('...','i/g/m')
正则表达式上的方法:reg.test(str)(检索字符串中指定的值。返回 true 或 false)
reg.exec(str)(检索字符串中指定的值。返回找到的值,并确定其位置)
...
支持正则表达式的string对象的方法:str.match(reg)(在字符串内检索指定的值,或找到一个或多个正则表达式的匹配)
str.search(reg)(检索与正则表达式相匹配的值。返回str中第一个与 reg相匹配的子串的起始位置,若没有找到任何匹配的子串,则返-1)
str.replace(reg/str,replacement)(用一些字符替换另一些字符,或替换一个与正则表达式匹配的子串)
...
正则表达式中一些元字符对应的表达式:\w===[0-9A-z_],\W===[^\w]
\d===[0-9],\D===[^\d]
\s===[\t\n\r\v\f],\S===[^\s]
\b===单词边界,\B===非单词边界
.===[^\r\n]
正则表达式的一些量词:n+(出现一次到多次)
n*(出现0次到多次)
n?(出现0到1次)
n{x}(出现x次)
n{x,y}(出现x到y次)
以上量词遵循贪婪匹配原则(尽可能匹配多的)
正向预查(从左到右进行匹配):?=xxx(匹配后面跟着xxx的东西)
?!xxx(匹配后面不跟着xxx的东西)
负向预查(从右到左进行匹配):?<=xxx(匹配紧跟xxx后面的东西)
?<!xxx(不匹配紧跟xxx后面的东西)
e.g.1 检验一个字符串首尾是否含有数字
var reg=/^\d|\d$/g;
var str='123abc';
reg.test(str) -> true
e.g.2 检验一个字符串首尾是否都含有数字
var reg=/^\d[\s\S]*\d$/g;
var str='123abc123';
reg.test(str) -> true
e.g.3 输出以下replace后的结果
var str='aa'; //var reg=/a/;
console.log(str.replace('a'/reg,'b'))
结果为ba(一般情况下只匹配到一个)
var reg=/a/g;
console.log(str.replace(reg,'b'))
结果为bb(全局匹配可匹配多个)
e.g.4 把aabb转成bbaa
var reg=/(\w)\1(\w)\2/g;
var str='aabb';
console.log(str.replace(reg,'$2$2$1$1'));
或:console.log(str.replace(reg,function($,$1,$2){
return $2+$2+$1+$1
}));
e.g.5 把一串数字用科学计数法表示(从右往左,每三个数打一个点)
var str='10000000000';
var reg=/(?=(\B)(\d{3})+$)/g;
str.replace(reg,'.');
详情可参考网址:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/RegExp
或https://www.w3school.com.cn/jsref/jsref_obj_regexp.asp
一些杂碎
一旦经历了var的操作,所得出的window上的属性,这种属性叫做不可配置的属性,delete不掉
undefined和null不与数字比较
typeof(arguments):object
document.documentElement等于html
在正则表达式中,加上?可打破贪婪匹配