前端开发

62 阅读12分钟

对象:

Array/String/Function/Math...对象具有属性和方法,都是预定义好,现在我们学习自定义对象 
2、面向对象:Object:三大特点:封装、继承、多态 
  1、开发方式: 面向过程:经过 - 开始->结束,其实我们一直以来的开发方式都是面向过程:先干什么再干什么最后干什么
  面向对象:对象(属性和方法),js有一句话万物皆对象,假设一个人是一个对象的话: 
   属性:姓名、性别、身高、体重、爱好、智商... 
   方法:吃饭、睡觉、拉粑粑、跑步、走路、打字、说话... 
   为什么要面向对象:现实生活中所有的数据都要保存在一个事物中才有意义 
   何时使用面向对象:以后做任何操作都要封装在一个对象中 - 建议:不太适合初学者
  2、封装对象:创建自定义对象:31、直接量方式:
	var obj={
		"属性名":"属性值",
		...
		"方法名":function(){},
		...
	}
	强调:
		1、其实属性名和方法名的""可以省略不写的 - 暂时建议写上,以后我们会学习JSON数据格式,要求必须加""
		2、访问对象的属性和方法
			*对象名.属性名	===	对象名["属性名"]
			*对象名.方法名();	===	对象名["方法名"]();
			JS种一切对象的底层都是hash数组
		3、访问到不存在的属性或者方法,返回undefined
		4、可以后期随时随地的添加新属性和新事件
		5、如果希望遍历出对象中的所有东西
			for(var i in 对象名){
				对象名[i]
			}
		6、如果你希望在对象的【方法中】使用对象自己的属性:写为:this.属性名
		   难点:this的指向:
		           1、单个元素绑定事件:this->这个元素
			   2、多个元素绑定事件:this->当前触发事件的元素
			   3、函数中使用thisthis->谁在调用此方法,this指向就是谁
			   4、定时器this->window
			   5、箭头函数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 xxx=new 类名("实参",...)

面向对象:
       优点:
        1、逼格高,所有的属性和方法都是保存在一个对象之中 -更符合现实生活:代码风格、代码速度
	2、每个功能特地的分开写 - 便于维护
	3、铁锁连舟 - 一个方法触发多个方法联动
       缺点:
        1、对新手不友好 - this的指向

1、Object:

1、继承:父对象的成员(属性和方法),子对象可以直接使用、 
  为什么:代码重用!节约内存空间!提升网站的性能! 
  何时继承:只要多个子对象公用的属性和【方法】,都要集中定义在父对象之中 
2、如何找到父对象(原型对象):保存一类子对象共有属性和共有方法的父对象:21、子对象.__proto__;//前提:必须先有一个对象
  2、构造函数名.prototype;//构造函数名,几乎人人都有,除了Math/Undefined/Null

3、面试题:两链一包:
   作用域链:查找变量
   闭包:保护一个可以反复使用的局部变量的一种词法结构
   原型链:
        每个对象都有一个属性:__proto__,可以一层一层的找到每个人的父亲,形成的一条链式结构,就称之为叫做原型链
        可以找到所有父对象的成员(属性和方法),作用:查找属性和【方法】
        最顶层是Object的原型,上面放着我们眼熟的toString,怪不得人人都可以使用toString
JS种万物皆对象
4、有了原型对象,设置共有属性和共有的方法
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{
				没有
			}
		}

笔试题2:修改/删除自有和共有
	自有:修改:obj.属性名=新值;
	           删除:delete obj.属性名;
	
	共有:修改:obj.__proto__.属性名=新值;
	           删除:delete obj.__proto__.属性名;

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

笔试题3:如何为老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;
		}
	}

笔试题4:判断x是不是一个数组:4种方式
	1、判断x是不是继承自Array.prototypeArray.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);
	
笔试题5:实现自定义继承:
		1、两个对象之间的继承:
			子对象.__proto__=父对象
	
		2、多个对象之间设置继承:
			构造函数名.prototype=父对象
			注意时机:一定要在创建对象之前 

1、ES5:

1、保护对象:保护对象的成员(属性和方法)
  如何保护:
  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、前辈们都没有保护,你保护个屁
2、数组的新的API361、判断:判断结果一定是一个布尔值
	every:每一个 - 要求每一个元素都要满足,结果才为true,只要有一个不满足就为false - 类似于&&
		语法:var bool=arr.every(function(val,i,arr){
				return 判断条件;
		      })

			//val - 当前值
			//i   - 当前下标
			//arr - 数组本身,前辈们确实提供了3个形参给我们,但我们需要哪个在用哪个

	some:有一些 - 要求每一个元素都不满足,结果才为false,只要有一个满足就为true - 类似于||
		语法:var bool=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 subArr=arr.filter(function(val,i,arr){
				return 判断条件;
		      })

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

以上6API的底层都是for循环,目的 - 简化for循环 - 以后能不写for就不写for
3Object.create():根据父对象创建子对象,继承已经设置好了
语法:var 子对象=Object.create(父对象,{
		"自有属性名":{四大特性},
		...
      });
4、面试时:严格模式:很严格
   开启:在你的任何作用域的顶部加上一句话:"use strict";
   功能:1、禁止给未声明的变量赋值 - 解决全局污染
         2、静默失败升级为报错
5、call/apply/bind:不是自己的方法也可以使用 - 笔试面试也很容易碰到
   call/apply:临时替换了函数中的this - 借用
	语法:函数名.call(借用的对象,实参,...); - 单独传入每一个实参
	      函数名.apply(借用的对象,arr); - 只能传入一个实参是一个数组,apply其实会悄悄的将数组打散
	强调:call/apply,相当于立刻调用函数,立即执行

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

   个人更推荐:call/apply,借,白嫖

   固定套路:
	1Math.max/min.apply(Math,arr);
	2Object.prototype.toString.call/apply(arr);
	3、类数组转为普通数组:类数组名称=Array.prototype.slice.call/apply(类数组);
            ES5其实没有什么简化,是提供了一些API

2、ES6:简化ECMAScript - 语法较大的变化

1、模板字符串:可以在字符串中放入变量 - 不需要再做字符串拼接,在字符串中实现了一个简单的js的环境
 语法:`我的名字叫${name}`
2、块级作用域:尽量以后【优先】使用let创建变量
 let 变量名=值;
 作用:1、禁止声明提前
       2、添加了块级作用域,一个{}就是一个块
       3、记录着当前触发事件的元素的下标
3、箭头函数:简化回调函数:
 公式:去掉function,()和{}之间添加=>,形参如果只有一个,则省略掉(),函数体如果只有一句话,省略{},
       函数体只有一句话并且是returnreturn和{}都省略
 特殊:千万不要将事件也简化为箭头函数 - 暂时
4for...of循环:垃圾
 for(var v of 数组名){
	v;//value当前值
 }

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

今日目标:找元素和找数据

1、什么是DOMDocument Object Model(文档对象模型) - 操作HTML文档 
   将每个元素/标签/文本/属性/注释都会看作是一个DOM节点/元素/对象 
面试题:HTML/XHTML/DHTML/XML?
1HTML - 网页
2XHTML - 更严格的网页
3DHTML - Dynamic:动态的网页,其实并不是新技术,也不是新概念,而是现有技术的一个整合统称:让我们的网页再离线版也能具有动态效果
	DHTML:HTML+CSS+JS
4XML - 全部自定义,数据格式:淘汰了:JSON数据格式

DOM:原本是可以操作一切结构化文档的 HTMLXML,后来为了方便各类开发者分为了3个方向
1、核心DOM:【无敌】,既可以操作HTML也可以操作XML
         缺点:API比较繁琐,getAttribute/setAttribute()

2HTML DOM:只能操作HTMLAPI简单:缺点:自定义的东西操作不了

3XML DOM:只能操作XML - 不会学习他

开发建议:优先使用HTML DOMHTML DOM实现不了再用核心DOM补充    

2、DOM树:树根:document - 不需创建,一个页面只有一个document,由js解释器自动创建

作用:通过树根,可以找到想要的DOM元素/节点/对象

3、每个DOM元素都有三大属性:

1、elem.nodeType:描述节点类型 
  document 节点:9 
  element 节点:1 
  attribute 节点:2 
  text 节点:3
 以前有用:判断xx是不是一个页面元素 - 因为以前我们的方法和你们现在的方法不一样

2、elem.nodeValue:获取【属性节点】的值

3、elem.nodeName:获取节点名称/标签名
注意:返回的是一个全大写组成的标签名     

4、通过 关系 获取元素

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

5、递归:简单来说就是函数中再次调用了函数本身,迟早有一天会停下来

何时使用:遍历DOM树,专门用于【遍历层级不明确】的树状结构 
如何使用: 
  function 函数名(根){ 
   1、第一层要做什么直接做        
   2、判断它有没有下一级,如果有下一级,则再次调用此函数,但是传入的实参是自己的下一级
}
      函数名(实际的根)
    算法:深度优先!优先遍历当前节点的子节点,子节点遍历完才会跳到兄弟节点
    缺点:同时开启大量的函数调用,浪费的内存,只有一个情况:【遍历层级不明确】
    递归 vs 循环
    递归:优点:直观,易用
      缺点:性能较差
    循环:优点:性能较好
      缺点:难得一批

6、遍历层级不明确的API:TreeWalker:一个在DOM树上行走的人

缺点:只能遍历层级不明确的DOM树,不能遍历层级不明确的数据 
如何使用:2步 
  1、创建TW 
     var tw=document.createTreeWalker(根元素,NodeFilter.SHOW_ELEMENT); 
  2、反复调用tw的nextNode函数找到节点
	while((node=tw.nextNode())!=null){
		node要干什么
	}

7、直接找元素:

1、通过HTML的一些特点找元素 
  你会的:id/class/标签 
  新的:通过name找元素:var elems=document.getElementsByName("name值")  
2、通过css选择器查找元素:2个方法 - 更适合用于复杂查找(多句才能找到最终元素)
  1、var elem=document.querySelector("任意css选择器");
	只会找到一个元素,如何同时匹配多个,也只会返回第一个
	没找到null

  2、var elem=document.querySelectorAll("任意css选择器");
	找到了是一个集合
	没找到是一个空集合
面试题:getXXXX和queryXXXX的区别?
返回的结果不一样:
	1、getXXXX:返回的是一个动态集合HTMLCollection
		特点:每一次DOM树修改过后,js都会悄悄的再次查找元素,保证数据和页面对应,效率相对较低

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

操作元素

1、元素的内容 
    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="新值"
2、元素的属性: 
    1、获取属性值: 
       核心DOM:elem.getAttribute("属性名") 
       HTML DOM:elem.属性名;
    2、设置属性值:
        核心DOM:elem.setAttribute("属性名","属性值");

        HTML DOM:elem.属性名="新值";
    3、删除属性值:
        核心DOM:elem.removeAttribute("属性名")
        HTML DOM:elem.属性名="" - 虽然简单,但是有的情况会删不干净,有的属性删不干净依然会有功能比如:href
    4、判断有没有 - 垃圾
   核心DOM:elem.hasAttribute("属性名");

   HTML DOM:elem.属性名!="";

   返回的结果是一个布尔值,只能判断有没有,不能判断是什么
   HTML DOM:有两个小缺陷:
    1、class需要写为className
    2、一切自定属性不能操作
    3、删除删不干净,依然会留下属性节点
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、创建元素&渲染页面:3步

1、先创建空标签 
   var elem=document.createElement("标签名");              
2、为其添加必要属性和事件
elem.属性名="属性值";
elem.on事件名=function(){操作}
3、渲染DOM树/页面:31、父元素.appendChild(elem);//elem追加到父元素里面,当最后一个儿子
   2、父元素.insertBefore(elem,已有子元素);//elem追加到父元素里面,插入在已有子元素的前面 - 影响其他元素的下标,不推荐
   3、父元素.replaceChild(elem,已有子元素);//elem追加到父元素里面,新元素替换掉已有子元素 - 不推荐

2、删除元素:elem.remove();

核心DOM学习完毕,你已经无敌了:总结一句话DOM可以干什么:增删改查DOM元素(元素、文本、属性、样式)

HTML DOM:简化核心DOM开发,核心语法太繁琐,提供了一些常用的对象 
 1、image对象:垃圾,仅仅只是简化了创建部分 
    创建:var img=new Image(); - 不是人人都可以简化为构造函数创建方式
 2、form对象:垃圾,仅仅只是简化了查找部分
查找form元素:var form=document.forms[i];
以及找表单控件:var inp=form.elements[i];
 3select对象:牛逼:提供了2个属性,2个方法,1个事件
     属性:
	1select.options === select.children; 获取select里面的所有的option
	2、*select.selectedIndex - 获取当前选中项的下标,一定是搭配联动一起使用

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

    专属事件:onchange - 选中项发生了变化才会触发
4、option对象:仅仅只是简化了创建部分
创建:var opt=new Option("innerHTML","value")

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

用户确认框:var bool=confirm("提示文字");