js-core第三周+DOM知识点汇总

145 阅读21分钟

js-core第三周:Function、Object、ES5、ES6、DOM

一、Function 功能函数

什么是函数:需要先定义好,可以反复调用的一个代码段

何时使用:1、不希望打开页面立刻执行 
         2、以后可以反复的使用 
         3、希望绑定在页面元素上

(一)、如何使用:

1、创建:3种

   *1、【声明方式】创建函数:
       function 函数名(形参,...){
	         函数体;
		   return 返回值;
	}

    2、【直接量方式】创建函数:
     var 函数名=function(形参,...){
	         函数体;
		 return 返回值;
    }
//通过此方法看出函数名其实就是一个变量名
            
   3、*构造函数方式:var 函数名=new Function("形参1","形参2",..."函数体;return 返回值");
     何时使用:如果你的函数体不是固定的而是动态拼接的一个字符串
        var arr=[12,5,25,432,7,54,4312,41];
        var user=prompt("请进行排序,如果输入a-b则为升序排列,如果输入b-a则为降序排列")
       var compare=new Function("a","b","return "+user);
       arr.sort(compare);

2、调用并且接住结果:

var  result=函数名(实参,...);

//return的本意其实是退出函数,但如果return后面跟着一个数据
顺便将数据返回到函数作用域的外部,但是return只负责返回,不负责保存
//就算你省略return,默认也会return undefined;
//具体要不要return,全看你需要不需要获得函数的结果
//往往前辈们提供的预定义函数底层都有一句return操作

(二)、***作用域:2种

1、全局作用域:

全局变量和全局函数,特点:在页面的任何位置都可以使用

2、函数/局部作用域:

局部变量和局部函数,特点:在【当前函数调用时内部可用】

导致变量的使用规则:优先使用自己的,自己没有找全局,全局没有就报错
特殊:缺点:
	1、千万不要在函数中对未声明的变量直接赋值 - 导致全局污染,解决:尽量变量使用前都要记得先var一下
	2、局部可用全局的,但是全局不能用到局部的 - 解决:看上面,搭配上return

3.特点

    1、全局作用域:随处可用,可以被反复使用。缺点:容易被污染

    2、函数/局部作用域:只能在函数内部调用时使用,不会被污染。缺点:一次性的,是会自动释放的。

(三)***声明提前:- 鄙视时

 在程序正式执行之前
 将var声明的变量(轻)和function【声明的】函数(重)
 都会悄悄的集中定义在当前作用域的顶部
 但是赋值留在原地

 声明方式创建的函数会完整的提前(第一种)
 直接量方式创建的函数不会完整的提前,只有变量部分会提前(第二种)

 何时使用:永远不会自己使用,垃圾干扰我们的判断 - 只会在鄙视中遇到
	  只要你遵守以下规则:
		1、变量名和函数名尽量不要重复
		2、先创建后使用
		3、如果鄙视时,遇到先使用后创建,多半都是在考你声明提前,先转换为我们认识的代码,再去判断

(四)、***按值传递:两个变量之间进行赋值

如果传递的是【原始类型】的值:
    修改一个变量,另一个变量是不会受到影响的,其实是复制了一个副本给对方

如果传递的是【引用类型】的对象:
    修改一个变量,另一个变量就会收到影响了,两者使用的是同一个地址值(浅拷贝)

(五)、重载overload:相同的函数名,根据传入的实参的不同,可以自动选择对应的函数进行执行

为什么:减轻程序员的压力
问题:js的语法不支持重载
js不允许多个同名函数同时存在,如果同时存在,最后的会覆盖之前所有的
解决:在【函数中】有一个对象 - arguments对象
什么是arguments:只能在函数中使用,自动创建,是一个类数组对象(支持下标、length、遍历)
	作用:***可以接收住所有的传入的实参

	***arguments可以做的事:
	     1、实现重载:通过在函数内部判断arguments的不同,执行不同的操作
	     2、以后有没有形参都无所谓啦
	     3、正式开发中,有可能会将多个函数整合为一个函数 - 减轻程序员的压力

(六)匿名函数:没有名字的函数

1、匿名函数自调

为什么:节约内存,因为匿名函数,没有变量引用着,用完,垃圾回收器就会发现此引用类型没有人引用着,
     则垃圾回收器的计数器为0,垃圾回收器就会吧此函数释放掉
(function(){
		//以后可以代替全局代码写法,尽量的不要再去外部书写任何代码
})();

2、匿名函数回调:将函数作为了一个实参,传递给其他函数调用

1、*学习回调的目的:让你们知道哪些东西叫做回调函数,只要是匿名函数,没有自调,就是回调
	arr.sort(function(){})
        str.replace(reg,function(){})			elem.onclick=function(){}

2、以后ES6有个技术,箭头函数:简化一切回调函数的

3、了解了一下回调函数的原理

(七) 函数的执行原理:

	1、程序加载时:
	   创建执行环境栈(ECS):保存函数调用顺序的数组
	   首先压入全局执行环境(全局EC)
	   全局EC引用着全局对象window
	   window中保存全局变量和全局函数

	2、定义函数时
	   创建出函数对象:封装代码段
	   在函数对象中有一个scope(作用域)属性:记录着此函数来自的作用域是哪里
	   全局函数的scope都是window

	3、调用前
	   在执行环境栈(ECS)压入新的EC(函数的EC)
	   创建活动对象(AO):保存着本次函数调用时用到的局部变量
	   在函数的EC中有一个scope chain(作用域链)属性引用着AO
	   AO有一个parent属性是函数的scope引用着的对象

	4、调用时:正是因为前面三步,我们才有了变量的使用规则:优先使用局部的,局部没有找全局,全局没有就报错

	5、调用完:函数的EC会出栈,AO自动释放,局部变量也就自动释放了
            

函数的执行原理.png

(八)***两链一包:作用域链(查找变量)、原型链(查找属性和方法)、闭包(保护一个反复使用的局部变量)

1.作用域链:

 以函数的EC的scope chain属性为起点,经过AO,逐级引用,形成的一条链式结构
            
 作用:查找变量,带来了变量的使用规则:优先使用局部的,局部没有找全局,全局没有就报错

作用域链.png

2、*****闭包 - js面试必然有一道题:两链一包

   闭包:我们发现全局和函数都有缺点,希望保护一个【可以反复使用的局部变量】的一种词法结构 - 其实还是函数
   
   何时使用:希望保护一个【可以反复使用的局部变量】的时候
       
   如何使用:
	1、两个函数进行嵌套
	2、外层函数创建出受保护的变量
	3、外层函数要return出内层函数
	4、内层函数要去操作受保护的变量

		function f1(){
			创建的受保护的变量
			return function(){
				操作受保护的变量
			}
		}

   强调:
	1、判断是不是闭包,有没有两个函数嵌套,返回内层函数,内层函数在操作受保护的变量
	2、外层函数调用几次,就会创建几个闭包,受保护的变量就有了几个副本
	3、同一次外层函数调用,返回的内层函数,都是在操作同一个受保护的变量

   缺点:受保护的变量,永远不会被释放,使用过多,会导致内层泄漏 - 不可多用
   问题:应该在哪里用呢? - 防抖节流
	1、三个事件需要防抖节流
		1、elem.onmousemove - 鼠标移动事件
		2、input.oninput - 每次输出/改变都会触发
		3window.onresize - 每次窗口的大小发生变化就会触发

		公式:
		
		function fdjl(){
			var timer=null;//2
			return function(){//3、
				if(timer){clearTimeout(timer);timer=null}//关闭之前的定时器
				timer=setTimeout(function(){//开启定时器,等1s才执行
					//操作由你来书写
				},1000)
			}
		}
		
		var result=fdjl(); 

3、原型链

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

函数的考点: 1、创建函数的三种方式

2、简单说说作用域

3、声明提前鄙视题

4、按值传递

5、重载

6、闭包

(九)、预定义全局函数:

前辈们提前定义好的方法,我们程序员可以直接调用,在哪里都可以使用
1、编码和解码:- 玩悄悄话,你编码,发到群里,其他人来解码
   问题:url中不允许出现多字节字符,如果出现会乱码
		utf-8编码格式下,一个汉字就是3字节
   解决:发送前,前端将多字节字符编码为单字节字符(数字、字母)
	 发送后,后端将单字节字符解码为多字节原文

   如何:
	编码:var code=encodeURIComponent(str);
	解码:var 原文=decodeURIComponent(code);
	其实这个东西在某次浏览器更新后,就当场淘汰了,浏览器自带此功能了

2isFinite(num):判断num是不是无穷大:true->有效数字	false->无穷大
	哪些会为falseNaNInfinity、分母为0

3、*牛逼的:parseInt/parseFloat/eval/isNaN
    

二、*****Object:对象

ArrayFunctionRegExpString...对象具有属性和方法,都是预定义好的,现在学会自定义对象
*** 面向对象:三大特点:封装、继承、多态:  ***
 ***开发方式:面向对象 和 面向过程;
	1、面向过程:经过 - 开始->结束,我们一直以为的开发方式就是面向过程,先干什么再干什么最后干什么
	2、面向对象:对象(属性和方法),js有一句话万物皆对象:假设一个人是一个对象的话:
	    属性:姓名、身高、体重、性别
	    方法:吃饭、睡觉、拉粑粑
	 为什么要面向对象:现实中所有的数据都必须包含在一个事物中才有意义
	 何时使用面向对象:以后做任何操作都要封装在一个对象中
	

(一)封装:创建自定义对象:3种

1、*直接量方式:适合创建单个对象
	var obj={
		"属性名":属性值,
		...
		"方法名":function(){},
		...
	}
      强调:1、其实属性名和方法名的双引号可以省略不写 - 暂时建议你加上为了以后学习的json数据格式
           2、访问对象的属性和方法
	      *obj.属性名		===		obj["属性名"]
	      *obj.方法名();		===		obj["方法名"]();
	        建议使用.访问属性和方法,更简单
	        ***js中一切都是对象,一切对象的底层都是hash数组
         3、访问到不存在的属性,返回undefined
         4、可以随时随地的添加新属性和新方法
         5、希望获取出对象所有的东西:遍历:for in,obj[i]才能拿到

         6、***如果你希望在对象的方法里使用对象自己的属性:写为this.属性名	
    *****难点:this的指向:
	   1、单个元素绑定事件 this->这个元素
	   2、多个元素绑定事件 this->当前触发事件的元素
	   3、函数中this->谁在调用此方法,this就是谁
	   4、定时器中this->window
	   5、箭头函数this->外部对象
	  6、构造函数中this->当前正在创建的对象

2、预定义构造函数方式:var obj=new Object();//空对象	-   垃圾
			//需要自己后续慢慢追加属性和方法
			obj.属性名=属性值;
			obj.方法名=function(){};

	   以上两个方法都有一个缺陷:一次只能创建一个对象

3、自定义构造函数方式:21、创建自定义构造函数
			function 类名(name,age,salary){
				this.name=name;
				this.age=age;
				this.salary=salary;
			}

	2、调用构造函数创建出对象
			var obj=new 类名(实参,...);

面向对象:
	优点:1、逼格高,所有的属性和方法都保存在了一个对象之中 - 更符合现实,更有意义
		   2、每个功能特地分开书写,越细越好 - 便于维护
		   3、铁索连舟 - 一个方法触发多个方法联动

	缺点:对新手不友好,this的指向非常恶心

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

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

1、***如何找到父对象(原型对象):保存一类子对象共有属性和共有方法的父对象

	1、对象名.__proto__;//必须先有一个对象
	2、构造函数名.prototype;//构造函数名:Array/Object/String/Number/Boolean/RegExp/Date...几乎人人都有,除了Math

2、面试题:两链一包:作用域链(查找变量)、原型链(查找属性和方法)、闭包(保护一个反复使用的局部变量)

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

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

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

4.***继承具有非常多的鄙视题:

1、判断是自有还是共有:
	1、判断自有:obj.hasOwnProperty("属性名");
	    如果为true,说明是自有,如果为false,可能是没有或共有
	2、判断共有:
		if(obj.hasOwnProperty("属性名")==false&&"属性名" in obj){//in关键字会查找整条原型链
			共有
		}else{
			没有
		     }

	完整版:
		if(obj.hasOwnProperty("属性名")){
				自有
		}else{
			if("属性名" in obj){
				共有
			}else{
				没有
			    }
		     }
		
2、修改&删除:自有和共有
	自有:修改:obj.属性名=新值;
	删除:delete obj.属性名;

	共有:修改:原型对象.属性名=新值; - 千万不要直接在本地做操作,非常危险,添加上一个同名属性
	删除:delete 原型对象.属性名; - 如果对本地删除,没有任何效果的

3、如何为老IE的数组添加indexOf方法 - 如何为一类人添加共有方法
	//判断是不是老IE
	if(Array.prototype.indexOf===undefined){
		//我为数组类添加了一个indexOf方法
	    Array.prototype.indexOf=function(key,starti){
		//用户如果没有传入开始位置,则设置为0
	         starti===undefined&&(starti=0);
		//从开始位置处,循环当前数组的每一个元素
	         for(var i=starti;i<this.length;i++){
		 //每一个元素和用户传入的关键字进行比较,如果比较到了
	            if(this[i]==key){
		  //返回下标
			return i;
		    }
		 }
		  //没找到返回-1
		   return -1;
	    }
	  }

4、判断x是不是一个数组 - 4种方式
	1、判断x是不是继承自Array.prototype;
	     Array.prototype.isPrototypeOf(x);//true说明是一个数组

	2、判断x是不是由Array这个构造函数创建的
	     x instanceof Array;//true说明是一个数组

	3Array.isArray(x);//true说明是一个数组,只有数组可以这么判断,ES5提供的一个新方法,老IE都不支持

	4、输出【对象的字符串】形式
		在Object的原型上保存着最古老最原始的toString方法
		原始的toString输出形式:[object 构造函数名]

	 ***多态:多种形态:子对象觉得父对象的成员不好用,在本地定义了同名成员覆盖了父对象的成员	
		固定套路:借用:Object.prototype.toString.apply(x)
		if(Object.prototype.toString.apply(obj)==="[object Array]"){
					console.log("数组")
		}else{
			console.log("不是数组")
		    }

	5、如何实现自定义继承
		1、两个对象之间继承:子对象.__proto__=新父对象
		2、多个对象之间继承:构造函数名.prototype=新父对象
	      时机:应该在开始创建对象之前设置好继承关系
                  

三、ES5

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

如何保护:

1、四大特性 - 每一个属性或方法都有四大特性

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

2、三个级别:

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

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

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

	其实保护对象对我们程序员并不重要:为什么
		1、如果你用的是面向过程,你保护个屁
		2、别人基本不可能知道我们的对象名叫什么
		3、前辈们都没有保护,你保护个啥

	学习的目的:应付鄙视和面试

(二)*****数组的新的API:3组6个

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;
		      },基础值)

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

(三)、Object.create():希望根据父对象创建子对象,继承自动设置完毕

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

(四)、面试:严格模式:很严格

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

(五)、*****call/apply/bind:不是自己的方法也可以使用,不管是笔试、面试、实际开发都很常用

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

bind:【永久替换了函数中的this】 - 买
	3件事:
	1、创建了一个和原函数功能完全相同的新函数
	2、将新函数中的this永久的绑定固定为了指定的对象,别人借不走
	3、将新函数中的部分参数永久固定
	语法:var 新函数=原函数.bind(指定对象,永久固定参数,...); - 不是立刻执行,需要自己调用
	强调:bind绑定的新函数是没有办法被call/apply借走的

个人更推荐:call/apply - 借,白嫖
固定套路:
  1、Math.max/min.apply(Math,arr)
  2、Object.prototype.toString.call/apply(arr)==="[object Array]"
  3、***类数组转为普通数组:保存一下=Array.prototype.slice.call/apply(类数组)

ES5其实并没有任何简化操作,只是提供了更多的API

四、ES6:简化了语法 - 变化较大

(一)*模板字符串

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

(二)*块级作用域:尽量以后创建变量【优先】使用let关键字

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

(三)***箭头函数:简化回调函数

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

(四)for of循环 - 垃圾

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

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

五、DOM Document Object Model(文档对象模型)

(一)什么是DOM:Document Object Model(文档对象模型)

将每一个标签/元素/属性/文本/注释,看做了一个DOM节点/元素/对象(提供了操作这些东西的属性和方法)

1.面试题:HTML/XHTML/DHTML/XML

HTML - 网页
XHTML - 更严格的网页
DHTML - Dynamic:动态的网页,其实并不是新技术、新概念,只是将现有技术的整合统称,使我们网页在离线版也具有动态效果
		DHTML:HTML+CSS+JS(dom)
XML - 数据格式

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

   1、核心DOM:【无敌】,既可以操作HTML 和 XML
	    缺点:API比较繁琐

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

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

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

(二)DOM树:树根:document - 不需要我们创建,一个页面只有一个document:由浏览器的js解释器自动生成

  可以通过数个你找到每一个DOM节点/元素/对象,提供了很多很多的API

(三)每个DOM元素都有三大属性:

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、判断他有没有下一级,如果有下一级再次调用此方法,但是传入的实参是下一级		
	}

	函数名(实际的根节点)

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

	递归 vs 循环
	递归:优点:直观、易用
	      缺点:性能较低
	循环:优点:性能高,几乎不占内存
	      缺点:难得一匹

(六)遍历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值")
2class/标签名: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

3. 总结:找元素

      1、直接找元素:getXXX、queryXXX
      2、通过关系
      3、曾经不明确才用递归

(八)操作元素:

1、元素的内容:3个

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

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

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

2、元素的属性:

  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、不能操作自定义属性

3、元素的样式:

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

(九)创建元素:2步

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

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

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

(十)渲染页面方式:3种

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

(十一)删除元素:elem.remove();

核心DOM已经全部学习完毕了:总结一句话说完:增删改查DOM(元素、内容、属性、样式)

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

1、Image对象:图片对象:

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

2、Form对象:表单对象:

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

3、*Select对象:

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


	方法:1、*select.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"))   

一阶段常识:
  表单控件元素必须添加name,才能传到服务器端/后端
  有的表单控件还要添加value,用户不能输入只能选择的,我们程序员就要把value提前写好,供用户选择