javascript第四周知识点

181 阅读14分钟

一、ES5:

1、保护对象:保护对象的属性和方法

1、四大特性可以保护对象:

1、对象的每一个属性都有4大特性,3个开关默认为true
value: 1001,//保存实际值的地方
\writable: true,//开关:控制属性是否可以被修改
enumerable: true,//开关:控制属性是否可以被for in循环遍历
\configurable: false//开关:控制属性是否可以被删除,总开关,一旦设置为false就不会允许修改其他特性,它本身一旦改为false不可逆\

2、修改对象的某个属性的四大特性:

Object.defineProperties(obj,{
    "属性名":{四大特性},
    ...
})

2、三个级别:

1、防扩展:防添加
Object.preventExtensions(obj);\

2、密封:防添加防删除
Object.seal(obj)\

3、冻结:防添加防删除防修改
Object.freeze(obj); \

个人不推荐:
1、如果以后你使用面向过程开发,没有对象,不用保护
前辈们都没有保护\

2、新的API:3组6个(6个API简化for循环)

1、数组中的元素是否满足我们的条件

1、every:判断数组中的元素是否满足我们的条件,类似于我们的&&,只要有一个为false则为false
2、some:判断数组中的元素是否满足条件的,类似于我们的||,只要有一个为true则为true
3、语法

arr.every/some(function(val,i,arr){
	//val:当前值
	//i:当前值的下标
	//arr:数组本身
	//虽然提供了3个形参,但是到底要用几个看我们自己
	//切忌:函数自带return undefined;
	return 判断条件;
})
                    案例:判断数组中的数字是否都是升序排列
			var arr1=[1,2,3,4,5];
			var arr2=[5634,4312,34,34,63,321,54,213];
			console.log(
				arr2.every(function(val,i,arr){
					if(i==arr.length-1){return true}
					return val<=arr[i+1]
				})
			);

2、遍历:把数组中的每个元素取出来执行相同 或 相似的操作

1、forEach:直接修改原数组

arr.forEach(function(val,i,arr){
	直接写操作;
})
                    案例:把每个元素*2,直接修改原数组
			var arr=[1,2,3,4,5];
			arr.forEach((val,i,arr)=>arr[i]=arr[i]*2)
			var newArr=arr.map(function(val){
				return val*2;
			})

2、map:不修改原数组,直接返回新数组

var newArr=arr.map(function(val,i,arr){
	return 直接写操作;
})

3、汇总和过滤:

1、过滤:筛选出符合条件的元素: - 不会修改原数组

var subArr=arr.filter(function(val,i,arr){
	return 判断条件;
})

2、汇总:将所有的数组的元素进行+-*/

var sum=arr.reduce(function(prev,val,i,arr){
	return prev+val;
},base)
//base指基础值

3、根据一个父对象创建一个子对象并且继承已经设置完毕,提前保护对象

var 子对象=Object.create(父对象,{
    "属性名":{四大特性},
	...
 }) 

4、call/apply/bind:替换了函数中的this

1、call/apply:临时替换了函数中的this - 借
差别:call:要求传入的实参必须单独传入
apply:要求传入的实参必须整理为一个数组,只能传一个数组参数
强调:call/apply相当于立刻调用函数
用法:方法名.call(借用的对象,实参,...)
方法名.apply(借用的对象,[实参,...]) - apply自动打散数组\

固定套路:
	a---- Object.prototype.toString.call/apply(数组)
	b---- Math.max/min.apply(Math,arr);
	c---- 关键点:将类数组转为普通数组
	      lis=Array.prototype.slice.call(lis);

2、bind:永久替换了函数中的this - 买
特点
1、创建了一个函数功能和原函数完全一样
2、将新函数的this永久绑定为了你指向的对象
3、将信函书中的部分固定参数提前永久绑定\

用法:var 新方法名=方法名.bind(永久绑定的对象,永久绑定的实参,...);
不会立刻执行,需要我们程序员手动调用
个人建议:如果你的这方法要经常反复使用,可以使用bind - 买
如果你需要一个方法立刻就要执行,建议call/apply - 借\

    案例:摇号功能
        <body>
		<ul id="list">
			<li>jerry</li>
			<li>tom</li>
			<li>jack</li>
			<li>mary</li>
			<li>rose</li>
			<li>andy</li>
			<li>jackson</li>
			<li>mike</li>
			<li>tony</li>
			<li>skyGood1</li>
			<li>skyGood2</li>
			<li>skyGood3</li>
			<li>skyGood4</li>
		</ul>
		<script type="text/javascript">
			var lis=document.getElementsByTagName("li");
			lis=Array.prototype.slice.call(lis);
			var nums=[];
			while(nums.length<lis.length){
				var r=parseInt(Math.random()*(lis.length)+1);
				if(nums.indexOf(r)==-1){
					nums.push(r);
				}
			}
			lis.forEach((val,i)=>val.setAttribute("dy",nums[i]))
			console.log(lis);
			lis.sort((a,b)=>a.getAttribute("dy")-b.getAttribute("dy"));
			lis.forEach(val=>list.appendChild(val))	;
		</script>
	</body>

5、严格模式:很严格

开启严格模式:"use strict"; - 可以出现在任何作用域的顶部
用处:
1、禁止给未声明的变量进行赋值
2、将静默失败升级为错误\

二、ES6:简化了/改变了语法:

1、模板字符串:支持直接在字符串中书写变量

`我的名字叫${name}`
再也不用""''
console.log(`单价为:${price},数量为:${count},总价为:${price*count}`);

2、块级作用域:

1、将var替换为let用于创建变量:优先使用let

2、作用:

1、let之前不允许出现未声明的同名变量- 解决声明提前
2、添加了块级作用域:一个{}就是一个块
3、绑定事件时,会记录着当前元素的下标-不需要自己定义自定义下标

3、箭头函数:简化一切的回调函数

回调函数:匿名函数,不是自调,就是回调
公式:去掉function,()和{}之间添加=>,如果形参只有一个省略(),如果函数体只有一句话省略{},如果函数体只有一句话并且是return,{}和return都省略
箭头函数中如果出现this->外部的对象

4、for...of循环

for(var v of arr){
    v;//当前值
}

缺点:
1、不能直接修改原数组,只能返回新数组
2、不能遍历hash数组,不能遍历对象(其实可以需要多加一句话)\

三、DOM

1、概念

DOM概念:Document Object Model
提供了专门用于操作HTML文档的API
DHTML:动态的HTML,一切实现网页动态效果的技术的统称,并不是什么新技术、新概念,仅仅只是一个统称
HTML+CSS+JS(DOM)\

2、面试/笔试题:HTML/XHTML/DHTML/XML分别是什么

1、HTML:超文本标记语言,网页
2、XHTML:可拓展超文本标记语言,更严格的网页
3、DHTML:动态的网页
4、XML:数据格式
5、DHTML模型:
a、DOM:见上面---W3C标准
b、BOM:Browser Object Model 提供专门用于操作浏览器的API
没有标准,但是大部分浏览器厂商已经统一的实现了,除了老IE,具有大量的兼容性问题,使用的较少(重点:定时器、event事件)

3、DOM:

本来是可以操作一切结构化文档(HTML/XML)的:3部分
1、核心DOM:万能!但是API比较繁琐:elem.setAttribute("属性名","属性值")
2、HTML DOM:只能操作HTML文档,API非常的简单:elem.属性名=值;
3、XML DOM:因为XML基本已经淘汰很多年了
建议:以后优先使用HTML DOM,HTML DOM实现不了,在用核心DOM补充

4、DOM树

DOM树:保存所有网页内容的树状结构:树根:document,不需要创建,由浏览器的js解释器自动创建,一个页面只有一个树根
DOM节点/对象/元素:一个标签、文本、属性、注释等等

1、每个DOM节点/对象/元素都有三大属性:

1、xx.nodeType:获取xx的节点类型
document:9
元素标签:1
文本节点:3
属性节点:2
对于现在的开发者,没用
2、属性节点.nodeValue:获取属性节点的节点值
现在依然没用:我们现在用getAttribute("")能直接获取到属性的值
3、xx.nodeName:获取属性节点的名称:
直接找子元素的话,可能找到多种标签,但是我们希望对不同的标签执行不同的操作,可用它
特殊:获取出来的标签都是全大写

2、通过节点之间关系 获取元素:

1、父:elem.parentNode;
2、子:elem.children; - 集合:只能找到儿子级
3、第一个儿子:elem.firstElementChild;
4、最后一个儿子:elem.lastElementChild;
5、前一个兄弟:elem.previousElementSibling;
6、后一个兄弟:elem.nextElementSibling;

3、递归:函数中,再一次调用函数自己,但迟早有一天要让他停下来

作用:专门用于遍历层级不明确的树状结构

实现递归
1、创建函数,传入实参树根,形参接住,直接做第一层要做的操作
	function f1(root){
		直接做第一层要做的操作
            //判断自己有没有下一级,如果有再次调用此方法,但传入的实参已经变成了你的下一级
}
2、调用函数
	f1(实际的根元素)

算法:深度优先!优先遍历当前节点的子节点,子节点遍历完,才会跳到兄弟节点
递归:优点:直观,易用
缺点:效率较低,同时开启的函数很多,占用内存空间,不是任何时候都要使用\

递归案例:
                <ul id="ul">
			<li>电影</li>
			<li>动漫
				<ul>
					<li>剑风传奇</li>
					<li>海绵宝宝</li>
					<li>哆啦A梦</li>
				</ul>
			</li>
			<li>剧集</li>
		</ul>
		<script type="text/javascript">
			//跳过了树根操作内部
			function getChildes(arr){
				for(var i=0;i<arr.length;i++){
					if(arr[i].nodeName=="UL"){
						arr[i].style.border="1px solid #000";	
					}else{
						arr[i].style.background="red";
					}			
					if(arr[i].children.length>0){//[]
						getChildes(arr[i].children)
					}
				}
			}
			getChildes(ul.children);
			
			//从树根开始就操作
			function getChildes(root){
				root.style.border="1px solid #000";
				for(var i=0;i<root.children.length;i++){
					getChildes(root.children[i]);
				}
			}
			getChildes(ul);
            </script>

4、遍历API:专门用于遍历层级不明确的树状结构

实现遍历API  21、创建treewalker对象
        var tw=document.createTreeWalker(root,NodeFilter.SHOW_ALL/SHOW_ELEMENT);
2、反复调用nextNode方法:
	while((node=tw.nextNode())!=null){
            node;//当前节点做什么操作
	}
遍历API案例:
        <body>
		<ul id="ul">
			<li>电影</li>
			<li>动漫
				<ul>
					<li>剑风传奇</li>
					<li>海绵宝宝</li>
					<li>哆啦A梦</li>
				</ul>
			</li>
			<li>剧集</li>
		</ul>
		<script type="text/javascript">
			var tw=document.createTreeWalker(ul,NodeFilter.SHOW_ELEMENT);	
			while((node=tw.nextNode())!=null){
				node.style.border="1px solid #000";
			}
		</script>
	</body>

深度优先算法
注意:此方法必须跳过起点

5、纯循环遍历层级不明确的树状结构

纯循环案例:
	<body>
		<ul id="ul">
			<li>电影</li>
			<li>动漫
				<ul>
					<li>剑风传奇</li>
					<li>海绵宝宝</li>
					<li>哆啦A梦</li>
				</ul>
			</li>
			<li>剧集
				<ul>
					<li>剑风传奇</li>
					<li>海绵宝宝</li>
					<li>哆啦A梦</li>
				</ul>
			</li>
		</ul>
		<script type="text/javascript">
		// 创建一个函数,形参为根元素
			function getChildes(root){
				// 保存住形参
				var org=root;//org=ul
				// 因为不知道到底要循环多少次,所以用while循环
				while(true){
					// 设置第一层元素的样式
					root.style.border="1px solid #000";
					// 判断有没有第一个儿子,有就将第一个儿子设置为root
					if(root.firstElementChild){
						root=root.firstElementChild
					}
					// 没有第一个儿子, 就将下一个兄弟设置为root
					else if(root.nextElementSibling){
						root=root.nextElementSibling
					}else{//没有儿子,也没有下一个兄弟
						// 此时的根元素的父元素没有下一个兄弟,并且这个根元素也不是最开始根元素的最后一个儿子
						while(!root.parentNode.nextElementSibling&&root!=org.lastElementChild){
							// 就将此时根元素的爸爸设置为根元素
							root=root.parentNode;
						}
						// 此时的根元素的父元素有下一个兄弟
						root=root.parentNode.nextElementSibling;
						// 如果此时的根元素是最开始根元素的最后一个儿子,就停止,但之前要先对他设置样式,因为如果不设置就退出的话,最后一个元素就没有样式
						if(root==org.lastElementChild){
							root.style.border="1px solid #000";
							break;
						}
					}
				}
			}
			getChildes(ul);
		</script>
	</body>

总结:以后不用遍历API(只能遍历页面元素),也不用纯循环(难度大),遇到层级不明确的时候,使用递归(不仅遍历元素,还能遍历数据)

6、查找元素:

        1、按照HTML的特点去查找元素:4var elem=document.getElementById("id");
		找到了是单个元素,没找到一定是null
		var elem=document.getElementsByTagName/ClassName/Name("标签/class名/name值")
		找到了是个集合,没找到一定是一个空集合[]
	 2、按照CSS选择器进行查找:21、单个元素,没找到一定是null,如果有多个,也只会找到第一个
			var elem=document.querySelector("任意css选择器")	
		2、多个元素:找到的是一个集合,没找到空集合
			var elem=document.querySelectorAll("任意css选择器")
          3、面试题:getXXX和querySelectXXX的区别?
		1、返回结果不同:
			getXXX - 返回是一个动态集合(每次DOM树修改,都会悄悄的再次查找)
			querySelectXXX - 返回是一个静态集合 (每次DOM树修改,不会再次查找,只管第一次找到的结果)
		2、动态集合,不支持forEach
		   静态集合,支持forEach
		3、复杂查找时,尽量使用var elem=document.querySelectorAll("任意css选择器")

7、操作元素

1、内容:

1、innerHTML:支持标签,并且没有兼容性问题
	获取:elem.innerHTML;
	设置:elem.innerHTML="新值";
   2、textContent:不支持标签,并且有兼容性问题,老IE:innerText,只不过小三上位(老IE不支持textContent,主流浏览器都支持innerText)
	获取:elem.innerText;
	设置:elem.innerText="新值";

   3、value:转为input、select准备的
	获取:elem.value;
	设置:elem.value="新值";

2、属性:

    获取属性值:
	核心DOM:万能的,可以操作一切属性
		elem.getAttribute("属性名");
	HTML DOM:只能操作标准属性,并且class需要写出className
		elem.属性名;
   设置属性值:
	核心DOM:万能的,可以操作一切属性
		elem.setAttribute("属性名","值");
	HTML DOM:只能操作标准属性,并且class需要写出className
		elem.属性名="新值";
   删除属性:
       核心DOM:elem.removeAttribute("属性名"); - 删干净整个属性节点
	HTML DOM:elem.属性名=""; - 赋值为空,删不干净,属性值确实没了,但属性名还在,有的属性只需要属性名也具有效果(href、disabled、readonly)
   判断属性:只能判断有没有
	核心DOM:elem.hasAttribute("属性名");
	个人更推荐:if(a1.getAttribute("属性名")=="属性值"){
			console.log("有并且是");
		    }

3、样式:

        内联:优先级最高,只会操作某个元素,不会牵一发动全身
	  获取:elem.style.css属性名;
	  设置:elem.style.css属性名="css属性值";
		css属性名如果有横线,去掉横线写为小驼峰命名法
        样式表:4//获取哪一个样式表
			var sheet=document.styleSheets[1];
			//获取所有的样式规则
			var rules=sheet.cssRules;
			//获取到了我想要操作的样式规则
			var rule=rules[36];
			//修改或获取样式
			console.log(rule.style.background);
			rule.style.background="purple";

一切的获取都是为了判断比较,切的设置都是在做修改操作

8、创建元素&渲染页面&删除元素:

1、创建元素:
1、var 空标签=document.createElement("标签名");

2、设置必要的属性或事件

    空标签.属性名="值";
    空标签.on事件名=function(){
    操作;
    }

3、渲染页面元素:3种
父元素.appendChild(新元素);//新元素会插入到父元素里面的末尾
父元素.insertBefore(新元素,已有子元素);//新元素会插入到父元素里面的已有子元素之前 - 不推荐:修改其他的人下标
父元素.replaceChild(新元素,已有子元素);//新元素会替换掉父元素里面的已有子元素

4、删除元素:
元素.remove();

9、HTML DOM提供了一些常用对象:简化了核心DOM的操作:但是不是人人都能简化

1、image:简化了创建
var img=new Image();
强调:在DOM中,不是人人都具有构造函数创建方式

2、form:简化了查找
查找form元素:var form=document.forms[i];
查找表单控件:var input=form.elements[i];
专属事件:onsubmit事件 -> 提交的一瞬间会执行,也可以阻止提交return false;

3、select:
属性:2个
1、options === children:获取到select下面的所有的option
2、selectedIndex - 获取到当前选中项的下标,只要是做联动,必不可少
方法:
1、select.add(option) - 完全等效于appendChild,追加元素
2、select.remove(i); - 删除select中的第i个option
专属事件:onchange-> 选中项发生改变时触发
4、option:简化了创建
var opt=new Option("innerHTML","value");

案例1:二级联动
<body>
	<select name="provs">
		<option>—请选择—</option>
		<option>北京市</option>
		<option>天津市</option>
		<option>河北省</option>
		<option>重庆省</option>
	</select>
	<select name="cities" class="hide">	
	</select>	
	<script>
		var cities=[//JSON:javascript object notation数据
			[],		/*0号下标没有元素*/
			[{"name":'东城区',"value":101},
			 {"name":'西城区',"value":102},
			 {"name":'海淀区',"value":103},
			 {"name":'朝阳区',"value":104},],
			[{"name":'河东区',"value":201},
			 {"name":'河西区',"value":202},
			 {"name":'南开区',"value":203}],
			[{"name":'石家庄市',"value":301},
			 {"name":'廊坊市',"value":302},
			 {"name":'保定市',"value":303},
			 {"name":'唐山市',"value":304},
			 {"name":'秦皇岛市',"value":305}],
			[{"name":'渝中区',"value":401},
			 {"name":'九龙坡区',"value":402},
			 {"name":'沙坪坝区',"value":403},
			 {"name":'渝北区',"value":404},
			 {"name":'江北区',"value":405}]
		];
		var sels=document.querySelectorAll("select");
		sels[0].onchange=function(){
			var i=this.selectedIndex;
			if(i>0){
				sels[1].className="";
				sels[1].innerHTML="";
				cities[i].forEach(obj=>{
					sels[1].add(new Option(obj.name,obj.value));
				})
			}else{
				sels[1].className="hide";
				sels[1].innerHTML="";
			}
		}	
	</script>
</body>
案例:根据数据渲染多级联动
	<div id="category">	
	</div>
	<script type="text/javascript">
		var categories=[
			{"id":10,"name":'男装',"children":[
				{"id":101,"name":'正装'},
				{"id":102,"name":'T恤'},
				{"id":103,"name":'裤衩'}
			]},
			{"id":20,"name":'女装',"children":[
				{"id":201,"name":'短裙'},
				{"id":202,"name":'连衣裙'},
				{"id":203,"name":'裤子',"children":[
					{"id":2031,"name":'长裤'},
					{"id":2031,"name":'九分裤'},
					{"id":2031,"name":'七分裤'}
				]},
			]},
			{"id":30,"name":'童装',"children":[
				{"id":301,"name":'帽子'},
				{"id":302,"name":'套装',"children":[
					{"id":3021,"name":"0-3岁"},
					{"id":3021,"name":"3-6岁","children":[
						{"id":2031,"name":'长裤'},
						{"id":2031,"name":'九分裤'},
						{"id":2031,"name":'七分裤',"children":[
							{"id":101,"name":'正装'},
							{"id":102,"name":'T恤'},
							{"id":103,"name":'裤衩'}
						]}
					]},
					{"id":3021,"name":"6-9岁"},
					{"id":3021,"name":"9-12岁"}
				]},
				{"id":303,"name":'手套'}
			]}
		];
		function f1(arr){
			console.log(arr);
			var sel=document.createElement("select");
			sel.add(new Option("请选择","-1000"));
			arr.forEach(obj=>{
				sel.add(new Option(obj.name,obj.id));
			})
			sel.onchange=function(){
				while(this.parentNode.lastElementChild!=this){
					this.parentNode.lastElementChild.remove();
				}
				var i=this.selectedIndex;
				if(i>0){
					if(arr[i-1].children){
						f1(arr[i-1].children)
					}
				}
			}
			category.appendChild(sel)
		}
		f1(categories)
	</script>
</body>
案例:根据数据生成一个表格
<body>
	<div id="data1">
	</div>
	<script type="text/javascript">
		var data=[
			{"id":1001,"name":'可口可乐',"price":2.5,"count":3000},
			{"id":1003,"name":'百事可乐',"price":2.5,"count":5000},
			{"id":1011,"name":'非常可乐',"price":2.3,"count":1000},
			{"id":1011,"name":'天府可乐',"price":2.3,"count":1000},
			{"id":1011,"name":'地府可乐',"price":2.3,"count":1000},
			{"id":1011,"name":'阳间可乐',"price":2.3,"count":1000},
			{"id":1011,"name":'阴间可乐',"price":2.3,"count":1000},
		];
		var table=document.createElement("table");
		var arr=["序号","名称","单价","数量","操作"];
		var tr=document.createElement("tr");
		arr.forEach(str=>{
			var td=document.createElement("td");
			td.innerHTML=str;
			tr.appendChild(td);
		})
		table.appendChild(tr);
		data.forEach(obj=>{
			var tr=document.createElement("tr");
			for(var i in obj){
				var td=document.createElement("td");
				td.innerHTML=obj[i];
				tr.appendChild(td);
			}
			var td=document.createElement("td");
			td.innerHTML="<button onclick='del(this)'>x</button>";
			tr.appendChild(td);
			table.appendChild(tr);
		})	
		data1.appendChild(table);
		function del(btn){
			var bool=confirm("确定要删除"+btn.parentNode.parentNode.firstElementChild.nextElementSibling.innerHTML+"吗?");//用户确认框
			if(bool){
				btn.parentNode.parentNode.remove();
			}
		}
	</script>
案例:<!--假设两个select元素,分别保存备选地区列表和选中地区列表
    实现两选择框之间选项的交换:
    包括:当个选中项左右移动
         多个选中项左右移动
         全左移和全右移
    要求:两个select中的地区都要按照名称首字母排序
-->
<body>
	<select id="unsel" size="5" multiple>
		<option>Argentina</option>
		<option>Brazil</option>
		<option>Canada</option>
		<option>Chile</option>
		<option>China</option>
		<option>Cuba</option>
		<option>Denmark</option>
		<option>Egypt</option>
		<option>France</option>
		<option>Greece</option>
		<option>Spain</option>
	</select>
	<div>
		<button id="btn1">&gt;&gt;</button>
		<button id="btn2">&gt;</button>
		<button id="btn3">&lt;</button>
		<button id="btn4">&lt;&lt;</button>
	</div>
	<select id="sel" size="5" multiple>
	</select>
	<script type="text/javascript">
		var larr=[],rarr=[],btns=document.querySelectorAll("button");
		//将页面的内容变为数据
		larr=unsel.innerHTML.trim().slice(8,-9).split(/<\/option>\s*<option>/);
		btns.forEach(val=>{
			val.onclick=function(){
				switch(this.innerText){
					case ">>":
					rarr=rarr.concat(larr);
					larr=[];
					break;
					case "<<":
					larr=larr.concat(rarr);
					rarr=[];
					break;
					case ">":
					for(var i=unsel.options.length-1;i>=0;i--){
						if(unsel.options[i].selected){
							rarr=rarr.concat(larr.splice(i,1))
						}
					}
					break;
					default:
					for(var i=sel.options.length-1;i>=0;i--){
						if(sel.options[i].selected){
							larr=larr.concat(rarr.splice(i,1))
						}
					}
				}
				larr.sort();
				rarr.sort();
				render();//渲染方法
			}
		})	
		function render(){
			if(larr.length>0){
				unsel.innerHTML="<option>"+larr.join("</option><option>")+"</option>"
			}else{
				unsel.innerHTML="";
			}
			
			if(rarr.length>0){
				sel.innerHTML="<option>"+rarr.join("</option><option>")+"</option>"
			}else{
				sel.innerHTML="";
			}
		}
	</script>
</body>
案例:根据数据渲染多级ul li
        <body id="bd">
		<script type="text/javascript">
			var json=[
				{"name":"王某某","children":[{"name":"王某1"},{"name":"王某2"}]},
				{"name":"张某某","children":[{"name":"张某1"},{"name":"张某2","children":[{"name":"张某2.1"}]},{"name":"张某3"}]},
				{"name":"刘某某","children":[{"name":"刘某1","children":[{"name":"刘某1.1"}]}]},
				{"name":"李某某","children":[{"name":"李某1","children":[{"name":"李某1.1","children":[{"name":"李某1.1.1","children":[{"name":"李某1.1.1.1"}]}]}]}]},
			];
			
			function createUl(arr,father){
				var ul=document.createElement("ul");
				arr.forEach(obj=>{
					var li=document.createElement("li");
					li.innerHTML=obj.name;
					ul.appendChild(li);
					if(obj.children){
						createUl(obj.children,li)
					}
				})
				father.appendChild(ul);
			}
			createUl(json,bd);
		</script>
	</body>

四:BOM

1、BOM:Browser Object Model

浏览器对象模型
提供了专门用于操作浏览器的API - 没有标准,使用的较少,但大部分浏览器厂商已经统一实现了(老IE)

2、BOM对象:

window(定时器)、history、*location、navigator、event、screen...

3、window对象:

1、扮演了2个角色:

1、浏览器中,window代替了ES的Global充当全局作用域--包含了所有的全局对象、变量、函数
2、指代当前浏览器的窗口

2、网页打开新链接的方式

作用:提升用户的体验感:
1、替换当前页面,可以后退:

HTML:<a href="url">文字</a>
JSopen("url","_self");

2、替换当前页面,禁止后退:使用场景:电商网页:结账完毕后,不允许用户后退 history对象:保存了当前窗口的历史记录,功能:前进后退
location:保存了当前窗口正在打开的url
location.replace("新url");//替换当前网址、不会产生历史记录

3、在新窗口打开,可以打开多个:

HTML:<a href="url" target="_blank">文字</a>
JSopen("url","_blank");

4、在新窗口打开,只能打开一个:使用场景:电商网页:跳转到 支付结账页面,只允许用户打开一个

HTML:<a href="url" target="自定义">文字</a>
JSopen("url","自定义");

自定义:窗口的底层都有一个名字,如果出现重复的名字则新窗口会替换掉原来的旧窗口

3、window对象:属性和方法:

属性:

获取浏览器窗口的大小:
1、文档显示区域的大小:body部分
innerWidth/innerHeight\

2、完整的浏览器大小:
outerWidth/outerHeight\

3、屏幕的大小:没用 - 桌面应用才用的
screen.width/height

方法:

1、打开新窗口:var newW=open("url","自定义name","width=,height=,left=,top=");
特殊:如果没传入第三个参数,新窗口会和浏览器并为一体
如果传入第三个参数,新窗口会脱离浏览器
建议宽高不小于200
有可能会被浏览器拦截

2、关闭窗口:窗口.close();

3、修改窗口的大小:新窗口.resizeTo(newW,newH);

4、修改窗口的位置:新窗口.moveTo(x,y);

5、定时器:2种
1、周期性定时器:先等待,在执行一次,在等待,在执行一次...
开启:timer=setInterval(callback,间隔毫秒数);
停止:clearInterval(timer);\

2、一次性定时器:先等待,在执行一次,结束
开启:timer=setTimeout(callback,间隔毫秒数);
停止:clearTimeout(timer);
注意:两种定时器底层相同,可以互换

案例:点不到的div
                <div id="d1">
			<button>x</button>
		</div>
		<script type="text/javascript">
			var rx=parseInt(Math.random()*((innerWidth-300)-0+1)+0);
			var ry=parseInt(Math.random()*((innerHeight-300)-0+1)+0);
			d1.style.left=rx+"px";
			d1.style.top=ry+"px";
			d1.onmouseover=function(e){
				var x=e.pageX;
				var y=e.pageY;
				while(true){
					var rx=parseInt(Math.random()*((innerWidth-300)-0+1)+0);
					var ry=parseInt(Math.random()*((innerHeight-300)-0+1)+0);
					if(!(rx<x&&x<rx+300&&ry<y&&y<ry+300)){
						d1.style.left=rx+"px";
						d1.style.top=ry+"px";
						break;
					}
				}
			}
		</script>
案例:倒计时
        <h1>距离放学还有:<span id="time"></span></h1>
		<button id="btn">|></button>
		<script type="text/javascript">
			// 定时器开启
			timer=setInterval(()=>{
				getTime();
			},500)
			// 页面一开始就要获得时间
			getTime();
			
			// 绑定事件
			btn.onclick=function(){
				if(this.innerText=="|>"){
					clearInterval(timer);
					this.innerHTML="||";
					// 放置停止时没有冒号
					time.innerHTML=time.innerHTML.replace(/\s/g,":")
				}else{
					timer=setInterval(()=>{
						getTime();
					},500)
					this.innerHTML="|>"
					
				}
			}	
			// 创建一个函数   日期对象的API都会修改原日期对象
			function getTime(){
				// var now=new Date();得到当前的时间
				var now=new Date();
				// 创建自定义时间
				var end=new Date("2021/12/17 21:00:00");
				//两个日期对象相减,得到毫秒差
				var ms=(end-now)/1000;//得到秒
				var h=parseInt(ms/3600);//得到小时
				h<10&&(h="0"+h);//短路操作
				var m=parseInt(ms%3600/60);//得到分
				m<10&&(m="0"+m);
				var s=parseInt(ms%60);//得到秒
				s<10&&(s="0"+s);
				// 将得到的时间设置到页面上
				time.innerHTML=time.innerHTML.indexOf(":")!=-1?h+" "+m+" "+s:h+":"+m+":"+s;
			}
		</script>
案例:浏览器右下角的广告框
                <div id="d1">
			<button id="btn">x</button>
		</div>
		<script type="text/javascript">
			btn.onclick=function(){
				d1.style.bottom="-100%";
				setTimeout(()=>{
					d1.style.bottom="0%";
				},Math.random()*10000);
			}
		</script>
案例:账户余额广告
<body>
    <div class="one">
        <span>账户余额:</span>
        <span class="df-integer">2.32</span>
        <span></span>
    </div>
    <div class="two">
        <span>余额宝:</span>
        <span class="df-integer">200</span>
        <span></span>
    </div>
    <script>
        // 设置定时器
        function time(){
            // 开启定时器
            timer=setInterval(() => {
                // 找到金额
                var moneys=document.querySelectorAll(".df-integer");
                moneys.forEach(money=>{
                    // 产生随机数
                    var num=parseInt(money.innerHTML)+ Math.random()*10000;
                    // 四舍五入,可自定义保留几位小数
                    money.innerHTML=num.toFixed(2);
                    // 超过10000000,定时器就关闭
                    if(money.innerHTML>10000000){
                        clearInterval(timer);
                    }
                })
            }, 100);
        }
        time()
    </script>
</body>

五:其他知识点

1、str.trim();//去掉字符串的开头结尾的空白字符
2、var bool=confirm("提示文字");//用户确认框
用户点击确定->true
用户点击取消->false\

3、扩展:a标签可以做的事?
a、跳转
b、锚点
c、下载:<a href="xx.rar/zip/exe">文字</a>
d、打开图片、txt:<a href="xx.图片后缀/txt">文字</a>
e、直接书写js:<a href="javascript:js代码;">文字</a>\

4、扩展:获取鼠标的坐标:
a、事件中传入一个形参e->获得到事件对象event(其中一个作用获取鼠标的坐标)
b、获取:
e.screenX/Y;//相对于屏幕
e.clientX/Y;//相对于浏览器客户端
e.pageX/Y;//相对于网页的

5、鼠标跟随效果:关键点:
a、绑定事件:window.onmousemove
b、图片位置一定和鼠标位置一起
c、图片的加载速度比js的执行速度要慢

图片的加载速度比js的执行速度要慢的解決方法:
img.onload=function(){
	//最后才会执行
}
案例:鼠标跟随效果
                <script type="text/javascript">
			window.onmousemove=function(e){
				var x=e.pageX;
				var y=e.pageY;
				var img=new Image();
				img.src="2.png";
				img.style.top=y+"px";
				img.style.left=x+"px";
				img.onload=function(){//加载事件:图片加载完毕后在执行的代码
					img.style.transform="rotateY(360deg)";//图片的加载速度比JS的执行速度慢	
					img.style.opacity="0";
				}
				bd.appendChild(img);
			}	
			setInterval(()=>{
				var imgs=document.querySelectorAll("img");
				for(var i=0;i<imgs.length-50;i++){
					imgs[i].remove();
				}
			},1000)
		</script>