JavaScript入门第四周复习

120 阅读8分钟

Object面向对象

继承

父对象的成员(属性和方法),子对象可以直接使用

        为什么:代码重用!节约内存空间!提升网站的性能!
        何时继承:只要多个子对象公用的属性和【方法】,都要集中定义在父对象之中

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

保存一类子对象共有属性和共有方法的父对象:2种

        1、子对象.__proto__;                 //前提:必须先有一个对象
	2、构造函数名.prototype;             //构造函数名,几乎人人都有,除了Math/Undefined/Null

面试题:两链一包

        作用域链:查找变量
        
	闭包:保护一个可以反复使用的局部变量的一种词法结构
            
	原型链:每个对象都有一个属性:__proto__,可以一层一层的找到每个人的父亲,形成的一条链式结构,就称之为叫做原型链,可以找到所有父对象的成员(属性和方法),作用:查找属性和【方法】
	最顶层是Object的原型,上面放着toString,所以都可以使用toString,JS中万物皆对象
            
            

设置共有属性和共有的方法(有原型对象)

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

笔试题1:判断自有和共有

         1、判断自有:
			obj.hasOwnProperty("属性名");
				如果结果为true,说明一定是自有,如果结果为false,可能是共有可能是没有
	 2、判断共有:
			if(obj.hasOwnProperty("属性名")==false&&"属性名" in obj){
                            //in 会自动在整条原型链上进行查找,找到结果为true,找不到结果为false
				说明是共有
			}else{
				说明是没有
			}

	 完整版公式:
			if(obj.hasOwnProperty("属性名")){
				自有
			}else{
				if("属性名" in obj){
					共有
				}else{
					没有
				}
			}
                            

修改/删除自有和共有

        自有:修改:obj.属性名=新值;
	      删除:delete obj.属性名;
		
	共有:修改:obj.__proto__.属性名=新值;
              删除:delete obj.__proto__.属性名;

	共有千万不要操作本地:
			修改:会在本地添加同名属性
			删除:没有效果

如何为老IE的数组添加indexOf方法:如何为一类人添加某个方法

        原理:if(Array.prototype.indexOf===undefined){//判断是不是老IE
			Array.prototype.indexOf=function(key,starti){
				starti===undefined&&(starti=0);
				for(var i=starti;i<this.length;i++){
					if(key==this[i]){
						return i;
					}
				}
				return -1;
			}
		}

判断x是不是一个数组

4种方式

        1、判断x是不是继承自Array.prototype
			Array.prototype.isPrototypeOf(x);

	2、判断x是不是由Array这个构造函数创建的
			x instanceof Array

	3、只有数组可用:ES5提供
                        Array.isArray(x);

	4、最麻烦:输出【对象的字符串】形式
                在Object的原型上保存着最原始的toString方法
                最原始的toString方法输出:[object 构造函数名]
                ***多态:子对象觉得父对象的成员不好用,在本地定义了同名成员,覆盖了父对象的成员
                这道题跳过数组的爸爸,拿到数组的爷爷上面的toString就能判断了
                固定套路:借用:Object.prototype.toString.apply(x);

实现自定义继承

        1、两个对象之间的继承:
			子对象.__proto__ = 父对象
		
	2、多个对象之间设置继承:
			构造函数名.prototype = 父对象
			注意时机:一定要在创建对象之前
                            

ES5

保护对象

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

        如何:
        1、四大特性:
           Object.defineProperties(obj,{
                "属性名":{
		 value:实际保存值,
		 writable: true/false,        //开关:控制着这个属性名是否可以被修改
		 enumerable: true/false,      //开关:控制着这个属性名是否可以被for in循环遍历到
		 configurable: true/false,    //开关:控制着这个属性名是否可以被删除 - 总开关,一旦设置为false,其他特性不允许再修改,一旦设置为false不可逆
                }
           })

        2、三个级别:
                1、防扩展:禁止给对象添加新属性
                        Object.preventExtensions(obj);
                2、密封:禁止给对象添加新属性和删除属性
                        Object.seal(obj);
                3、冻结:禁止给对象添加和删除和修改
                        Object.freeze(obj);

                其实保护对象没有多大用处:
		1、面向过程开发,无需保护
		2、别人基本不可能知道我们的对象名字
		3、前辈们都没有保护

数组的新API

判断

判断结果是一个布尔值

        every:每一个 - 要求每一个元素都要满足,结果才为true,只要有一个不满足就为false - 类似于&&
		语法:var bool=arr.every(function(val,i,arr){
				return 判断条件;
		      })

	some:有一些 - 要求每一个元素都不满足,结果才为false,只要有一个满足就为true - 类似||
		语法:var bool=arr.some(function(val,i,arr){
				return 判断条件;
		      })
val - 当前值
i - 当前下标
arr - 数组本身
前辈们提供了3个形参,需要哪个用哪个

遍历

将数组中的每个元素取出来执行相同或相似的操作

        forEach:遍历数组,直接修改原数组
		语法:arr.forEach(function(val,i,arr){
				直接做操作
		      })

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

过滤和汇总

        过滤:筛选出自己想要的,但是不会修改原数组
		语法:var subArr=arr.filter(function(val,i,arr){
				return 判断条件;
		      })

	汇总:把数组中的每个元素汇总到一起
		语法:var sum=arr.reduce(function(prev,val,i,arr){
				return prev+val;
		      },基础值);      //基础值会和最后的结果加在一起

以上6个API的底层都是for循环,目的 - 简化for循环

Object.create()

根据父对象创建子对象,继承已经设置好了

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

严格模式

面试题

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

call/apply/bind

不是自己的方法也可以使用 - 笔试面试也很容易出现

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

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

ES6

简化ECMAScript - 语法较大的变化

模板字符串

可以在字符串中放入变量 - 不需要再做字符串拼接,在字符串中实现了一个简单的js的环境

        语法:`我的名字叫${name}`

块级作用域

尽量【优先】使用let创建变量

         let 变量名=值;
         作用:1、禁止声明提前
               2、添加了块级作用域,一个{}就是一个块
               3、记录着当前触发事件的元素的下标

箭头函数

简化回调函数

        公式:去掉function,()和{}之间添加=>,形参如果只有一个,则省略掉()
              函数体如果只有一句话,省略{},
              函数体只有一句话并且是returnreturn和{}都省略

for...of循环

垃圾

        for(var v of 数组名){
                v;  //value当前值
        }

         缺点:1、不能修改原数组,只能返回新数组
               2、不能遍历hash数组,也不能遍历对象,只能遍历索引数组
               

DOM

        Document Object Model(文档对象模型) - 操作HTML文档
        将每个元素/标签/文本/属性/注释都会看作是一个DOM节点/元素/对象

面试题:HTML/XHTML/DHTML/XML?

        1HTML - 网页
        2XHTML - 更严格的网页
        3DHTML - Dynamic:动态的网页,是现有技术的一个整合统称:让网页在离线版也能具有动态效果
           DHTML:HTML+CSS+JS
        4XML - 全部自定义,数据格式:淘汰了

DOM:原本是可以操作一切结构化文档的 HTML 和 XML,后来为了方便各类开发者分为了3个方向

        1、核心DOM:【无敌】,既可以操作HTML也可以操作XML
        2HTML DOM:只能操作HTML,API简单。缺点:不能操作自定义属性
        3、XML DOM:只能操作XML
        
        开发建议:优先使用HTML DOM,HTML DOM实现不了再用核心DOM补充

DOM树

        树根:document - 不需创建,一个页面只有一个document,由js解释器自动创建
        作用:通过树根,可以找到想要的DOM元素/节点/对象
            

每个DOM元素都有三大属性

elem.nodeType

描述节点类型

        document 节点:9
	element 节点:1
	attribute 节点:2
	text 节点:3

	以前有用:判断xx是不是一个页面元素

elem.nodeValue

获取【属性节点】的值

elem.nodeName

获取节点名称/标签名

        注意:返回的是一个全大写组成的标签名
        

通过 关系 获取元素

        父:xx.parentNode;
        子:xx.children; - 只能找到儿子,是个集合
        第一个儿子:xx.firstElementChild;
        最后一个儿子:xx.lastElementChild;
        前一个兄弟:xx.previousElementSibling;
        后一个兄弟:xx.nextElementSibling;
        

递归

函数中再次调用了函数本身,最后也会停下来

        何时使用:遍历DOM树,专门用于【遍历层级不明确】的树状结构
        如何使用:
                function 函数名(根){
                        1、第一层要做什么直接做

                        2、判断它有没有下一级,如果有下一级,则再次调用此函数,但是传入的实参是自己的下一级
                }
                函数名(实际的根)

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

        递归 vs 循环
        递归:优点:直观,易用
              缺点:性能较差
        循环:优点:性能较好
              缺点:难得一批

遍历层级不明确的API

TreeWalker:一个在DOM树上行走的人

        缺点:只能遍历层级不明确的DOM树,不能遍历层级不明确的数据
        如何使用:2步
                 1、创建TW
                        var tw=document.createTreeWalker(根元素,NodeFilter.SHOW_ELEMENT);

                 2、反复调用tw的nextNode函数找到节点
                        while((node=tw.nextNode())!=null){
                                node要干什么
                        }

直接找元素

通过HTML的一些特点找元素

        新的:通过name找元素:var elems=document.getElementsByName("name值");

通过css选择器查找元素

2个方法 - 更适合用于复杂查找(多句才能找到最终元素)

        1var elem=document.querySelector("任意css选择器");
		只会找到一个元素,如何同时匹配多个,也只会返回第一个
		没找到null

	2var elem=document.querySelectorAll("任意css选择器");
		找到了是一个集合
		没找到是一个空集合

getXXXX和queryXXXX的区别?

        返回的结果不一样:
		1、getXXXX:返回的是一个动态集合HTMLCollection
			特点:每一次DOM树修改过后,js都会悄悄的再次查找元素,保证数据和页面对应,效率相对较低

		2、queryXXX:返回的是一个静态集合Nodelist
			特点:只管查找的一次找出来的结果,后续DOM树的修改,我们的数据也不会变化,效率相对较高
			优点:1、查找简单
			      2、还支持forEach

总结:找元素的方式

        1、直接找元素:getXXX、queryXXX
        2、通过节点的关系
        3、层级不明确的情况 - 递归
        

操作元素

操作元素的内容

        1、*elem.innerHTML:获取或者设置开始标签到结束标签之间的HTML代码,没有兼容性问题,可以识别标签
                获取:elem.innerHTML
                设置:elem.innerHTML="新内容"

        2、elem.textContent:获取或者设置开始标签到结束标签之间的纯文本,但是有兼容性问题,不可以识别标签
                获取:elem.textContent
                设置:elem.textContent="新文本"
                特殊:老IE才叫elem.innerText;    //所有的主流浏览器来将就了老IE

        3、*input.value:获取或设置单标签(input)的内容准备的
                获取:input.value
                设置:input.value="新值"

操作元素的属性

        1、*获取属性值:
                核心DOMelem.getAttribute("属性名")

                HTML DOMelem.属性名;

        2、*设置属性值:
                核心DOMelem.setAttribute("属性名","属性值");

                HTML DOMelem.属性名="新值";

        3、删除属性值:
                *核心DOMelem.removeAttribute("属性名")

                HTML DOMelem.属性名=""     - 虽然简单,但是有的情况会删不干净,有的属性删不干净依然会有功能,比如:href

        4、判断有没有 - 垃圾
                核心DOMelem.hasAttribute("属性名");

                HTML DOMelem.属性名!="";

                返回的结果是一个布尔值,只能判断有没有,不能判断是什么

        HTML DOM:有两个小缺陷:
                1class需要写为className
                2、一切自定义属性不能操作
                3、删除删不干净,依然会留下属性节点
                

操作元素的样式

        1、内联样式:
                获取:elem.style.css属性名
                设置:elem.style.css属性名="css属性值"
                特殊:
                        1、css属性名如果有横线要省略,变为小驼峰命名法
                        2、获取的时候只能获取到内联样式

        2、样式表: - 不推荐
           获取到你想要操作的某个样式表
		var sheet=document.styleSheets[i];
           获取样式表中所有的样式规则
		var rules=sheet.cssRules;
           选出自己想要操作的某个规则
		var rule=rules[i];
           操作
		获取:console.log(rule.style.css属性名);
		设置:rule.style.css属性名="css属性值"

           为什么更推荐内联:
                1、不会牵一发动全身
                2、优先级一定是最高的
                

创建元素&渲染页面

        1、先创建空标签
                var elem=document.createElement("标签名");
	
        2、为其添加必要属性和事件
                elem.属性名="属性值";
                elem.on事件名=function(){操作}

        3、渲染DOM树/页面:31、父元素.appendChild(elem);              //elem追加到父元素里面,当最后一个儿子
                2、父元素.insertBefore(elem,已有子元素);   //elem追加到父元素里面,插入在已有子元素的前面 - 影响其他元素的下标,不推荐
                3、父元素.replaceChild(elem,已有子元素);   //elem追加到父元素里面,新元素替换掉已有子元素 - 不推荐

删除元素

        elem.remove();
        

HTML DOM

简化核心DOM开发,核心语法太繁琐,提供了一些常用的对象

image对象

        垃圾,仅仅简化了创建部分
	创建:var img=new Image(); - 不是人人都可以简化为构造函数创建方式

form对象

        垃圾,仅仅只是简化了查找部分
	查找form元素:var form=document.forms[i];
	以及找表单控件:var inp=form.elements[i];

select对象

提供了2个属性,2个方法,1个事件

        属性:
		1select.options === select.children; 获取select里面的所有的option
		2、*select.selectedIndex - 获取当前选中项的下标,搭配联动一起使用

	方法:
		1、*sel.add(option); - 完全相当于appendChild
		2、elem.remove(i);   - 当remove删除元素出现过后,就淘汰了

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

option对象

只是简化了创建部分

        创建:var opt=new Option("innerHTML","value")

	一句话完成4个操作:sel.add(new Option("XXX","xxx"));
		1、创建了空的option	
		2、为option设置了innerHTML
		3、为option设置了value
		4、上树