JS难点合集

    技术2026-01-02  1

    JavaScript难点总结

    预编译/作用域链/闭包脚本的预编译函数作用域 [[scope]]闭包 DOM/BOMNode节点事件冒泡BOM动画 构造函数/工厂方法构造对象的多种方法 原型/原型链原型原型链 面对对象面向对象的特征js的继承拷贝 正则表达式

    预编译/作用域链/闭包

    JavaScript引擎处理脚本的过程 预编译过程(第一次扫描)创建全局对象GO(Global Object/window)加载脚本文件预编译 找到所有的变量声明,按照变量名加入GO,如果以存在,忽略。 找到所有的函数声明,按照变量名加入GO,如果以存在,替换。 非声明不参与预编译解释执行(二次扫描)

    脚本的预编译

    没有var的变量,都不是变量声明,全部都认为是window的全局变量,不参与预编译 console.log(a);//报错,a is not defined a = 5; console.log(a);// 5 即使a在函数中,a也是全局变量,是运行时生效,不是预编译时生效 console.log(a);//报错,a is not defined f(); function f(){ a = 5; } console.log(a);// 5 脚本中,所有的变量声明和函数声明,在脚本的预编译阶段完成,所有变量的声明与实际的书写位置无关 console.log(a);//undefined var a = 5; console.log(a);// 5 console.log(f);// function f function f(){ console.log('here'); } 脚本中,如果变量与函数同名,函数可以覆盖变量,而变量不能覆盖函数 console.log(f);//function f function f(){ console.log('here'); } var f = 123; 脚本中, 如果有多个函数同名,最后声明的函数将覆盖所有前面的同名函数声明并且,忽略参数个数,也就是说,JS不支持重载 console.log(f);// function f(a,b) 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);//undefined var a = 5; console.log(a);//5 console.log(fin);//function fin function fin(){ console.log('here'); } } f(); 函数中,如果变量与函数同名,函数可以覆盖变量,而变量不能覆盖函数 function f(){ console.log(fin);//function fin function fin(){ console.log('here'); } var fin = 123; } f(); 函数中, 如果有多个函数同名,最后声明的函数将覆盖所有前面的同名函数声明并且,忽略参数个数,(JS不支持重载) function f(){ console.log(fin);// function f(a,b) 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);//123 } f(); function f1(){ console.log(a)//报错,a is not defined } f1(); function f2(a){ console.log(a)//undefined } f2(); function f3(a){ console.log(a)//undefined } f3(); var a2 = 123; function f4(){ function fin(){ console.log(a2);//123 } fin(); } f4(); function f5(a){ function fin(){ console.log(a);//undefined } fin(); } f5(); function f6(a){ console.log(a)//5 var a = 6 console.log(a)//6 } 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();//1 counter();//2 counter();//3 缓存存储结构, 多个变量,多个函数 //累加器 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]();//1 counterAction[1]();//3 counterAction[2]();//0 counterAction[0]();//1 模块化 //累加器 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();// 1 1 counter.counterAddTwo();// 1 3 counter.clearAction();// 1 0 counter.counterAdd();// 1 1 var counter2 = createCounter(2); counter2.counterAdd();//2 1

    DOM/BOM

    类的使用 //添加删除class function addClassName(element, className){ // 1. 判断是否已经存在 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){ // 1. 判断是否已经存在 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'; } //鼠标触碰时backgroundColor变成黄色 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> <!-- 12345 --> <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); //3 text #text //(空格) // 1 element DIV null // 2 attribute class cls // 2 attribute style color: #f00 // 3 text #text //(空格) // 1 element P null // 3 text #text asdjfkladjfkljas // 3 text #text //(空格) // 1 element DIV null // 3 text #text kajdfkajd // 3 text #text //(空格) // 3 text #text // (空格) + ABCDE //(空格) // 1 element UL null // 2 attribute class c123 // 3 text #text //(空格) // 1 element LI null // 3 text #text asdfadf // 3 text #text //(空格) // 1 element LI null // 3 text #text adsfa // 3 text #text //(空格) // 3 text #text //(空格) // 8 comment #comment 12345 // 3 text #text //(空格) // 1 element P null // 3 text #text asdflkajdfkjas // 3 text #text //(空格) // 1 element H1 null // 3 text #text ajdfkadsf // 3 text #text //(空格) </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); //执行顺序 //div grand father is clicked! 捕获! //div father is clicked!捕获! //div son is clicked! 捕获AAAAAAA! //div son is clicked! 冒泡AAAAAAA! //div son is clicked! 捕获BBBBBBB! //div son is clicked! 冒泡BBBBBBB! //div father is clicked!冒泡! //div grand father is clicked! 冒泡! 取消冒泡 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){ // 运动的对象 element // 起始点: element.offsetLeft // 终止点: leftEnd var leftStart = element.offsetLeft; // 时间的跨度: duration // 时间的间隔: interval // 计算step var stepTmp = (leftEnd - leftStart)/(duration/interval); var step = Math.floor(stepTmp); // step必须是整数,且不能等于零 if(step === 0){ step = stepTmp > 0 ? 1 : -1; } // 设定定时器 // timer在闭包中,需要有方法让第二次调用的事件中能够关闭前一次产生的timer element.timer && clearInterval(element.timer); element.timer = setInterval(function(){ // 定时器停止 // 考虑向左运动和向右运动 // 解决到leftEnd位置不精确的问题 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">&lt;</span> <span class="right">&gt;</span> </div> </div> </div> <script type="text/javascript"> function animation(element, leftEnd, duration, interval){ // 运动的对象 element // 起始点: element.offsetLeft // 终止点: leftEnd var leftStart = element.offsetLeft; // 时间的跨度: duration // 时间的间隔: interval // 计算step var stepTmp = (leftEnd - leftStart)/(duration/interval); var step = Math.floor(stepTmp); // step必须是整数,且不能等于零 if(step === 0){ step = stepTmp > 0 ? 1 : -1; } // 设定定时器 // timer在闭包中,需要有方法让第二次调用的事件中能够关闭前一次产生的timer element.timer && clearInterval(element.timer); element.timer = setInterval(function(){ // 定时器停止 // 考虑向左运动和向右运动 // 解决到leftEnd位置不精确的问题 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'); //试跑 //animation(ul,-width*3, DURATION*3, INTERVAL); //绑定事件 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); // } //}); //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); // } //}); 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()); } // 利用getScroll()更改class 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');//选择id window.onscroll = function(){ if(getScroll().top >= fixedDiv.previousElementSibling.offsetHeight){ fixedDiv.className = 'class 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);//true js是Person类型的对象 console.log(js instanceof Object);//true js是一个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){ //判断传入对象是什么类型 // console.log(mouse instanceof 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);//Person {name: "js", age: 19, say: ƒ} console.log(js1);//Person1 {name: "js1", age: 191, say: ƒ} 组合继承(借用构造函数+原型链)原型链继承 语法: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){ // 1. 拷贝 a 的原型 var b = Object.create(a.__proto__); // 2. 把a自有成员拷贝给b 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
    转载请注明原文地址:https://ipadbbs.8miu.com/read-61879.html
    最新回复(0)