Bootstrap

51-js整理

JavaScript简介

JavaScript是什么

JavaScript 编程语言 流程控制

Netscape在最初将其脚本语言命名为LiveScript,后来Netscape在与Sun合作之后将其改名为JavaScript。JavaScript最初受Java启发而开始设计的,目的之一就是“看上去像Java”,因此语法上有类似之处,一些名称和命名规范也借自Java。JavaScript与Java名称上的近似,是当时Netscape为了营销考虑与Sun微系统达成协议的结果。Java和JavaScript的关系就像张雨和张雨生的关系,只是名字很像。

Java 是 服务器端的编程语言
JavaScript 是运行在客户端(浏览器)的编程语言

JavaScript是一种运行在客户端 的脚本语言

JavaScript的解释器被称为JavaScript引擎,为浏览器的一部分,广泛用于客户端的脚本语言,最早是在HTML(标准通用标记语言下的一个应用)网页上使用,用来给HTML网页增加动态功能。

现在的意义(应用场景)

JavaScript 发展到现在几乎无所不能。

  • 网页特效
  • 服务端开发(Node.js)
  • 命令行工具(Node.js)
  • 桌面程序(Electron)
  • App(Cordova)
  • 控制硬件-物联网(Ruff)
  • 游戏开发(cocos2d-js)

JavaScript和HTML、CSS的区别

  • HTML:提供网页的结构,提供网页中的内容
  • CSS: 用来美化网页
  • JavaScript: 可以用来控制网页内容,给网页增加动态的效果

JavaScript的组成

ECMAScript - JavaScript的核心

  • ECMA 欧洲计算机制造联合会

  • 网景:JavaScript

  • 微软:JScript

  • 定义了JavaScript的语法规范

  • JavaScript的核心,描述了语言的基本语法和数据类型,ECMAScript是一套标准,定义了一种语言的标准与具体实现无关

BOM - 浏览器对象模型(Borwser Object Model)

  • 一套操作浏览器功能的API

  • 通过BOM可以操作浏览器窗口,比如:弹出框、控制浏览器跳转、获取分辨率等

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

  • 一套操作页面元素的API

  • DOM可以把HTML看做是文档树,通过DOM提供的API可以对树上的节点进行操作

js的特点(JS是什么)

  • 是一门脚本语言

  • 是一门解释性语言

  • 是一门动态类型的语言

  • 是一门基于对象的语言

  • 是一门弱类型的语言

js的代码分为三个地方写

  • 1 在html的文件中 script标签中

  • 2 js代码可以在html的标签中写

  • 3 js文件中写js代码 但是 需要html的页面中引入script的标签中的src=“js的路径”

js代码的注意问题

  • 1、在一对script标签中有错误的js代码 那么该错误的代码后面的js代码不会执行

  • 2、如果第一对script标签中有错误 不会影响后面的script标签中的js代码执行

  • 3、script的标签中可以写什么内容 type="text/javascript"是标准写法或者写language="JavaScript"都可以 但是 目前在我们的html页面中 type和language都可以省略 原因:html是遵循h5的标准

  • 4、有可能会出现这种情况:script标签中可能同事出现type和langeuage的写法

  • 5、script标签在页面中可以出现多对

  • 6、script标签一般是放在body的标签的最后的 有时候会放在head标签中 目前讲课的时候 都在body标签中

  • 7、如果script标签是引入外部js的作用 那么这对标签中不要写任何js代码 如果要写 重新写一对script标签 里面写代码

面向对象的特点

封装

就是包装 把一些重用的内容进行包装 在需要的时候 直接使用
把一个值存放再一个变量中国 把一些重用的代码放在函数中 把好多相同功能的函数 放在一个对象中 把好多功能的对象 放在一个文件中 把一些相同的内容放在一个对象中

继承

类与类之间的关系 js中没有类的概念 js中有构造函数的概念 是可以有继承的是基于原型的

多态

同一行为 针对不同的对象 产生了不同的效果

标识符

变量,对象的属性以及函数的名字。

  • 由字母、数字、下划线、$符号组成,不能以数字开头
  • 不能是关键字和保留字,例如:for、while。
  • 区分大小写
  • 变量名必须有意义
  • 遵守驼峰命名法。首字母小写,后面单词的首字母需要大写

变量

变量

(存储数据的容器/或者操作数据)

变量声明

(有var 有变量名字 没有值)

变量初始化

(有var 有变量名字 有值)

  • 1 操作的数据都是在内存中操作

  • 2 js中存储数据使用变量的方式(名字,值----->数据)

  • 3 js中声明变量用var-------->存储数据 数据应该有对应的数据类型

  • 4 js中的字符串类型的值都用双引号或者单引号

常量

值本身

数据类型

js的数据类型有哪些?

js中的原始数据类型:

  • Number(数字类型)、

  • String(字符串类型)、

  • Boolean(布尔值)、

  • Undefined(未定义/空值)、

  • Null(对象指向为空)、

  • Object(对象) 、

  • function(函数)

Number:数字类型(整数和小数)

  • 数字类型:number类型

  • 无论是整数还是小数都是数字类型

  • 不要用小数验证小数

  • 不要使用NaN判断是不是NaN 应该使用isNaN(值或者是变量)

  • 想要表示十六进制:0x开头

  • 想要表示八进制:以0开头

  • 想要表示十进制:就是正常的数字

  • 最小值:Number.MIN_VALUE,这个值为: 5e-324

  • 最大值:Number.MAX_VALUE,这个值为: 1.7976931348623157e+308

  • 无穷大:Infinity

  • 无穷小:-Infinity

String

字符串类型(字符串类型的值一般都是用双引号或者单引号引起来)

Boolean

布尔类型(布尔类型的值只有两个 true(真1) false(假0))

Null

空类型 值只有一个:null 一个对象的指向为空了 此时可以赋值为null

Undefined

  • 未定义 值只有一个:undefined

  • 什么情况下结果是undefined

  • 变量声明了 没有赋值 结果是undefined

  • 函数没有明确返回值 如果接收了 结果也是undefined

  • 如果一个变量的结果是undefined和一个数字进行计算 结果是:NaN(not an number)不是一个数字 也没有意义

操作符号/运算符号

算术运算符:+ - * / %

  • 算术运算表达式:由算术运算符连接起来的表达式

  • 一元运算符:这个操作符只需要一个操作数就可以 ++ –

  • 二元运算符:这个操作符只需要两个操作数

  • 复合运算符:+= -= *= /= %=

  • 复合运算表达式:由复合运算符连接起来的表达式

关系运算符:> < >= <=

  • ==不严格的

  • ===严格的

  • !=不严格的不等

  • !==严格的不等

  • 关系运算表达式:由关系运算表达式连接起来的表达式

  • 关系运算表达式的结果是布尔类型

逻辑运算符

  • &&--------逻辑与----并且

  • |--------逻辑或------或者

  • !---------逻辑非------取反-----取非

  • 逻辑运算表达式:由逻辑运算符连接起来的表达式

  • 表达式1&&表达式2

  • 如果有一个为false 整个结果就是false

  • 表达式1||表达式2

  • 如果有一个为true 整个结果为true

  • !表达式1

  • 表达式1的结果为true 整个结果为false

  • 表达式1的结果为false整个结果为true

一元运算符

  • ++ 、 - -都是运算符

  • ++ 、- - 分为:前+ 和 后+ and 前- 后-

	 *如果++在后面:如: num++ + 10 参与运算
	 *先参与运算 运算结束后自身再加1
	 *如果++在前面: 如: ++num + 10 参与运算
	 *先自身加 然后再参与运算

获取变量的类型typeof

typeof

数据类型转换

其他类型转成数字类型:三种方式

		1、parseInt();//转整数
			console.log(parseInt("10"));//10
			console.log(parseInt("10sfsdff"));//10
			console.log(parseInt("g10"));//NaN
			console.log(parseInt("1afas0"));//1
			console.log(parseInt("10.98"));//10
			console.log(parseInt("10.98fsfasf"));//10
		2、parseFloat();//转小数
			console.log(parseFloat("10"));//10
			console.log(parseFloat("10sfsdff"));//10
			console.log(parseFloat("g10"));//NaN
			console.log(parseFloat("1afas0"));//1
			console.log(parseFloat("10.98"));//10.98
			console.log(parseFloat("10.98fsfasf"));//10.98
		3、Number();//转数字
			console.log(Number("10"));//10
			console.log(Number("10sfsdff"));//NaN
			console.log(Number("g10"));//NaN
			console.log(Number("1afas0"));//NaN
			console.log(Number("10.98"));//10.98
			console.log(Number("10.98fsfasf"));//NaN
		4、还可以用+号转。
	总结:想要转整数用parseInt() 想要转小数用parseFloat()
	想要转数字:Number();要比上面的两种方式都严格

其他类型转字符串类型:

		1、.toString()
			var num = 10;
			console.log(num.toString());//字符串类型
		2、String
		    var num1 = 20;
		    console.log(String(num1));
		如果变量有意义调用.toString()使用转换
		如果变量没有意义使用String()转换
		var num2;
		console.log(num2.toString());
		var num3 = null;
		console.log(num3.toString());

		这个可以
		var num2;
		console.log(String(num2));
		var num3 = null;
		console.log(String(num3));

其他类型转布尔类型

	    Boolean(值)
	    console.log(Boolean(1));//true
	    console.log(Boolean(0));//false
	    console.log(Boolean(11));//true
	    console.log(Boolean(-10));//true
	    console.log(Boolean("哈哈"));//true
	    console.log(Boolean(""));//false
	    console.log(Boolean(null));//false
	    console.log(Boolean(undefined));//false		

转Number

- String:纯数字转换结果是字面量的形式转换,其他所有情况均为NaN。
- Boolean: true为1,false为0。 

转String

所有类型转换为字符串类型就以字面量的形式进行转换。

转Boolean

- Number:除了0和NaN以外全部返回true。
- String:除了空字符串以外其他情况全为true。
- undefined:undefined是false。
- null:null是false。
-

判断相等

==判断相等的总结

 1. 数字和Boolean进行比较1==true为true,2==true为false.
 2. NaN == NaN  返回false
 3. undefined == null 返回true

如何判断一个值是否为NaN

isNaN(exp) 如果exp的返回值是NaN则返回true

判断具体的流程

 1 有NAN,一律返回false
 2  有布尔类型,布尔类型转换成数字比较
 3  有string类型,两种情况: 
     1. 对象,对象用toString方法转换成string相比。
     2. 数字,string类型转换成数字进行比较
 4 null和undefined不会相互转换,相等
 5 有数字类型,和对象相比,对象用valueof转换成原始值进行比较
 6 其他情况,一律返回false

条件判断语句

if语句

   if语句:主要是判断
	 语法:
	 if(表达式){
	 	代码块
	 } 
	 执行过程:
	 先判断表达式的结果是true还是false 如果是true则执行代码块 如果是false大括号中的代码是不执行的
	  例子:
	  问小张帅不帅 则输出真的帅
	    var str = "帅";
	    if (str == "帅") {
	    	console.log("真的好帅");
	    }

if-else语句

if-else语句
两个分支  只能执行一个分支

if-else的语法:
if(表达式){
	代码1
}else{
	代码2
}		    
	
 执行过程:
 如果表达式的结果是true则执行代码1 如果表达式的结果是false 则执行代码2
 	
 	案例1:
提示用户输入年龄-----
	var age = prompt("请输入您的年龄");//弹窗------并且有输入 输入的内容存储在age变量中
	console.log(age);

		var age = prompt("请输入您的年龄");
		//判断
		if (age >= 18) {
			console.log("可以看电影了");
		}else{
			console.log("看什么看 回家写作业去");
		}
	判断两个数字中的最大值:
		var num1 = 10;
		var num2 = 20;
		if (num1>num2) {
			console.log(num1);
		}else{
			console.log(num2);
		}
	判断一个数字是奇数还是偶数
		var number = parseInt(prompt("请输入一个数字"));
		if (number%2==0) {
			console.log("偶数");
		}else{
			console.log("奇数");
		}    
		
	判断一个年份是不是闰年
	var year = 1988;
	if(year % 4 == 0 && year % 100 != 0 || year % 400 == 0){
		console.log("闰年");
	}else{
		console.log("平年");
	}

if-else if

 if-else if-else if-else if..........else.......多个分支 最终也是执行一个
  
 if-else if语句........
 
 语法:
 if(表达式1){
 	代码1
 }else if(表达式2){
 	代码2
 }else if(表达式3){
 	代码3
 }else{
 	代码4
 }
 
  else if---------这种结构可以写多个 具体多少个看需求
  else------结构是可以不用写的 具体也是看需求

  执行过程:
  	先判断表达式1的结果,
  	如果为true则执行代码1
  	如果为false则判断表达式2
  	如果表达式2为true则执行代码2
  	如果为false则判断表达式3
  	如果表达式3为true则执行代码3
  	否者执行代码4
  	例子:成绩的结果是在90到100(包含)之间则显示A级
    如果成绩是80到90(包含)之间则显示B级
    如果成绩是70到80(包含)之间则显示C级
    如果成绩是60到70(包含)之间则显示D级
    如果成绩是0到60(包含)之间则显示E级
 
实现代码:
var score = Number(prompt("请输入您的成绩"));
if (!isNaN(score)) {
	if (score > 90 && score <= 100) {
		console.log("A级");
	}else if (score > 80 && score <= 90) {
		console.log("B级");
	}else if (score > 70 && score <= 80) {
		console.log("C级");
	}else if (score > 60 && score <= 70) {
		console.log("D级");
	}else{
		console.log("E级");
	}

}

三元表达式

三元表达式:
运算符号:  ? :
语法:
	 var 变量 =  表达式1?表达式2:表达式3;
执行过程:
  表达式1的结果是true还是false
    如果是true则执行表达式2 然后把结果给变量 
    如果表达式1的结果是false 则执行表达式3 把结果给变量
    
    //两个数字中的最大值
		 var x = 10;
		 var y = 20;
		 var result = x > y ? x : y;
		 console.log(result);

	//显示成年还是未成年
		var age = 18;
		var result2 = age >= 18 ? "成年了" : "未成年";
		console.log(result2);

swith-case语句

swith-case语句-------分支语句-----多分支语句
  		 语法:
  		 switch(表达式){
  		 	case 值1:
  		 		代码1;
  		 		break;
  		 	case 值2:
  		 		代码1;
  		 		break;
  		 	case 值3:
  		 		代码1;
  		 		break;
  		 	case 值4:
  		 		代码1;
  		 		break;
  		 	.....多个case
  		 	default:代码5;	
  		 }
  		 
  	
  	注意问题:
  	default后面的break是可以省略的
  	default也可以省略
  	
  	
  	switch-case语句中和case后面的值做比较的时候使用的是严格的模式
  	break也是可以省略的
  	
  	执行过程:
  	获取表达式的值  和值1做比较 如果一样 则执行代码1 遇到break则跳出整个语句 后面代码不执行
  	如果表达式的值和值1不一样 则和值2做比较 如果相同则执行代码2 遇到break跳出
  	否则和值3比较 相同则执行代码3  遇到break跳出 否则和值4做比较 相同则执行代码4  遇到break跳出 否则直接执行代码5
  	
  	 
  	 
  	 例子:获取一个人的成绩级别 如果是A级则显示90到100直接的分数
  	 	
  	 如果是B级则显示80到90分
  	 如果是C级则显示70到80之间
  	 如果是D级则显示60到70之间
  	 	 *如果是E级则显示59到0之间
  	  
  	 	
  	 	var jiBie = "E";
  	 	switch(jiBie){
  	 		case "A":
  	 			console.log("90到100");
  	 			break;
  	 		case "B":
  	 			console.log("80到90");
  	 			break;
  	 		case "C":
  	 			console.log("70到80");
  	 			break;
  	 		case "D":
  	 			console.log("60到70");
  	 			break;
  	 		case "E":
  	 			console.log("0到59");
  	 			break;
  	 	}


  	  注意问题
  	 	
  	 	 var num = "10";//字符串
  	 	 switch(num){
  	 	 	case 10:
  	 	 	 	console.log("数字的10");
  	 	 	 	break;
  	 	 	case "10":
  	 	 		console.log("字符串的10");
  	 	 		break;
  	 	 }


  	 	 根据数字显示对应的星期
  	     var num = parseInt(prompt("请输入一个星期的数字"));
  	     switch(num){
  	     	case 1:console.log("星期一");break;
  	     	case 2:console.log("星期二");break;
  	     	case 3:console.log("星期三");break;
  	     	case 4:console.log("星期四");break;
  	     	case 5:console.log("星期五");break;
  	     	case 6:console.log("星期六");break;
  	     	case 7:console.log("星期日");break;
  	     	default: console.log("您输入有误");
  	     }

循环语句

for循环

/**
		 *for循环:
		 *
		 *语法:
		 *for(表达式1;表达式2;表达式3){
		 *	循环体;
		 *}
		 *
		 *
		 *执行过程:
		 
		 **先执行一次表达式1 然后判断表达式2 如果不成立则直接跳出循环
		 *如果表达式2成立 执行循环体的代码 结束后 跳到表达式3执行 然后跳到表达式2 判断表达式2是否成立 不成立 跳出循环
		 *如果表达式2成立 则执行循环体 然后跳到表达式3  再跳表达式2  判断是否成立 一直如此
		 *
		 *for(var i=0;i<10;i++){
		 *}
		 *
		 * 
		 */

for循环写乘法口诀

 document.write("<table border='1' cellspacing='0' cellpadding='0'>" );
 	  	for (var i = 1; i <= 9; i++) {
 	  		document.write("<tr>");
 	  		for (var j = 1; j <= i; j++) {
 	  			document.write("<td>");
 	  			document.write(j+"*"+i+"="+ i*j);
 	  			document.write("</td>");
 	  		}
 	  		document.write("</tr>");
 	  	}

 	  document.write("</table>")

while循环

//while循环
	/**
	 *while循环语法:
	 *
	 *计数器
	 *var 变量 = 0;
	 *while(循环的条件){
	 *	循环体;
	 *	计数器++;
	 *}
	 *
	 *执行过程:
	 *先判断条件是否成立(条件的结果是true还是false)如果是false 那么循环的代码(while的大括号中的代码都不执行),如果是true 那么先执行循环体 然后执行计数器 然后 直接去--------->循环的条件 再次判断是否成立 成立则继续执行循环体 否则跳出循环 执行完循环体之后 计数器加1 然后再去循环的条件 判断 成立则循环 否则跳出循环
	 *
	 *
	 * var i = 0;
	 * while(i<20){
	 * 	 循环体;
	 * 	 i++;
	 * }
	 * 
	 * 
	 */

计算1~100之间所有的数字之和

var sum = 0;//存储最终结果
	   var i = 0;//计数器
	   while(i<=100){
	   		sum+=i;//不停的计算数字的和 sum = sum + i;
	   		i++;
	   }
	   console.log("和为:"+sum);

du while 语句

 /**
		  *do-while循环
		  *语法:
		  *do{
		  *	循环体;
		  *}while(条件);
		  *
		  *执行过程:
		  *先执行一次循环体 然后判断天键是否成立 不成立 则跳出循环 成立 则执行循环体 然后 再判断条件是否成立 成立则继续循环 否则跳出
		  *
		  * 
		  */
		 //例子:
		 //输出:哈哈 我又变胖了  10次
		 // var i = 0;//计数器
		 // do{
		 // 	console.log("哈哈 我又变胖了");
		 // 	i++;
		 // }while(i<10);

while 和do while 区别

while循环特点:先判断后循环 有可能一次循环体都不执行
do-while循环特点:先循环 后判断 至少执行一次循环体

break关键字

// break 关键字:如果在循环中使用
// 遇到break,立即跳出循环

 // for (var i = 0; i < 10; i++) {
	// 		while(true){
	// 			console.log("哈哈哈");
	// 			break;
	// 		}
	// 	}
	// 	console.log("哈哈  我出来了");
//找到100到200之间第一个可以被7整除的数字

for (var i = 100; i <= 200; i++) {
			if (i%7==0) {
				console.log(i);
				break;
			}
		}

contiue关键字

 // continue关键字:在循环中如果遇到continue关键字 直接开始下一次循环

   //           var i = 0;
			// while(i<10){
			// 	console.log("哈哈");
			// 	continue;
			// 	i++;
			// }

// 求100~200之间所有的奇数之和(用continue)
        var sum =0;
        var i=100;
        while (i<=200){
        	if(i%2==0){
        		//如果是偶数------->跳过这个数字
        		i++;
        		continue;
        	}
        	    sum+=i;
        	    i++;
        }
        console.log(sum);

break和continue:只能出现在循环语句内部,当循环体内部执行到break关键字时,整个程序会跳出(结束)最近的循环。当循环体内部执行到continue关键字时,程序会结束本一次循环进入到下一次循环。

函数

函数的定义

一段可以被高度复用的代码段

函数声明

function 函数名(){
  // 函数体
}

函数表达式

var fn = function() {
  // 函数体
}

函数声明的时候,函数体并不会执行,只要当函数被调用的时候才会执行。

/**
	 *函数:把一坨重复的代码进行封装 在需要的时候拿出来
	 *函数作用:代码的重用
	 *
	 *函数的参数:
	 *1、形参:函数定义的时候 函数名字后面的小括号里面的变量
	 *2、实参:函数调用的时候 函数名字后面的小括号里的变量或者值
	 *
	 *返回值:
	 *函数中有return  函数有返回值
	 *函数中没有return 函数没有返回值
	 *没明确返回值:函数中没有return或者return后面没有任何内容如果一个函数没有明确的返回值 接收这个函数 结果是undefined
	 *
	 *有参数有返回值的函数
	 *无参数无返回值的函数
	 *无参数有返回值的函数
	 *有参数无返回值的函数
	 *
	 *arguments--------------->获取函数调用的时候 传入的实参的个数
	 *arguments是一个伪数组 是一个对象
	 *arguments.length------>是实参的个数
	 *arguments[索引]------->实参的值
	 *
	 *
	 *作用域:变量的使用范围
	 *全局作用域:全局变量在任何位置都可以使用的范围
	 *局部作用域:局部变量只能在某个地方使用----函数内
	 *作用链:在一个函数中使用一个变量 先在该函数中搜索这个变量 找到了则使用 找不到则继续向外面找这个变量 找到则使用 一直找到全局作用域 找不到则是undefined
	 *全局变量:只要在函数外面声明的变量都可以看成或者理解成是全局变量
	 *局部变量:在函数中定义的变量
	 *
	 *预解析:在代码之前做的事情
	 *变量的声明和函数的声明被提前了 变量和函数的声明会提升到当前所在的作用域的最上面
	 *函数中的变量声明 会提升到该函数的作用域的最上面(里面)
	 *如果有多对script标签都有相同名字的函数 预解析的时候是分段的 互不影响
	 *
	 参数对象:参数对象的使用场景只是在调用函数时传入实参的数量不确定的情况下
	 那么我们就会把形参去掉用参数对象arguments来代替。
	 参数对象的本质是一个类数组,拥有数组下标的特性和.length特性。
	 */


函数的调用

函数名();

特点:

函数体只有在调用的时候才会执行,调用需要()进行调用。
可以调用多次(重复使用)

函数返回值详解

  • ​ 如果函数没有显示的使用 return语句 ,那么函数有默认的返回值:undefined

  • ​ 如果函数使用 return语句,那么跟再return后面的值,就成了函数的返回值

  • ​ 如果函数使用 return语句,但是return后面没有任何值,那么函数的返回值也是:undefined

  • ​ 函数使用return语句后,这个函数会在执行完 return 语句之后停止并立即退出,也就是说return后面的所有其他代码都不会再执行。

IIFE自调用函数

var a = (function(n,m){
	return(n+m)
})(10,5)
console.log(a)

匿名函数如何使用

将匿名函数赋值给一个变量,这样就可以通过变量进行调用
匿名函数自调用

关于自执行函数(匿名函数自调用)的作用:防止全局变量污染。

函数是一种数据类型

function fn() {}
console.log(typeof fn);

函数作为参数

因为函数也是一种类型,可以把函数作为两一个函数的参数,在两一个函数中调用

函数做为返回值

因为函数是一种类型,所以可以把函数可以作为返回值从函数内部返回

function fn(b) {
  var a = 10;
  return function () {
    alert(a+b);
  }
}
fn(15)();

函数作用域

在js中没有块级作用域的概念,只有函数作用域的概念,只有函数才是一个封闭的作用域,在函数体内部声明的变量在函数体外部无法访问。

局部变量和全局变量冲突:以局部变量为准。

函数体内声明全局变量

如果想在函数体内部声明一个全局变量,那么只要去掉声明变量的var关键字即可。

在局部作用域发生嵌套的时候,内层函数是可以访问到外层函数的局部变量的,反之则不行。

变量提升

JS解释器的一种机制,当我们执行某一个函数的时候,JS解释器会从上到下把函数中的局部变量提取出来放到函数体的顶端先声明但不赋值,此时该变量里面存储的值就为undefined。

参数对象arguments

参数对象的使用场景只是在调用函数时传入实参的数量不确定的情况下,那么我们就会把形参去掉用参数对象arguments来代替。参数对象的本质是一个类数组,拥有数组下标的特性和.length特性。

作用域

es5之前没有块级作用域

//作用域:变量的使用范围
	/**
	 *
	 *全局变量:声明的变量是使用var声明的 那么这个变量就是全局变量 全局变量可以在页面的任何位置使用 
	 *局部变量:在函数内部定义的变量 是局部变量 外面不能使用 
	 *全局变量 如果页面不关闭 那么就不会被释放 就会占空间 消耗内存
	 *
	 *
	 *全局作用域:全局变量的使用范围
	 *局部作用域:局部变量的使用范围
	 *
	 * 
	 *块级作用域:一对大括号就可以看成是一块 在这个块区域中定义的变量 只能在这个区域中使用 但是 js中在这个块级作用域中定义的变量 外面也可以使用
	 *说明:js没有块级作用域 只有函数除外
	 *
	 *隐式全局变量:声明的变量没有var 就叫隐式全局变量
	 *全局变量是不能被删除的 隐式全局变量是可以被删除的
	 *定义变量使用var 是不会被删除的 没有var是可以删除的
	 * 
	 */

作用域链

只有函数可以制造作用域结构, 那么只要是代码,就至少有一个作用域, 即全局作用域。凡是代码中有函数,那么这个函数就构成另一个作用域。如果函数中还有函数,那么在这个作用域中就又可以诞生一个作用域。
将这样的所有的作用域列出来,可以有一个结构: 函数内指向函数外的链式结构。就称作作用域链。

预解析

JavaScript代码的执行是由浏览器中的JavaScript解析器来执行的。JavaScript解析器执行JavaScript代码的时候,分为两个过程:预解析过程和代码执行过程

预解析过程:

  • 把变量的声明提升到当前作用域的最前面,只会提升声明,不会提升赋值。
  • 把函数的声明提升到当前作用域的最前面,只会提升声明,不会提升调用。
  • 先提升var,在提升function
//预解析:提前解析代码
		
		/**
		 *预解析: 就是在解析代码之前
		 *预解析做什么事情?
		 *把变量的声明提前了------提前到当前所在的作用域的最上面
		 *函数的声明也会被提前-----提前到当前所在的作用域的最上面
		 *
		 *
		 * 
		 */
		//函数在调用的时候 会把函数的声明提升到作用域的最上面
		 // f1();//调用
		 // var num = 20;//把这个变量的声明会提升到变量使用之前
		 // function f1() {
		 // 	console.log(num);
		 // }

		 // function f1() {
		 // 	console.log("今天外面好吵");
		 // }
		 // f1();
		 // function f1() {
		 // 	console.log("这是f1啊啊啊");
		 // }
		 // f1();


		 //把变量的声明提前了
		 var num;
		 console.log(num);//undefined
		 num = 10;
		 function f1() {
		 	console.log("哈哈 这是f1啊啊啊啊");
		 }
		 f1();

欲解析分段

<body>
	<script>
	//预解析中 变量的提升 只会在当前的作用域中提升 提前到当前的作用域的最上面
	//函数中的变量只会提前到函数的作用域中的最前面 不会出去
	//预解析会分段(多对script标签中函数重名 预解析的时候不会冲突)
		function f1() {
			console.log(num);//undefined
			var num = 10;
		}
		f1();
		console.log(num);


		function f1() {
			console.log("哈哈哈");
		}
	</script>
	<script>
		f1();
		function f1() {
			console.log("嘎嘎");
		}

	</script>
</body>
	 

数组

数组基本概念

若干个值的有序集合。拥有下标的特性,可以使用数组[数字]的形式从一个数组集合中提取出来某一个元素;拥有.length特性,可以使用数组.length返回数组中成员的数量(数组的长度)

//数组:一组有序的数据
	//数组的作用:可以一次性存储多个数据
	//数组的定义:
	/**
	 *1、通过构造函数的方式创建数组
	 *语法:
	 *  var 数组名 = new Array();
	 *  var array = new Array();//定义了一个数组
	 *  数组的名字如果直接输出 那么 就可以把数组中的数据显示出来 如果没有数据 就看不到数据
	 *  var 数组名 =  new Array(长度);
	 *  如果数组中没有数据 但是有长度---------,数组中的每个值就是undefined
	 *  构造函数的方式创建数组的时候 如果在Array(一个数字)------->数组的长度(数组元素的个数) 如果在Array(多个值)这个数组中就有数据了 数组的长度就是这些数据的个数
	 *  
	 *2、通过字面量的方式创建数组
	 *var 数组名 = [];//空数组
	 *var array = [];
	 *
	 *无论是构造函数的方式还是字面量的方式 定义的数组 如果有长度 那么默认是undefined
	 *
	 * 数组:一组有序的数据
	 * 数组元素:数组中存储的每一个数据 都可以叫数组的元素 比如:存储了3个数据 数组中就有3个元素
	 * 数组长度:就是数组元素的个数 比如有3个元素 就说 这个数组的长度是3
	 * 数组索引(下标):用来存储或者访问数组中的数据的 索引从0开始 到长度减1结束 数组的索引和数组的长度的关系:长度减1就是最大的索引值
	 *
	 *
	 * 如果设置数组中的某个位置的值
	 * 数组名[下标] = 值;
	 * arr[3] = 100;
	 * 如何获取数组中某个位置的值
	 * var result = 数组名[下标]
	 * console.log(result);
	 * 
	 */

	 //通过构造函数的方式定义一个数组
		 // var array = new Array(5);//没有数据 空数组
		 // console.log(array);


		// var arr1 = new Array();//构造函数的方式------空数组
	 	//var arr2 = new Array(5);//构造函数的方式定义了一个数组 数组中有5个元素 每个数据是undefined
		// console.log(arr2[3]);
		// var arr3 = new Array(10,20,100,30,50,60);
		// console.log(arr3);

		// var arr = new Array(10,20,30,40,100);
		// console.log(arr[4]);//获取
	 //设置
	 	//arr[3] = 1000;
	 	//console.log(arr);


	 //通过字面量的方式 创建一个数组
	 	// var arr = [];
	 	// console.log(arr);

数组注意问题

	var arr = [10,2,3,4,1];
		//长度:5
		//索引:0~4
		//console.log(arr);

		//数组中存储的数据类型一定是一样的吗?类型可以不一样
		// var arr = [10,"哈哈",true,null,undefined,new Object()];
		// console.log(arr);


		//数组中的长度是不是可以改变呢? 可以
		var arr = [];
		//通过索引来设置数组中的元素的值
		arr[0] = 10;
		arr[1] = 20;
		console.log(arr.length);
		//获取元素的值 通过索引的方式
		console.log(arr[2]);

数组的全局方法

普通方法

* push(exp)   向后插
* pop()    删除最后一个
* unshift(exp) 向前插
* shift()   删除第一个
* splice(index,howmany,repalcement) 添加/删除/替换
* splice(要插入位置的下标,0,要插入的元素) 往数组中某一个位置塞一个元素进去 
* splice(要删除元素的下标,1) 从数组中删除掉某一个元素 
* concat(array)  数组拼接
* join()  将数组转换为字符串,参数作为分隔符。
* reverse() 颠倒数组中元素的顺序
* slice(start,end)  从数组中截取一部分,end作为结束下标在结果集中并不包含该元素。
* 
// sort方法在不传入参数的情况下,默认以字符编码顺序进行排序,而非数学大小关系进行排序。
	// 使用sort进行按照数学大小关系进行排序。
	arr.sort(function(num1,num2){
		return num1-num2;
	})
/**
	 *Array.isArray(对象)----------->判断这个对象是不是数组
	 *instanceOf关键字
	 *.concat(数组,数组,数组........) 组合一个新数组
	 *.every(函数)-------返回的是布尔类型 函数作为参数使用 函数中有三个参数 第一个参数是元素的值 第二个是索引值 第三个参数是原来的数组(没用)
	 *如果这个数组中的每个元素的值都符合条件 最后才返回的是true
	 *.filter(函数) 返回的是数组中每一个元素都符合条件的元素 组成一个新数组
	 *.push(值)------>把值追加到数组中 加到最后------返回值也是追加数据之后的数组长度
	 *.pop()------>删除数组中最后一个元素 返回值就是删除的这个值
	 *.shift()-------->删除数组中的第一个元素 返回值就是删除的这个值
	 *.unshift()------>向数组的第一个元素前面插入一个新的元素------>返回值是插入后的长度
	 *.forEach(函数)方法-----遍历数组用-----相当于for循环
	 *.indexOf(元素值)-------返回的索引没有则是-1
	 *.join("字符串")------返回的是一个字符串
	 *.map(函数)----------数组中的每一个元素都要执行这个函数 把执行后的结果重新的全部放在一个新的数组中
	 *.reverse()----->反转数组
	 *.sort()------------排序的 可能不稳定 如果不稳定 请写MDN中那个固定代码
	 *.arr.slice(开始索引,结束索引) 把截取的数组的值放在一个新的数组中 但是不包含结束的索引对应的元素值
	 *.splice(开始的位置,要删除的个数,替换的元素的值)一般是用于删除数组中的元素 或者是替换元素 或者是插入元素
	 *
	 * 
	 */

ES5下的Array的全局方法

* forEach(function(item,index,array){})

	// forEach:在循环数组的时候可以使用
	var arr = [1,5,6,4];

	arr.forEach(function(item,index,array){
		// 本次循环出来的成员
		console.log(item)
		// 本次循环的下标
		console.log(index)
		// 被循环数组本身
		console.log(array)
	})
* map(function(item,index,array){})

	// map 修改原数组中成员并返回一个新的数组
	var arr = [1,5,6,4];
	var newArr = arr.map(function(item,index,array){
		//修改原成员
		return item+1;
	})
* filter(function(item.index,array){})

	// filter过滤某一个数组中的成员,并返回一个新数组。

	// 从一个结果集中过滤掉某一些元素并返回新元素
	var arr = [1,5,6,3,4,1,2,5];
	console.log(arr.filter(function(item){
		return !!(item % 2)
	}))
* reduce(function(temp,temp2){})

	// reduce返回一个结果(不一定是数组),循环次数是整个数组长度-1,第一个参数第一次循环时代表数组的第0位,以后每一次都代表的是上一次循环的return语句的返回值;第二个参数第一次循环代表的是下标为1的那位元素,以后为下标为2,下标为3....。如果我们希望将某一个数组中的所有成员进行累计计算的话,此时是最适合reduce方法的使用的。

	var arr = [1,5,6,3,4,1,2,5];
	console.log(arr.reduce(function(temp,temp2){
		console.log(temp)
		return temp + temp2;
	}))

对象

值的无序集合,一个key:value这种键值对的数据结构,对象也可以被看做成是若干个属性的无序集合。

定义对象和遍历

	// 声明一个空对象
	var o = {}
	// 声明一个不空的对象
	var o = {
		name:"王大伟",
		age:18,
		sex:"male",
		married:false,
		// 方法:如果对象中哪一条属性的属性值是一个函数,那么我们就不太愿意把它叫成属性,而愿意叫做方法。
		sayHello:function(){
			alert("我叫王大伟~O(∩_∩)O")
			return 1;
		}
	}
	
	// !!对象的查询的两种写法:
	Object.属性名  	在Object对象中直接查找某个属性名的所对应的属性值
	Object["属性名"] 与上面等价
	Object[属性名]  把属性名看做成一个变量,首先从js执行环境上下文中获取该变量存放的值(如:"a"),然后再从Object对象中查找变量中存放的值所对应的属性的属性值(a属性的属性值)

	// 对象的修改/添加
	Object.属性名 = 新值
	Object["属性名"] = 新值

	// 删除掉对象中的某一条属性
	delete Object.属性名

	// 检测一个属性在另外一个对象中是否存在
	
	"属性名" in Object

	// 枚举对象

	for(var i in Object){
		// 循环体执行次数取决于Object属性的个数
		// 每一次进入循环体的时候i依次代表了对象的属性名

		// 对象的属性名
		// console.log(i)
		// 对象的属性值
		// console.log(Object[i])
	}

编程思想

/**
	 *编程思想:把一些生活中做事的经验融入到程序中
	 *面向过程:凡事都亲力亲为 每件事的具体过程都要知道 注重的是过程
	 *面向对象:根据需求找对象 所有的事情都用对象来做 注重的是结果
	 *
	 *
	 *面向对象特性:封装 继承 多态(抽象性)
	 *js不是面向对象的语言,但是可以模拟面向对象的思想
	 *js是一门基于对象的语言:
	 *万物皆对象-------->狗 人 
	 *
	 *
	 *什么是对象?
	 *看得见 摸得到 具体特指的某个东西
	 *
	 *找对象
	 *描述对象
	 *文字描述找对象
	 *小明牵着小狗去逛街
	 *一台电视机正在播放新闻
	 *
	 *
	 *分析对象有什么特点:特征和行为
	 *对象:有特征和行为 具体特指的是某一个事物
	 *
	 *对象:有属性和方法 具体特指的是某一个事物
	 *
	 * 
	 *没有对象
	 *
	 *
	 *
	 *
	 *
	 *创建对象
	 *
	 * 
	 */

	 // 声明一个空对象
	var o = {}
	// 声明一个不空的对象
	var o = {
		name:"王大伟",
		age:18,
		sex:"male",
		married:false,
		// 方法:如果对象中哪一条属性的属性值是一个函数,那么我们就不太愿意把它叫成属性,而愿意叫做方法。
		sayHello:function(){
			alert("我叫王大伟~O(∩_∩)O")
			return 1;
		}
	}
	
	// !!对象的查询的两种写法:
	Object.属性名  	在Object对象中直接查找某个属性名的所对应的属性值
	Object["属性名"] 与上面等价
	Object[属性名]  把属性名看做成一个变量,首先从js执行环境上下文中获取该变量存放的值(如:"a"),然后再从Object对象中查找变量中存放的值所对应的属性的属性值(a属性的属性值)

	// 对象的修改/添加
	Object.属性名 = 新值
	Object["属性名"] = 新值

	// 删除掉对象中的某一条属性
	delete Object.属性名

	// 检测一个属性在另外一个对象中是否存在
	
	"属性名" in Object

	// 枚举对象

	for(var i in Object){
		// 循环体执行次数取决于Object属性的个数
		// 每一次进入循环体的时候i依次代表了对象的属性名

		// 对象的属性名
		// console.log(i)
		// 对象的属性值
		// console.log(Object[i])
	}
!基本类型和引用类型:除了Object以外所有的数据类型都是基本类型,Object自身是引用类型。在将引用类型的变量(a)赋值给另外的一个变量(b)时,b本身存储的只是一个对a的引用,也可以理解为是一个快捷方式,所以无论修改的是a还是b都会对另外的一个变量产生影响。而基本类型,在将一个变量赋值给另外的一个变量时,执行的是值的拷贝,此时有两个相同的值,所以无论修改了哪一个都不会对另外的一个变量产生影响。

入代码片

创建对象三种方式

//第一种创建对象的方式:
      1 调用系统的构造函数创建对象
	  var 变量名 = new Object(); Object是系统的构造函数
	//实例化对象:
		var obj = new Object();
		//对象有特征------属性和行为-----方法
		//添加属性------如何添加属性? 对象.名字 = 值
		obj.name = "小白";
		obj.age = 38;
		obj.sex = "女";
		//如何添加方法--------对象.名字 = 函数
		obj.eat = function() {
			console.log("我喜欢吃油炸榴莲凉拌臭豆和大蒜");
		}
		obj.play = function() {
			console.log("我喜欢玩飞机模型");
		}
		obj.cook = function() {
			console.log("切菜");
			console.log("洗菜");
			console.log("放油");
			console.log("放盐");
			console.log("炒");
			console.log("装盘");
			console.log("吃");
		}
		//方法的调用
		obj.eat();
		console.log(obj.name);
		
	2、自定义构造函数创建对象
	//自定义构造函数创建对象  自己定义一个构造函数 自定义构造函数 创建对象

   自定义构造函数创建对象做了那些事?
	 1、在内存中开辟(申请一块空闲的空间)空间 存储创建的新的对象
	 2、把this设置为当前的对象
	 3、设置对象的属性和方法
	 4、把this对象返回
	 
	//函数和构造函数的区别 名字是不是大写(首字母是大写)
	function Person(name,age) {
		this.name = name;
		this.age = age;
		this.sayHi = function() {
			console.log("我叫:"+this.name+",年龄是:"+this.age);
		};
	}
	//自定义构造函数创建对象 先自定义一个构造函数 创建对象
	var obj = new Person("小明",10);
	console.log(obj.name);
	console.log(obj.age);
	obj.sayHi();

	var obj2 = new Person("小红",18);
	console.log(obj2.name);
	console.log(obj2.age);
	obj2.sayHi();

	console.log(obj instanceof Person);
	console.log(obj2 instanceof Person);

	//自定义构造函数创建对象
	function Dog(name,age,sex) {
		this.name = name;
		this.age = age;
		this.sex = sex;
	}
	var dog = new Dog("小黑",20,"男");
	console.log(dog instanceof Dog);
	console.log(dog instanceof Person);//false

3、字面量的方式创建对象
	var obj = {};
		obj.name = "小白";
		obj.age = 10;
		obj.sayHi = function() {
			console.log("我是:"+this.name);
		}
		obj.sayHi();

		var obj2 = {
			name:"小明",
			age:20,
			sayHi:function() {
				console.log("我是:"+this.name);
			},
			eat:function() {
				console.log("吃了");
			}
		}
		obj2.sayHi();
		obj2.eat();

工场模式创建对象

 //工厂模式创建对象
	   function createObject(name,age) {
	      var obj = new Object();
	      //添加属性
	      obj.name = name;
	      obj.age = age;
	      //添加方法
	      obj.sayHi = function() {
	      	 console.log("萨瓦斯卡 我叫:"+this.name+"我今年:"+this.age);
	      }
	      return obj;
	    }

	    //创建人的对象
	    var per1 = createObject("小白",18);
	    per1.sayHi();
	    //创建另外一个人的对象
	    var per2 = createObject("小红",22);
	    per2.sayHi();

数据类型

  • 原始数据类型:number string boolean undefined null object

  • 基本类型(简单类型),值类型:number string boolean

  • 复杂类型(引用类型): object

  • 空类型:undefined null

//var num = 10;//值类型 在栈上
//var obj = {};//复杂类型 对象在堆上 地址(引用)在栈上

//值类型之间的传递 传递的是值
//引用类型之间的传递 传递的是地址(引用)
//值类型作为函数的参数 传递的是值
//引用类型作为函数的参数 传递的是地址

基本类型和引用类型:除了Object以外所有的数据类型都是基本类型,Object自身是引用类型。在将引用类型的变量(a)赋值给另外的一个变量(b)时,b本身存储的只是一个对a的引用,也可以理解为是一个快捷方式,所以无论修改的是a还是b都会对另外的一个变量产生影响。而基本类型,在将一个变量赋值给另外的一个变量时,执行的是值的拷贝,此时有两个相同的值,所以无论修改了哪一个都不会对另外的一个变量产生影响。

基本包装类

普通变量不能直接调用属性或者方法
对象可以直接调用属性和方法

基本包装类型:本身是基本类型 但是在执行代码的过程中 如果这种类型的变量调用了属性或者方法 那么这种类型就不再是基本类型了 而是基本包装类型 这个变量也就不是普通变量了 是基本包装类型对象

//string number boolean

	var str = "hello";
	str = str.replace("ll","HH");
	console.log(str);

	var str = new String("hello");
	str =  str.replace("ll","aa");
	console.log(str);
	str = null;

	var num = 10;//number
	console.log(num.toString());
	 

	 //如果是一个对象&&true 那么结果是true
	 //如果是true&&对象 那么结果就是对象
	 
	var flag = new Boolean(false);
	var result = true&&flag;
	 console.log(result);
	 	
	 	var num = 10;
	 	var num2 = Number("10");//转换 没有new-----类型转
		var num3 = new Number("10");//基本包装类型
		console.log(num2,num3);

String对象的全局方法

/**
	 *字符串的常用属性:
	 *.length--------->字符串的长度
	 *.charAt(索引),返回值是指定索引位置的字符串 超出索引结果是空字符串
	 *.fromCharCode(数字值,可以是多个参数),返回的是ASCII码对应的值
	 *.concat(字符串1,字符串2.........)返回的是拼接之后的新的字符串
	 *.indexOf(要找的字符串,从某个位置开始的索引);返回的是这个字符串的索引值 没找到则返回-1
	 *.lastIndexOf(要找的字符串);从后向前找但是索引仍然是从左向右的方式 找不到则返回-1
	 *.replace("原来的字符串","新的字符串");用来替换字符串的
	           // 默认的是惰性匹配
	           str.replace("d","m")
	          // 全局匹配:必须使用正则表达式来表述
	           str.replace(/d/g,"m")
	 *.slice(开始的索引,结束的索引);从索引(开始的索引)的位置开始提取 到索引为(结束的索引)的前一个结束 没有结束的索引
	 *.split("字符串或正则表达式,从该参数指定的地方分割 stringObject。",切割后留下的个数);把字符串分割为字符串数组。
	 *.substr(开始的位置,个数);返回的是截取后的新的字符串
	 *.substring(开始的索引,结束的索引)返回的是截取后的字符串 不包含结束的索引的字符串
	 *.toLowercase();转小写
	 *.toLocaleUpperCase();转大写
	 *.toUpperCase();转大写
	 *.trim();干掉字符串两端的空格
	 *
	 */

Math对象

* abs() 返回绝对值
* ceil() 向上取整
* floor() 向下取整:类似于parseInt()
* round() 四舍五入
* max() 返回最大值或最小值
* min()
* random() 返回0-1之间的伪随机数(不可能等于1,有可能等于0)

随机产生一个16进制颜色的函数

//随机产生一个十六进制的颜色值
		// console.log(parseInt(Math.random()*5));

	   function getColor() {
	  	 	var str = "#";
	  	 	//一个十六进制的值的数组
	  	 	var arr = ["0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f"];
	  	 	for (var i = 0; i < 6; i++) {
	  	 		//产生每一个随机数都是一个索引 根据索引找到数组中对应的值
	  	 		var num = parseInt(Math.random()*16);
	  	 		str += arr[num];
	  	 	}
	  	 	return str;  
	   }
	   //页面加载的事件
	   window.onload = function() {
	   	 //在文档中通过id属性的值查找这个元素(标签) 设置标签的背景颜色
	   	 document.getElementById("dv").style.backgroundColor = getColor();
	   }

Date对象

// 如果想要使用Date对象首先先得实例化
var date = new Date()
* getFullYear() 返回年份
* getMonth() 返回月份(0开始计数)现实中需要加1
* getDate() 返回日期
* getHours() 返回小时数
* getMinutes() 返回分钟数
* getSeconds() 返回秒数
* getDay()  返回星期(周日返回的是0)
* getTime() 获取时间戳(19701100:00:00到现在一共经过了多少毫秒)

格式化日期

/**
 * 格式化日期
 * @param dt 日期对象
 * @return {string}   返回值是格式化的字符串日期
 */
function getDates(dt){
	var str = "";//存储时间的字符串
	//获取年
	var year = dt.getFullYear();
	//获取月
	var month = dt.getMonth()+1;
	//获取日
	var day = dt.getDate();
	//获取小时
	var hour = dt.getHours();
	//获取分钟
	var min = dt.getMinutes();
	//获取秒
	var sec = dt.getSeconds();
	month = month<10?"0"+month:month;
	day = day<10?"0"+day:day;
	hour = hour<10?"0"+hour:hour;
	min = min<10?"0"+min:min;
	sec = sec<10?"0"+sec:sec;
	str = year + "年" + month + "月" + day + "日" + hour + ":" + min + ":" + sec;
	return str;
}

数据类型检测

  • typeof
  • instanceof
  • Object.prototype.toString.call()

Web API

API的概念

API(Application Programming Interface,应用程序编程接口)是一些预先定义的函数,目的是提供应用程序与开发人员基于某软件或硬件得以访问一组例程的能力,而又无需访问源码,或理解内部工作机制的细节。

  • 任何开发语言都有自己的API
  • API的特征输入和输出(I/O)
  • API的使用方法(console.log())

Web API的概念

浏览器提供的一套操作浏览器功能和页面元素的API(BOM和DOM)

此处的Web API特指浏览器提供的API(一组方法),Web API在后面的课程中有其它含义

BOM(浏览器对象模型)

BOM的概念

BOM(Browser Object Model) 是指浏览器对象模型,浏览器对象模型提供了独立于内容的、可以与浏览器窗口进行互动的对象结构。BOM由多个对象组成,其中代表浏览器窗口的Window对象是BOM的顶层对象,其他对象都是该对象的子对象。

我们在浏览器中的一些操作都可以使用BOM的方式进行编程处理,

比如:刷新浏览器、后退、前进、在浏览器中输入URL等

BOM的顶级对象window

window是浏览器的顶级对象,当调用window下的属性和方法时,可以省略window

注意:window下一个特殊的属性 window.name

三个对话框

  • 1 alert(“内容”) 弹出框

  • 2 confirm(“提示信息”) 确认框 确定返回true 取消返回false

  • 3 prompt(“提示信息”,“默认值”) 输入框 返回数值为字符串

     window.alert("您好啊");
     //注意:弹出框是具有阻塞行为的
     
     //prompt([描述信息],[默认值]);
     window.prompt("请输入账号");
     
     //返回boolean值 
	 //confirm([描述信息]);
     var result=window.confirm("你确定退出吗");
     console.log(result)

两个定时器

setTimeout()和clearTimeout()
在指定的毫秒数到达之后执行指定的函数,只执行一次

	var a = setTimeout(function(){
	 	console.log("heihei");
	 },5000);
	
	clearTimeout(a)

setInterval()和clearInterval()

// 创建一个定时器,每隔1秒调用一次
var timerId = setInterval(function () {
  var date = new Date();
  console.log(date.toLocaleTimeString());
}, 1000);

// 取消定时器的执行
clearInterval(timerId);

onload ,onunload ,onbeforunload事件

window.onload = function(){
    console.log("页面加载完成"); 
}
  // 页面关闭后触发
window.onunload=function(){
	alert("哈哈");
}

// 页面关闭前触发
window.onbeforunload=function(){
	alert("哈哈");
}

location 地址对象

location对象是window对象下的一个属性,可以省略window对象
location可以获取或者设置浏览器地址栏的URL

URL统一资源定位符 (Uniform Resource Locator, URL)

scheme://host:port/path?query#fragment
scheme:通信协议
	常用的http,ftp,maito等
host:主机
	服务器(计算机)域名系统 (DNS) 主机名或 IP 地址。
port:端口号
	整数,可选,省略时使用方案的默认端口,如http的默认端口为80。
path:路径
	由零或多个'/'符号隔开的字符串,一般用来表示主机上的一个目录或文件地址。
query:查询
	可选,用于给动态网页传递参数,可有多个参数,用'&'符号隔开,每个参数的名和值用'='符号隔开。例如:name=zs
fragment:信息片断
	字符串,锚点.
<input type="button" value="显示效果" id="btn" />
	<script>

    console.log(window.location);
    console.log(window.location.hash);
    console.log(window.location.host);
    console.log(window.location.hostname);
    console.log(window.location.pathname);
    console.log(window.location.port);
    console.log(window.location.protocol);
    console.log(window.location.search);

onload=function(){
	document.getElementById("btn").onclick=function(){
		location.href="https://music.163.com";
		location.assign("https://music.163.com");
		location.reload();
		location.replace("https://www.baidu.com");
	}
}

    // location:地址对象
	// loaction.href url读
	// location.href = "http://www.baidu.com" url写
	// location.replace("http://www.baidu.com") 重新加载	
	// location.reload(); 刷新
	</script>

history对象

history.back()
history.forward()
history.go()

navigator对象

//通过userAgent可以判断用户浏览器的类型
console.log(window.navigator.userAgent);
//通过platform可以判断浏览器所在的系统平台类型.
console.log(window.navigator.platform);

DOM的概念

文档对象模型(Document Object Model,简称DOM),是W3C组织推荐的处理可扩展标志语言的标准编程接口。在网页上,组织页面(或文档)的对象被组织在一个树形结构中,用来表示文档中对象的标准模型就称为DOM。Document Object Model的历史可以追溯至1990年代后期微软与Netscape的“浏览器大战”,双方为了在JavaScript与JScript一决生死,于是大规模的赋予浏览器强大的功能。微软在网页技术上加入了不少专属事物,既有VBScript、ActiveX、以及微软自家的DHTML格式等,使不少网页使用非微软平台及浏览器无法正常显示。DOM即是当时蕴酿出来的杰作。

  • 文档:一个网页可以称为文档
  • 节点:网页中的所有内容都是节点(标签、属性、文本、注释等)
  • 元素:网页中的标签
  • 属性:标签的属性
Document 对象使我们可以从脚本中对 HTML 页面中的所有元素进行访问
Document 对象是 Window 对象的一部分,可通过 window.document 属性对其进行访问

document

document对象下获取页面元素的某些方法

getElementById("ID名") 通过ID获取元素 -> Node
getElementsByTagName("标签名") 通过标签名获取元素 ->  NodeList
getElementsByClassName("class名") 通过类名获取元素 -> NodeList
querySelector("ID,类,标签元素") 获取单个的ID,,标签元素 -> Node
querySelectorAll("ID,类,标签元素") 批量获取 类 和  标签  -> NodeList
getElementsByName("name名")通过name名获取元素-> NodeList
//如果获取到的是NodeList,调用方法和属性需要遍历

document.write(“heihei”)

window.document.write("heihei");
//一:有自动解析标签的功能
//二:与事件连用,会覆盖原本的内容

NodeList和HTMLcollection的区别

<body>
    <ul>
        <li class="a1">a1</li>
        <li class="a1">a2</li>
        <li class="a1">a3</li>
        <li class="a1">a4</li>
    </ul>

    <script>
        // 只有querySelectorAll返回的节点集合才是真实的NodeList
        // 获取之后的数组成员是静态的,无论在获取之后如何删除页面中的元素,这个数组的成员数量都不会发生改变。
        let liList1 = document.querySelectorAll("li")
        
        // 获取之后的数组成员是动态的。
        let liList2 = document.getElementsByTagName("li")
        let liList3 = document.getElementsByClassName("a1")     
    </script>

Node节点-Object数据类型,对应的是页面中的某一个元素,带的某些方法和属性

className //设置/获取该元素class属性的属性值

style //设置/获取元素的行内样式;设置非行内样式
getComputedStyle(元素,false)["attr"];//获取非行内样式、也就是读取,谷歌 火狐支持
my$("dv").currentStyle.left//IE8支持

setAttribute("key"."value") //设置该元素的某一个行内属性的属性值
getAttribute("key") //获取该元素的某一个行内属性的属性值
removeAttribute("class");//移除自定义属性/也可以移除元素的自带的属性

let newItem = _mbli.cloneNode(true);// 克隆
function getStyle(element,attr){
		  //判断浏览器是否支持这个方法
 return   window.getComputedStyle?window.getComputedStyle(element,null)[attr]:element.currentStyle[attr];
}

tab切换-用到自定义属性设置,获取,和移除。

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>Document</title>
	<style>
		*{
			margin: 0;
			padding: 0;
		}
		ul{
			list-style-type: none;
		}
		.box{
			width: 400px;
			height: 300px;
			border: 1px solid #ccc;
			margin: 100px auto;
			overflow: hidden;
		}
		.hd{
			height: 45px;
		}
		.hd span{
			display: inline-block;
			width: 90px;
			background-color: pink;
			line-height: 45px;
			text-align: center;
			cursor: pointer;
		}
		.hd span.current{
			background-color: purple;
		}
		.bd li{
			height: 255px;
			background-color: purple;
			display: none;
		}
		.bd li.current{
			display: block;
		}

	</style>
</head>
<body>
	<div class="box" id="box">
		<div class="hd">
			<span class="current">体育</span>
			<span>娱乐</span>
			<span>新闻</span>
			<span>综合</span>
		</div>
		<div class="bd">
			<ul>
				<li class="current">我是体育模块</li>
				<li>我是娱乐模块</li>
				<li>我是新闻模块</li>
				<li>我是综合模块</li>
			</ul>
		</div>
	</div>
	<script src="common.js"></script>
	<script>
		//获取最外面的div
		var box = my$("box");
		//获取的是里面的第一个div
		var hd = box.getElementsByTagName("div")[0];
		//获取的是里面的第二个div
		var bd = box.getElementsByTagName("div")[1];
		//获取所有的li标签
		var list = bd.getElementsByTagName("li");
		//获取所有的span标签
		var spans = hd.getElementsByTagName("span");
		//循环遍历的方式 添加点击事件
		for (var i = 0; i < spans.length; i++) {
			//在点击之前就把索引保存在span标签中
			spans[i].setAttribute("index",i);//======================
			spans[i].onclick = function(){
				//第一件事 所有的span的类样式全部移除
				for (var j = 0; j < spans.length; j++) {
					spans[j].removeAttribute("class");
				}
				//第二件事:当前被点击的span应用类样式
				this.className = "current";
				//span被点击的时候 获取存储的索引值
				// alert(this.getAttribute("index"));
				var num = this.getAttribute("index");//=============================
				//获取所有的li标签 每个li标签先全部隐藏
			   for (var k = 0; k < list.length; k++) {
			   		list[k].removeAttribute("class");
			   }
			   //当前被点击的span对应的li标签显示
			   list[num].className = "current";
			}
		}


	</script>
</body>
</html>

outerHTML/innerText/innerHTML/textContent各种文本内容

outerHTML/innerText/innerHTML

  • innerHTML

  • 将元素中所有的内容都获取到 包括HTML标签 但是不包括自身标签

  • innerText

  • 将元素的内容获取出来不包括HTML标签

  • outerHTML

  • 将自身以及子元素所有的内容都获取出来 包括HTML标签 包括自身标签

    	<div id="d">
			老王<span>住你隔壁</span>
		</div>
<script>
	var oD = document.querySelector("#d");
	//innerHTML  该元素的所有内容  最常用的
    console.log(oD.innerHTML);
	//innerText  该元素文本内容,如果元素内有标签,不管标签
	console.log(oD.innerText);
	//outerHTML  包含元素自身标签的所有内容
	console.log(oD.outerHTML);
//总结:如果使用innerText主要是设置文本的 设置标签内容 是没有标签的效果的
//总结:innerHTML是可以设置文本内容
//总结:innerHTML主要的作用是在标签中设置新的html标签内容 是有标签效果的
		
</script>

innerText和textContent

<input type="button" value="设置值" id="btn" />
	<div id="dv">哦 这真是一个神奇的一天</div>
	<script src="common.js"></script>
	<script>
	//设置标签中的文本内容的时候 应该使用textContent属性 谷歌 火狐支持 IE8不支持
	//设置标签中的文本内容的时候 应该使用innerText属性 谷歌 火狐 IE8都支持
	
	//如果这个属性在浏览器中不支持 那么这个属性的类型就是undefined
	//判断这个属性的类型 是不是undefined 就知道浏览器是否支持
	
	my$("btn").onclick = function(){
	my$("dv").textContent = "哈哈";
	console.log(typeOf(my$("dv").textContent));
	}
	</script>

innerText和textContent 两者兼容代码

//兼容代码
		//设置任意的标签中间的任意文本内容
		function setInnerText(element,text){
			//判断浏览器是否支持这个属性
			if (typeof element.textContent == "undefined"){
				element.innerText = text;
			}else{//支持这个属性
				element.textContent = text;
			}
		} 
	    //获取任意标签中间的文本内容
	    function getInnerText(element){
	    	if (typeof element.textContent == "undefined") {
	    		return element.innerText;
	    	}else{
	    		return element.textContent;
	    	}
	    }

节点

节点的相关属性

	 节点的属性(可以用标签----元素.出来,可以使用属性节点.出来,文本节点.出来)
	 1 nodeType:节点的类型: 
	      1-------------标签
          2------属性 
          3-------文本
	 2 nodeName:节点的名字:
	      标签的节点-----大写的标签名字
	      属性节点-----小写的属性名字
	      文本--------#text
	 3 nodeValue:节点的值:
	      标签节点--------null 
	      属性节点-----属性的值  
	      文本节点------文本内容

根据层级关系访问节点/元素

	<script>
		//12行代码:都是获取节点和元素的
		var ulObj = document.getElementById("uu");
		//父级节点
		console.log(ulObj.parentNode);//div
		//父级元素
		console.log(ulObj.parentElement);//div
		//子节点
		console.log(ulObj.childNodes);//
		//子元素
		console.log(ulObj.children);
		console.log("=====================================");
		//第一个子节点
		console.log(ulObj.firstChild);
		//第一个子元素
		console.log(ulObj.firstElementChild);
		//最后一个子节点
		console.log(ulObj.lastChild);
		//最后一个子元素
		console.log(ulObj.lastElementChild);
		//某个元素的前一个兄弟节点
		console.log(my$("three").previousSibling);
		//某个元素的前一个兄弟元素
		console.log(my$("three").previousElementSibling);
		//某个元素的后一个兄弟节点
		console.log(my$("three").nextSibling);
		//某个元素的后一个兄弟元素
		console.log(my$("three").nextElementSibling);

		//总结:凡是获取节点的代码在谷歌和火狐得到的都是相关的节点
		//凡是获取元素的代码在谷歌和火狐得到的都是 相关的元素
		//凡是子节点和兄弟节点开始 凡是获取节点的代码在IE8中得到的都是元素 
		//获取元素的相关代码在IE8中得到的都是undefined------元素的代码IE不支持

    	var ulObj = document.getElementById("uu");
		console.log("======================================================");
		//第一个子节点
		console.log(ulObj.firstChild.innerText);//----------------IE8中式第一个子元素
		//第一个子元素
		console.log(ulObj.firstElementChild);//------------------------IE中不支持
		//最后一个子节点
		console.log(ulObj.lastChild.innerText);//---------------IE8中是最后一个子元素
		//最后一个子元素
		console.log(ulObj.lastElementChild);//------------------IE中不支持
		//某一个元素的前一个兄弟节点
		console.log(my$("three").previousSibling.innerText);
		//某一个元素的前一个兄弟节点
		console.log(my$("three").previousElementSibling);
		//某一个元素的后一个兄弟节点
		console.log(my$("three").nextSibling.innerText);
		//某一个元素的后一个兄弟元素
		console.log(my$("three").nextElementSibling);
	</script>

节点的创建-添加,插入,替换,删除。

 //创建一个元素节点
document.createElement(HTML标签名)
 //newChild 被添加到孩子列表中的末端。
node.appendChild(newChild)
   
 // 将 newChild 节点插入到 referenceNode 之前。
node.insertBefore(newChild, referenceNode)

 //用newChild节点替换oldChild节点
node.replaceChild(newChild, oldChild) 

//删除node节点中的firstEle
node.removeChild(firstEle);

 //删除node子节点。
node.remove() 

小案例

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>Document</title>
	<style>
		div{
			width: 200px;
			height: 200px;
			border: 1px solid red;
		}
	</style>
</head>
<body>
	<input type="button" value="显示效果" id="btn" />
	<input type="button" value="干掉第一个子元素" id="btn2" />
	<input type="button" value="干掉所有子元素" id="btn3" />
	<div id="dv"></div>
	<script src="common.js"></script>
	<script>
		var i = 0;
		my$("btn").onclick = function(){
			i++;
			var obj = document.createElement("input");
			obj.type = "button";
			obj.value = "按钮"+ i;
			// my$("dv").appendChild(obj);//追加子元素
			//把新的子元素插入到第一个子元素的前面
			my$("dv").insertBefore(obj,my$("dv").firstElementChild);
			// my$("dv").replaceChild();//--------自己玩
		};
		my$("btn2").onclick = function(){
			//移除父级元素中第一个子级元素
			my$("dv").removeChild(my$("dv").firstElementChild);
		};
		my$("btn3").onclick = function(){
			//点击按钮删除div中所有的子级元素
			//判断父级元素中有没有第一个子级元素
			while(my$("dv").firstElementChild){
				my$("dv").removeChild(my$("dv").firstElementChild);
			}
			
		}
	</script>
</body>
</html>

常用的各种位置获取

offset系列-偏移量

以后获取元素的宽和高 位置 应该使用offset系列来获取,只能获取,不能写入,写入需要用style

读 :读出的结果是纯数字		 
offsetWidth:获取元素的宽
offsetHeight: 获取元素的高
offsetLeft: 获取元素距离左边位置的值
offsetTop: 获取元素距离上面位置的值
offsetParent:用于获取定位的父级元素

1 没有脱离文档流:relative
offsetLeft: 父级元素padding+父级元素的border+自己margin ,
2 脱离文档流了:absoulte
offsetLeft:  主要是自己的left和自己的margin


写 : 需要加px的字符串
oSon.style.left = "166px";
oSon.style.top = "166px";
oSon.style.width = "166px";
oSon.style.height = "166"+"px";

元素跟着鼠标动

<head>
	<meta charset="UTF-8">
	<title>Document</title>
	<style type="text/css">
		#tiger {
			height: 100px;
			width: 100px;
			background-color: red;
			position: absolute;
			left: 200px;
			top: 500px;
		}
	</style>
</head>

<body>
	<div id="tiger">

	</div>
	<script>
		var lh = document.querySelector("#tiger");
		document.onmousemove = function (evt) {
			var e = evt || event;

			lh.style.left = e.pageX - lh.offsetWidth / 2 + "px";
			lh.style.top = e.pageY - lh.offsetHeight / 2 + "px";
		}
	</script>
</body>

client系列-可视区域

clientWidth:  可视区域的宽(没有边框) 边框内部的宽度
clientHeight: 可视区域的高(没有边框) 边框内部的高度
clientLeft: 左边边框的宽度
clientTop: 上面的边框的宽度

scroll系列-滚动偏移

console.log(my$("dv").scrollWidth);//元素中内容的实际的宽
console.log(my$("dv").scrollHeight);//元素中内容的实际的高
console.log(my$("dv").scrollTop);//向上卷出去的距离
console.log(my$("dv").scrollLeft);//向左卷曲出去的距离

//scrollWidth:元素中内容的实际的宽(没有边框) 如果没有内容就是元素的宽
//scrollHeight:元素中内容的实际的高(没有边框) 如果没有内容就是元素的高

//时时的获取向上卷曲出去的距离的值
		 
//div的滚动事件
 my$("dv").onscroll = function(){
   console.log(this.scrollTop);
 }

window.innerWidth和window.innerHeight

                   可视窗口的宽度
	var maxLeft = window.innerWidth - oBox.offsetWidth;
	               可视窗口的高度
	var maxTop = window.innerHeight - oBox.offsetHeight;

事件

(事件三要素:事件源,事件类型,事件对象)–事件对象

1 事件: 对某个元素的某种操作
2 事件对象:当某个事件触发时产生的对象,就是事件对象。
3 event使用前提,必须有事件
              不同的事件产生的事件对象不同。
4 事件对象的兼容: var e = evt || event;
5 事件对象拥有该事件相关的属性和方法

6 事件处理函数,事件触发了,函数的代码就会执行,执行的时候,函数调用的时候
通过arguments.length 可以得出:事件处理函数中实际上都是有一个参数的 
这个参数和事件有关系 是一个对象--->事件参数对象
7 谷歌和火狐中都有这个事件参数对象evt, IE8中没有
8 事件参数对象:evt-----在IE8中用window.event来代替

滚动条事件-window.onscroll

<head>
	<meta charset="UTF-8">
	<title>Document</title>
	<style type="text/css">
			body{
				height: 1000px;
			}
			#btn{
				position: absolute;
				top:1000px;
			}
		</style>
</head>
<body>
	<button id="btn">回去</button>
	<script>
	// 滚动条事件
	window.onscroll = function(){
		// 兼容问题,获取滚动条移动的垂直距离
		var _top =document.body.scrollTop || document.documentElement.scrollTop;
		console.log(_top);
	}
	// 返回顶端
		var oBtn = document.querySelector("button");
		oBtn.onclick = function(){
			document.documentElement.scrollTop = document.body.scrollTop = 0;
		}
	</script>
</body>

鼠标事件对象的几个属性

	<script>
		document.onclick = function(evt){
			var e = evt || event;
			// 距离文档document  X轴和Y轴的距离
			console.log(e.pageX +","+e.pageY);
			// 距离浏览器窗口的距离,可视化范围。
			console.log(e.clientX +","+e.clientY);
			// 距离最近父元素的距离
			console.log(e.offsetX +","+ e.offsetY);
		}
           pageX和pageY在谷歌和火狐可以使用,IE8不能用。
		</script>

键盘事件对象

//键盘事件源都是document
	//键盘抬起的时间点触发
	document.onkeyup = function(){
		console.log("heihei");
	}
	//键盘落下的时间点触发
	document.onkeydown = function(){
		console.log("heihei");
	}
	//生成一个字符串触发
	document.onkeypress = function(){
		console.log("heihei");
	}
	
	//--------------------------------------------------------------------
	//事件对象
	document.onkeypress = function(evt){
		var e = evt || event;
		//65 97 48字符0 32空格 13回车
		//keyCode:获取按下字符的ASC码值
		//兼容写法
		var key = e.keyCode || e.which || e.charCode;
		console.log(key);
		
		//ctrl + 回车  10
		if(key == 10 && e.ctrlKey){
			console.log("发送");
		}
		//判断ctrl是否被按下
		console.log(e.ctrlKey);		
		console.log(e.keyCode);
		console.log(e.which);
		console.log(e.charCode);
	}	
	//将ASC码值转换为键盘码
	console.log(String.fromCharCode(98));

常用事件

onclick	      当用户点击某个对象时调用的事件句柄。
ondblclick	  当用户双击某个对象时调用的事件句柄。
onmousedown	  鼠标按钮被按下。
onmousemove	  鼠标被移动。
onmouseout	  鼠标从某元素移开。
onmouseover	  鼠标移到某元素之上。
onmouseup	  鼠标按键被松开。

onkeydown	某个键盘按键被按下。
onkeypress	某个键盘按键被按下并松开。
onkeyup	    某个键盘按键被松开。

onload	   一张页面或一幅图像完成加载。
onunload	用户退出页面。

onblur	   元素失去焦点。
onfocus	   元素获得焦点。

onerror	    在加载文档或图像时发生错误。
onabort	    图像的加载被中断。

onsubmit	确认按钮被点击。
onreset	    重置按钮被点击。
onresize	窗口或框架被重新调整大小。
onselect	文本被选中。
onchange	域的内容被改变。
oncontextmenu   鼠标右键事件

事件流-事件冒泡-事件捕获

事件流-事件冒泡-事件捕获

   事件流:
  当某个事件执行时,从子元素向父元素触发 或 从父元素向子元素触发 称为事件流
  事件流的两种模式:

事件冒泡

什么是事件冒泡

从子元素向父元素触发 当某个事件触发时,同样的事件会向父元素触发。
但并不是所有事件都会产生冒泡问题 onfocus onblur onload不会产生冒泡问题 。
多个元素嵌套 有层次关系 这些元素都注册了相同的事件 
如果里面的元素的事件触发了 外面的元素的该事件自动的触发了 
	document.body.onclick = function () {
		alert("body");
	}
	document.onclick = function () {
		alert("document");
	}
	window.onclick = function () {
		alert("window");
	}

阻止事件冒泡

window.event.cancelBubble = true;  //IE特有的 谷歌支持 火狐不支持
e.stopPropagation();  //谷歌和火狐支持

//阻止事件冒泡:一定加在事件传播源
function stopMp (evt) {
		var e = evt || event;		
		//兼容写法
		return  e.stopPropagation?e.stopPropagation():e.cancelBubble = true;	
}

事件的三个阶段,可以通过e.eventPhase这个属性可以知道当前的事件是什么阶段

事件有三个阶段:		
		 1、事件捕获阶段:从外向内
		 2、事件目标阶段: 最开始选择的那个
		 3、事件冒泡阶段:从里向外
		 
		 为元素绑定事件:
		 addEventListener("没有on的事件类型",事件处理函数,控制事件阶段的)
		 事件触发的过程中 可能会出现事件冒泡的效果 为了阻止事件冒泡------>
		 window.event.cancelBubble = true;谷歌IE8支持火狐不支持
		 window.event就是一个对象 是IE中的标准
		 e.stopPropagation();阻止事件冒泡-------->谷歌和火狐支持
		 window.event和e都是事件参数对象 一个是IE的标准 一个火狐的标准
		 事件参数e在IE8的浏览器中是不存在的 此时用windo.event来代替
		 addEventListener中第三个参数是控制事件阶段的
		 事件的阶段有三个:
		 通过e.eventPhase这个属性可以知道当前的事件是什么阶段
		 1----->捕获阶段
		 2------>目标阶段
		 3------>冒泡
		 一般默认的都是冒泡阶段 很少用捕获阶段
		 冒泡阶段:从里到外
		 捕获阶段:从外向内

一个元素同时拥有捕获和冒泡的情况下,执行顺序是什么?

  document.addEventListener("click",function(){
            alert("document捕获");
    },true);
    document.addEventListener("click",function(){
            alert("document冒泡");
    },false);
    window.addEventListener("click",function(){
            alert("window捕获");
    },true);
    window.addEventListener("click",function(){
            alert("window冒泡");
    },false);
    // 先捕获,后冒泡

阻止浏览器默认行为

第一种方法:

//组织浏览器的默认行为写法1
		//e.preventDefault();
		//e.returnValue = false;
		
	 function stopMr (evt) {
		var e = evt || event;	
		//兼容写法  
		e.preventDefault?e.preventDefault():e.returnValue = false;	
	}

第二种方法,也可以阻止a标签的默认行为。

     return false;

事件的绑定(监听)与解绑

事件的绑定的三种方法

前两种方法
<body>
<!-- "fun()" == fun  由于fun不能直接书写
但结果都是在绑定函数本身而不是函数调用
-->
<div id="box" onclick="fun()"></div>
</body>
<script>
//1.以js的方法绑定obj.onclick = function(){}

//2.以html元素直接绑定
 function fun(){
	 console.log("heihei");
 }
</script>
通过addEventListener或者attachEvent来绑定事件
1   对象.addEventListener("事件类型",事件处理函数,false);------谷歌和火狐支持 IE8不支持
        参数1:事件的类型------事件的名字 没有on
		参数2:事件处理函数------函数(命名函数,匿名函数)
		参数3:布尔类型  true为捕获 false或者不写都为冒泡。
2   对象.attachEvent("有on的事件类型",事件处理函数)--------谷歌和火狐不支持 IE8支持
         参数1:事件类型-----事件名字 有on
	     参数2:事件处理函数-------函数(命名函数,匿名函数)
3   事件监听的好处
1---可以为同样的元素绑定多次同一个事件
2---程序员可以使用事件监听的方式 确定触发的过程是冒泡还是捕获

绑定事件的兼容

//为任意元素 绑定任意的事件 (任意的元素 事件的类型 事件的处理函数),
//事件类型传入的时候不加on
		function addEventListener(element,type,fn){
			//判断浏览器是否支持这个方法
			if (element.addEventListener) {
				element.addEventListener(type,fn,false);
			}else if (element.attachEvent) {
				element.attachEvent("on"+type,fn);
			}else{
				element["on"+type]=fn;
			}
		}

绑定事件的区别

 总结绑定事件的区别:
	 addEventListener();
	 attachEvent();
	 相同点:都可以为元素绑定事件
	 不同点:
	 1、方法名不同
	 2、参数个数不一样 addEventListener三个参数 attachEvent两个参数
	 3、addEventListener 谷歌 火狐 IE11支持 IE8不支持
	    attachEvent 谷歌火狐不支持 IE11不支持IE8支持
	 4、this不同
	    addEventListener中的this是当前绑定事件的对象
	    attachEvent中的this的window
	 5、addEventListener中事件的类型(事件的名字)没有on
	 	attachEvent中的事件的类型(事件的名字)有on

为元素解绑

通过removeEventListener或者detachEvent解绑
 解绑事件:
	  注意:用什么方式绑定事件 就应该用对应的方式解绑事件
	  1、解绑事件
	  	对象.on事件名字 = 事件处理函数------>绑定事件
	  	对象.on事件名字 = null
	  2、解绑事件
	  	对象.addEventListener("没有on的事件类型",命名函数,false);-------绑定事件
	  	对象.removeEventListener("没有on的事件类型",函数名字,false);
	  3、解绑事件
	   对象.attachEvent("on事件类型",命名函数);-------绑定事件
	   对象.detachEvent("on事件类型",函数名字)
	   解绑事件的时候 需要在绑定事件的时候 使用命名函数  
解绑的兼容写法
//为任意元素 解绑任意的事件 (任意的元素 事件的类型 事件的处理函数),
//事件类型传入的时候不加on
function removeEventListener(element,type,fnName){
		if (element.removeEventListener) {
			element.removeEventListener(type,fnName,false);
		}else if(element.detachEvent){
			element.detachEvent("on"+type,fnName);
		}else{
			element["on"+type] = null;
		}
	}

事件委托

事件委托的有关概念

委托:让别人去做
事件委托:某个事件让其他元素来完成
    例如:页面上有1000个li,为每一个li添加单机事件    使用委托只需要在li父级上加一次事件就可以
委托的好处:
   1. 把某个事件加到父元素上,提高程序的执行效率
   2. 动态创建的元素 可以在创建元素的函数体外部为其添加事件
委托的机制:
        利用事件冒泡(常见) 或者 事件捕获
        不是所有事件都可以实现事件委托  常见到也就那么几个 
委托的实现方法:
父级元素.事件 = function(){
}
	var oUl = document.querySelector("ul");
	//事件委托中不能用this,应该获取真实的事件操作源 事件委托的核心。
	//e.target || e.srcElement;获取真实的事件操作源 事件委托的核心。
	//tagName 可以获取事件元素的名称  在打印台打印出来是大写的
	oUl.onclick = function(evt){
		var e = evt || event;
		
		//获取真实的事件操作源 事件委托的核心
		var target = e.target || e.srcElement;
		
		//console.log(target.tagName);
		if(target.tagName == "LI"){
			target.style.backgroundColor = "red";
		}
		
	}
	

事件委托小案例-追加li,鼠标划过背景变色

<body>
		<ul>
			<li>1</li>
			<li>2</li>
			<li>3</li>
			<li>4</li>
			<li>5</li>
		</ul>
		<button type="button">添加</button>
	</body>
<script>
	var oUl = document.querySelector("ul");
	
	oUl.onmouseover = function(evt){
		var e = evt || event;
		var target = e.target || e.srcElement;
		
		if(target.tagName == "LI"){
			target.style.backgroundColor = "hotpink";
		}
	}
	
	oUl.onmouseout = function(evt){
		var e = evt || event;
		var target = e.target || e.srcElement;
		
		if(target.tagName == "LI"){
			target.style.backgroundColor = "";
		}
	}
	
	var oBtn = document.querySelector("button");

	oBtn.onclick = function(){
		var _li = document.createElement("li");
		_li.innerHTML = "heihei";
		oUl.appendChild(_li);
	}
</script>

创建对象的三种方式

字面量的方式

 //1 实例对象
 var per1 = {
 	name:"卡卡西",
 	age:20,
 	sex:"男",
 	eat:function(){
 		console.log("吃饭");
 	}
 }

调用系统的构造函数

 //2 调用系统的构造函数
 var per2 = new Object();
 	per2.name = "大蛇丸";
 	per2.age = 30;
 	per2.sex = "男";
 	per2.eat=function(){
 		console.log("吃榴莲");
 	}

自定义构造函数创建对象(具备构造函数功能的普通函数)

 //3 自定义构造函数方式
 function Person(name,age,sex){
 	this.name = name;
 	this.age = age;
 	this.sex = sex;
 	this.play = function(){
 		console.log("天天打游戏");
 	};
 }
 var per = new Person("雏田",18,"女");
 console.log(per instanceof Person);

构造函数 自定义构造函数创建对象做的事情

构造函数:如果函数中的逻辑是生成一个对象的并将其返回,我们就将其称之为构造函数。

 function Person(name,age,sex){
	 	this.name = name;
	 	this.age = age;
	 	this.sex = sex;
	 	this.play = function(){
	 		console.log("天天打游戏");
	 	};
	 }
	 //创建对象------>实例化一个对象的同时对属性进行初始化
	  var per = new Person("雏田",18,"女");
	  /**
	   * 
	   *1、开辟空间存储对象
	   *2、把this设置为当前的对象
	   *3、设置属性和方法的值
	   *4、把this对象返回
	   *
	   *
	   * 
	   */

工厂模式创建对象(严格意义上的构造函数)

创建对象----->实例化一个对象的同时对属性进行初始化

工厂模式创建对象(严格意义上的构造函数)

	 工厂模式:
	 函数名小写
	 有new
	 有返回值
	 new之后的对象是当前的对象
	 直接调用函数就可以创建你对象
		function Person(name,age){
			this.name = name;
			this.age = age;
			this.sayHi = function(){
				console.log("您好");
			}
		}
		var per1 = new Person("小红",20);
		
	 new 关键字为我们做了什么:
	 // 1.在构造函数里先声明了一个空对象。
	 // 2.将本函数中的this指向改变成刚才生成的空对象。
	 // 3.在函数体最后将那个对象返回。

自定义构造函数创建对象(具备构造函数功能的普通函数)

	 自定义构造函数:
	 函数名是大写(首字母)
	 没有new
	 没有返回值
	 this是当前的对象
	 通过new的方式创建对象
	function createObject(name,age){
			var obj = new Object();
			obj.name = name;
			obj.age = age;
			obj.sayHi = function(){
				console.log("您好");
			};
			return obj;
		}
		var per2 = createObject("小明",20);

	 两者共同点:都是函数 都可以创建对象 都可以传入参数	

构造函数和实例对象之间的关系

实例对象和构造函数之间的关系

  • ​ 实例对象是通过构造函数来创建的-----创建的过程就叫实例化
  • ​ 在每一个实例对象的__proto__中同时中有一个 constructor 属性,该属性指向创建该实例的构造函数

  • ​ 构造函数是根据具体的事物抽象出来的抽象模板

  • ​ 实例对象是根据抽象的构造函数模板得到的具体实例对象

如何判断对象是不是这个构造函数

  • ​ 通过构造器的方式 实例对象.构造器 == 构造函数名字

  • ​ 对象 instanceof 构造函数 (建议用这种)

		//自定义构造函数------>实例化对象
		function Person(name,age,sex){
			this.name = name;
			this.age = age;
			this.eat = function(){
				console.log("吃榴莲");
			}
		}
		//构造函数------>创建对象
		var per = new Person("小苏",38,"女");
		per.eat();
		
		console.dir(per);
		console.dir(Person);

		//实例对象的构造器(构造函数)
		//实例对象的构造器是指向Person 结果是true 所以 这个实例对象per就是通过Person来创建的
		
		console.log(per.constructor==Person);

	    console.log(per.__proto__.constructor == Person);
		console.log(per.__proto__.constructor == Person.prototype.constructor);

		//构造函数
		function Animal(name){
			this.name = name;
		}
		//实例对象
		var dog = new Animal("小白");
		console.dir(dog);//实例对象
		console.dir(Animal);//构造函数的名字

		console.log(dog.__proto__.constructor == Person);
		console.log(dog.__proto__.constructor == Animal);

		//判断这个对象是不是这个构造函数
		console.log(dog.constructor == Animal);
		console.log(dog instanceof Person);

构造函数创建对象带来的问题-浪费内存

	function Person(name,age){
			this.name = name;
			this.age = age;
			this.eat = myEat;
		}
		var per1 = new Person("小黑",20);
		var per2 = new Person("小白",30);
		console.dir(per1);
		console.dir(per2);
		console.log(per1.eat == per2.eat);

使用构造函数带来的最大的好处就是创建对象更方便了,但是其本身也存在一个浪费内存的问题:

在示例中,从表面上好像没什么问题,但是实际上这样做,有一个很大的弊端。
那就是对于每一个实例对象,eat都是一模一样的内容,
每一次生成一个实例,都必须为重复的内容,多占用一些内存,如果实例对象很多,会造成极大的内存浪费。

对于这种问题可以使用原型对象来解决,Javascript 规定,每一个构造函数都有一个 prototype 属性,指向另一个对象。
这个对象的所有属性和方法,都会被构造函数的实例继承。

这也就意味着,我们可以把所有对象实例需要共享的属性和方法直接定义在 prototype 对象上。

原型和原型对象

原型的理解

原型的理解:每一个构造函数都有一个prototype属性,该属性指向了一个对象,这个对象我们叫原型对象。通过构造函数生成的实例化对象,如果在调用属性或方法时从自身找不到该属性或方法,此时不会直接报错或返回undefined,而是去找它的构造函数的原型对象,看它里面有没有该属性或方法,如果有则直接借用,如果没有则看看该原型对象还有没有prototype属性,如果有则重复上面的操作,如果没有则返回undefined或报错。

	(黄) 原型?
	 实例对象中有__proto__这个属性 叫原型 也是一个对象 这个属性是给浏览器使用的 
	 不是标准的属性---------->__proto__------>可以叫做原型对象
	 
	 构造函数中有prototype这个属性 叫原型 也是一个对象 这个属性是给程序员使用的
	 是标准的属性------>prototype---->也可以叫原型对象
	 
	 实例对象的__proto__和构造函数中的prototype相等------>true
	 
	 又因为实例对象是通过构造函数来创建的 构造函数中有原型对象prototype 实例对象的__proto__指向构造函数的原型对象prototype

构造函数、实例、原型三者之间的关系

	 构造函数可以实例化对象 
	 构造函数中有一个属性叫prototype 是构造函数的原型对象
	 构造函数的原型对象(prototype)中有一个constructor构造器 这个构造器指向的是自己所在的原型对象所在的构造函数
	 实例对象的原型对象(__proto__)指向的是该构造函数的原型对象
	 构造函数的原型对象(prototype)中的方法可以被实例对象直接访问

利用原型共享数据-解决内存浪费的问题

  • 什么样子的数据是需要写在原型中?

  • 需要共享的数据就可以写在原型中

  • 原型的作用之一:共享数据

  • 属性需要共享, 方法也需要共享, 不需要共享的数据写在构造函数中 , 需要共享的数据写在原型中

	//构造函数
	function Student(name,age,sex){
		this.name = name;
		this.age = age;
		this.sex = sex;
	}
	Student.prototype.height = "188";
	Student.prototype.weight = "55kg";
	Student.prototype.study = function(){
		console.log("每天敲500行代码");
	}
	
	Student.prototype.eat = function(){
		console.log("吃一个10斤的西瓜");
	}
	//实例化对象 并初始化
	var stu = new Student("晨光",57,"女");

原型的简单的语法

每添加一个属性和方法就要敲一遍 Person.prototype
为减少不必要的输入,更常见的做法是用一个包含所有属性和方法的对象字面量来重写整个原型对象:

这样做的好处就是为 Person.prototype 添加成员简单了,但是也会带来一个问题,那就是原型对象丢失了 constructor 成员。

所以,我们为了保持 constructor 的指向正确,需要手动修改构造器的指向。

	function Student(name,age,sex){
			this.name = name;
			this.age = age;
			this.sex = sex;
		}
		//简单的原型的写法
		Student.prototype = {
			//手动修改构造器的指向
			constructor:Student,
			height:"188",
			weight:"55kg",
			study:function(){
				console.log("哈哈 敲代码 好开心啊");
			},
			eat:function(){
				console.log("我要吃好多好吃的");
			}
		};
		var stu = new Student("段飞",20,"男");
		stu.eat();
		stu.study();
		console.dir(Student);
		console.dir(stu);

原型中的方法可以相互访问

	 //原型中的方法 是可以相互访问的
		  function Animal(name,age){
		  	this.name = name;
		  	this.age = age;
		  }
		  //原型中添加方法
		  Animal.prototype.eat = function(){
		  	console.log("动物吃东西");
		  	this.play();
		  }
		  Animal.prototype.play = function(){
		  	console.log("玩球");
		  	this.sleep();
		  }
		  Animal.prototype.sleep = function(){
		  	console.log("睡觉");
		  };
		  var dog =  new Animal("小白",20);
		  dog.eat();

实例对象使用属性和方法层层的搜索

实例对象使用的属性或者方法 先在实例中查找 找到了则直接使用找不到 
则去实例对象__protso__指向的原型对象prototype中早 找到了则使用 找不到则报错

为内置对象添加原型方法

		// 字面量的声明方式
		var str = "abcdef";
		// 构造函数的声明方式
		let str1 = new String("abcdef");

		// 为String对象扩展reverse方法
		String.prototype.reverse = function(){
			let result = "";
			for(let i = 0; i < this.length;i++){
				result += this[this.length - 1 - i];
			}
			return result;
		}

原型及原型链

//使用对象------>使用对象中的属性和对象中的方法 使用对象就要先有构造函数
	//构造函数
	 function Person(name,age) {
	 	// 属性
	 	this.name = name;
	 	this.age = age;
	 	//在构造函数中的方法
	 	this.eat = function(){
	 		console.log("吃好吃的");
	 	}
	 }
	 //添加共享的属性
	 Person.prototype.sex = "男";
	 //添加共享的方法
	 Person.prototype.sayHi = function(){
	 	console.log("您好啊 今天会小雪吗");
	 }
	 //实例化对象 并初始化
	 var per = new Person("小明",20);
	 per.sayHi();

	 //如果想要使用一些属性和方法 并且属性的值在每个对象中都是一样的 方法在每个对象中的操作也都是一样的 那么 为了共享数据 节省内存空间 是可以把属性和方法通过原型的方式进行赋值
	
	 console.dir(per);//实例对象的结构
	 console.dir(Person);//构造函数的结构

	 //实例对象的__proto__和构造函数的原型prototype指向是相同的
	 //实例对象中的__proto__原型指向的是构造函数中的原型prototype
	 console.log(per.__proto__==Person.prototype);
	 //实例对象中的__proto__是原型 浏览器用
	 //构造函数中的prototype是原型 程序员用
	 

	 //原型链:是一种关系 实例对象和原型对象之间的关系 关系是通过原型(__proto__)来联系的

原型的指向是否可以改变

//构造函数中的this就是实例对象
	//原型对象中方法中的this就是实例对象
		// function Person(age){
		// 	this.age = age;
		// 	console.log(this);
		// };
		// Person.prototype.eat = function(){
		// 	console.log(this);
		// 	console.log("您吃了没 吃点啥");
		// };
		// var per = new Person(10);
		// per.eat();
		// console.log(per);

		function Student(){

		}
		Student.prototype.study = function(){
			console.log("好好学习 天天向上");
		}
		Student.prototype = {
			eat:function(){
				console.log("哈哈 榴莲好吃吗");
			}
		};
		var stu = new Student();
		stu.eat();

		// //人的构造函数
		// function Person(age){
		// 	this.age = 0;
		// }
		// //人的原型对象方法
		// Person.prototype.eat = function(){
		// 	console.log("人的吃");
		// }
		// //学生的构造函数
		// function Student(){
			
		// }
		// Student.prototype.sayHi = function(){
		// 	console.log("嗨  你好啊");
		// };
		// //学生的原型 指向了一个人的实例对象
		//  Student.prototype = new Person(10);
		//  var stu = new Student();
		//  stu.eat();
		//  stu.sayHi();

		//  //原型的指向可以改变
		//  //实例对象的原型__proto__指向的是该对象所在的构造函数的原型对象
		//  //构造函数的原型对象(prototype)指向如果改变了 实例对象的原型(__proto__)指向也会发生改变
		 
		//  //实例对象和原型对象之间关系是通过__proto__原型来联系起来的 这个关系就是原型链

原型最终指向哪里

​ 实例对象中有__proto__原型
构造函数中有prototype原型
prototype是对象
所以 prototype 这个对象中也有__proto__那么 指向了哪里
实例对象中的__proto__指向的是构造函数的prototype
所以 prototype这个对象中的__proto__指向的应该是某个构造函数的原型prototype

function Person(){

		}
		Person.prototype.eat = function() {
			console.log("吃东西");
		};
		var per = new Person();
		console.dir(per);
		console.dir(Person);
 	
 		//Person的prototype中的__proto__的指向
 		console.log(Person.prototype.__proto__);

 		//per实例对象的__proto__-------->Person.prototype的__proto__指向不同
 		console.log(per.__proto__ == Person.prototype.__proto__);

 		console.log(per.__proto__.__proto__ == Person.prototype.__proto__);

 		console.log(Person.prototype.__proto__ == Object.prototype);
 		console.log(Object.prototype.__proto__);//null

原型对象使用建议

  • 私有成员(一般就是非函数成员)放到构造函数中
  • 共享成员(一般就是函数)放到原型对象中
  • 如果重置了 prototype 记得修正 constructor 的指向

原型指向改变如何添加方法和访问

如果原型指向改变了 那么就应该在原型改变指向之后添加原型方法

//人的构造函数
	// 	function Person(age){
	// 		this.age = age;
	// 	}
	// //人的原型中添加方法
	// 	Person.prototype.eat = function(){
	// 		console.log("人正在吃饭");
	// 	}
	// //学生的构造函数
	// 	function Student(sex){
	// 		this.sex = sex;
	// 	}
	// //学生的原型中添加方法-------------------先在原型中添加方法
	//    Student.prototype.sayHi = function(){
	//    		console.log("您好啊");
	//    }
	// //改变原型对象的指向
	//    Student.prototype = new Person(10);

	//    var stu = new Student("男");
	//    stu.eat();
	//    stu.sayHi();



	//人的构造函数
	// 	function Person(age){
	// 		this.age = age;
	// 	}
	// //人的原型中添加方法
	// 	Person.prototype.eat = function(){
	// 		console.log("人正在吃饭");
	// 	};
	// //学生的构造函数
	// 	function Student(sex){
	// 		this.sex = sex;
	// 	}
	// //改变原型对象的指向
	// 	Student.prototype = new Person(10);
	// //学生的原型中添加方法------先在原型中添加方法
	// 	Student.prototype.sayHi = function(){
	// 		console.log("您好啊");
	// 	}
	// 	var stu = new Student("男");
	// 	stu.eat();
	// 	stu.sayHi();
	// 	console.dir(stu);
	//如果原型指向改变了 那么就应该在原型改变指向之后添加原型方法
	
		function Person(age){
			this.age = age;
		}
	//指向改变了
		Person.prototype = {
			eat:function(){
				console.log("吃");
			}
		}
	//先添加原型方法
		Person.prototype.sayHi = function(){
			console.log("你好");
		}
		var per = new Person(10);
		per.sayHi();

实例对象的属性和原型对象中的属性重名问题

	function Person(age,sexx){
			this.age = age;
			this.sexx = sexx;
		}
		// Person.prototype.sex = "女";
		var per = new Person(10,"男");
		// console.log(per.sex);
		//因为js是一门动态类型的语言 对象没有什么 只要点了 那么这个对象就有了这个东西 没有这个属性 只要对象.属性名字,对象就有这个属性了 但是 该属性没有赋值 所以 结果是undefined
		// console.log(per.dadafasfsafas);
		// console.log(shfashfsfhaf);


		//实例对象访问这个属性 应该先从实例中找 找到了就直接用 找不到就去指向的原型对象中找 找到了就是用 找不到?======
		//通过实例对象能否改变原型对象中的属性值?不能
		//就想改变原型对象中属性的值 怎么办  直接通过原型对象.属性 = 值, 可以改变
		

		
		Person.prototype.sex = "嘎嘎";
		per.sex = "人";
		console.log(per.sex);
			
		// per.prototype.sex = "哈哈";
		// console.log(per.sex);

原型链

// 原型链:实例对象和原型对象之间的关系 通过__proto__来联系

	</script>
	<div id="dv"></div>
	<script>
	 var divObj = document.getElementById("dv");
	 console.dir(divObj);
	 console.log(divObj.__proto__);
	 console.log(HTMLDivElement.prototype.__proto__);
	 console.log(HTMLElement.prototype.__proto__);
	 console.log(Element.prototype.__proto__);
	 console.log(Node.prototype.__proto__);
	 console.log(EventTarget.prototype.__proto__);

	 divObj.__proto__------>
	 HTMLDivElement.prototype的__proto__---->
	 HTMLElement.prototype的__proto__---->
	 Element.prototype的__proto__------>
	 Node.prototype的__proto__----->
	 EventTarget.prototype的__proto__----->
	 Object.protorype没有__proto__ 所以 Object.prototype中的__proto__是null

原型用途

 1 为本地对象扩展原型方法
 2 将构造函数中重复的属性或方法从私有属性与私有方法中提取出来,放到构造函数的原型对象中,从而减少内存的开销
 3 实现继承

继承

/**
 *面向对象编程思想:根据需求 分析对象 找到对象有什么特征和行为 通过代码的方式来实现需求 要想实现这个需求 就要创建对象 要象创建对象就应该显示有构造函数 然后 通过构造函数来创建对象 通过对象调用属性和方法来实现相应的功能及需求 即可
 *
 * 
 *首先js不是一门面向对象的语言 js是一门基于对象的语言 那么 为什么学习js还要学面向对象?因为面向对象的思想适合于人的想法 编程起来会更加的方便 及后期的维护
 *
 *
 *面向对象的编程语言中有类(class)的概念(也是一种特殊的数据类型),但是js不是面向对象的语言 所以 js中没有类(class) 但是js可以模拟面向对象的思想 js中会通过构造函数来模拟类的概念
 *
 *
 *小明,小红,小白 都是人
 *共同的特征和行为
 *特征----->属性
 *行为----->方法
 *
 *面向对象的特征:封装 继承 多态
 *
 *封装:就是包装
 *一个值存储在一个变量中-------封装
 *一坨重复的代码放在一个函数中-------封装
 *一系列的属性放在一个对象中------封装
 *一些功能类似的函数(方法)放在一个对象中-----封装
 *好多类似的对象放在一个js文件中--------封装 
 *
 *
 *继承:首先继承是一种关系 类(class)与类之间的关系 js中没有类 但是可以通过构造函数模拟类 然后 通过原型来实现继承 继承也是为数据共享 js中的继承也是为了实现数据共享
 *
 *
 *
 *原型的作用之一:数据共享 节省内存空间
 *原型的作用之二:为了实现继承
 *
 *
 *继承是一种关系:
 *父类级别与类几倍的关系
 *
 *例子:
 *小杨------>人,姓名,有钱,帅,有功夫-----降龙十八掌
 *小杨子----->人
 *继承:
 *姓氏-------继承
 *外表-------继承
 *财产-------继承
 *功夫-------继承
 *
 *人:姓名 性别 年龄 吃饭 睡觉
 *
 *学生类别:姓名 性别 年龄 吃饭 睡觉 学习行为
 *老师类别: 姓名 性别 年龄 吃饭 睡觉 教学行为 领工资
 *程序员:姓名 性别 年龄 吃饭 睡觉 敲代码 领工资
 *司机类别:姓名 性别 年龄 吃饭 睡觉 开车 领工资
 *
 *
 * 多态:一个对象有不同的行为 或者同一个行为针对不同的对象 产生不同的结果 要想有多态 就要先继承 js中可以模拟多态 但是不会去使用 也不会模拟
 * 
 */

通过原型继承

改变学生的原型指向即可

相同的代码太多 造成了代码的亢余(重复的代码)

	<script>
	//js中通过原型来实现继承
	function Person(name,age,sex){
		this.name = name;
		this.age = age;
		this.sex = sex;
	}
	Person.prototype.eat = function(){
		console.log("人可以吃");
	}
	Person.prototype.sleep = function(){
		console.log("人可以睡觉");
	}
	Person.prototype.play = function(){
		console.log("人可以玩耍");
	}

	function Student(score){
		this.score = score;
	}
	//改变学生的原型指向即可===================>学生和人已经发生关系
	Student.prototype = new Person("小明",10,"男");
	Student.prototype.study = function(){
		console.log("学习使我快乐");
	}

	//相同的代码太多 造成了代码的亢余(重复的代码)
	
	var stu = new Student(100);
	console.log(stu.name);
	console.log(stu.age);
	console.log(stu.sex);
	stu.eat();
	stu.play();
	stu.sleep();

	console.log("下面的是学生对象中自己有的");
	console.log(stu.score);
	stu.study();

继承小案例

	//动物有名字 有体重 有吃东西的行为
	//小狗有名字 有体重 有吃东西的行为 还有颜色 还有咬人的行为
	//哈士奇有名字 有体重 有吃东西的行为 还有颜色 还有咬人的行为 分性别 逗主人开心
	

	//动物的构造函数
	  function Animal(name,weight){
	  	 this.name = name;
	  	 this.weight = weight;
	  }
	//动物的原型方法
	  Animal.prototype.eat = function(){
	  	 console.log("天天吃东西 就是吃");
	  }
	//狗的构造函数
	  function Dog(color){
	  	 this.color = color;
	  }
	  Dog.prototype = new Animal("哮天犬","50kg");
	  Dog.prototype.bitPerson = function(){
	  	 console.log("咬你 咬你 咬你");
	  }

	 //哈士奇
	 function ErHa(sex){
	 	this.sex = sex;
	 }
	 ErHa.prototype = new Dog("黑白色");
	 ErHa.prototype.playHost = function(){
	 	console.log("来啊 一起玩耍");
	 }

	 var erHa = new ErHa("雄性");
	 console.log(erHa.name,erHa.weight,erHa.color);
	 erHa.eat();
	 erHa.bitPerson();
	 erHa.playHost();

用构造函数实现继承 --构造函数的属性继承

  • 借用构造函数:构造函数名字.call(当前对象,属性,属性,属性…)

  • 解决了属性继承 并且值不重复的问题

  • 缺陷:父级类别中的方法不能继承

		 // function Person(name,age,sex,weight){
		 // 	this.name = name;
		 // 	this.age = age;
		 // 	this.sex = sex;
		 // 	this.weight = weight;
		 // }
		 // Person.prototype.sayHi = function(){
		 // 	console.log("您好");
		 // }
		 // function Student(score){
		 // 	this.score = score;
		 // }
		 // //希望人的类别中的数据可以共享给学生------继承
		 // Student.prototype = new Person("小明",10,"男","50kg");

		 // var stu = new Student("100");
		 // console.log(stu.name,stu.age,stu.sex,stu.weight,stu.score);
		 // stu.sayHi();


		 // var stu2 = new Student("120");
		 // stu2.name = "张三";
		 // stu2.age = 20;
		 // stu2.sex = "女";
		 // console.log(stu2.name,stu2.age,stu2.sex,stu2.weight,stu2.score);
		 // stu2.sayHi();
		 // var stu3 = new Student("130");
		 // console.log(stu3.name,stu3.age,stu3.sex,stu3.weight,stu3.score);
		 // stu3.sayHi();

		 //为了数据共享 改变原型指向 做到了继承-----通过改变原型指向实现的继承
		 //缺陷:因为改变原型指向的同时实现继承 直接初始化了属性 继承过来的属性的值都是一样的了 所以 这就是问题
		 //只能重新调用对象的属性进行重新赋值
		 

		 //解决方案:继承的时候  不用改变原型的指向 直接调用父级的构造函数的方法的方式来为属性赋值就可以了-------借用构造函数:把要继承的父级的构造函数拿过来 使用一下就可以了


		 //借用构造函数:构造函数名字.call(当前对象,属性,属性,属性.........)
		 //解决了属性继承 并且值不重复的问题
		 //缺陷:父级类别中的方法不能继承
		 function Person(name,age,sex,weight){
		 	this.name = name;
		 	this.age = age;
		 	this.sex = sex;
		 	this.weight = weight;
		 }
		 Person.prototype.sayHi = function(){
		 	console.log("您好啊");
		 }
		 function Student(name,age,sex,weight,score){

		 	//借用构造函数
		 	Person.call(this,name,age,sex,weight);
		 	this.score = score;
		 }
		 var stu1 = new Student("小明",10,"男","10kg","100");
		 console.log(stu1.name,stu1.age,stu1.sex,stu1.weight,stu1.score);

		 var stu2 = new Student("小红",10,"女","50kg","100");
		 console.log(stu2.name,stu2.age,stu2.sex,stu2.weight,stu2.score);

组合继承

	//原型实现继承
	//借用构造函数实现继承
	//组合继承:原型继承+借用构造函数继承
	
	function Person(name,age,sex){
		this.name = name;
		this.age = age;
		this.sex = sex;
	}
	Person.prototype.sayHi = function(){
		console.log("萨瓦迪卡");
	}
	function Student(name,age,sex,score){
		//借用构造函数:属性值重复的问题
		Person.call(this,name,age,sex);
		this.score = score;
	}
	//改变原型指向-------继承
	Student.prototype = new Person();//不传值
	Student.prototype.eat = function(){
		console.log("吃东西");
	}
	var stu = new Student("小黑",20,"男","100分");
	console.log(stu.name,stu.age,stu.sex,stu.score);
	stu.sayHi();
	stu.eat();
	var stu2 = new Student("小白",200,"女","1010分");
	console.log(stu2.name,stu2.age,stu2.sex,stu2.score);
	stu2.sayHi();
	stu2.eat();
    var stu3 = new Student();
	console.log(stu3.name,stu3.age,stu3.sex,stu3.score);
	stu2.sayHi();
	stu2.eat();
	//属性和方法都被继承了

拷贝继承 (for-in)–构造函数的原型方法继承

拷贝继承:把一个对象中的属性或者方法直接复制到另一个对象中

		// var obj1 = {
		// 	name:"小糊涂",
		// 	age:20,
		// 	sleep:function() {
		// 		console.log("睡觉了");
		// 	}
		// };
		//实例对象中有__proto__原型----------
		//构造函数中有prototype原型-------
		
		//改变了地址的指向
		// var obj2 = obj1;
		// console.log(obj2.name,obj2.age);
		// obj2.sleep();

		// var obj1 = {
		// 	name:"小糊涂",
		// 	age:20,
		// 	sleep:function() {
		// 		console.log("睡觉了");
		// 	}
		// };

		// var obj2 = {};
		// for (var key in obj1) {
		// 	 obj2[key] = obj1[key];
		// }
		// console.log(obj2.name);

		function Person(){

		}
		Person.prototype.age = 10;
		Person.prototype.sex = "男";
		Person.prototype.height = 100;
		Person.prototype.play = function(){
			console.log("玩的好开心");
		};

		var obj2 = {};
		//Person的构造函数中有原型prototype,  prototype就是一个对象,那么里面 age,sex,height,play都是该对象中的属性或者方法
		for (var key in Person.prototype) {
			obj2[key] = Person.prototype[key];
		}
		console.dir(obj2);
		obj2.play();


总结继承

		原型作用:数据共享 目的是:为了节省内存空间
		原型作用: 继承 目的是:为了节省内存空间
		
	 原型继承:改变原型的指向
	 借用构造函数继承:主要是为了解决属性的问题
     组合继承:原型继承+借用构造函数继承(既能解决属性问题 又能解决方法问题)  
	 拷贝继承:就是把对象中需要共享的属性或者方法 直接遍历的方式复制到另一个对象中

es6通过class类继承

	class Person {
		constructor(name,age,sex){
			this.name = name;
			this.age = age;
			this.sex = sex;
		}
		sayName(){
			alert(this.name)
		}
	}
	let wdw = new Person("王大伟",18,'男')
	

	class SuperMan extends Person{
		constructor(name,age,sex,skill){
			// 继承父类的私有属性
			super(name,age,sex)
			this.skill = skill
		}
		dazhao(){
			alert(this.skill)
		}
	}

	let gangtiexia = new SuperMan("钢铁侠",18,"男","钞能力");

	class Person {
		constructor(name,age,sex){
			this.name = name;
			this.age = age;
			this.sex = sex;
		}
		sayName(){
			alert(this.name)
		}
	}
	let wdw = new Person("王大伟",18,'男')
	

	class SuperMan extends Person{
		constructor(name,age,sex,skill){
			// 继承父类的私有属性
			super(name,age,sex)
			this.skill = skill
		}
		dazhao(){
			alert(this.skill)
		}
	}

	let gangtiexia = new SuperMan("钢铁侠",18,"男","钞能力");

call和apply

call和apply

 apply和call的使用方法	  
	   apply的使用语法
	   函数名字.apply(对象,[参数1,参数2,.......]);
	  方法名字.apply(对象,[参数1,参数2,.......]);
	   call的使用语法
	   函数名字.call(对象,参数1,参数2,.......);
	   方法名字.call(对象,参数1,参数2,.......);
	   
	   作用:改变this的指向
	   不同的地方:参数传递的方式不一样
	   
	    只要是象使用别的对象的方法 并且希望这个方法是当前对象的 那么就可以使用apply或者call方法来改变this的指向
	   

	function fn(num1,num2){
		console.log(this)	
		console.log(num1 + num2)
	}
	// 以下的两种写法是完全等价的。
	fn()
	fn.call()

	// 调用并改变本次函数的this指向
	fn.call(1)
	// 调用并改变本次函数的this指向并传入实参
	fn.call("hello",1,2)
	
	// 调用并改变本次函数的this指向
	fn.apply(1)
	// 调用并改变本次函数的this指向并传入实参
	fn.apply("hello",[1,2])

//apply和call的使用
	//作用:可以改变this的指向
	

	// function f1(x,y){
	// 	console.log("结果是:"+(x+y)+this);
	// 	return "10000";
	// }
	// f1(10,20);//函数的调用

	// console.log("===================================");
	// //此时f1实际上就是当成对象来使用的 对象可以调用方法的
	// //cpply和call方法也是函数的调用的方式
	//   f1.apply();
	//   f1.call();
	// console.log("===================================");
	//   f1.apply(null);
	//   f1.call(null);

	// //apply和call都可以让函数或者方法来调用 传入参数和函数自己调用的写法不一样 但是效果是一样的
	
	// var result1 = f1.apply(null,[10,20]);
	// var result2 = f1.call(null,10,20);
	// console.log(result1);
	// console.log(result2);


	 // function f1(x,y){
	 // 	console.log("这个函数是window对象的一个方法:"+(x+y)+this.sex);
	 // }
	 // window.f1(10,20);
	 // //obj是一个对象
	 // var obj = {
	 // 	age:10,
	 // 	sex:"男"
	 // };

	 // window.f1.apply(obj,[10,20]);
	 // window.f1.call(obj,10,20);
	 // console.dir(obj);


	 //apply和call可以改变this的指向
	 

	 function Person(age,sex){
	 	this.age = age;
	 	this.sex = sex;
	 }
	 //通过原型添加方法
	 Person.prototype.sayHi = function(x,y){
	 	console.log("您好啊:"+this.sex);
	 	return 1000;
	 };
	 var per = new Person(10,"男");
	 per.sayHi();

	 console.log("==============================");

	 function Student(name,sex){
	 	this.name = name;
	 	this.sex = sex;
	 }
	 var stu = new Student("小明","人妖");
	 // console.log(stu);
	 var r1 = per.sayHi.apply(stu,[10,20]);
	 var r2 = per.sayHi.call(stu,10,20);

	 console.log(r1);
	 console.log(r2);



call和apply区别

call和apply都是函数的方法,该方法可以帮助函数调用一次自己,但是和普通调用不同,call和apply的调用可以调整本次执行函数中this的指向,这个指向会被第一个参数代替。如果这个函数在调用数需要传递实参,那么call方法会把实参依次排列在第一的参数之后,而apply则是将实参放到一个数组中,并把数组作为apply方法的第二个参数。call和apply常常被用在构造函数实现继承功能的场景下。

bind

bind() 函数会创建一个新函数(称为绑定函数),新函数与被调函数(绑定函数的目标函数)具有相同的函数体(在 ECMAScript 5 规范中内置的call属性)。
当目标函数被调用时 this 值绑定到 bind() 的第一个参数,该参数不能被重写。绑定函数被调用时,bind() 也接受预设的参数提供给原函数。
一个绑定函数也能使用new操作符创建对象:这种行为就像把原函数当成构造器。提供的 this 值被忽略,同时调用时的参数被提供给模拟函数。

语法:

fun.bind(thisArg[, arg1[, arg2[, ...]]])

参数:

  • thisArg
    • 当绑定函数被调用时,该参数会作为原函数运行时的 this 指向。当使用new 操作符调用绑定函数时,该参数无效。
  • arg1, arg2, …
    • 当绑定函数被调用时,这些参数将置于实参之前传递给被绑定的方法。

返回值:

返回由指定的this值和初始化参数改造的原函数拷贝。

		function f1(x,y) {
			console.log((x+y)+":======>"+this.age);
		}

		//复制了一份的时候 把参数传入到f1函数中 x========>10, y=====>20, null就是this,默认是window

		//bind方法 是复制的意思 参数可以在复制的时候传进去 也可在复制之后传进去
		//apply和call是调用的时候改变this指向
		//bind方法 是复制一份的时候 改变this的指向
		// var ff = f1.bind(null);
		// ff(10,20);

		// function Person(){
		// 	this.age = 1000;
		// }
		// Person.prototype.eat = function() {
		// 	console.log("这是吃的方法")
		// };
		// var per = new Person();

		// var ff = f1.bind(per,10,20);
		// ff();


		function Person(age){
			this.age = age;
		}
		Person.prototype.play = function(){
			console.log(this+"======>"+this.age);
		}
		function Student(age){
			this.age = age;
		}
		var per = new Person(10);
		var stu = new Student(20);

		//复制了一份
		var ff = per.play.bind(stu);
		ff();
		/**
		 *bind是用来复制一份
		 *使用语法:
		 *
		 * 函数名字.bind(对象,参数1,参数2,.........);---------->返回值是复制之后的这个函数
		 *方法名字.bind(对象,参数1,参数2......)------->返回值是复制之后的这个方法
		 *
		 * 
		 */

	function ShowRandom(){
			//1~10的随机数
			this.number = parseInt(Math.random()*10+1);
		}
		//添加原型方法
		ShowRandom.prototype.show1 = function(){
			//改变了 定时器中的this的指向了  本来的定时器中的this是window 现在是实例对象了
			window.setInterval(this.show2.bind(this),1000);
		};
		ShowRandom.prototype.show2 = function(){
			//显示随机数
			console.log(this.number);
		}
		//实例对象
		var sr = new ShowRandom();
		//调用方法
		sr.show1();


call和apply与bind总结

- call 和 apply 特性一样
  - 都是用来调用函数,而且是立即调用
  - 但是可以在调用函数的同时,通过第一个参数指定函数内部 this 的指向
  - call 调用的时候,参数必须以参数列表的形式进行传递,也就是以逗号分隔的方式依次传递即可
  - apply 调用的时候,参数必须是一个数组,然后在执行的时候,会将数组内部的元素一个一个拿出来,与形参一一对应进行传递
  - 如果第一个参数指定了 null 或者 undefined 则内部 this 指向 window
  
- bind
  - 可以用来指定内部 this 的指向,然后生成一个改变了 this 指向的新的函数
  - 它和 call、apply 最大的区别是:bind 不会调用
  - bind 支持传递参数,它的传参方式比较特殊,一共有两个位置可以传递
    - 1. 在 bind 的同时,以参数列表的形式进行传递
    - 1. 在调用的时候,以参数列表的形式进行传递
    - 那到底以谁 bind 的时候传递的参数为准呢还是以调用的时候传递的参数为准
    - 两者合并:bind 的时候传递的参数和调用的时候传递的参数会合并到一起,传递到函数内部


function ShowRandom(){
		//1~10的随机数
		this.number = parseInt(Math.random()*10+1);
	}
	//添加原型方法
	ShowRandom.prototype.show1 = function(){
		//改变了 定时器中的this的指向了  本来的定时器中的this是window 现在是实例对象了
		window.setInterval(this.show2.bind(this),1000);
	};
	ShowRandom.prototype.show2 = function(){
		//显示随机数
		console.log(this.number);
	}
	//实例对象
	var sr = new ShowRandom();
	//调用方法
	sr.show1();

  • call 和 apply 特性一样
    • 都是用来调用函数,而且是立即调用
    • 但是可以在调用函数的同时,通过第一个参数指定函数内部 this 的指向
    • call 调用的时候,参数必须以参数列表的形式进行传递,也就是以逗号分隔的方式依次传递即可
    • apply 调用的时候,参数必须是一个数组,然后在执行的时候,会将数组内部的元素一个一个拿出来,与形参一一对应进行传递
    • 如果第一个参数指定了 null 或者 undefined 则内部 this 指向 window
  • bind
    • 可以用来指定内部 this 的指向,然后生成一个改变了 this 指向的新的函数
    • 它和 call、apply 最大的区别是:bind 不会调用
    • bind 支持传递参数,它的传参方式比较特殊,一共有两个位置可以传递
        1. 在 bind 的同时,以参数列表的形式进行传递
        1. 在调用的时候,以参数列表的形式进行传递
      • 那到底以谁 bind 的时候传递的参数为准呢还是以调用的时候传递的参数为准
      • 两者合并:bind 的时候传递的参数和调用的时候传递的参数会合并到一起,传递到函数内部

伪数组和数组

在JavaScript中,除了5种原始数据类型之外,其他所有的都是对象,包括函数(Function)。

对象与数组的关系

在说区别之前,需要先提到另外一个知识,就是 JavaScript 的原型继承。
所有 JavaScript 的内置构造函数都是继承自 Object.prototype
在这个前提下,可以理解为使用 new Array()[] 创建出来的数组对象,都会拥有 Object.prototype 的属性值。

var obj = {};// 拥有 Object.prototype 的属性值
var arr = [];
//使用数组直接量创建的数组,由于 Array.prototype 的属性继承自 Object.prototype,
//那么,它将同时拥有 Array.prototype 和 Object.prototype 的属性值

可以得到对象和数组的第一个区别:对象没有数组 Array.prototype 的属性值。

什么是数组

数组具有一个最基本特征:索引,这是对象所没有的,下面来看一段代码:

var obj = {};
var arr = [];
 
obj[2] = 'a';
arr[2] = 'a';
 
console.log(obj[2]); // => a
console.log(arr[2]); // => a
console.log(obj.length); // => undefined
console.log(arr.length); // => 3
  • obj[2]输出’a’,是因为对象就是普通的键值对存取数据
  • 而arr[2]输出’a’ 则不同,数组是通过索引来存取数据,arr[2]之所以输出’a’,是因为数组arr索引2的位置已经存储了数据
  • obj.length并不具有数组的特性,并且obj没有保存属性length,那么自然就会输出undefined
  • 而对于数组来说,length是数组的一个内置属性,数组会根据索引长度来更改length的值
  • 为什么arr.length输出3,而不是1
    • 在给数组添加元素时,并没有按照连续的索引添加,所以导致数组的索引不连续,那么就导致索引长度大于元素个数

什么是伪数组

  1. 拥有 length 属性,其它属性(索引)为非负整数(对象中的索引会被当做字符串来处理,这里你可以当做是个非负整数串来理解)
  2. 不具有数组所具有的方法

伪数组,就是像数组一样有 length 属性,也有 0、1、2、3 等属性的对象,看起来就像数组一样,但不是数组,比如:

var fakeArray = {
  "0": "first",
  "1": "second",
  "2": "third",
  length: 3
};
 
for (var i = 0; i < fakeArray.length; i++) {
  console.log(fakeArray[i]);
}
 
Array.prototype.join.call(fakeArray,'+');

常见的伪数组有:

  • 函数内部的 arguments
  • DOM 对象列表(比如通过 document.getElementsByTags 得到的列表)
  • jQuery 对象(比如 $("div")

伪数组是一个 Object,而真实的数组是一个 Array。

伪数组存在的意义,是可以让普通的对象也能正常使用数组的很多方法,比如:

var arr = Array.prototype.slice.call(arguments);
 
Array.prototype.forEach.call(arguments, function(v) {
  // 循环arguments对象
});

// push
// some
// every
// filter
// map
// ...

以上在借用数组的原型方法的时候都可以通过数组直接量来简化使用:

var obj = {
  0: 'a',
  1: 'b',
  2: 'c',
  length: 3
}

;[].push.call(obj, 'd')

console.log([].slice.call(obj))

;[].forEach.call(obj, function (num, index) {
  console.log(num)
})

小结

  • 对象没有数组 Array.prototype 的属性值,类型是 Object ,而数组类型是 Array
  • 数组是基于索引的实现, length 会自动更新,而对象是键值对
  • 使用对象可以创建伪数组,伪数组可以正常使用数组的大部分方法

闭包

闭包是什么

闭包就是在函数体内部return另外的一个函数体,并且将外层函数利用IIFE的写法直接将其调用并讲内层函数的引用返回给一个变量。解决的问题是可以讲全局变量变为一个局部变量,并且该变量被修改的唯一途径就是调用内层函数。

闭包就是能够读取其他函数内部变量的函数,
由于在 Javascript 语言中,只有函数内部的子函数才能读取局部变量,
因此可以把闭包简单理解成 “定义在一个函数内部的函数”。
所以,在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。

闭包的模式

函数模式的闭包 对象模式的闭包

闭包的作用

可以在函数外部读取函数内部成员

让函数内成员始终存活在内存中

缓存数据 延长数据链

闭包的优缺点

优点:缓存数据,可以在函数体外访问函数体内部的变量。

缺点:理论上如果有贼多的闭包,会造成内存泄露,

造成内存泄露的情况有:闭包,全局变量,定时器,DOM节点

闭包小案例

	//函数模式的闭包
		function f2(){
			var num = 10;
			return function(){
				num++;
				return num;
			}
		}
		var ff = f2();
		console.log(ff());//11
		console.log(ff());//12
		console.log(ff());//13
	//闭包的方式 产生三个随机数 但是都是相同的
		function f1(){
			var num = parseInt(Math.random()*10+1);
			return function(){
				console.log(num);
			}
		}
		var ff = f1();
		ff();
		ff();
		ff();

沙箱

沙箱

环境 黑盒 在一个虚拟的环境中模拟真实的世界 做实验 实验结果和真实世界的结果是一样的 但是不会影响真实世界 沙箱避免命名重复

沙箱小案例

	<div>这是div</div>
	<div>这是div</div>
	<div>这是div</div>
	<p>这是p</p>
	<p>这是p</p>
	<p>这是p</p>
	<script>
		 var getTag = 10;
		 var dvObjs = 20;
		 var pObjs = 30;
		 (function(){
		 	//根据标签名字获取元素
		 	function getTag(tagName){
		 		return document.getElementsByTagName(tagName);
		 	}
		 	//获取所有的div
		 	var dvObjs = getTag("div");
		 	for (var i = 0; i < dvObjs.length; i++) {
		 		dvObjs[i].style.border = "2px solid pink";
		 	}
		 	//获取所有的p
		 	var pObjs = getTag("p");
		 	for (var i = 0; i < pObjs.length; i++) {
		 		pObjs[i].style.border = "2px solid pink";
		 	}
		 }());
		 console.log(getTag);
		 console.log(dvObjs);
		 console.log(pObjs);	
	</script>

递归

递归

函数中调用函数自己 此时就是递归 递归一定有结束的条件 否则就是死循环,

递归一般应用在遍历上

递归轻易不要用 效率很低

递归一般应用

  • 深拷贝
  • 菜单树
  • 遍历 DOM 树

递归小案例

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>Document</title>
</head>
<body>
	<script>
	//求n个数字的和 计算1+2+3+5
	 // var sum = 0;
	 // for (var i = 1; i <= 5; i++) {
	 // 	sum+=i;
	 // }
	 // console.log(sum);


	 //递归实现:求n个数字之和
	 
	 //函数的声明
	 // function getSum(x){
	 // 	if (x==1) {
	 // 		return 1;
	 // 	}
	 // 	return x+getSum(x-1);
	 // }
	 // //函数的调用
	 // console.log(getSum(5));
	 /**
	  *执行过程:
	  * 代码执行getSum(5)------>进入函数 此时x是5  执行的是5+getSum(4) 此时代码等待
	  * 此时5+getSum(4)代码先不进行计算 先执行getSum(4) 进入函数 执行的是4+getSum(3) 等待
	  * 先执行的是getSum(3) 进入函数 执行3+getSum(2) 等待 先执行getSum(2) 进入函数 执行2+getSum(1) 等待 先执行getSum(1) 执行的是x==1的判断 return 1 所以此时getSum(1)的结果是1 开始向外走出去 2+getSum(1)此时结果是:2+1
	  * 执行:
	  * getSum(2)-----》2+1
	  * 3+getSum(2)-----》3+2+1
	  * 4+getSum(3)-----》4+3+2+1
	  * 5+getSum(4)-------》5+4+3+2+1
	  * 
	  *结果是:15
	  *
	  *
	  *
	  * 
	  */
	 
	 //递归案例:求一个数字各个位数上的数字的和:123------>6------>1+2+3
	 //52
	
	  	function getEverySum(x){
	  		if (x<10) {
	  			return x;
	  		}
	  		//获取的是这个数字的个位数
	  		return x % 10 + getEverySum(parseInt(x/10));//----- getEverySum(5)  x=5
	  	}	
	  	console.log(getEverySum(52));

	  	//执行顺序:
	  	/**
	  	 *getEverySum(52)-----》进入函数 x是52 执行的是x % 10 + getEverySum(parseInt(x/10)) 此时等待  代码先不进行计算 先执行getEverySum(parseInt(52/10)) 进入函数执行的是 getEverySum(parseInt(52/10)) 进入函数 执行的是x<10 retrun 5 所以 此时的结果是:
	  	 * 
	  	 *52 % 10 + getEverySum(5)-------------> 2 + 5
	  	 *
	  	 * 
	  	 */
	  
	  	//执行顺序:
	  	/**
	  	 *getEverySum(523)-----》进入函数 x是523 执行的是x % 10 + getEverySum(parseInt(x/10)) 此时等待  代码先不进行计算 先执行getEverySum(parseInt(523/10))【getEverySum(52)】 
	
	  	 进入函数执行的是 x % 10 + getEverySum(parseInt(x/10))【52%10+getEverySum(5)】 代码先不进行计算 先执行gerEverySum(5) 此时执行的是x<10的结果 return 5 所以
	  	 *
	  	 *52%10+getEverySum(5) --------> 2 + 5
	  	 *523%10+getEverySum(52)--------> 3 + 2 + 5 
	  	 * 
	  	 *
	  	 *
	  	 * 
	  	 */
	  	
	  	//求斐波那契数列
	  	   function getFib(x){
	  	   		if (x==1||x==2) {
	  	   			return 1;
	  	   		}
	  	   		return getFib(x-1)+getFib(x-2);
	  	   }
	  	   console.log(getFib(12));


	</script>
</body>
</html>

浅拷贝

浅拷贝

拷贝就是复制 就相当于把一个对象中的所有的内容 复制一份给另一个对象 直接复制 或者说 就是把一个对象的地址给了另一个对象 他们指向相同 两个对象之间有共同的属性或者方法 都可以使用

浅拷贝小案例

var obj1 = {
			age:10,
			sex:"男",
			car:["奔驰","宝马","劳斯莱斯","特斯拉"]
		};
		//另一个对象
		var obj2 = {

		}

		//写一个函数 作用:把一个对象的属性复制到另一个对象中 浅拷贝
		//把a对象中的所有属性复制到对象b中
		function extend(a,b){
			for (var key in a) {
				b[key] = a[key];
			}
		}
		extend(obj1,obj2);
		console.dir(obj2);//-----开始的时候这个对象是空对象
		console.dir(obj1);


深拷贝

深拷贝

拷贝还是复制 深:把一个对象中所有的属性或者方法 一个一个的找到 并且另一个对象中开辟相应的空间 一个一个的存储到另一个对象中

深拷贝小案例

			var obj1 = {
				age:10,
				sex:"男",
				car:["奔驰","宝马","劳斯莱斯","特斯拉"],
				dog:{
					name:"大白",
					age:5,
					color:"黑白色"
				}
			};
			var obj2 = {};//空对象
			//通过函数实现 把对象a中的所有的数据深拷贝到对象b中
			function extend(a,b){
				for (var key in a) {
					//先获取a对象中每个属性的值
					var item = a[key];
					//判断这个属性的值是不是数组
					if (item instanceof Array) {
						//如果是数组  那么在b对象中添加一个新的属性 并且这个属性值也是数组
						b[key] = [];
						//调用方法 把a对象中这个数组的属性值一个一个的复制到b对象的这个数组属性中
						extend(item,b[key]);

					}else if(item instanceof Object){//判断这个属性是不是对象类型的 如果是对象类型的  那么在b对象中添加一个属性 是一个空对象
						b[key] = {};
						//再次调用这个函数 把a对象中的属性对象的值一个一个的复制到b对象的这个属性对象中
						extend(item,b[key]);

					}else{
						//如果是普通的数据 直接复制到b对象中
						b[key] = item;
					}
				}
			}
			extend(obj1,obj2);
			console.dir(obj1);
			console.dir(obj2);

遍历DOM树

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>Document</title>
</head>
<body>
	<h1>遍历DOM树</h1>
	<p style="color:green;">Tip:可以在遍历的回调函数中任意定制需求</p>
	<div>
		<ul>
			<li>123</li>
			<li>456</li>
			<li>789</li>
		</ul>
		<div>
			<div>
				<span>haha</span>
			</div>
		</div>
	</div>
	<div id="demo_node">
		<ul>
			<li>123</li>
		</ul>
		<p>hello</p>
		<h2>world</h2>
		<div>
			<p>dsa</p>
			<h3>
				<span>dasda</span>
			</h3>
		</div>
	</div>
	<script>
		//获取页面中的根节点----根标签
		 var root = document.documentElement;//html
		 //函数遍历DOM树
		 //根据根节点 调用fn的函数 显示的是根节点的名字
		 function forDOM(root1){
		 	//调用f1 显示的是节点的名字
		 	// f1(root1);
		 	//获取根节点中所有的子节点
		 	var children = root1.children;
		 	//调用遍历所有子节点的函数
		 	forChildren(children);
		 }

		 //给我所有子节点 我把这个子节点中的所有的子节点显示出来
		 function forChildren(children){
		 	//遍历所有子节点
		 	for (var i = 0; i < children.length; i++) {
		 		//每个子节点
		 		var child = children[i];
		 		//显示每个子节点的名字
		 		f1(child);
		 		//判断child下面有没有子节点 如果还有子节点 那么就继续遍历
		 		child.children&&forDOM(child);

		 	}
		 }
 		function f1(node){
		 	console.log("节点名字:"+node.nodeName);
		 }

		 //函数调用 传入根节点
		 forDOM(root);

		 //节点:nodeName nodeType nodeValue
		 

		 //第一个函数:给我一个根节点 我把 每个子节点的名字显示出来(children)
		 //获取这个根节点的子节点
		 //var children = 根节点.children
		 //调用第二个函数
		 
		 //第二个函数:给我所有子节点 我把每个子节点的名字显示出来(children)
		 //for(var i=0;i<children.length;i++){
		 //   //每个子节点
		 //   var child = children[i];
		 //   f1(child);给我节点 我显示该节点的名字
		 //   child是子节点 但是如果child里面还有子节点 此是child就是爹了  
		 //   child.children&&第一个函数(child);
		 //
		 //}
		 //
		
	</script>
</body>
</html>

本地储存

cooking 只能存储字符串

		// let date = new Date("2020-12-05 01:00:00");
		// 设置一个会话cookie
		// document.cookie = "age=18";
		// document.cookie = "sex=1";

		// 设置存储日期
		// document.cookie = `name=wangdawei;expires=${date}`;

		// 封装读取读cookie的函数
		function getCookie(key){
			let cookie = document.cookie;
			let arr = cookie.split("; ");
			let result = {}
			arr.forEach(item=>{
				let key = item.split("=")[0];
				let value = item.split("=")[1];
				result[key] = value;
			})

			if(key){
				return result[key];
			}
			return result;
		}
		// 删除cookie的函数
		function removeCookie(key){
			let guoqu = new Date("1970-01-01 00:00:00")
			if(key){
				document.cookie = `${key}=beybey;expires=${guoqu}`
			}
			else{
				let cookie = getCookie();

				for(let i in cookie){
					document.cookie = `${i}=beybey;expires=${guoqu}`
				}
			}
		}
		// 写入cookie的函数
		function setCookie(key,value,expires){
			if(typeof expires === "number"){
				let date = new Date();
				date.setDate(date.getDate()+expires)
				document.cookie = `${key}=${value};expires=${date}`;
			}
			else{
				document.cookie = `${key}=${value};expires=${expires}`;
			}
		}


		setCookie("name","wangdawei",10)
		setCookie("age","18",30)
		setCookie("sex","1",new Date("2022-01-01"))

localStorage 只能存储字符串

		// 设置
		// localStorage.setItem("name","wangdawei")
		// 获取
		// localStorage.getItem("name")
		// 删除
		// localStorage.removeItem("name")
		// 清空
		// localStorage.clear()
		// 数据条数
		// localStorage.length

sessionStorage 只能存储字符串

    sessionStorage.setItem("name","wangdawei")
	sessionStorage.getItem()
	sessionStorage.removeItem()
	sessionStorage.clear()
	sessionStorage.length()

cookie,localStorage和sessionStorage之间的区别是什么?

只能存储字符串,可以将对象JSON.stringify() 编码后存储

容量较大,sessionStorage约5M、localStorage约20M

* cookie:操作麻烦,需要大量的字符串处理。兼容性好,数据的生命周期可以灵活地设置。
* localStorage:相对于cookie来说兼容性稍差。数据的生命周期是永久性存储。
* sessionStorage:数据生命周期在会话期(在当前标签页中),其余的所有特性都类似于localStorage的。

ajax

ajax简介

概述 Web 程序最初的目的就是将信息(数据)放到公共的服务器,让所有网络用户都可以通过浏览器访问。

在此之前,我们可以通过以下几种方式让浏览器发出对服务端的请求,获得服务端的数据:

地址栏输入地址,回车,刷新

特定元素的 href 或 src 属性

表单提交

这些方案都是我们无法通过或者很难通过代码的方式进行编程(对服务端发出请求并且接受服务端返回的响应), 如果我们可以通过 JavaScript 直接发送网络请求,那么 Web 的可能就会更多,随之能够实现的功能也会更多,至 少不再是“单机游戏”。

AJAX(Asynchronous JavaScript and XML),最早出现在 2005 年的 Google Suggest,是在浏览器端进行网络编 程(发送请求、接收响应)的技术方案,它使我们可以通过 JavaScript 直接获取服务端最新的内容而不必重新加载 页面。让 Web 更能接近桌面应用的用户体验。

说白了,AJAX 就是浏览器提供的一套 API,可以通过 JavaScript 调用,从而实现通过代码控制请求与响应。实现 网络编程。

ajax基本步骤

// 1.实例化XMLHttpRequest对象
	let http = new XMLHttpRequest()

	// 2.规划一个HTTP请求
	// mehods: String,请求方式,可以是"get"也可以是"post"
	// url:String,请求地址,比如:"http://192.168.0.1/xxxx.txt"
	// async:Boolean,同步或异步,可选参数,默认是异步的。
	http.open(method,url,async)

	// 3.发送HTTP请求
	http.send()

	// 4.接收来自服务器端的请求
	http.onreadystatechange = function(){
		if(http.readyState === 4){
			console.log(http.responseText)
		}
	}

带请求参数

let http = new XMLHttpRequest()
			http.open("get","http://10.35.164.50/message.php?name=wangdawei");
			http.send()
			http.onreadystatechange = function(){
				if(http.readyState === 4){
					console.log(http.responseText)
				}
			}

readyState 状态

由于 readystatechange 事件是在 xhr 对象状态变化时触发(不单是在得到响应时),也就意味着这个事件会被 触发多次,所以我们有必要了解每一个状态值代表的含义:

状态 状态描述 说明

0 UNSENT 代理(XHR)被创建,但尚未调用 open() 方法。

1 OPENED open() 方法已经被调用,建立了连接。

2 HEADERS_RECEIVED send() 方法已经被调用,并且已经可以获取状态行和响应头。

3 LOADING 响应体下载中, responseText 属性可能已经包含部分数据。

4 DONE 响应体下载完成,可以直接使用 responseText 。

遵循 HTTP

本质上 XMLHttpRequest 就是 JavaScript 在 Web 平台中发送 HTTP 请求的手段,所以我们发送出去的请求任然是 HTTP 请求,同样符合 HTTP 约定的格式

get请求(速度快 安全性不好)

var xhr = new XMLHttpRequest()
// GET 请求传递参数通常使用的是问号传参
// 这里可以在请求地址后面加上参数,从而传递数据到服务端
xhr.open('GET', './delete.php?id=1')
// 一般在 GET 请求时无需设置响应体,可以传 null 或者干脆不传
xhr.send(null)
xhr.onreadystatechange = function () {
if (this.readyState === 4) {
console.log(this.responseText)
}
}
// 一般情况下 URL 传递的都是参数性质的数据,而 POST 一般都是业务数据

post请求 (速度慢 安全性高)

var xhr = new XMLHttpRequest()
// open 方法的第一个参数的作用就是设置请求的 method
xhr.open('POST', './add.php')
// 设置请求头中的 Content‐Type 为 application/x‐www‐form‐urlencoded
// 标识此次请求的请求体格式为 urlencoded 以便于服务端接收数据
xhr.setRequestHeader('Content‐Type', 'application/x‐www‐form‐urlencoded')
// 需要提交到服务端的数据可以通过 send 方法的参数传递
// 格式:key1=value1&key2=value2
xhr.send('key1=value1&key2=value2')
xhr.onreadystatechange = function () {
if (this.readyState === 4) {
console.log(this.responseText)
}
}

post传参

      let http = new XMLHttpRequest()
        http.open("POST","http://127.0.0.1:8080/getAll");
        // 设置请求头
        http.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
        http.send("name=wangdawei&age=18");
        http.onreadystatechange = function(){
            if(http.readyState === 4){
                consolelog(http.responseText)
            }
        }

JSON的正反解析

JSON.stringify(xxxx) // 反解析
JSON.parse(xxxx) //正解析

post和get请求的区别

1

get请求一般用去请求获取数据
post一般作为发送数据到后台时使用
2
get请求也可传参到后台,但是其参数在浏览器的地址栏的url中可见,所以隐私性安全性较差,且参数长度也是有限制的
post请求传递参数放在Ruquset body中,不会在url中显示,比get要安全,且参数长度无限制
3
get请求刷新浏览器或回退时没有影响
post回退时会重新提交数据请求
4
get请求可被缓存
post请求不会被缓存
5
get请求保留在浏览器历史记录中
post请求不会保留在浏览器历史记录中
6
get请求可被收藏为书签
post请求不能被收藏为书签
7
get请求只能进行url编码
post支持多种编码方式
8
get请求比较常见的方式是通过url地址栏请求
post最常见是通过form表单发送数据请求

跨域

什么是跨域

浏览器的同源组策略:浏览器安全防护的一种机制,
它规定了一个来自某个服务器的静态文件不允许向另外一台跨域服务器发送Ajax请求。
只有主机名,协议名,端口号三者都一致的情况下才会被视作是同域(或一台服务器),
只要三者有任何一个不一样,那么这个Ajax请求就会被同源组策略所阻止。

服务器端反向代理解决跨域(我用的是node js写接口)

server.all("*",function(req,res,next){
	    //设置允许跨域的域名,*代表允许任意域名跨域
	    res.header("Access-Control-Allow-Origin","*");
	    //允许的header类型
	    res.header("Access-Control-Allow-Headers","content-type");
	    //跨域允许的请求方式 
	    res.header("Access-Control-Allow-Methods","DELETE,PUT,POST,GET,OPTIONS");
	    if (req.method.toLowerCase() == 'options')
	        res.send(200);  //让options尝试请求快速结束
	    else
	        next();
	})

JSONP解决跨域

原理

JSONP不是一套标准技术,而是解决跨域问题的方法之一。
1 具体的工作机制是,前端利用一个script标签向后端发送一个HTTP请求,并且提前准备好一个回调函数,这个回调函数的函数名会在请求参数中在callback参数值中传递给后端。
2 后端接收到来自前端发送请求后,拆解出callback参数的参数值并且以此为函数名拼接一段执行调用函数的JS代码段返回给前端并把数据当做这个函数的实参一并发送。
3 前端接收到执行函数的代码段后会自动调用之前声明的回调函数,回调函数中的形参就代表后端返回的数据。

前端

        function wangxiaowei(data){
    		console.log(data)
    	}

    	let script = document.createElement("script")
    	script.setAttribute("src","http://10.35.164.50:8080/test?callback=wangxiaowei");
    	document.querySelector("body").appendChild(script)

后端(用node js写的后端)

let server = require('express')();
let mysql = require('mysql')
let sql = mysql.createConnection({
  host     : 'localhost',
  user     : 'root',
  password : '123456',
  database : 'tech'
});


server.get("/test",function(request,response){

	let callback = request.query.callback;

	sql.query(`SELECT * FROM flights`,(error,data)=>{
		if(error){
			console.log(error)
			response.end("error")
			return
		}
		response.end(`${callback}(${JSON.stringify(data)})`)
	})

})


server.listen(8080)

H5-新增API

全屏方法

HTML5规范允许用户自定义网页上任一元素全屏显示。

  • Node.requestFullScreen() 开启全屏显示
  • Node.cancelFullScreen() 关闭全屏显示
  • 由于其兼容性原因,不同浏览器需要添加前缀如:
    webkit内核浏览器:webkitRequestFullScreen、webkitCancelFullScreen,如chrome浏览器。
    Gecko内核浏览器:mozRequestFullScreen、mozCancelFullScreen,如火狐浏览器。
  • document.fullScreen检测当前是否处于全屏
    不同浏览器需要添加前缀
    document.webkitIsFullScreen、document.mozFullScreen

多媒体

自定义播放器

方法

方法描述
addTextTrack()向音频/视频添加新的文本轨道
canPlayType()检测浏览器是否能播放指定的音频/视频类型
load()重新加载音频/视频元素
play()开始播放音频/视频
pause()暂停当前播放的音频/视频

属性

属性描述
audioTracks返回表示可用音轨的 AudioTrackList 对象
autoplay设置或返回是否在加载完成后随即播放音频/视频
buffered返回表示音频/视频已缓冲部分的 TimeRanges 对象
controller返回表示音频/视频当前媒体控制器的 MediaController 对象
controls设置或返回音频/视频是否显示控件(比如播放/暂停等)
crossOrigin设置或返回音频/视频的 CORS 设置
currentSrc返回当前音频/视频的 URL
currentTime设置或返回音频/视频中的当前播放位置(以秒计)
defaultMuted设置或返回音频/视频默认是否静音
defaultPlaybackRate设置或返回音频/视频的默认播放速度
duration返回当前音频/视频的长度(以秒计)
ended返回音频/视频的播放是否已结束
error返回表示音频/视频错误状态的 MediaError 对象
loop设置或返回音频/视频是否应在结束时重新播放
mediaGroup设置或返回音频/视频所属的组合(用于连接多个音频/视频元素)
muted设置或返回音频/视频是否静音
networkState返回音频/视频的当前网络状态
paused设置或返回音频/视频是否暂停
playbackRate设置或返回音频/视频播放的速度
played返回表示音频/视频已播放部分的 TimeRanges 对象
preload设置或返回音频/视频是否应该在页面加载后进行加载
readyState返回音频/视频当前的就绪状态
seekable返回表示音频/视频可寻址部分的 TimeRanges 对象
seeking返回用户是否正在音频/视频中进行查找
src设置或返回音频/视频元素的当前来源
startDate返回表示当前时间偏移的 Date 对象
textTracks返回表示可用文本轨道的 TextTrackList 对象
videoTracks返回表示可用视频轨道的 VideoTrackList 对象
volume设置或返回音频/视频的音量

事件

事件描述
abort当音频/视频的加载已放弃时
canplay当浏览器可以播放音频/视频时
canplaythrough当浏览器可在不因缓冲而停顿的情况下进行播放时
durationchange当音频/视频的时长已更改时
emptied当目前的播放列表为空时
ended当目前的播放列表已结束时
error当在音频/视频加载期间发生错误时
loadeddata当浏览器已加载音频/视频的当前帧时
loadedmetadata当浏览器已加载音频/视频的元数据时
loadstart当浏览器开始查找音频/视频时
pause当音频/视频已暂停时
play当音频/视频已开始或不再暂停时
playing当音频/视频在已因缓冲而暂停或停止后已就绪时
progress当浏览器正在下载音频/视频时
ratechange当音频/视频的播放速度已更改时
seeked当用户已移动/跳跃到音频/视频中的新位置时
seeking当用户开始移动/跳跃到音频/视频中的新位置时
stalled当浏览器尝试获取媒体数据,但数据不可用时
suspend当浏览器刻意不获取媒体数据时
timeupdate当目前的播放位置已更改时
volumechange当音量已更改时
waiting当视频由于需要缓冲下一帧而停止

地理定位

在HTML规范中,增加了获取用户地理信息的API,
这样使得我们可以基于用户位置开发互联网应用,
即基于位置服务 (Location Base Service)

  • 获取当前地理信息
navigator.geolocation.getCurrentPosition(successCallback, errorCallback) 
  • 重复获取当前地理信息
navigator. geolocation.watchPosition(successCallback, errorCallback)
  • 当成功获取地理信息后,会调用succssCallback,并返回一个包含位置信息的对象position。
    position.coords.latitude纬度
    position.coords.longitude经度
    position.coords.accuracy精度
    position.coords.altitude海拔高度
  • 当获取地理信息失败后,会调用errorCallback,并返回错误信息error
  • 在现实开发中,通过调用第三方API(如百度地图)来实现地理定位信息,这些API都是基于用户当前位置的,并将用位置位置(经/纬度)当做参数传递,就可以实现相应的功能。

本地存储

随着互联网的快速发展,基于网页的应用越来越普遍,
同时也变的越来越复杂,为了满足各种各样的需求,会经常性在本地存储大量的数据,
HTML5规范提出了相关解决方案。

  • 特性
    • 设置、读取方便
    • 容量较大,sessionStorage约5M、localStorage约20M
    • 只能存储字符串,可以将对象JSON.stringify() 编码后存储
  • window.sessionStorage
    • 生命周期为关闭浏览器窗口
    • 在同一个窗口(页面)下数据可以共享
  • window.localStorage
    • 永久生效,除非手动删除(服务器方式访问然后清除缓存)
    • 可以多窗口(页面)共享
  • 方法
    • setItem(key, value) 设置存储内容
    • getItem(key) 读取存储内容
    • removeItem(key) 删除键值为key的存储内容
    • clear() 清空所有存储内容

历史管理

提供window.history,对象我们可以管理历史记录,
可用于单页面应用,Single Page Application,可以无刷新改变网页内容。

  • pushState(data, title, url) 追加一条历史记录
    • data用于存储自定义数据,通常设为null
      ​+ title网页标题,基本上没有被支持,一般设为空
      ​+ url 以当前域为基础增加一条历史记录,不可跨域设置
  • replaceState(data, title, url) 与pushState()基本相同,
    不同之处在于replaceState(),只是替换当前url,不会增加/减少历史记录。
  • onpopstate事件,当前进或后退时则触发

离线应用

HTML5中我们可以轻松的构建一个离线(无网络状态)应用,只需要创建一个cache manifest文件。

  • 优势
    • 1、可配置需要缓存的资源
    • 2、网络无连接应用仍可用
    • 3、本地读取缓存资源,提升访问速度,增强用户体验
    • 4、减少请求,缓解服务器负担
  • 缓存清单
    • 一个普通文本文件,其中列出了浏览器应缓存以供离线访问的资源,推荐使用.appcache为后缀名
    • 例如我们创建了一个名为demo.appcache的文件,然后在需要应用缓存在页面的根元素(html)添加属性manifest=“demo.appcache”,路径要保证正确。
  • manifest文件格式
    • 1、顶行写CACHE MANIFEST
    • 2、CACHE: 换行 指定我们需要缓存的静态资源,如.css、image、js等
    • 3、NETWORK: 换行 指定需要在线访问的资源,可使用通配符
    • 4、FALLBACK: 换行 当被缓存的文件找不到时的备用资源
  • 其它
    • 1、CACHE: 可以省略,这种情况下将需要缓存的资源写在CACHE MANIFEST
    • 2、可以指定多个CACHE: NETWORK: FALLBACK:,无顺序限制
    • 3、#表示注释,只有当demo.appcache文件内容发生改变时或者手动清除缓存后,才会重新缓存。
    • 4、chrome 可以通过chrome://appcache-internals/工具和离线(offline)模式来调试管理应用缓存

文件读取

HTML5新增内建对象,可以读取本地文件内容。

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>Document</title>
</head>
<body>
	<input type="file" class="file" multiple />
	<img src="" alt="" id="img" />
	<script>
	// 获取文件表单元素
		var file = document.querySelector('.file');

	//选择文件后触发
		file.onchange = function(){
			// 初始化了一个文件读取对象
			var reader = new FileReader();
			//读取文件数据  this.files[0] 文件表单元素选择的第一个文件
			reader.readAsDataURL(this.files[0]);
			//读取的过程就相当于 加载的过程
			//读取完毕后 预览
			reader.onload = function(){
				/*读取完毕后 base64位数据 表示图片*/
				console.log(this.result);
				document.querySelector("#img").src = this.result;
			}
		}



	</script>
</body>
</html>

网络状态

  • 我们可以通过window.onLine来检测,用户当前的网络状况,返回一个布尔值
    • window.online用户网络连接时被调用
    • window.offline用户网络断开时被调用

navcat链接数据库

准备工作

​ 里面有Apache htdocs MySQL

​ Apache 打开80端口

​ htdocs 存放请求的资源

​ MySQL 数据库

MYSQL

数据库的结构关系

​ 库 > 表 > 列(字段)和行(数据)

​ 每一个表都有一个id的字段,该字段是该表的主键,并且一般来说会不为空并自动自增。

数据类型

* int 整型(整数数字)
* float 浮点型(小数)
* varchar 字符型(字符串)
* date 日期(YYYY-MM-DD)
* datetime 日期时间(YYYY-MM-DD HH:MM:SS)

SQL语句

	<!-- 查数据表内所有数据 -->
	SELECT * FROM 表名 
	<!-- 查根据条件查询数据 -->
	SELECT * FROM 表名 WHERE 字段名=某值
	SELECT * FROM 表名 WHERE 字段名=某值 AND 字段名=某值
	SELECT * FROM 表名 WHERE 字段名=某值 OR 字段名=某值
	
	<!-- 根据某个字段名进行排序 -->
	SELECT * FROM 表名 ORDER BY 字段名 
	<!-- 根据某个字段名进行反向排序 -->
	SELECT * FROM 表名 ORDER BY 字段名 DESC
	
	
	<!-- 删除表内所有数据 -->
	DELETE FROM 表名
	<!-- 查根据条件删除数据 -->
	DELETE FROM 表名 WHERE 字段名=某值
	
	<!-- 插入新数据 -->
	INSERT INTO 表名 (字段名1,字段名2,字段名3,....) VALUES (值1,值2,值3,.....)
	
	<!-- 根据条件修改数据 -->
	UPDATE 表名 SET 字段名=某值,字段名=某值.... WHERE 字段名=某值

node.js 操作数据库

模块

模块:用require()引入的一个对象;一类是系统模块,还有一类是第三方模块。

fs模块–fs.writeFile(file, data[, options], callback)

	let fs = require("fs");
	// 异步写入
	fs.writeFile("路径","写入的数据",function(err){
		
	})
	
	// 异步读取
	error  //读取失败
	data   //读取的数据
	       // data读取到的是2进制数据文件
	fs.readFile("test.txt",function(error,data){
		if(error){
			console.log("读取失败")
			return;
		}
		
		console.log(String(data))
		
	})

第三方模块下载的方法

将npm下载地址切换为国内淘宝镜像:npm config set registry http://registry.npm.taobao.org

下载安装第三方模块:npm install 模块名 --save

第三方模块官网集合:https://www.npmjs.com/

express(第三方模块)

	let server =  require("express")();
	
	require("express")这个东西是一个函数。
	require("express")()这个东西返回一个对象。
	


	// 计划监听来自前端的HTTP请求,如果请求地址是/a,则自动调用第二个参数的函数体
	// get两个参数 第一个参数是字符串
	server.get("/a",function(request,response){

         // request.query来自前端请求的所带参数的对象
		// 接收到来自前端的请求name参数,并赋值到name变量中
		let name = request.query.name;
		console.log("接收到来自前端的请求")
		// 向前端返回一段内容
		response.end() //有可能乱码,改用send();
		response.send(`${name},success!!!!`)
	})
	
	// 真正将上面的计划执行监听到本机的81端口上。
	server.listen(81)
let server = require("express")();
let fs = require("fs");
let mysql = require('mysql');

// 规划链接
let sql = mysql.createConnection({
	host: 'localhost',
	user: 'root',
	password: '123456',
	database: 'tech'
});

// 尝试链接
sql.connect();






// 计划监听来自前端的HTTP请求,如果请求地址是/a则自动调用第二个参数的函数体
server.get("/a", function (request, response) {

	// 接收到来自前端的请求name参数,并赋值到name变量中
	let name = request.query.name;
	console.log("接收到来自前端的请求")
	// 向前端返回一段内容
	response.send(`${name},success!!!!`)
})

// 计划监听来自前端的HTTP请求,如果请求地址是/a则自动调用第二个参数的函数体
server.get("/b", function (request, response) {
	console.log("接收到来自前端的请求")
	// 向前端返回一段内容
	response.end("ok!")
})

// 监听来自前端的studentsList路由请求
// 当接收到请求后使用fs模块的readFile方法去取服务器的students.json文件
// 并将其转换为字符串后向前端返回

server.get("/studentsList", function (request, response) {
	fs.readFile("students.json", function (error, data) {
        
		response.send(String(data))
         // response.end(data)  读取的本地文件乱码
         // response.send(data)  二进制文件,发送到前端会直接下载
         // response.send(String(data))  前端收到的是字符串,需要转换为杰森
	})
})

// 操作的是数据库
// 接收到来自前端的queryStudent路由请求
// 并且接收来自前端请求的id参数
// 从数据中找到与id值相对应的那个学生信息并将其返回。
server.get("/queryStudent", function (request, response) {

	// 接收到来自前端的请求id参数,并赋值到id变量中
	let id = request.query.id;

	// 执行sql任务
	sql.query(`SELECT * FROM students WHERE id=${id}`, function (error, data) {
		if (error) {
			console.log(error)
		} else {
            
			response.send(JSON.stringify(data[0]))
		}
	})


})


// 真正将上面的计划执行监听到本机的81端口上。
server.listen(81)

mysql(第三方模块)

	let mysql      = require('mysql');

	// 规划链接
	let sql = mysql.createConnection({
	  host     : 'localhost',
	  user     : 'root',
	  password : '123456',
	  database : 'tech'
	});
	 
	// 尝试链接
	sql.connect();


	// 执行sql任务
	//data数据从数据库拿到的是Json对象,向前端发送需要先转换成字符串
	sql.query("SELECT * FROM students",function(error,data){
		if(error){
			console.log(error)
		}
		else{
			console.log(data)
		}
	})

2-6 解决跨域 服务器反向代理代码

	server.all("*",function(req,res,next){
	    //设置允许跨域的域名,*代表允许任意域名跨域
	    res.header("Access-Control-Allow-Origin","*");
	    //允许的header类型
	    res.header("Access-Control-Allow-Headers","content-type");
	    //跨域允许的请求方式 
	    res.header("Access-Control-Allow-Methods","DELETE,PUT,POST,GET,OPTIONS");
	    if (req.method.toLowerCase() == 'options')
	        res.send(200);  //让options尝试请求快速结束
	    else
	        next();
	})

expresse-static:NodeJS环境中向前端提供静态服务的第三方模块,可以代替Apache

const server = require("express")();
const expressStatic = require("express-static")
const mysql = require('mysql');
const port = 8080;
// 拦截所有请求,并且声明静态请求目录
server.use(expressStatic(`${__dirname}/static`));

打开数据库文件出错

SET NAMES utf8mb4;删掉这句话
SET FOREIGN_KEY_CHECKS = 0;

/*
 Navicat Premium Data Transfer

 Source Server         : heihei
 Source Server Type    : MySQL
 Source Server Version : 50090
 Source Host           : localhost:3306
 Source Schema         : tech

 Target Server Type    : MySQL
 Target Server Version : 50090
 File Encoding         : 65001

 Date: 24/11/2020 17:58:01
*/

/*SET NAMES utf8mb4;删掉这句话*/
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for flights
-- ----------------------------
DROP TABLE IF EXISTS `flights`;
CREATE TABLE `flights`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `airNo` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
  `destination` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
  `DepTime` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
  `landTime` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
  PRIMARY KEY USING BTREE (`id`)
) ENGINE = MyISAM AUTO_INCREMENT = 37 CHARACTER SET = utf8 COLLATE = utf8_bin ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of flights
-- ----------------------------
INSERT INTO `flights` VALUES (7, 'CES2321', 'Karamay (KRY)', '14:51 CST', '18:26 CST');
INSERT INTO `flights` VALUES (11, 'CES2321', 'Karamay (KRY)', '14:51 CST', '18:26 CST');
INSERT INTO `flights` VALUES (12, 'CES2259', 'Lüliang (LLV)', '14:44 CST', '15:31 CST');
INSERT INTO `flights` VALUES (14, 'CES2259', 'Lüliang (LLV)', '14:44 CST', '15:31 CST');
INSERT INTO `flights` VALUES (15, 'CES2321', 'Karamay (KRY)', '14:51 CST', '18:26 CST');
INSERT INTO `flights` VALUES (16, 'CES2259', 'Lüliang (LLV)', '14:44 CST', '15:31 CST');
INSERT INTO `flights` VALUES (17, 'CES2321', 'Karamay (KRY)', '14:51 CST', '18:26 CST');
INSERT INTO `flights` VALUES (18, 'CES2321', 'Karamay (KRY)', '14:51 CST', '18:26 CST');
INSERT INTO `flights` VALUES (19, 'CES2259', 'Lüliang (LLV)', '14:44 CST', '15:31 CST');
INSERT INTO `flights` VALUES (20, 'CES2321', 'Karamay (KRY)', '14:51 CST', '18:26 CST');
INSERT INTO `flights` VALUES (21, 'CES2321', 'Karamay (KRY)', '14:51 CST', '18:26 CST');
INSERT INTO `flights` VALUES (22, 'CES2259', 'Lüliang (LLV)', '14:44 CST', '15:31 CST');
INSERT INTO `flights` VALUES (23, 'CES2321', 'Karamay (KRY)', '14:51 CST', '18:26 CST');
INSERT INTO `flights` VALUES (24, 'CES2321', 'Karamay (KRY)', '14:51 CST', '18:26 CST');
INSERT INTO `flights` VALUES (25, 'CES2259', 'Lüliang (LLV)', '14:44 CST', '15:31 CST');
INSERT INTO `flights` VALUES (26, 'CES2321', 'Karamay (KRY)', '14:51 CST', '18:26 CST');
INSERT INTO `flights` VALUES (27, 'CES2321', 'Karamay (KRY)', '14:51 CST', '18:26 CST');
INSERT INTO `flights` VALUES (28, 'CES2259', 'Lüliang (LLV)', '14:44 CST', '15:31 CST');
INSERT INTO `flights` VALUES (29, 'CES2321', 'Karamay (KRY)', '14:51 CST', '18:26 CST');
INSERT INTO `flights` VALUES (30, 'CES2321', 'Karamay (KRY)', '14:51 CST', '18:26 CST');
INSERT INTO `flights` VALUES (31, 'CES2259', 'Lüliang (LLV)', '14:44 CST', '15:31 CST');
INSERT INTO `flights` VALUES (32, 'CES2321', 'Karamay (KRY)', '14:51 CST', '18:26 CST');
INSERT INTO `flights` VALUES (33, 'CES2321', 'Karamay (KRY)', '14:51 CST', '18:26 CST');
INSERT INTO `flights` VALUES (34, 'CES2259', 'Lüliang (LLV)', '14:44 CST', '15:31 CST');
INSERT INTO `flights` VALUES (35, 'CES2321', 'Karamay (KRY)', '14:51 CST', '18:26 CST');
INSERT INTO `flights` VALUES (36, 'CES2259', 'Lüliang (LLV)', '14:44 CST', '15:31 CST');

SET FOREIGN_KEY_CHECKS = 1;

悦读

道可道,非常道;名可名,非常名。 无名,天地之始,有名,万物之母。 故常无欲,以观其妙,常有欲,以观其徼。 此两者,同出而异名,同谓之玄,玄之又玄,众妙之门。

;