1.要有函数嵌套,且里层函数要操作外层函数被保护的变量。
2.返回里层函数
3.里层定义的函数要被调用,哪怕是返回一个匿名函数也行。
4.用一个变量接住,方便后续的使用。
注意:外层函数调用几次就会创建几个闭包。受保护的变量就有几个副本。同一次外层函数调用,返回的内层函数,都是在操作同一个受保护的变量
在三个特定事件里可以使用闭包达到节流防抖的作用:分别是
对象的三个特点:封装、继承、多态
1.继承:子对象能够直接使用父对象的属性和方法。
2.如何查找父对象、原型对象:保存子类对象共有属性和共有方法的对象。
对象名.__proto__;//必须先创建一个实例对象
构造函数名.prototype;//通过构造函数名的方式来查找
4.顶层对象Object的原型对象有toString( )方法,所以是个对象都有这个方法。
5.有了原型对象,就可以设置共有属性和方法了。
原型对象.属性名 = 属性值。
原型对象.方法名 = function(){}
继承过程中的笔试题:
1.判断属性或方法是自有还是共有的?
1.1判断自有:obj.hasOwnProperty( "属性名" );
1.2判断共有:if( obj.hasOwnProperty( "属性名" ) == false && ”属性名“ in obj ){//in关键字会查找整个原型链
共有
}else{
没有
}
2.修改&删除自有或共有
自有的修改:obj.属性名 = 新值;
删除:delete obj.属性名;
共有的修改:原型对象.属性名 = 新值; //千万不要直接在本地做操作,非常危险,添加上一个同名属性
删除:delete 原型对象.属性名; //如果对本地删除,没有任何效果的
注意:所以这个要用 delete 子对象.__proto__.父对象的属性 这种格式来删除。
3.老IE添加indexOf()方法。
4.判断一个数据类型是不是一个数组。
四种API:
x instanceof Array 其他对象也可以如法炮制 |
Array.isArray(x); 只能用于判断是不是数组 |
保护对象
1.四大特性-每一个属性或方法都有四大特性
如何设置四大特性:
Object.defineProperties(obj,{
"属性名":{
value: 实际保存值的地方,
writable: true/false,//开关:控制着这个属性是否可以被修改
enumerable: true/false,//开关:控制着这个属性是否可以被for in循环遍历到
configurable: true/false,//开关:控制着这个属性是否可以被删除
}
})
2.1防扩展:禁止给对象添加新属性
Object.preventExtensions( obj );
2.2密闭:禁止给对象添加新属性和删除属性
Object.seal( obj );
2.3冻结:禁止给对象添加新属性和删除属性和修改属性
Object.freeze( obj );
数组的新API:
用于判断的API:
every:每个元素都要满足,结果才为true,只要有一个不满足,结果则为false 类似逻辑判断&&,遇到返回的是false就不会再执行操作了;语法如下
arr.every(
function(val,i,arr){
return 判断条件;
}
)
some:所有的元素都不满足,结果才为false,只要有一个满足,结果就为true,类似于逻辑运算||,碰到true就不会再执行下去了。语法如下
arr.some(
function(val,i,arr){
return 判断条件;
}
)
遍历:将数组中的每一个元素取出来执行相同或相似的操作
forEach:遍历数组,直接修改原数组;语法如下:
arr.forEach(function(val,i,arr){
直接做操作
})
注意:forEach里面的val 和 arr[ i ]还是有差别的,后者不光保存着值,还记录了数据的位置信息。
var newArr=arr.map(function(val,i,arr){
return 操作;
})
过滤:筛选自己想要的,但是不会修改原来的数组;语法如下:
var newArr = arr.filter(function(val,i,arr){
return 操作;
})
汇总:把数组中的每个元素都汇总在一起格式如下:
var result=arr.reduce(function(prev,val,i,arr){
return prev+val;
},基础值)
Object.create( ):希望根据父对象创建子对象,继承自动设置完毕。语法如下:
var 子对象 = Object.create(
父对象,{
“自有属性”:{四大特性}
}
)
call()和apply( )方法的功能相同,但是传入的实参不同,call()可以传入多个实参,而apply只能传入一个数组。两者的语法如下:
两者立刻调用(借用),相当于执行了立刻执行函数,立刻执行。
函数名.call(借用的对象,实参,...); // 单独传入每一个实参
函数名.apply(借用的对象,[实参,...]); // 只能传入一个实参,是一个数组,但是其实apply也会悄悄的将数组打散
而bind则是永久替换了函数中的this
1.创建了一个和原函数功能完全相同的新函数
2.将新函数中的this永久的绑定固定伪类指定的对象,被人借不走
3.将新函数中的部分参数永久固定
var 新函数 = 原函数.bind(指定对象,永久固定参数,...) //不是立刻执行,不要自己调用
关于call()和apply()的固定套路:
Math.max().apply(Math,arr) //获取数组中的最大最小值的时候
Object.prototype.toString.call/apply(arr) === "[objdct Array]"
//类数组转换为普通数组
let result = Array.prototype.slice.call/apply(类数组)
箭头函数:简化回调函数
for(var v of arr){
v;//当前元素
}
1.不能修改原数组,只能返回新数组。
DOM
面试题:HTML/XHTML/DHTML/XML HTML - 网页 XHTML - 更严格的网页 DHTML - Dynamic:动态的网页,其实并不是新技术、新概念,只是将现有技术的整合统称,使我们网页在离线版也具有动态效果 DHTML:HTML+CSS+JS(dom)
DOM分为核心DOM、HTML DOM、XML DOM。
优缺点 | |
---|---|
核心DOM | 【无敌】,既可以操作HTML 和 XML 缺点:API比较繁琐 |
HTML DOM | 只能操作HTML,API简单,缺点:比如属性部分,只能操作标准属性,不能操作自定义属性 |
XML DOM |
开发建议:优先使用HTML DOM,HTML DOM实现不了的时候在用核心DOM进行补充
DOM树由浏览器的js解释器自动生成
每个DOM都有三大属性:nodeType、nodeValue、nodeName
1、xx.nodeType:描述节点的类型 document节点:9 元素节点:1 属性节点:2 文本节点:3
2、xx.nodeValue:描述节点值,说白了就是获取属性值
3、xx.nodeName:描述节点名称 - 判断xx是什么标签 - 后期搭配上事件委托(利用冒泡)
递归:自己调用自己,知道终止条件满足
function 函数名(root){ 1、第一层要做什么操作就直接做
2、判断他有没有下一级,如果有下一级再次调用此方法,但是传入的实参是下一级
}
递归 vs 循环 递归:优点:直观、易用 缺点:性能较低 循环:优点:性能高,几乎不占内存 缺点:难得一匹
遍历的API:TreeWalker:一个在DOM树上行走的人
缺点:1.会自动跳过根节点的操作 2.只能遍历层级不明确的DOM树,而不能遍历层级不明确的数据。
公式:
1.固定公式:
1.创建tw对象:var tw = document.createTreeWalker( 根元素,NodeFilter.SHOW / SHOW_ELEMENT )
2.反复调用nextNode方法找到每一个元素
while((node=tw.nextNode())!==null){
node要做什么操作
}
得到的是 | |
---|---|
没找到元素的children | 列表的长度为0 |
没找到元素或对象的属性 | undefined |
某个元素没找到儿子 |
1.创建元素,添加元素
document.createElement('p') ;
渲染页面:父元素.appendchild ;
父元素.insertBefore(新元素,已有子元素);//新元素会追加到父元素中当儿子,会插到已有子元素的前面
父元素.replaceChild(新,已有子元素);新元素会替换到父元素中当儿子,会替换已有子元素
2.用方法来添加
element.insertAdjacentHTML("position", "标签");
通过标签名获取元素
通过TagName来查询
element.getElementsByTagName()
注: 1.得到的是一个类数组元素
2.想要操作里面的元素记得添加下标(用类名查找的时候如果遇见多个标签同名,同理)
通过ID来查询
element.getElementById("id值")
注: 1、如果页面上有多个重复的id,只会返回第一个 2、此方法找到的是单个元素 - DOM节点是可以直接用于做操作的 3、此方法你不能使用 - 以后id留给后端使用,而且此方法一次也只能找到一个元素操作起来不方便
两种获取元素的方式有何区别:getXXX 、 querySelectorXXX有什么区别 ?
1.返回的结果不同:
1.getXXX:得到的是HTMLCollection==>是一个动态集合
2.queryXXX:得到的是一个nodeList ===>是一个静态集合
动态集合:
根据DOM树的改变,动态集合也会悄悄改变,每次修改DOM树,getXXX都会悄悄再次查找元素。缺点:每次都会悄悄重新查找,效率低下,不能使用forEach。
静态集合:
每次修改DOM树,静态集合都不会发生变化,只会认准你找的时候的第一次找到的元素,优点:每次不会悄悄重新查找,效率更高,支持forEach
----根据标签名获取元素中的指定的后代元素
element.childNodes
----根据标签名获取元素中的父亲元素
element.parentNode
----获取当前元素的所有子节点
element.children
----获取当前元素的所有子元素
element.firstChild
----获取第一个子节点
element.firstElementchild
----获取第一个子元素
element.lastChild
----获取最后一个子节点
element.lastElementchild
----获取最后一个子元素
element.previoussibling
----获取前一个兄弟节点
element.previousElementSibling
----获取前一个兄弟元素
element.nextSibling
----获取后一个兄弟节点
element.nextElementSibling
----获取后一个兄弟元素
如何读取元素的属性:
元素.属性名
ele.name
ele.value
ele.id
ele.className
如何设置元素属性
元素.属性名 = 属性值
ele.name = XXX
ele.value = XXX
ele.id = XXX
ele.className = XXX
读取一个标签内部的文本:
<span>哈哈哈</span>
span.innerHTML
span.innerText
span.firstChild.nodeValue
span.textContent
获取&设置属性
属性:什么是属性:HTML属性:id、class、title、alt、style、type、href...只要是放在HTML标签上的我们都称为属性
使用核心DOM获取属性:
1、获取属性值:elem.getAttribute("属性名"); - 多半用于判断操作
2、设置属性值:elem.setAttribute("属性名","属性值"); - 修改
使用HTML DOM获取属性:
1、获取属性值: elem.属性名 虽然简单,但是不能操作自定义属性。
2、设置属性值:elem.属性名 = 新值。
删除属性
核心DOM操作:elem.removeAttribute( "属性名" ) - 完整删除整个属性节点
HTML DOM:elem.属性名="" 删不干净,只能删掉属性值,属性节点依然存在,而有的属性,只需要写出属性名就已经有作用(比如:href、disabled、readonly)了
判断有没有某个属性
核心DOM的操作:elem.hasAttribute("属性名"); 结果是一个布尔值
设置内容
内容:innerHTML /innerText/value ;
注:主流浏览器也支持innerText
获取内容的API:textContent:获取 和 设置元素的文本,不能识别标签 - 有兼容性问题老IE不支持
value属性:专门为单标签(input)操作内容准备的
获取:input.value; - 多半用于判断操作
设置:input.value="新内容"; - 修改内容
操作样式
元素.style.样式名 = 样式值 注:如果样式名中间有横线链接,记得去掉横线采用驼峰命名法。
修改内联样式:
获取元素的属性:
getComputedStyle(元素).属性
// - 该函数用来获取元素的当前样式
// - 参数:
// 1.要获取样式的对象
// 2.要获取的样式伪元素
// - 返回值:
// 返回一个对象,对象中存储了当前元素的所有样式
在样式表中修改样式
//1、获取你想要操作的样式表 var sheet=document.styleSheets[i]; //2、获取所有样式规则 var rules=sheet.cssRules; //3、数出 你需要操作的某个样式规则 var rule=rules[i]; //4、对此规则做获取或者设置 console.log(rule.style.background); rule.style.background="purple";
注:client、offset、scroll三个系列获取值均不带单位
clientWidth,clientHeight用来获取盒子内部大小(包含内边距和内容)
offsetWidth,offsetHeight用来获取盒子可见框的大小(内容区,内边距,边框)
clientTop & clientLeft 返回元素上/左边框的大小
注:以上四个属性是只读属性,只能获取,不能修改。
offsetParent 获取当前元素的定位父元素
//定位父元素是离当前元素最近的开启了定位的祖先元素,如果所有祖先元素都没有开启定位,则返回body
offsetLeft 获取当前元素相对其定位元素(offsetParent )的左侧偏移量
offsetTop 获取当前元素相对其定位元素(offsetParent )的上侧偏移量
offsetWidth & offsetHeight 返回自身包含padding、边框、内容区域的宽/高度
scrollTop 垂直滚动条的滚动的 距离 (唯二可修改的)
scrollLeft 水平滚动的 距离 (唯二可修改的)
scrollHeight 设置可滚动区域的高度
scrollWidth 设置可滚动区域的宽度
注:当scrollHeight - scrollTop = clientHeight ; 时说明滚动条滑到底了
删除元素
删除节点
父节点.removeChild( ); 或 子节点.remove( );
HTML DOM常用对象:HTML DOM就是对核心DOM进行了简化:
1、Image对象:图片对象;
简化了创建方式:var img = new Image( );
2.Form对象:表单对象:
简化了查找元素:var form = document.forms[ i ];//获取页面上的第 i 个from元素;
简化了查找表单控件元素:var inp = form.elements[ i ]//获取此form表单中的第 i 个表单控件元素;
3.Select对象:
属性:
1.select.options === select.children 获取到select下面的所有option
2.select.selectedIndex; //获取到选中项的下标
方法:
1.select.add( option );//将option上树
2.select.remove( i ) ;//删除下标为 i 的option
专属事件:onchange 选中项发生改变后才触发
4.Option对象:
简化了创建方式:var opt = new Option( "innerHTML",”value“ ); ==>这个方法最好搭配select.selectedIndex使用
对象都有属性和方法,有三大特点,继承、封装、多态
封装对象的三种方法:
1.直接量方法
2.用构造函数的方式创建
格式:function 类名(name,age,salary){
this.name=name;
this.age=age;
this.salary=salary;
}
var obj=new 类名(实参,...);
js的底层万物皆对象,对象底层都是哈希数组。
倘若访问不存在的属性则返回一个哈希数组。
可以使用for...in来遍历对象的属性,而object[ i ]则可以访问 对象的属性值。
如果在对象中的方法中要用到对象的属性,则可以使用this来代替创建的对象,以此来引用对象的属性。