JS学习第四周

140 阅读12分钟

Object

继承

父对象的成员(属性和方法),子对象可以直接使用
为什么使用:代码重用,节约内存空间,提升网站性能
何时使用:只要多个子对象公用的属性和【方法】,都要集中定义在父对象中

如何找到父对象(原型对象)

保存一类子对象共有属性和共有方法的父对象
1、对象名._proto_;//必须先有一个对象
2、构造函数名.prototype;//构造函数名:Array/Object/String/Number/Boolean/RegExp/Date...几乎人人都有,除了Math

两链一包

作用域链(查找变量)、原型链(查找属性和方法)、闭包(保护一个反复使用的局部变量)
每个对象都有一个属性:._proto_,可以一层一层的找到每个人的父亲,形成的一条链式结构,我们就称之为叫做原型链可以找到所有父对象的成员(属性和方法)
作用:查找找共有属性和共有方法
最顶层是Object的原型,上面放着我们很眼熟的toString方法,怪不得人人都可以使用toString
JS万物皆对象

有了原型对象,设置共有的属性和方法

原型对象.属性名=属性值
原型对象.方法名=function(){};

ES5

保护对象

保护对象的成员(属性和方法)

四大特性

每一个属性或方法都有四大特性
如何设置四大特性:
Object.defineProperties(obj,{
			"属性名":{
				value: 实际保存值的地方,
				writable: true/false,//开关:控制着这个属性是否可以被修改
				enumerable: true/false,//开关:控制着这个属性是否可以被for in循环遍历到
				configurable: true/false,//开关:控制着这个属性是否可以被删除
			}
		})

三个级别

1、防扩展:禁止给对象添加新属性
		Object.preventExtensions(obj);

2、密封:禁止给对象添加新属性和删除属性
		Object.seal(obj);

3、冻结:禁止给对象添加新属性和删除属性和修改属性
		Object.freeze(obj);

判断数组的API

1、判断:判断结果一定是一个布尔值
	every:每一个 - 要求每一个元素都要满足,结果才为true,只要有一个不满足,结果则为false - 类似&&:碰到false就不会再执行后续操作了
		语法:arr.every(function(val,i,arr){
				//val - 当前值
				//i - 当前下标
				//arr - 数组本身
				return 判断条件
		      })

	some:有一些 - 要求每一个元素都不满足,结果才为false,只要有一个满足,结果则为true - 类似于||:碰到true就不会再执行后续操作了
		语法:arr.some(function(val,i,arr){
				return 判断条件
		      })

2、遍历:将数组中每一个元素取出来执行 相同 或 相似的操作
	forEach:遍历数组,直接修改原数组
		语法:arr.forEach(function(val,i,arr){
				直接做操作
		      })

	map:遍历数组,不修改原数组,返回一个新数组
		语法:var newArr=arr.map(function(val,i,arr){
				return 操作;
		      })

3、汇总和过滤:
	过滤:筛选出自己想要的,但是不会修改原数组
		语法: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(父对象,{
		"自有属性":{四大特性},
		...
      })

严格模式

开启:在你的任何作用域的顶部都可以加上这句话:"use strict"
功能:1、禁止给未声明的变量赋值 - 解决了全局污染
2、静默失败升级为错误

call/apply/bind

call/apply:【临时替换函数中的this】 
	语法:函数名.call(借用的对象,实参,...); - 单独传入每一个实参
	      函数名.apply(借用的对象,[实参,...]); - 只能传入一个实参,是一个数组,但是其实apply也会悄悄的将数组打散
	强调:call/apply,相当于立刻调用函数,立刻执行

bind:【永久替换了函数中的this3件事:
	1、创建了一个和原函数功能完全相同的新函数
	2、将新函数中的this永久的绑定固定为了指定的对象,别人借不走
	3、将新函数中的部分参数永久固定
	语法:var 新函数=原函数.bind(指定对象,永久固定参数,...); - 不是立刻执行,需要自己调用
	强调:bind绑定的新函数是没有办法被call/apply借走的
            
固定套路:
  1Math.max/min.apply(Math,arr)
  2Object.prototype.toString.call/apply(arr)==="[object Array]"
  3、类数组转为普通数组:保存一下=Array.prototype.slice.call/apply(类数组)

ES6

1、模板字符串:可以再字符串中放入变量,不需要在做字符串的拼接了,简化了输入法的切换,${}还实现了一个简单的js环境
	语法:`我的名字叫${name}`

2、块级作用域:尽量以后创建变量【优先】使用let关键字
	let 变量名=值;
	作用:
	  1、禁止了声明提前
	  2、添加了块级作用域:一个{}就是一个块
	  3、记录着当前触发事件的元素的下标

3、箭头函数:简化回调函数
	公式:去掉function,在()和{}之间添加=>,如果形参只有一个,省略(),如果函数体只有一句话,省略{},如果函数体只有一句话并且是returnreturn和{}都省略
	特殊:千万不要将事件也简化为箭头函数 - this会失效 - 暂时

4for of循环 - 垃圾
	for(var v of arr){
		v;//当前元素
	}

	缺点:1、不能修改原数组,只能返回新数组
	      2、不支持hash数组,不支持对象

DOM

什么是DOM:Document Object Model(文档对象模型)
将每一个标签/元素/属性/文本/注释,看做了一个DOM节点/元素/对象(提供了操作这些东西的属性和方法)\原本是可以操作一切结构化文档的 HTML 和 XML,后来为了方便各类开发者分为了3个部分
1、核心DOM:可以操作HTML 和 XML
缺点:API比较繁琐

2、HTML DOM:只能操作HTML,API简单,缺点:比如属性部分,只能操作标准属性,不能操作自定义属性

3、XML DOM:只能操作XML,XML基本已经被淘汰了 - 现在最流行的数据格式json代替了

开发建议:优先使用HTML DOM,HTML DOM实现不了的时候在用核心DOM进行补充

DOM树

树根:document - 不需要我们创建,一个页面只有一个document:由浏览器的js解释器自动生成
可以通过数个你找到每一个DOM节点/元素/对象,提供了很多很多的API

三大属性

1、xx.nodeType:描述节点的类型
	document节点:9
	元素节点:1
	属性节点:2
	文本节点:3

	以前有用:判断xx是不是一个页面元素 - 因为以前我们找元素的方法和大家现在不一样

2、xx.nodeValue:描述节点值,说白了就是获取属性值
	以前有用:因为我们获取一个属性值没有现在这么容易

3、***xx.nodeName:描述节点名称 - 判断xx是什么标签 - 后期搭配上事件委托(利用冒泡)
	注意:返回的是一个全大写的标签名

通过关系获取元素

父元素:xx.parentNode;
子元素:xx.children; - 集合
第一个子元素:xx.firstElementChild;
最后一个子元素:xx.lastElementChild;
前一个兄弟元素:xx.previousElementSibling;
后一个兄弟元素:xx.nextElementSibling;

递归

简单来说就是函数中,又一次调用了函数自己,迟早有一天会停下来
何时使用:遍历DOM,专门用于【遍历层级不明确】的情况 - 不光可以遍历层级不明确的DOM树,还可以遍历层级不明确的数据
如何使用递归:2步
	function 函数名(root){
		1、第一层要做什么操作就直接做

		2、判断他有没有下一级,如果有下一级再次调用此方法,但是传入的实参是下一级		
	}

	函数名(实际的根节点)

	算法:深度优先!优先遍历当前节点的子节点,子节点遍历完毕才会调到兄弟节点
	缺点:同时开启大量的函数调用,消耗能存,只有一个情况采用:【遍历层级不明确】

遍历API

【遍历层级不明确】的情况:TreeWalker:一个在DOM树上行走的人
缺点:1、会自动跳过根节点的操作
2、只能遍历层级不明确的DOM树,而不能遍历层级不明确的数据

固定公式:
1、创建tw对象:
	var tw=document.createTreeWalker(根元素,NodeFilter.SHOW_ALL/SHOW_ELEMENT);

2、反复调用nextNode方法找到没一个元素
	while((node=tw.nextNode())!==null){
		node要做什么操作
	}

API直接查找元素

1、按照HTML的特点去查找元素
        1、id:var elem=document.getElementById("id值")
        2、class/标签名:var elems=document.getElementsByClassName/TagName/Name("class/标签名");
        建议:表单控件元素尽量可以不写class,因为必须要写name

2、按照CSS的选择器去查找元素
        1、单个元素:var elem=document.querySelector("任意css选择器");
	强调:万一选择器匹配到多个元素,只会返回第一个
	      没找到null

        2、多个元素:var elems=document.querySelectorAll("任意css选择器");
	强调:找到了集合,没找到空集合
	更适合做复杂查找

        面试题:getXXX 和 querySelectorXXX 有什么区别?
	返回结果不同:
		1、getXXX:HTMLCollection - 是一个动态集合
		2、queryXXX:nodeList - 是一个静态集合

动态集合:根据DOM树的改变,动态集合也会悄悄改变,每一次修改DOM树,getXXX都会悄悄再次查找元素
		    缺点:每次都会悄悄重新查找,效率较低,不能使用forEach

静态集合:每次修改DOM树,静态集合都不会发生变化,只会认准你找的时候的第一次找到的元素
		    优点:每次不会悄悄重新查找,效率较高,支持forEach

总结:找元素
1、直接找元素:getXXX、queryXXX
2、通过关系
3、层级不明确才用递归

操作元素

元素的内容

1、innerHTML:获取 和 设置元素的内容,并且识别标签 - 没有兼容性问题,老IE也支持
	获取:elem.innerHTML;	-   往往用于判断
	设置:elem.innerHTML="新内容"	-   添加/修改

2、textContent:获取 和 设置元素的文本,不能识别标签 - 有兼容性问题老IE不支持
	获取:elem.textContent;	-   往往用于判断
	设置:elem.textContent="新文本"	-   添加/修改
	解决:innerText - 小三上位了,此方法本来是属于老IE的,但是现在主流浏览器也支持

3、value:获取 和 设置input的内容
	获取:input.value;	-   往往用于判断
	设置:input.value="新文本"	-   添加/修改

元素的属性

1、获取属性值: - 往往用于判断
	核心DOMelem.getAttribute("属性名");	-   虽然API比较繁琐,但是无敌的

	HTML DOMelem.属性名	  -	虽然简单,但是不能操作自定义属性

2、设置属性值: - 添加/修改
	核心DOMelem.setAttribute("属性名","属性值");	-   虽然API比较繁琐,但是无敌的

	HTML DOMelem.属性名="属性值"	  -	虽然简单,但是不能操作自定义属性

3、删除属性值:
       *核心DOMelem.removeAttribute("属性名") - 完整删除整个属性节点

	HTML DOMelem.属性名=""	  -  删不干净,只能删掉属性值,属性节点依然存在,而有的属性,只需要写出属性名就已经
            有作用(比如:hrefdisabledreadonly)了

4、判断有没有 - 垃圾
	elem.hasAttribute("属性名"); - 结果是一个布尔值,但是没有用,只能判断有没有,不能判断具体属性值是什么
	真正推荐判断,你还是使用获取属性值

建议:以后优先使用HTML DOMHTML DOM实现不了在用核心DOM补充
	小心:1class要写为className
	      2、不能操作自定义属性

元素的样式

1、内联样式:
	优点:1、优先级高	2、不会牵一发动全身
	获取:elem.style.css属性名;
	设置:elem.style.css属性名="css属性值";

	特殊:1、css属性名,把横线去掉换成小驼峰命名法
	      2、获取也只能获取内联样式 - 小缺点

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";

创建元素

1、创建空标签:var elem=document.createElement("标签名");
	 比如:var a=document.createElement("a");

2、添加必要的属性 和 事件
	elem.属性名="属性值"
	elem.on事件名=function(){操作}

以上两步只是在js内存中创建出了元素,还需要渲染到DOM树上

渲染页面的方式

1、父元素.appendChild(新);//新元素会追加到父元素中当最后一个儿子
2、父元素.insertBefore(新,已有子元素);//新元素会追加到父元素中当儿子,会插到已有子元素的前面
3、父元素.replaceChild(新,已有子元素);//新元素会替换到父元素中当儿子,会替换已有子元素

删除元素

elem.remove();

HTML DOM常用对象:HTML DOM就是对核心DOM进行了简化:

1、Image对象:图片对象:
   简化了创建方式:var img=new Image();
   不是人人都能简化创建,只有个别可以

2、Form对象:表单对象:
   简化了查找元素:var form=document.forms[i];//获取页面上的第i个form元素
   简化了查找表单控件元素:var inp=form.elements[i]//获取此form表单中的第i个表单空间元素

3、Select对象:
	属性:1select.options === select.children 获取到select下面的所有option
	      2、*select.selectedIndex;//获取到选中项的下标


	方法:1select.add(option);//将option上树
	      2select.remove(i);//删除下标为i的option

    专属事件:onchange - 选中项发生改变后才会触发

4、Option对象:
   简化了创建方式:var opt=new Option("innerHTML","value");
   建议,如果以后希望创建出opt并且放到select中:一句话完成4个操作
	select.add(new Option("innerHTML","value"))