JavaScript难点总结
预编译/作用域链/闭包脚本的预编译函数作用域 [[scope]]闭包
DOM/BOMNode节点事件冒泡BOM动画
构造函数/工厂方法构造对象的多种方法
原型/原型链原型原型链
面对对象面向对象的特征js的继承拷贝
正则表达式
预编译/作用域链/闭包
JavaScript引擎处理脚本的过程
预编译过程(第一次扫描)创建全局对象GO(Global Object/window)加载脚本文件预编译 找到所有的变量声明,按照变量名加入GO,如果以存在,忽略。 找到所有的函数声明,按照变量名加入GO,如果以存在,替换。 非声明不参与预编译解释执行(二次扫描)
脚本的预编译
没有var的变量,都不是变量声明,全部都认为是window的全局变量,不参与预编译
console
.log(a
);
a
= 5;
console
.log(a
);
即使a在函数中,a也是全局变量,是运行时生效,不是预编译时生效
console
.log(a
);
f();
function f(){
a
= 5;
}
console
.log(a
);
脚本中,所有的变量声明和函数声明,在脚本的预编译阶段完成,所有变量的声明与实际的书写位置无关
console
.log(a
);
var a
= 5;
console
.log(a
);
console
.log(f
);
function f(){
console
.log('here');
}
脚本中,如果变量与函数同名,函数可以覆盖变量,而变量不能覆盖函数
console
.log(f
);
function f(){
console
.log('here');
}
var f
= 123;
脚本中, 如果有多个函数同名,最后声明的函数将覆盖所有前面的同名函数声明并且,忽略参数个数,也就是说,JS不支持重载
console
.log(f
);
function f(a
){
console
.log('here1');
}
function f(a
,b
){
console
.log('here2');
}
函数调用
创建活动对象AO(Active Object)
预编译 scope chain 初始化arguments 初始化形参,将arguments中的值赋值给形参 找出所有的变量声明,按照变量名加入AO,如果已经存在,忽略。 找出所有的函数声明,按照函数名加入AO,如果已经存在,替换。 this初始化
解释执行
函数中,所有变量声明和函数声明,在函数的预编译阶段完成,所有变量的声明与实际的书写位
function f(){
console
.log(a
);
var a
= 5;
console
.log(a
);
console
.log(fin
);
function fin(){
console
.log('here');
}
}
f();
函数中,如果变量与函数同名,函数可以覆盖变量,而变量不能覆盖函数
function f(){
console
.log(fin
);
function fin(){
console
.log('here');
}
var fin
= 123;
}
f();
函数中, 如果有多个函数同名,最后声明的函数将覆盖所有前面的同名函数声明并且,忽略参数个数,(JS不支持重载)
function f(){
console
.log(fin
);
function fin(a
){
console
.log('here1');
}
function fin(a
,b
){
console
.log('here2');
}
}
f();
当函数预编译后,遇到需要访问的变量或者函数,优先考虑自己AO中定义的变量和函数如果找不到,才会在其定义的上一层AO中寻找,直至到GO,再找不到才报错
var a1
= 123;
function f(){
console
.log(a1
);
}
f();
function f1(){
console
.log(a
)
}
f1();
function f2(a
){
console
.log(a
)
}
f2();
function f3(a
){
console
.log(a
)
}
f3();
var a2
= 123;
function f4(){
function fin(){
console
.log(a2
);
}
fin();
}
f4();
function f5(a
){
function fin(){
console
.log(a
);
}
fin();
}
f5();
function f6(a
){
console
.log(a
)
var a
= 6
console
.log(a
)
}
f6(5);
函数作用域 [[scope]]
执行环境(execution context):定义了执行期间可以访问的变量和函数。
作用域链:是AO和GO构成的链所谓执行环境,就是根据作用域链依次查找变量和函数:找到即停;全部找完无果,报错
每个函数在定义(函数声明\函数表达式)时会拷贝其父亲函数的作用域链;在函数被调用时,生成AO然后将AO压入作用域链的栈顶。
全局-预编译 1.Global EC (Object) 2.初始化 scope chain (Array) 3.Global EC的scope chain拷贝Global Object的地址 4.GO或者AO初始化
函数执行预编译过程
var g
= 'g';
function fa(){
var a
= 'a';
function fb(){
var b
= 'b';
}
fb();
}
fa();
1~3 1.Global EC(Object)初始化 scope chain (Array) 2.Global EC的scope chain拷贝Global Object的地址 3.GO初始化this => window, g => undefined , fa => (function) 赋值g => ‘g’, 运行fa()
4~7 4.生成fa 的 EC 5.初始化 fa的 scope chain 6.fa的scope chain拷贝父函数的作用域链,也就是GO 7.生成 fa 的 AO,将其压入栈顶 ,fa AO初始化this => window ,a => undefined , fb => (function) 赋值a => ‘a’, 运行fb()
8~12 8.生成fb 的 EC 9.初始化 fb的 scope chain 10~11.fb的scope chain拷贝父函数的作用域链,也就是fa的scope chain[GO , fa AO] 12.生成 fb 的 AO,将其压入栈顶 ,fb AO初始化this => window ,b => undefined 赋值b => ‘b’ fb函数退出前,fb的AO引用次数1,fa的AO引用次数2,GO的引用次数3
13.fb函数退出,fb的EC被销毁,fb的scope chain释放,scope chain内的地址引用次数减一,fb的AO引用次数为0,fb的AO释放。 14.fa函数退出,fa的EC被销毁,fa的scope chain释放,scope chain内的地址引用次数减一,fa的AO引用次数为0,fa的AO释放。
闭包
闭包的定义:函数的AO通过scope chain相互连接起来,使得函数体内的变量都可以保存在函数的AO,这样的特性称为“闭包”。
闭包的危险:闭包会造成原有AO不释放,产生内存泄漏闭包的应用实现公有变量:1.缓存存储结构,2.封装,实现属性私有化,3.模块化开发,防止污染全局变量闭包的预编译及原理
function outer(){
var scope
= 'outer';
function inner(){
return scope
;
}
return inner
;
}
var fn
= outer();
fn()
预编译过程: 1.Global EC(Object)初始化 scope chain (Array) 2.Global EC的scope chain拷贝Global Object的地址 3.GO初始化this => window 变量函数初始化, outer => (function),fn => undefined fn => (function outer) 运行fn 4.生成outer的EC 5.初始化outer的scope chain 6.outer的scope chain拷贝父函数的作用域链,也就是GO 7.生成 outer的 AO,将其压入栈顶 ,outer AO初始化this => window, scope => undefined, inner => (function) return inner (实际上fn()= inner()) 8.inner声明处,拷贝了outer的scope chain。outer运行结束,outer的EC被销毁,因为inner 拷贝的scope chain继续指向outer的AO,所以outer的AO不释放。
9.Global EC(Object)初始化 scope chain (Array) 10.Global EC的scope chain拷贝Global Object的地址 11.GO初始化this => window
12.生成inner的EC 13.初始化inner的scope chain 14.inner的scope chain拷贝父函数的作用域链,也就是outer的scope chain[GO, outer AO] 15.生成 inner的 AO,将其压入栈顶 ,outer AO初始化this => window 16.return scope 17.inner函数退出,inner的EC被销毁 因为outer的AO被inner拷贝,outer AO的引用次数为1,所以outer AO始终无法释放。
闭包的使用公有变量
function createCounter(){
var count
= 0;
function counterAdd(){
count
++;
console
.log(count
);
return count
;
}
return counterAdd
;
}
var counter
= createCounter();
counter();
counter();
counter();
缓存存储结构, 多个变量,多个函数
function createCounter(){
var count
= 0;
function counterAdd(){
count
++;
console
.log(count
);
return count
;
}
function counterAddTwo(){
count
+= 2;
console
.log(count
);
return count
;
}
function clearAction(){
count
= 0;
console
.log(count
);
return count
;
}
return [counterAdd
, counterAddTwo
, clearAction
];
}
var counterAction
= createCounter();
counterAction
[0]();
counterAction
[1]();
counterAction
[2]();
counterAction
[0]();
模块化
function createCounter(x
){
var count
= 0;
var counter
= {
counterAdd
: function(){
count
++;
console
.log(x
, count
);
return count
;
},
counterAddTwo
: function(){
count
+= 2;
console
.log(x
, count
);
return count
;
},
clearAction
: function(){
count
= 0;
console
.log(x
, count
);
return count
;
}
};
return counter
;
}
var counter
= createCounter(1);
counter
.counterAdd();
counter
.counterAddTwo();
counter
.clearAction();
counter
.counterAdd();
var counter2
= createCounter(2);
counter2
.counterAdd();
DOM/BOM
类的使用
function addClassName(element
, className
){
var classNames
= element
.className
.split(' ');
for(var i
= 0; i
< classNames
.length
; i
++){
if(classNames
[i
] === className
){
break;
}
}
if(i
=== classNames
.length
){
if(element
.className
!== ''){
element
.className
+= ' ' + className
;
}else{
element
.className
= className
;
}
}
}
function removeClassName(element
, className
){
var classNames
= element
.className
.split(' ');
for(var i
= 0; i
< classNames
.length
; i
++){
if(classNames
[i
] === className
){
break;
}
}
if(i
!== classNames
.length
){
classNames
.splice(i
, 1);
element
.className
= classNames
.join(' ');
}
}
用JS实现类似CSS中hover一样的功能
onmuseover 鼠标悬停时触发(事件)onmuseout 鼠标离开后触发(事件)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<style type="text/css">
li {
list-style: none;
border: 1px solid #ccc;
background-color: #fff;
}
</style>
</head>
<body>
<ul>
<li>第零行
</li>
<li>第一行
</li>
<li>第二行
</li>
<li>第三行
</li>
<li>第四行
</li>
<li>第五行
</li>
</ul>
<script type="text/javascript">
var lis = document.querySelectorAll('li');
lis.forEach(function(element){
element.onmouseover = function(){
this.style.backgroundColor = '#ff0';
}
element.onmouseout = function(){
this.style.backgroundColor = "#fff"
}
});
</script>
</body>
</html>
提示文本框
onfocus获得焦点onblur失去焦点
var body
= document
.querySelector('body');
body
.innerHTML
= '<input type="text" value="请输入搜索内容" id="txt" />';
document
.querySelector('#txt').onfocus = function(){
if(this.value
=== '请输入搜索内容'){
this.value
= '';
this.style
.color
= '#000';
}
}
document
.querySelector('#txt').onblur = function(){
if(this.value
=== ''){
this.value
= '请输入搜索内容';
this.style
.color
= '#CCCCCC';
}
}
Node节点
DOM元素节点树
父(parent)子(child)和同胞(sibling)等术语用于描述这些关系。父节点拥有子节点。同级的子节点被称为同胞(兄弟或姐妹)。在节点树中,顶端节点被称为根(root)每个节点都有父节点、除了根(它没有父节点)一个节点可拥有任意数量的子节点同胞是拥有相同父节点的节点
nodeType/nodeName/nodeValue(任何node都支持)
遍历 DOM Tree
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<div id="div1">
<div class="cls" style="color: #f00">
<p>asdjfkladjfkljas
</p>
<div>kajdfkajd
</div>
</div>
ABCDE
<ul class="c123">
<li>asdfadf
</li>
<li>adsfa
</li>
</ul>
<p>asdflkajdfkjas
</p>
<h1>ajdfkadsf
</h1>
</div>
<script type="text/javascript">
var div = document.querySelector('#div1');
function showNode(node){
var type = '';
switch(node.nodeType){
case 1:
type = 'element';
break;
case 2:
type = 'attribute';
break;
case 3:
type = 'text';
break;
case 8:
type = 'comment';
break;
default:
type = 'unknown';
}
return ('' + node.nodeType + ' ' + type + ' ' + node.nodeName + ' ' + node.nodeValue);
}
function goThrough(node){
if(node.childNodes != undefined){
for(var i = 0; i < node.childNodes.length; i++){
var a = node.childNodes[i];
console.log(showNode(a));
if(a.attributes != undefined && a.attributes.length != 0){
for(var j = 0; j < a.attributes.length; j++){
var b = a.attributes[j];
console.log(showNode(b));
}
}
goThrough(a);
}
}
}
goThrough(div);
</script>
</body>
</html>
childNodes
返回节点的子节点集合(所有节点) children
返回节点的子节点集合(只返回元素节点) children 返回元素节点
function getChildren(element
){
var children
= [];
if(element
.nodeType
=== 1 && element
.childNodes
!= undefined
){
for(var i
= 0; i
< element
.childNodes
.length
; i
++){
var a
= element
.childNodes
[i
];
if(a
.nodeType
=== 1){
children
.push(a
);
}
}
}
return children
;
}
创建节点document.write()
如果在页面加载完后再次调用,会把页面上以前的所有元素清除。一般只用于页面加载时,页面加载完成后不能再使用。 innerHTML
定义innerHTML 属性设置或返回表格行的开始和结束标签之间的 HTML
var div
= document
.querySelector('#div');
div
.innerHTML
= '嘉树同学真帅'
createElement()
通过指定名称创建一个元素
var p
= document
.createElement('p');
p
.innerHTML
= '嘉树同学真帅';
document
.body
.appendChild(p
);
appendChild() 方法可向节点的子节点列表的末尾添加新的子节点。createAttribute()
创建一个指定名称的属性,并返回Attr对象属性。 创建元素及属性的方法方法一
var input
= document
.createElement('input');
var type
= document
.createAttribute('type');
type
.value
= 'button';
input
.setAttributeNode(type
);
var value
= document
.createAttribute('value');
value
.value
= '按钮';
input
.setAttributeNode(value
)
document
.body
.appendChild(input
);
方法二
var input
= document
.createElement('input');
input
.setAttribute('type', 'button');
input
.setAttribute('value', '按钮');
document
.body
.appendChild(input
);
方法三
var body
= document
.querySelector('body');
body
.innerHTML
= '<input type="button" value="按钮" />';
DOM删除
remove()删除
var input
= document
.createElement('input');
input
.setAttribute('type', 'button');
input
.setAttribute('value', '按钮');
document
.body
.appendChild(input
);
input
.remove();
removeChild()父亲清理门户
var body
= document
.querySelector('body');
var input
= document
.createElement('input');
input
.setAttribute('type', 'button');
input
.setAttribute('value', '按钮');
document
.body
.appendChild(input
);
body
.removeChild(input
)
DOM改动
replaceChild替换子元素
var div
= document
.createElement('div');
document
.body
.appendChild(div
);
var p
= document
.createElement('p');
p
.innerHTML
= '嘉树同学真帅';
var div1
= document
.querySelector('div');
div1
.appendChild(p
);
var p2
= document
.createElement('p');
p2
.innerText
= '嘉树同学特别帅!';
div1
.replaceChild(p2
, p
);
事件
事件绑定
1,onType: 兼容非常性好,如果调用两次,后面的回调会覆盖前面的回调。2,addEventListener(‘type’,function) 如果调用两次, 都有效,IE8及以下不兼容。function的this指向元素。3,attachEvent(‘onType’, function) Chrome不支持 ,只有IE的attachEvent, this是window,其他的都是被触发的元素如何让IE和Chrome同时兼容。
var div
= document
.createElement('div');
document
.body
.appendChild(div
);
div
.style
.width
= '100px';
div
.style
.height
= '100px';
div
.style
.backgroundColor
= '#FF0000'
var d
= document
.querySelector('div');
function divClick(){
console
.log('我被点击了!');
}
function addEvent(element
, type
, func
){
if(element
.addEventListener
){
element
.addEventListener(type
, func
);
}else if(element
.attachEvent
){
element
.attachEvent('on' + type
, function(){
func
.call(element
);
});
}else{
element
['on' + type
] = func
;
}
}
addEvent(d
, 'click', divClick
);
通过原型的方式实现
var div
= document
.createElement('div');
document
.body
.appendChild(div
);
div
.style
.width
= '100px';
div
.style
.height
= '100px';
div
.style
.backgroundColor
= '#FF0000'
var d
= document
.querySelector('div');
function divClick(){
console
.log('我被点击了!');
}
Element
.prototype
.addEvent = function(type
, func
){
var element
= this;
if(element
.addEventListener
){
element
.addEventListener(type
, func
);
}else if(element
.attachEvent
){
element
.attachEvent('on' + type
, function(){
func
.call(element
);
});
}else{
element
['on' + type
] = func
;
}
}
d
.addEvent('click', divClick
);
事件解绑
1,onType = none2,removeEventListener(‘type’, function) removeEventListener 只能删除 addEventListener加上去的 删除时,必须有函数;如果你不知道函数,JS途径没有办法查询,更无法删除3,detachEvent(‘onType’, function) Chrome不支持如何让IE和Chrome同时兼容
var div
= document
.createElement('div');
document
.body
.appendChild(div
);
div
.style
.width
= '100px';
div
.style
.height
= '100px';
div
.style
.backgroundColor
= '#FF0000'
var d
= document
.querySelector('div');
function divClick(){
console
.log('我被点击了!');
}
Element
.prototype
.addEvent = function(type
, func
){
var element
= this;
if(element
.addEventListener
){
element
.addEventListener(type
, func
);
}else if(element
.attachEvent
){
element
.attachEvent('on' + type
, function(){
func
.call(element
);
});
}else{
element
['on' + type
] = func
;
}
}
d
.addEvent('click', divClick
);
Element
.prototype
.detachEvent = function(type
, func
){
var element
= this;
if(element
.removeEventListener
){
element
.removeEventListener(type
, func
);
}else if(element
.detachEvent
){
element
.detachEvent('on' + type
, function(){
func
.call(element
);
});
}else{
element
['on' + type
] = func
;
}
}
d
.detachEvent('click', divClick
);
冒泡
addEventListener有三个参数,第三个参数是表明是否是捕获,如果第三个参数不写,那么事件触发的传递方式就是冒泡,即从底向上,如果是ture,即是捕获,即从上向下
捕获和冒泡的执行顺序: 1. 祖先节点,按照从高到低,触发捕获, 2. 节点本身,按照绑定顺序,触发冒泡+ 捕获,3. 祖先节点,按照从低到高,触发冒泡大部分浏览器不支持捕获,目前Chrome是支持捕获的
var body
= document
.querySelector('body');
body
.innerHTML
= '<div class="clsg" style="position: relative;width: 500px;height: 500px;background-color: #0f0;"><div class="clsf" style="position: absolute;width: 300px;height: 300px;background-color: #f00;left: 520px;top: 100px;"><div class="clss" style="position: absolute;width: 100px;height: 100px;background-color: #00f;left: 320px;top: 100px;"></div></div></div>';
var divg
= document
.querySelector('.clsg');
var divf
= document
.querySelector('.clsf');
var divs
= document
.querySelector('.clss');
divg
.addEventListener('click', function(){
console
.log('div grand father is clicked! 冒泡!');
}, false);
divf
.addEventListener('click', function(){
console
.log('div father is clicked!冒泡!');
}, false);
divg
.addEventListener('click', function(){
console
.log('div grand father is clicked! 捕获!');
}, true);
divf
.addEventListener('click', function(){
console
.log('div father is clicked!捕获!');
}, true);
divs
.addEventListener('click', function(){
console
.log('div son is clicked! 捕获AAAAAAA!');
}, true);
divs
.addEventListener('click', function(){
console
.log('div son is clicked! 冒泡AAAAAAA!');
}, false);
divs
.addEventListener('click', function(){
console
.log('div son is clicked! 捕获BBBBBBB!');
}, true);
divs
.addEventListener('click', function(){
console
.log('div son is clicked! 冒泡BBBBBBB!');
}, false);
取消冒泡
stopPropagation,IE8及以下不支持冒泡取消的方法, IE8 中是e.cancelBubble = true;如何用同时兼容IE8和其Chrome同时兼容
var body
= document
.querySelector('body');
body
.innerHTML
= '<div class="clsg" style="position: relative;width: 500px;height: 500px;background-color: #0f0;"><div class="clsf" style="position: absolute;width: 300px;height: 300px;background-color: #f00;left: 520px;top: 100px;"><div class="clss" style="position: absolute;width: 100px;height: 100px;background-color: #00f;left: 320px;top: 100px;"></div></div></div>';
var divg
= document
.querySelector('.clsg');
var divf
= document
.querySelector('.clsf');
var divs
= document
.querySelector('.clss');
Element
.prototype
.stopPropagation = function(e
){
e
= e
|| window
.event
;
if(e
.stopPropagation
){
e
.stopPropagation();
}else{
e
.cancelBubble
= true;
}
}
Element
.prototype
.preventDefault = function(e
){
e
= e
|| window
.event
;
if(e
.preventDefault
){
e
.preventDefault();
}else{
e
.returnValue
= false;
}
}
divg
.addEventListener('click', function(){
console
.log('div grand father is clicked! 冒泡!');
}, false);
divf
.addEventListener('click', function(){
console
.log('div father is clicked!冒泡!');
}, false);
divs
.addEventListener('click', function(e
){
console
.log('div son is clicked! 冒泡!');
this.stopPropagation(e
);
e
= e
|| window
.event
;
var target
= e
.target
|| e
.srcElement
;
}, false);
阻止默认事件行为
1, return false2.Chrome: e.preventDefault() IE8: e.returnValue = false; 事件的委托
触发事件的起源target;利用冒泡+事件的起源,避免在非常大数量的子元素上绑定事件,而只在少量父元素上绑定事件
var body
= document
.querySelector('body');
body
.innerHTML
= '<ul><li>没点过</li><li>没点过</li><li>没点过</li></ul>'
Element
.prototype
.addEvent = function(type
, func
){
var element
= this;
if(element
.addEventListener
){
element
.addEventListener(type
, func
);
}else if(element
.attachEvent
){
element
.attachEvent('on' + type
, function(){
func
.call(element
);
});
}else{
element
['on' + type
] = func
;
}
}
Element
.prototype
.stopPropagation = function(e
){
e
= e
|| window
.event
;
if(e
.stopPropagation
){
e
.stopPropagation();
}else{
e
.cancelBubble
= true;
}
}
Element
.prototype
.preventDefault = function(e
){
e
= e
|| window
.event
;
if(e
.preventDefault
){
e
.preventDefault();
}else{
e
.returnValue
= false;
}
}
Element
.prototype
.getTarget = function(e
){
e
= e
|| window
.event
;
return (e
.target
|| e
.srcElement
);
}
var ul
= document
.querySelector('ul');
ul
.addEvent('click', function(e
){
var target
= this.getTarget();
if(target
!== this){
target
.innerHTML
= '点过';
}
});
BOM
加载事件1,onload2,DOMContentLoadedsrc CSS外部文件,img, 他们的加载是异步的, 不会阻塞DOM的parse过程,,HTML文件下载完了,DOM parse完了,然后加载的CSS外部文件, window.onload,在css加载完成后触发,DOMContentLoaded在HTML文件下载完成,DOM parse后完成触发。3,location对象
Location 对象存储在 Window 对象的 Location 属性中,表示那个窗口中当前显示的文档的 Web 地址。它的 href 属性存放的是文档的完整 URL,其他属性则分别描述了 URL 的各个部分。4,history.back()History 对象包含用户(在浏览器窗口中)访问过的 URL。
H1
<input type="button" id="to2" value="去h2页面" />
<input type="button" id="forward" value="前进" />
<script type="text/javascript">
var btnTo2 = document.querySelector('#to2');
var btnForward = document.querySelector('#forward');
btnTo2.addEventListener('click', function(){
location.href = 'h2.html';
});
btnForward.addEventListener('click', function(){
history.forward();
});
</script>
H2
<input type="button" id="back" value="后退" />
<script type="text/javascript">
var btnBack = document.querySelector('#back');
btnBack.addEventListener('click', function(){
history.back();
});
</script>
navigator对象
Navigator 对象包含有关浏览器的信息。 Date对象setTimeout()
用于在指定的毫秒数后调用函数或计算表达式,只执行次会导致多个线程积压 setInterval()可按照指定的周期(以毫秒计)来调用函数或计算表达式,可以不停地调用函数
动画
使用Date匀速移动一个元素
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<style type="text/css">
* {
margin: 0;
padding: 0;
}
#div1 {
width: 200px;
height: 200px;
background-color: #f00;
position: absolute;
margin-top: 50px;
}
</style>
</head>
<body>
<input type="text" id="pos" value="0" />
<input type="button" id="btn" value="移动" />
<div id="div1"></div>
<script type="text/javascript">
var btn = document.querySelector('#btn');
var DURATION = 1000;
var INTERVAL = 20;
btn.addEventListener('click', function(){
var div = document.querySelector('#div1');
var pos = document.querySelector('#pos');
animation(div, parseInt(pos.value), DURATION, INTERVAL);
});
function animation(element, leftEnd, duration, interval){
var leftStart = element.offsetLeft;
var stepTmp = (leftEnd - leftStart)/(duration/interval);
var step = Math.floor(stepTmp);
if(step === 0){
step = stepTmp > 0 ? 1 : -1;
}
element.timer && clearInterval(element.timer);
element.timer = setInterval(function(){
if(Math.abs(leftEnd - element.offsetLeft) <= Math.abs(step)){
element.style.left = leftEnd + 'px';
clearInterval(element.timer);
}else{
element.style.left = (element.offsetLeft + step) + 'px';
}
}, interval);
}
</script>
</body>
</html>
轮播图
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<style type="text/css">
* {
margin: 0;
padding: 0;
}
#box{
width: 730px;
height: 454px;
padding: 5px;
border: 1px solid #CCCCCC;
margin: 100px auto;
}
#box>.inner_box{
width: 730px;
height: 454px;
overflow: hidden;
position: relative;
}
#box>.inner_box>ul {
width: 500%;
overflow: hidden;
position: absolute;
left: 0;
top: 0;
}
#box>.inner_box>ul>li{
float: left;
list-style: none;
}
#box>.inner_box>.page {
position: absolute;
bottom: 10px;
left: 10px;
}
#box>.inner_box>.page>span{
display: inline-block;
width: 16px;
height: 16px;
background-color: #FFFFFF;
text-align: center;
line-height: 16px;
cursor: pointer;
}
#box>.inner_box>.arrow>span {
position: absolute;
width: 40px;
height: 40px;
background-color: #000;
top: 50%;
margin-top: -20px;
opacity: 0.3;
border: 1px solid #fff;
text-align: center;
line-height: 40px;
font-weight: 700;
font-family: '黑体';
font-size: 30px;
color: #fff;
cursor: pointer;
display: none;
}
#box:hover>.inner_box>.arrow>span {
display: block;
}
#box>.inner_box>.arrow>.left {
left: 0;
}
#box>.inner_box>.arrow>.right {
right: 0;
}
#box>.inner_box>.page>span.active {
color: #fff;
background-color: #ff8000;
}
</style>
</head>
<body>
<div id="box">
<div class="inner_box">
<ul>
<li><a href="#"><img src="image/1.jpg" ></a></li>
<li><a href="#"><img src="image/2.jpg" ></a></li>
<li><a href="#"><img src="image/3.jpg" ></a></li>
<li><a href="#"><img src="image/4.jpg" ></a></li>
<li><a href="#"><img src="image/5.jpg" ></a></li>
</ul>
<div class="page" data-index="0">
<span class="active">1
</span>
<span>2
</span>
<span>3
</span>
<span>4
</span>
<span>5
</span>
</div>
<div class="arrow">
<span class="left"><</span>
<span class="right">></span>
</div>
</div>
</div>
<script type="text/javascript">
function animation(element, leftEnd, duration, interval){
var leftStart = element.offsetLeft;
var stepTmp = (leftEnd - leftStart)/(duration/interval);
var step = Math.floor(stepTmp);
if(step === 0){
step = stepTmp > 0 ? 1 : -1;
}
element.timer && clearInterval(element.timer);
element.timer = setInterval(function(){
if(Math.abs(leftEnd - element.offsetLeft) <= Math.abs(step)){
element.style.left = leftEnd + 'px';
clearInterval(element.timer);
}else{
element.style.left = (element.offsetLeft + step) + 'px';
}
}, interval);
}
var innerBox = document.querySelector('#box>.inner_box');
var width = innerBox.offsetWidth;
var DURATION = 2000;
var INTERVAL = 20;
var ul = document.querySelector('#box>.inner_box>ul');
var spans = document.querySelectorAll('#box>.inner_box>.page>span');
var spanDiv = document.querySelector('#box>.inner_box>.page');
function changeSpans(spanDiv, spans, oldX, x){
spans[oldX].setAttribute('class', '');
spans[x].setAttribute('class', 'active');
spanDiv.dataset['index'] = x;
}
for(var i = 0; i < spans.length; i++){
spans[i].addEventListener('click', function(){
var x = parseInt(this.innerHTML) - 1;
var oldX = parseInt(spanDiv.dataset['index']);
if(x !== oldX){
changeSpans(spanDiv, spans, oldX, x);
animation(ul, -width*x, DURATION * Math.abs(oldX - x), INTERVAL);
}
});
}
var leftSpan = document.querySelector('#box>.inner_box>.arrow>.left');
var rightSpan = document.querySelector('#box>.inner_box>.arrow>.right');
leftSpan.addEventListener('click', function(){
var oldX = parseInt(spanDiv.dataset['index']);
if(oldX > 0){
var x = oldX - 1;
changeSpans(spanDiv, spans, oldX, x);
animation(ul, -width*x, DURATION, INTERVAL);
}else{
var x = 4;
changeSpans(spanDiv, spans, oldX, x);
animation(ul, -width*x, DURATION, INTERVAL);
}
});
rightSpan.addEventListener('click', function(){
var oldX = parseInt(spanDiv.dataset['index']);
if(oldX < spans.length - 1){
var x = oldX + 1;
changeSpans(spanDiv, spans, oldX, x);
animation(ul, -width*x, DURATION, INTERVAL);
}else{
var x = 0;
changeSpans(spanDiv, spans, oldX, x);
animation(ul, -width*x, DURATION, INTERVAL);
}
});
var LARGERINTERVAL = 8000;
setTimeout(function(){
var oldX = parseInt(spanDiv.dataset['index']);
var x = 0;
var realDuration = DURATION;
if(oldX < spans.length - 1){
x = oldX + 1;
}else{
x = 0;
realDuration = ( spans.length-1 )*DURATION
}
changeSpans(spanDiv, spans, oldX, x);
animation(ul, -width*x, realDuration, INTERVAL);
setTimeout(arguments.callee, LARGERINTERVAL);
}, LARGERINTERVAL);
</script>
</body>
</html>
scrollLeft,scrollTop,scrollWidth, scrollHeight
scrollLeft: 返回元素左边缘与视图之间的距离scrollTop: 返回元素上边缘与视图之间的距离scrollWidth: 返回元素的整体宽度(width+padding+border+margin)scrollHeight: 返回元素的整体宽度(height+padding+border+margin) pageXOffset 和 pageYOffset 属性返回文档在窗口左上角水平和垂直方向滚动的像素。
function getScroll(){
return {
left
: window
.pageXOffset
|| document
.documentElement
.scrollLeft
|| document
.body
.scrollLeft
|| 0,
top
: window
.pageYOffset
|| document
.documentElement
.scrollTop
|| document
.body
.scrollTop
|| 0
};
}
window
.onscroll = function(){
console
.log(getScroll());
}
function getScroll(){
return {
left
: window
.pageXOffset
|| document
.documentElement
.scrollLeft
|| document
.body
.scrollLeft
|| 0,
top
: window
.pageYOffset
|| document
.documentElement
.scrollTop
|| document
.body
.scrollTop
|| 0
};
}
var fixedDiv
= document
.querySelector('id');
window
.onscroll = function(){
if(getScroll().top
>= fixedDiv
.previousElementSibling
.offsetHeight
){
fixedDiv
.className
= 'class class';
fixedDiv
.nextElementSibling
.style
.marginTop
= fixedDiv
.offsetHeight
+ 'px';
}else{
fixedDiv
.className
= 'class';
fixedDiv
.nextElementSibling
.style
.marginTop
= '0';
}
}
构造函数/工厂方法
构造对象的多种方法
工厂方法
function createPerson(name
,age
,saidWords
){
var person
= new Object;
person
.name
= name
;
person
.age
= age
;
person
.say = function(){
console
.log(saidWords
);
}
return person
;
}
直接构造
var person
= new Object;
person
.name
= name
;
person
.age
= age
;
person
.say = function(){
console
.log(saidWords
);
}
var person
= {
person
.name
= name,
person
.age
= age,
person
.say = function(){
console
.log(saidWords
);
}
};
构造函数
function Person(name
,age
,saidWords
){
this.name
= name
;
this.age
= age
;
this.say = function(){
console
.log(saidWords
);
}
}
var js
= new Person('js',19,'hello');
构造函数不建议return
如果构造函数返回原始类型变量,返回无效,函数会把this隐式返回;如果返回的是对象类型,隐式返回失效
原型/原型链
原型
构造对象中的问题构造100个对象,每个对象内容相同却占用100份内存
function Person(name
,age
,saidWords
){
this.name
= name
;
this.age
= age
;
this.say = function(){
console
.log(saidWords
);
}
}
var persons
= [];
for(var i
= 0; i
< 100; i
++){
persons
.push(new Person('js',19,'hello'));
}
解决 -》 原型
function Person(name
,age
,saidWords
){
this.name
= name
;
this.age
= age
;
}
Person
.prototype
.say = function(saidWords
){
console
.log(saidWords
);
}
每个函数,都有一个内置属性,叫做prototype(原型)prototype是对象如果一个对象是通过new产生的,那么这个对象将有缺省的属 ,__proto__这个属性指向函数的原型。
原型链
从Object开始,每一层底层的构造函数构造出实例,作为上一层构造函数的原型对象构造好后,通过对象的__proto__,可以一直追溯到Object\null,这个链表称为原型链
var object
= new Object();
object
.objectName
= 'object';
function Grand(){
this.grandA
= 'a';
}
Grand
.prototype
= object
;
object
.constructor
= object
;
var grand
= new Grand();
grand
.grandName
= 'grand';
function Father(){
this.fatherA
= 'a2';
}
Father
.prototype
= grand
;
grand
.constructor
= Father
;
var father
= new Father();
father
.fatherName
= 'father';
function Son(){
this.sonA
= 'a3';
}
Son
.prototype
= father
;
father
.constructor
= Son
;
var s
= new Son();
s
.sonName
= 'son';
所有挂在原型链对象上的属性和方法,能够被所有的派生实例共享
如果访问一个示例的属性或者方法,先从对象本身找起,如果发现没有,会沿着原型链由近至远寻找。如果找不到,返回undefined
如何判断传入对象的类型
instanceof
function Person(name
,age
,saidWords
){
this.name
= name
;
this.age
= age
;
this.say = function(){
console
.log(saidWords
);
}
}
var js
= new Person('js',19,'hello');
console
.log(js
instanceof Person);
console
.log(js
instanceof Object);
面对对象
面向对象的特征
封装
给对象的数据或者方法赋予标准的访问权限,没有权限不可以访问(js不支持)js对象所有的属性和方法都是公开的,但是可以使用立即执行函数和闭包把一部分隐藏起来
继承
(js支持)定义father和son两个构造函数,father能够被调用的地方,son也可以。son继承自father,father能干的事情,son也可以
多态
(js支持)A继承自B,如果A修改了B已经存在的属性或者方法,A的实例在任何地方按照名字调用修改后的属性和方法,可以成功调用(js支持)覆盖(js不支持)重载
面向对象思想接口处理
function Animal(gender
,weight
){
this.gender
= gender
;
this.weight
= weight
;
}
Animal
.prototype
.eat = function(){
console
.log('animal is eating');
}
Animal
.prototype
.sleep = function(){
console
.log('sleep,ZZzz');
}
function cat(gender
,weight
,name
){
Animal
.call(this,gender
,weight
);
this.name
= name
;
}
cat
.prototype
= new Animal();
cat
.prototype
.constructor
= cat
;
Object
.defineProperty(cat
.prototype
,'constructor',{
enumerable
:false
})
cat
.prototype
.eat = function(mouse
){
if(mouse
instanceof Mouse){
console
.log('cat is eating a mouse');
mouse
.f1();
mouse
.f2();
mouse
.f3();
}else{
console
.log('cat cannot eat an animal not mouse');
}
}
function Mouse(){
}
Mouse
.prototype
= new Animal();
Mouse
.prototype
.constructor
= Mouse
;
Mouse
.prototype
.f1 = function(){
console
.log('Mouse f1')
}
Mouse
.prototype
.f2 = function(){
console
.log('Mouse f2')
}
Mouse
.prototype
.f3 = function(){
console
.log('Mouse f3')
}
function Vole(gender
,weight
,name
){
Animal
.call(this,gender
,weight
);
this.name
= name
;
}
Vole
.prototype
= new Mouse();
Vole
.prototype
.constructor
= Vole
;
Vole
.prototype
.eat = function(){
console
.log('a vole is eating crop');
}
function Rat(){
}
Rat
.prototype
= new Mouse();
Rat
.prototype
.constructor
= Rat
;
Rat
.prototype
.eat = function(){
console
.log('a Rat is eating garbage');
}
var v
= new Vole();
var r
= new Rat();
var c
= new cat();
c
.eat(v
);
c
.eat(r
);
c
.eat(c
);
var c2
= new cat('female', 3, 'c2');
var v1
= new Vole('female',1,'v1');
var r1
= new Vole('male',1,'r1');
console
.log(c2
);
console
.log(v1
);
console
.log(r1
);
js的继承
借用构造函数继承
function Person(name
,age
,saidWords
){
this.name
= name
;
this.age
= age
;
this.say = function(){
console
.log(saidWords
);
}
}
function Person1(name
,age
,saidWords
){
Person
.call(this,name
,age
);
}
var js
= new Person('js',19,'hello');
var js1
= new Person1('js1',191,'hello1');
console
.log(js
);
console
.log(js1
);
组合继承(借用构造函数+原型链)原型链继承
语法:Object.defineProperty(obj, prop, descriptor)obj 要定义属性的对象 , 要定义或修改的属性的名称或 Symbol , descriptor 要定义或修改的属性描述符
Object
.defineProperty(XX.prototype
,'constructor',{
enumerable
:false
})
拷贝
直接拷贝
function F1(a
,b
){
this.a
= a
;
this.b
= b
;
}
F1.prototype
.f1 = function(){
console
.log(this.a
);
}
var f1
= new F1();
f1
.b
= 2;
var f2
= Object
.create(f1
.__proto__
);
for(var i
in f1
){
if(f1
.hasOwnProperty(i
)){
f2
[i
] = f1
[i
]
}
}
拷贝构造函数
function F1(a
, b
){
if(a
.__proto__
&& a
.__proto__
.constructor
.name
=== 'F1'){
for(var i
in a
){
if(a
.hasOwnProperty(i
)){
this[i
] = a
[i
];
}
}
}else{
this.a
= a
;
this.b
= b
;
}
}
var f1
= new F1([1,2], {a
: 3, b
: 4});
var f2
= new F1(f1
);
浅拷贝和深拷贝
浅拷贝:如果拷贝对象是数组或者对象,那么只拷贝引用深拷贝:如果拷贝对象是数组或者对象,那么完整拷贝数组和对象
function deepCopyObject(a
){
var b
= Object
.create(a
.__proto__
);
for(var i
in a
){
if(a
.hasOwnProperty(i
)){
if(!(a
[i
] instanceof Object)){
b
[i
] = a
[i
];
}else{
b
[i
] = deepCopyObject(a
[i
]);
if(a
[i
] instanceof Array){
b
[i
].length
= a
[i
].length
;
}
}
}
}
return b
;
}
var f1
= {
a
:[1,2],
b
:{a
:3,b
:4}
};
var f2
= deepCopyObject(f1
);
正则表达式
正则表达式(regular expression)描述了一种字符串匹配的模式(pattern),可以用来检查一个串是否含有某种子串、将匹配的子串替换或者从某个串中取出符合某个条件的子串等。正则表达式的语法及使用方法 : https://www.runoob.com/regexp/regexp-syntax.html