目录
4. JavaScript = ECMAScript + JavaScript自己特有的东西(BOM+DOM)
(2) 使用typeof(检查数据类型)检查一个布尔值时,会返回一个boolean
(3)布尔值true和false参与加法运算时,true当数字型1来看,false当数字型0来看
(2)for(in)方法:使用map的方式返回所有非稀疏的节点的key
(3)forEach方法:返回所有数字的且非稀疏的节点的value
(2) 预解析分为变量预解析(变量提升)和函数预解析(函数提升)
(3) 在object调用的函数中,this被指定为object,谁调用,指向谁
(4) 在call/apply中,this可以被指定,被指定为第一参数
(2) 数:1970年1月1日到现在的毫秒数(时间戳,永远不会重复,一直在增加)
(3) setFullYear()//月份设定特殊,从0-11
(6) 设定年setFullYear和getFullYear
(2)直接量(通过字面量创建)(用两个撇斜杠,/expression/param)
(2)根据class类名获取element元素(h5新增,IE9以下不支持)
(5)querySelector() 返回指定css选择器的第一个元素
(6)querySelectorAll() 返回指定css选择器的所有元素
(3)修改样式属性:Element.style(产生的是行内样式,权重较高,属性采取驼峰命名法)
(5)排他思想:tab标签切换,点击谁谁的样式改变,且始终只有一个元素样式改变了
(8)移除属性 Element.removeAttribute('index');
(1)parentNode 父节点 (得到离元素最近的父节点,找不到则返回null)
(4)d.attachEvent('onclick',fn1);
(1)数组方法 : forEach(), map(), filter(), some(), every();
(2)若函数体中只有一句代码,且代码执行结果就是返回值,可以省略大括号
(3)在箭头函数中,如果形参只有一个,形参外侧的小括号也可以省略
(4)箭头函数不绑定this关键字,箭头函数中的this,指向的是函数定义位置的上下文this
一、概念
1. 一门客户端脚本语言
* 运行在客户端浏览器中的。每一个浏览器都有JavaScript的解析引擎
* 脚本语言:不需要编译,直接就可以被浏览器解析执行了
2. 功能:
* 可以来增强用户和html页面的交互过程,可以来控制html元素,让页面有一些动态的效果,增强用户的体验。
3. JavaScript发展史:
1992年,Nombase公司,开发出第一门客户端脚本语言,专门用于表单的校验。命名为 : C-- ,后来更名为:ScriptEase
1995年,Netscape(网景)公司,开发了一门客户端脚本语言:LiveScript。后来,请来SUN公司的专家,修改LiveScript,命名为JavaScript
1996年,微软抄袭JavaScript开发出JScript语言
1997年,ECMA(欧洲计算机制造商协会),制定出客户端脚本语言的标准:ECMAScript,就是统一了所有客户端脚本语言的编码方式。
4. JavaScript = ECMAScript + JavaScript自己特有的东西(BOM+DOM)
DOM 页面文档对象模型(标准编程接口,通过DOM提供的接口对页面上的各种元素进行大小/位置/颜色等操作)
BOM浏览器对象模型(与浏览器互动的对象结构,通过BOM可以操作浏览器窗口,比如弹出框/控制浏览器跳转/获取分辨率等)
* ECMAScript:客户端脚本语言的标准
二、JavaScript的三种引入方式
1. 行内式js
(1)可以将js代码填写到body下标签的onclick属性中(当我们点击按钮时,js代码才会执行,每点一次执行一次) 虽然可以写在标签的属性中,但是他们属于结构与行为耦合,不方便维护,不推荐使用
<body>
<span onclick="alert('警告')"> 首页 </span>
<button onclick="alert('这是一个警告框');">
点我一下
</button>
</body>
(2)可以将js代码填写到超链接a标签的href属性中(当我们点击超链接时,js代码才会执行,每点一次执行一次)
<body>
<a href="javascript:alert('这是一个警告框');">
点我一下
</button>
</body>
2. 内嵌式的js
javascript(js代码需写到script标签中)js语言从上往下执行指令
script标签一般放在head标签内部的最后或是body标签内部的最后
注意:如果js代码里有获取元素的操作,建议把script标签位于body标签内部的最后位置,避免js获取dom元素时,dom文档还没加载完毕,无法获取到需要的dom元素
<script type="text/javascript">
alert('警告');
</script>
(1)控制浏览器弹出一个警告框
alert("这是一个警告框");
(2)在页面( body) 中输出一个内容
document.write("我是页面中输出的内容");
(3)向控制台输出一个内容
console.log("我是在控制台输出的内容");
(4)prompt() 可以弹出一个提示框, 该提示框中会带有一个文本框
用户可以在文本框中输入一段内容, 该函数需要一个字符串作为参数, 该字符串会作为提示框的提示文字
用户输入的内容将会作为函数的返回值返回, 可以定义一个变量来接收该内容
<script>
var score = prompt('请输入小明的期末成绩:')
alert(score);
</script>
3. 外部js
可以将js代码编写到外部js文件中(外部js文件中不出现script标签),然后通过script标签引入(可以在不同的页面中同时引用,也可以利用到浏览器的缓存机制,推荐使用)(script标签一旦引入外部文件了,就不能再填写内部代码了,写了浏览器也会忽略 ,如果需要,可以再创建一个新的script标签用于编写内部代码)
script标签一般放在head标签内部的最后或是body标签内部的最后
注意:如果js代码里有获取元素的操作,建议把script标签位于body标签内部的最后位置,避免js获取dom元素时,dom文档还没加载完毕,无法获取到需要的dom元素
<head>
<script type="text/javascript" src="js/web_01.js">
</script>
</head>
<body>
<script type="text/javascript" src="js/web_01.js">
</script>
</body>
三、JavaScript的使用
1. 基本语法
(1)注释
(注释中的内容不会被执行,但是可以在源代码中查看,养成良好的注释习惯,可以通过注释对代码进行一些简单的调试)
a. 多行注释
/*
*/
b. 单行注释
//
(2)js中严格区分大小写
Alert('错误不能执行') //错误示范
(3)js中每一条语句以分号结尾(如果不写,浏览器会自动添加,但是会消耗一些系统资源,而且可能会加错分号,所以开发中必须写分号)
(4)js中会忽略多个空格和换行,所以可以利用空格和换行对代码进行格式美化
2. 字面量
(都是一些不可改变的值,比如:1、2、3、4),字面量都是可以直接使用,但是一般不会直接使用字面量,可用变量保存字面量,方便调用
3. 变量
变量是程序在内存中申请一块用来存放数据的空间, (比如:X=3.14159)变量可以用来保存字面量,而且变量的值是可以改变的,变量更加方便使用,所以开发中都是通过变量去保存一个字面量,很少直接使用字面量
4. 声明变量(比如:设x=2)
(1)在js中使用var关键字来声明一个变量
var a //声明一个变量a
a=13 //给a赋值
console.log(a);//在控制台输出a的值
var b=25 //简写 声明和赋值同时进行
console.log(b);
(2)通过变量对字面量进行描述
var age=21
console.log(age);
(3), 逗号 可以分割多个语句,一般可以声明多个变量时使用
var age=21,
b=12;
console.log(age);
(4)更新变量:一个变量被重新赋值后,它原有的值就会被覆盖,变量值将以最后一次赋的值为准
(5)声明变量的特殊情况
只声明不赋值,结果是undefined
不声明不赋值,直接使用会报错
不声明直接赋值使用,是全局变量(可以使用,但不提倡)
(6)两个变量交换:需要一个临时变量
(理解思路:左手是苹果,右手是橘子,首先把左手的苹果放到桌子上,此时左手为空,再把右手的橘子交到左手上,空着的右手再拿上桌子上的橘子,这样就实现了位置交换,左手拿橘子右手拿苹果了)
var a = '苹果',
b = '橘子';
var temp;/*声明一个临时变量*/
temp = a ;/*temp = '苹果'*/
a = b;/*a = '橘子'*/
b = temp;/*b = '苹果'*/
变量值互换的两种方法
var inum1=123 ,inum2=456;
(1)var inum3 = inum2 ;
inum1 = inum3;
inum2 = inum1;
(2)inum1+=inum2;(inum1=123+456)
inum2 = inum1-inum2;
inum1 = inum1-inum2;
5. 标识符
(在js中所有的可以由我们自主命名的都可以称为是标识符)(例如:变量名、函数名、属性名都属于标识符)
命名一个标识符时需要遵守如下规则:
(1) 标识符中可以含有字母、数字、下划线、$(一般使用描述性的名称,比如:age,sum),变量名称对大小写敏感
var b_$=25
(2)标识符不能以数字开头
(3)标识符不能js中的关键字或保留字
var var=123 //错误写法
var if=123 //错误写法
(4)标识符一般都采用驼峰式命名法(首字母小写,后面单词的首字母大写,其余字母小写)
helloWorld
(5)js底层保存标识符时实际上是采用的Unicode编码,所以理论上讲,所有的utf-8中含有的内容都可以作为标识符(也包含中文)
var 体重=21 //中文也可做标识符,但不提倡用
console.log(体重);
6. 数据类型
在计算机中,不同的数据所需占用的空间不同,为了把数据分成所需内存大小的不同数据,充分利用存储口空间,于是定义了不同数据类型(js的变量数据类型只有在程序运行时,根据等号右边赋的值确定,变量的数据类型是可变的)
(1)值/原始(简单数据)类型:
number(数字)、string(字符串)、boolean(布尔值)、undefined、null
a. number:数字。 整数/小数/NaN(not a number 一个不是数字的数字类型)
b. string:字符串。 字符串 "abc" "a" 'abc'
c. boolean: true和false
d. null:一个对象为空的占位符
e. undefined:未定义。如果一个变量没有给初始化值,则会被默认赋值为undefined
***简单数据类型返回的都是它本身,但null返回的是一个空的object对象
***如果有个变量打算存储为对象,但暂时还没有确定的属性和方法,这个时候就可以把这个变量赋值为null
(2)引用(复杂数据)类型:
通过new关键字创建的对象,array、object对象、Date,function
typeof 返回数据类型
var x = 1;
console.log(typeof(x) + '<br>');//number
var x = "abc";
console.log(typeof(x) + '<br>');//string
var x = true;
console.log(typeof(x) + '<br>');//boolean
var x = null;
console.log(typeof(x) + '<br>');//object
var x = undefined;
console.log(typeof(x) + '<br>');//undefined
var x2;
console.log(typeof(x) + '<br>');//undefined
7. 数据类型转换
(1)隐式类型转换
a. 转换成数字number
var sNum = '2';
var iNum = 2;
var iRet = sNum + iNum;
console.log('数据类型为'+ typeof(iRet) + '<br>');//string 22
var iRet = +sNum;
console.log('数据类型为'+ typeof(iRet) + '<br>');//number 22
var iRet = sNum - iNum;
console.log('数据类型为'+ typeof(iRet) + '<br>');//number 0
b. 转换string
var Stoken = ' ' + 2;
console.log('数据类型为'+ typeof(Stoken) + '<br>');//string
var Stoken = ' ' + null;
console.log('数据类型为'+ typeof(Stoken) + '<br>');//string
var Stoken = ' ' + undefined;
console.log('数据类型为'+ typeof(Stoken) + '<br>');//string
var Stoken = ' ' + true;
console.log('数据类型为'+ typeof(Stoken) + '<br>');//string
c.转换成布尔值(临界点0和非0的区别)
var bvar = !0;
console.log(typeof(bvar) + '<br>')//bolean true
var bvar = !1;
console.log(typeof(bvar) + '<br>')//bolean false
var bvar = !1000;
console.log(typeof(bvar) + '<br>')//bolean false
var bvar = !!1000;
console.log(typeof(bvar) + '<br>')//bolean true
(2)显式类型转换
Number(); 数据类型转化为number数字
parseInt(); 数据类型转化为number数字取整或截取number数字
parseFloat(); 与parseInt类似,但支持小数点和科学计数法
(isNaN(); 判断是不是一个数--调用Number();函数)/*NAN:不是一个数字*/
String(变量); 数据类型转化为string字符串
变量.toString(); 数据类型转化为string字符串(可以转进制数)
Boolean(); 数据类型转化为boolean布尔值
8. number(数字)
整数和浮点数(NAN是一种特殊的number)
inumber=Number.MAX_VALUE 最大值
inumber=Number.MIN_VALUE 最小值
inumber=Number.MAX_INFNITY 无限大
document.write('isFinite(inumber)=' + isFinite(inumber)+'<br />')
判断inumber是否是无限大(finity有限/infinity无限)(所有的INFNITY都是一样的不能用来比较)
document.write('isNaN(inumber)=' + isNaN(inumber)+'<br />')
判断inumber是否是NaN(NaN:not a number)(NaN==NaN=false NaN!=NaN=true)
在js中,各进制表示法
(1)如果需要表示16进制的数字,则需要以0x开头(转换成10进制输出)
a=0x10;//a=16
(2)如果需要表示8进制的数字,则需要以0开头(转换成10进制输出)(有些浏览器会当成10进制数解析)
b=070; //b=56
b=parseint(a,8);可以在parseInt()中传递一个第二个参数来指定数字的进制(所有浏览器适用)(parseInt(score/10) 结果取整)
(3)如果需要表示2进制的数字,则需要以0b开头(转换成10进制输出)(并不是所有浏览器都支持)
c=0b10;//c=2
(4)浮点数
fnumber=5.61e7;//fnumber=56100000(科学计数法:e7表示*10^7)
0.1.+0.2为什么不等于0.3
因为浮点数运算的精度问题。
在计算机运行过程中,需要将数据转化成二进制,然后再进行计算。
小数转化为IEEE754的过程:先转化为二进制的形式,然后再用科学计数法表示,
接着把通过科学计数法表示的二进制数用IEEE754标准表示。
js中的Number类型遵循IEEE754标准,在IEEE754标准的64位浮点数相加,
因为浮点数自身小数位数的限制而截断的二进制在转化为十进制,
就变成0.30000000000000004,所以在计算时会产生误差。
9. 将其他数据类型转化为number数字
(1)Number本质上可以转换成数字
(2)ParseInt看上去像数字的可以转换成数字
(3)Number可以,但parseInt不可以的:false、null
(4)Number不可以,但parseInt可以的:数字开头掺杂其他
(5)都不可以的:undefined、字母开头的字符串
(6)Number()(false、null、' '转化后数值为0/undefined、不是数 转化后数值为Nan)
(7)+ //正号-不影响值,可以对其他的数据类型使用+,将其转化为number,他的原理和number函数一样
var iNum = Number(undefined);
console.log(typeof(iNum) + '<br>')//number Nan
var iNum = Number(null);
console.log(typeof(iNum) + '<br>')//number 0
var iNum = Number('10');
console.log(typeof(iNum) + '<br>')//number 10
var iNum = Number('1.5');
console.log(typeof(iNum) + '<br>')//number 1.5
var iNum = Number('1.5.3');
console.log(typeof(iNum) + '<br>')//number Nan
var iNum = Number(' ');
console.log(typeof(iNum) + '<br>')//number 0
parseInt()(不是数 转化后数值为Nan)
var iNum = parseInt('1.12');
console.log(typeof(iNum) + '<br>')//number 1
console.log(iNum.toFixed(1) + '<br>')// 1.1 保留一位小数
var iNum = parseInt('2.5.6');
console.log(typeof(iNum) + '<br>')//number 2
var iNum = parseInt('123a');
console.log(typeof(iNum) + '<br>')//number 123
var iNum = parseInt('a123');
console.log(typeof(iNum) + '<br>')//number Nan
var iNum = parseInt('0xA0');//0x代表16进制数
console.log(typeof(iNum) + '<br>')//number 160
var iNum = parseInt('070');//0代表8进制数,但在此无效
console.log(typeof(iNum) + '<br>')//number 70
var iNum = parseInt(' ');
console.log(typeof(iNum) + '<br>')//number 0
var iNum = parseInt('1.1E5');//科学计数法,但在此无效
console.log(typeof(iNum) + '<br>')//number 1
var iNum = parseInt('A0',16);//16进制数
console.log(typeof(iNum) + '<br>')//number 160
var iNum = parseInt('70',8);//8进制数
console.log(typeof(iNum) + '<br>')//number 56
parseFloat();与parseInt类似,但支持小数点和科学计数法
var iNum = parseFloat('1.1E5');//科学计数法(浮点数)
console.log(typeof(iNum) + '<br>')//number 110000
var iNum = parseFloat('10');//科学计数法(浮点数)
console.log(typeof(iNum) + '<br>')//number 10
var iNum = parseFloat('10.00');//科学计数法(浮点数)
console.log(typeof(iNum) + '<br>')//number 10
var iNum = parseFloat('10.5');//科学计数法(浮点数)
console.log(typeof(iNum) + '<br>')//number 10.5
var iNum = parseFloat('1.5.3');//科学计数法(浮点数)
console.log(typeof(iNum) + '<br>')//number 1.5
var iNum = parseFloat('1.7 ');//科学计数法(浮点数)
console.log(typeof(iNum) + '<br>')//number 1.7
var iNum = parseFloat('1.5aaa');//科学计数法(浮点数)
console.log(typeof(iNum) + '<br>')//number 1.5
var iRet = '2';
console.log('数据类型为'+ typeof(+iRet) + '<br>');//number 2
隐式转换:
console.log(+'12')//12
console.log('12'-0)//12
console.log('12'-'10')//2
console.log('12'*1)//12
console.log('12'/6)//2
10. string(字符串)
(1)用英文引号(js推荐使用单引号,引号嵌套:外单内双)
sTocken = 'hello world!'
var length = sTocken.length;检测获取字符串的长度//length=11
(2)字符串拼接(数值相加,字符相连):
console.log('沙漠'+'骆驼');/*沙漠骆驼*/
console.log('沙漠'+'33');/*沙漠33*/
console.log('沙漠'+'true');/*沙漠true*/
console.log('33'+'33');/*3333*/
(3)常用方法
var ilength = sTocken.indexOf(o);查询o是第几个字符//ilength=4
document.write('chartAt='+sTocken.charAt(ilength)+'<br>');提取出o//chartAt=o
document.write('substring='+sTocken.substring(0,ilength+1)+'<br>');
截取到0的一段字符串//substring=hello
sTocken2 = 'abcd abce a234 1314 scfdva'
var iStart = sTocken2.indexOf(' '), iEND
if(iStart!=-1){//查询第一个空格
iEND = sTocken2.indexOf(' ',iStart+1);
if (iEND!=-1){//查询第二个空格
document.write('substring = ['+sTocken2.substring(start+1,iEND)+']<br>');
}
}截取第二个单词
(4)在字符串中使用转义字符输出unicode编码(有些浏览器不支持)
a. js中:\u四位编码
console.log("\u2620")
b. html中:&#四位编码
<h1>☠</h1> (需把16进制转换为10进制)
11. 将其他数据类型转化为string字符串
String();
var Stoken = String(2);
console.log(+ typeof(Stoken)+'<br>')//string
var Stoken = String(null);
console.log(+ typeof(Stoken)+'<br>')//string
var Stoken = String(undefined);
console.log(+ typeof(Stoken)+'<br>')//string
var Stoken = String(true);
console.log(+ typeof(Stoken)+'<br>')//string
toString();
var inumb1 = 80;
Stoken = inumb1.toString();
console.log(+ typeof(Stoken)+'<br>')//string 80
var inumb1 = 80;
Stoken = inumb1.toString(16);十转成16进制数
console.log(+ typeof(Stoken)+'<br>')//string 50
var inumb1 = 80;
Stoken = 80.toString();不可以,出错
console.log(+ typeof(Stoken)+'<br>')//string 80
Stoken = true.toString();
console.log(+ typeof(Stoken)+'<br>')//string true
Stoken = null.toString();不可以,出错
console.log(+ typeof(Stoken)+'<br>')//string
Stoken =undefined.toString();不可以,出错
console.log(+ typeof(Stoken)+'<br>')//string 80
例题:将一个二进制字符串转化成16进制字符串
var sString = '10101001'
var nNum = parseInt(sString,2);
if(!isNaN(nNum)){
console.log(nNum.toString(16));
}
12. Boolean 布尔值
(1)(布尔值只有两个),主要用来做逻辑判断
true //表示真
false //表示假
(2) 使用typeof(检查数据类型)检查一个布尔值时,会返回一个boolean
var bool = true/false
console.log(typeof bool);返回boolean
console.log(bool);输出值
(3)布尔值true和false参与加法运算时,true当数字型1来看,false当数字型0来看
console.log(true + 1);//2
console.log(false + 1);//1
13. 将其他数据类型转化为Boolean
使用Boolean()函数
(初值为空值,否定值会被转换为false,如:'' 0 NaN null undefined,其他都转换为true)
(1)数字转Boolean,除了0和NaN是false,其他都是true
(2)字符串转Boolean,除了空串和是false,其他都是true
(3) null和undefined转Boolean都会转换为false
(4) 对象也会转换为true
var a=123;
a=Boolean(a);
console.log(typeof a);
console.log(a);
14. undefined 未定义
(1)undefined类型的值就一个,就undefined,当声明一个变量,但是并不给变量赋值时,他的值就是Undefind
(2)使用typeof检查一个undefind时,会返回一个undefined
var b=undefined;
undefined==undefined=true
undefined==null=true
15. null
(1)null类型的值就一个,这个值专门用来表示一个空的对象,使用typeof检查一个null值时,不会返回一个null,会返回object
var a=null;
(null==null=true/undefined==null=true)
(2)任何数据类型和字符串相加,结果都是字符串相连(字符型)
a. NaN undefined和数字相加,最后结果是NaN
b. null和数字相加,结果是原数字
16. 运算符
(1) 算数运算符
+ - * / %(取余:取余为0则证明这个是能被整除)
(浮点数运算时精度有问题,尽量避免浮点数直接运算和比较)
var = sToken = 5 + 5//=10
var = sToken = "5" + 5//=55
var = sToken = "5" - 5//=0
var = sToken = 5 - 5//=0
var = sToken = "5" - "5"//=0
var = sToken = 4 / 3//=1.3333333
var = sToken = 4 / 0//=infinity
var = sToken = 0 / 0//=Nan
var = sToken = 4.5 % 3//=1.5
var = sToken = 4 % 0//=Nan
var = sToken = 0 % 0//=Nan
运算符的优先级:先乘除后加减
从上往下优先级依次降低(优先级越高越优先运算,优先级相同则从左往右运算)
如果优先级不确定 可以用()改变其优先级
(注:除===和!===判断内容是:不仅判断数值,而且判断类型,其他关系运算符都只判断数值)
. 【】 new
. () //小括号
. ++ -- ! //一元运算符
. ! +(单目) -(单目) typeof void delete
. % //算数运算符,先乘除
. +(双目) -(双目) //算数运算符,后加减
. << >> >>>
. < <= > >= //关系运算符
. ==(判等于) !==(非全等于) ===(全等于) //相等运算符
. &
. ^
. &&//逻辑运算符,先与
. ||//逻辑运算符,后或
. = += -= *= /= %= <<= >>= >>>= &= ^= |= //赋值运算符
. ,(多个逗号表达式,只以最后一个表达式作为自己的表达式,前面的都抛掉) //逗号运算符
(2) 一元运算符
只需要一个操作数(数值取反)
+ //正号-不影响值,可以对其他的数据类型使用+,将其转化为number,他的原理和number函数一样
- //负号-值为负,对于非number类型的值,先转化为number,然后再运算
(3) 逻辑运算符
&& || !(与或非运算)
与运算:遇假为假,两真则真
或运算:遇真为真,两假则假
非运算:非真为假,非假为真
短路运算(逻辑中断):当有多个表达式(值),左边的表达式值可以确定结果时,就不再运算右边的表达式的值;
a. 与运算
如果第一个值为true,则必然返回第二个值,如果第一个值为false,则直接返回第一个值
如果两个值都为true,则返回后面的
如果有false,则返回false的原值
如果两个值都为false,则返回前面的
b. 或运算
如果第一个值为true,则必然返回第一个值,如果第一个值为false,则直接返回第二个值
c. 非运算(布尔值取反)
d. && || ( 短路运算) 非布尔值的情况
对于非布尔值进行与或运算时,会将其转换为布尔值,然后再运算,并且返回原值
与运算:
如果第一个值为true,则必然返回第二个值,如果第一个值为false,则直接返回第一个值
如果两个值都为true,则返回后面的
如果有false,则返回false的原值
如果两个值都为false,则返回前面的
或运算:
如果第一个值为true,则必然返回第一个值
如果第一个值为false,则直接返回第二个值
(4) 赋值运算符
= 可以将符号右侧的值赋值给符号左侧的变量
+=(a+=5等价于a=a+5)
-=(a-=5等价于a=a-5)
*=(a*=5等价于a=a*5)
/=(a/=5等价于a=a/5)取模
%=(a%=5等价于a=a%5)
a=5
后置递增 a++(a++=5 a=6)先运算后赋值(先返回原值后自加1)
前置递增 ++a(++a=6 a=6)先赋值后运算(先自加1后返回值)
(5)比较运算符
> < >= <= == != === !==
***注意 =: 赋值,把右边给左边
==: 判断,判断两边值是否相等(有隐式转换,会把字符型转换为数值型)
===: 全等,判断两边的值和数据类型是否完全相同
数字比较
document.write('[>]2>1?'+ (2>1)+'<br />');//true
document.write('[>]1>2?'+ (1>2)+'<br />');//false
字符串比较(从左往右一个字符一个字符的比较)
document.write('[>] \'aaa\'>\'abb\'?'+ ('aaa'>'abb')+'<br />');//false
document.write('[>] \'aba\'>\'Abb\'?'+ ('aaa'>'abb')+'<br />');//false
数字字符串和数字比较(将数字字符串直接变成数字进行比较)
document.write('[>] \'25\'>4?'+ ('25'>'4')+'<br />');//true
5==NaN=false
false==0=true(===为false)
true==1=true(===为false)
true==2=false
undefined==0=false
null==0=false
"5"==5=true
"5"===5=false
null===undefined=false
(6)条件运算符(也叫三元运算符)
语法:
条件表达式?语句1:语句2
执行流程:
条件运算符在执行时,首先对条件表达式进行求值,
如果该值为true,则执行语句1,并返回执行结果
如果该值为false,则执行语句2,并返回执行结果
如果条件的表达式的求值结果是一个非布尔值,会将其转换为布尔值,然后再运算,并执行语句
var a=10,b=20;//a与b谁大
a>b ?alert("a大"):alert("b大")
var max= a>b ?a:b //a与b中取最大
console.log("max="+max)\
var max= a>b ?a>c?a:c:b>c?b:c; //a、b、c中取最大
console.log("max="+max)
17. 语句
(1)我们的程序是由一条一条语句构成的,语句是按照自上而下一条一条执行的
(2)在js中,可以使用大括号{}来为语句进行分组,同一个{}中的语句我们称为是一组语句(一个代码块),要么都执行,要么都不执行
(3)js中的代码块,只具有分组的作用,没有其他的用途,代码块中的内容,在外部是完全可见的
18. if else 语句
适用于分支较少范围判断的时候
if(判断条件){
条件满足执行语句1
}
else{
不满足条件则执行语句2
}
例子 var iscore = 78 (90-100优/75-90良/60-75中/0-60差)
(1)最不好(if)//单条件单分支
if(iscore>=90 && iscore<=100){document.write('iscore=优')}
if(iscore>=75 && iscore<90){document.write('iscore=良')}
if(iscore>=60 && iscore<75){document.write('iscore=中')}
if(iscore>=0 && iscore<60){document.write('iscore=差')}
(2)不好(if...else)/*单条件双分支 */
if(iscore>=90){
document.write('iscore=优')
}
else{
if(iscore>=75){
document.write('iscore=良')
}
else{
if(iscore>=60){
document.write('iscore=中')
}
else{
document.write('iscore=差')
}
}
}
(3)最优(if...else if ... else)/*多条件多分支*/
if(iscore>=90){
document.write('iscore=优')
}
else if(iscore>=75){
document.write('iscore=良')
}
else if(iscore>=60){
document.write('iscore=中')
}
else{
document.write('iscore=差')
}
19. swith语句
单条件多分支,利用表达式的值和case后面的选项值相匹配(全等,值和数据类型都匹配),匹配上就执行case里面的语句,没有匹配上则执行default里面的语句*/ (适用于case为比较确定的值时,分支较多时switch效率更高)
switch(变量或表达式){
case 1(变量取值为常量1时):满足条件执行语句1;
break;(必须break,不然会一直往下执行,会出错)
case 2(变量取值为常量2时):满足条件执行语句2;
break;
case 3(变量取值为常量3时):满足条件执行语句3;
default(变量最后一种取值情况或容错):满足条件执行语句last;
break;
var score=86;
switch(score){
case score>=60;
console.log("合格")
break;
default:
console.log("不合格")
break;
}
var day = 6;
switch(day){
case 0:x = '星期天';
break;(必须break,不然会一直往下执行,会出错)
case 1:x = '星期一';
break;
case 2:x = '星期一';
break;
case 3:x = '星期二';
break;
case 4:x = '星期三';
break;
case 5:x = '星期四';
break;
case 6:x = '星期五';
break;
default:x = 'error';
break;
}
document.write('今天是'+ x + '<br />')
20. 循环
重复执行某些代码
(1) for 循环语句
满足条件进入循环运行一次,直到条件不满足跳出循环,通常和计数有关系
for(初始变量; 条件表达式; 操作表达式){
循环体
}
初始变量:var声明的一个普通变量,通常用于作为计数器使用,
条件表达式:决定循环是否继续执行,即终止的条件,
操作表达式:每次循环最后执行的代码,经常用于对计数器变量进行更新(递增或递减)
for(var i = 0; i < 20; i++){
console.log('哈喽');/*循环执行相同的代码*/
document.write(i + '<br>');/*循环执行不同的代码*/
}
等价于
var i = 0
for(i < 20; ){
console.log('哈喽');
document.write(i + '<br>');
i++;
}
for循环算数运算
var sum = 0;
for(var i = 1; i <=100; i++){
sum+=i;
}
求1-100之间所有偶数的和和奇数的和
var even = 0,
odd = 0;
for(var i = 1; i <= 100; i ++){
if(i % 2 == 0){
even = even + i;
}else{
odd = odd + i;
}
}
console.log('1-100之间偶数和是'+even);
console.log('1-100之间奇数数和是'+odd);
双重for循环
循环嵌套是指在一个循环语句中再定义一个循环语句的语法结构(可以把里面的循环看成是外面循环的语句,外层循环一次,里面的循环执行全部)
for(外层的初始化变量;外层的条件表达式;外层的操作表达式){
for(里层的初始化变量;里层的条件表达式;里层的操作表达式){
执行语句;
}
}
for(var i = 1;i <= 3;i ++){
console.log('这是外层循环第'+ i +'次');
for(var j = 1;j <= 3;j ++){
console.log('这是里层循环第'+ j +'次');
}
}
打印5行5列星星
var str = '';
for(var i = 1;i <= 5;i ++){ /*外层循环负责打印5行*/
for(var j = 1;j <= 5;j ++){ /*内层循环负责一行打印5个*/
str = str + '*'; /*内层语句循环5次,每次+1个*/
}
str = str + "\n"; /*外层循环换行5次*/
}
console.log(str);
打印10行倒三角
var str = '';
for(var i = 1;i <= 10;i ++){ /*外层循环负责打印10行*/
for(var j = i;j <= 10;j ++){ /*内层循环负责一行打印10-i+1个*/
str = str + '*'; /*内层语句循环10次,每次+1个*/
}
str = str + "\n"; /*外层循环换行10次*/
}
console.log(str);
九九乘法表(正三角)
var result = '';
for(var i = 1;i <= 9;i ++){ /*外层循环负责打印9行*/
for(var j = 1;j <= i;j ++){ /*内层循环负责一行打印i个*/
result += j + 'x' + i + '=' + i * j + '\t'; /*内层语句循环i次,每次+1个*/
}
result = result + '\n'; /*外层循环换行9次*/
}
console.log(result);
(2)while 循环语句
满足条件进入循环运行一次,直到条件不满足跳出循环,适用于判断条件比较复杂时使用
while (i<20){
document.write(i + '<br>');
i++;
}
输入内容不符时循环
var message = prompt('你是爱坤吗?');
while (message != '是'){
alert('再给你一次重新组织语言的机会!');
message = prompt('你是爱坤吗?');
}
alert('姬霓太美!!!')
(3)do while 循环语句
先执行一次循环体后再判断是否满足条件,满足条件则继续进入循环,直到条件不满足跳出循环(do while至少循环一次,执行更自然)
var i=0;
do{
document.write(i + '<br>');
i++;
}
while(i<20);
21. 循环语句中的 break 、continue
(1)continue
只跳出当前循环(立即跳出本次循环,其后的语句本次不再执行,继续开始下一次循环。)(计划吃5个包子,吃到第3个发现上面有虫子,还没吃饱,只扔掉第三个包子,接着吃第4个和第5个)
求1-100之间,除了能被7整除之外的整数和
var sum = 0;
for(var i = 1; i<=100; i++){
if(i % 7 == 0){
continue;
}
sum+=i;
}
console.log(sum);
(2)break
直接跳出循环(立即结束整个循环并转到循环后续语句执行)(计划吃5个包子,吃到第3个发现上面有虫子,瞬间没有食欲了,于是把第3个和剩下的包子都扔了)
while (i<20){
if(i == 10){
break;
}
document.write(i + '<br>');
i++;
}
(3)案例
a. 找出5000以内满足%7==5、%13==7、%17==13的前5个数
var count=0
for(i=1,i<=5000,i++){
if (i % 7 == 5 && i % 13 == 7 && i % 17 == 13){
document.write('5000以内' + i '<br>')
count++;
if (count >=5){
break;
}
}
}
b. 10的阶乘
var iRet = 1 , n = 10 ;
for(var i = n , i>=1 , i--){
if (!isFinite(iRet)){
document.write('无限大');
break;
}
iRcet *= i;
}
document.write('10的阶乘=' + iRcet + '<br>');
c. 1累加到100
var n = 100 , iret = 0;
var iRet = 1 , n = 10 ;
for(var i = 1 , i<=n , i++){
if (!isFinite(iRet)){
document.write('无限大');
break;
}
iRcet += i;
}
document.write('1累加到100=' + iRcet + '<br>');
d. 5的n次幂
var n = 10 ,iret = 1,inumb = 5;
for(var i = 0 , i<=n , i++){
if (!isFinite(iRet)){
document.write('无限大');
break;
}
iRcet *= inumb;
}
document.write('1累加到100=' + iRcet + '<br>');
22. 数组(Arry:js的数组是弱类型)
数组是一组数据的集合,其中每个元素被称为元素,在数组中可以存放任意类型的元素,数组是一种将一组数据存储在单个变量名下的优雅方式
如何判断一个变量是数组:
var arr = [];
console.log(arr.constructor.name == 'Array');
(1)创建数组:
a.使用构造函数方式生成数组:
arr = new Array();//等价于arr = [];
arr1 = new Array(0,1,2,3);//等价于arr1 = [0,1,2,3];
*arr2 = new Array(4);//等价于arr2 = [,,,];
b.使用数组字面量创建数组:(最常见)
var 数组名 = [数组元素];
var arr = [];空数组
var arr1 = [0,1,2,3];
var arr2 = [0,1,,3];
var arr3 = [,,,];
var arr1 = [0,accc,undefined,null,true,{}];
稀疏数组/矩阵:插入空的empty length不消耗内存,调用时再取用)
larr[3] = 5;// [,,,5]
larr[1.5] = 7;// 1.5:7
可以把数组当map用
key = value(map不影响length)
var a4 = [];
a4['China'] = '满汉全席';
a4['American'] = '火机';
a4['France'] = '牛排';
console.log(a4);
(2) 访问数组(数组的索引)
索引号从0开始计数,通过"数组名[索引号]"的形式获取数组中的元素(若没有这个元素,则返回undefined)
console.log(arr1[1]);
console.log(arr4[3]);
console.log(arr[1]);
(3)插入数组(新增元素):根据数组名和索引号添加(不要直接给数组名赋值,不然所有数组元素都没有了)
a.修改length长度
var arr1 = [0,1,2,3]; // length = 4
arr1.length = 5;
console.log(arr1); //[0,1,2,3,empty]
console.log(arr1[4]); //undefined
b.修改索引号:如果一个下标位置原来不存在,会添加,还会增加length(数组有长度length),如果下标存在,则新值会覆盖原值
var arr2 = [0,1,2,3];
arr[3] = 5;(第4个位置插入5)
则 arr3 = [0,1,2,5];
arr[4] = 6;(第5个位置插入6)
则 arr3 = [0,1,2,5,6];
例题:将数组[2,6,4,9,1,7,45,35,19,24,12]中大于10的元素选出来放到一个新数组中
var arr = [2,6,4,9,1,7,45,35,19,24,12];
var newArr = [];新数组length为0
(var j = 0;)
for (var i = 0; i <= 10; i++) {
f(arr[i] > 10){
newArry[newArry.length] = arr[i]; 或 (newArry[j] = arr[i]; j++;)
//新数组索引号应该从0开始
}
}
(4) 删除指定数组元素(数组中的成员函数)
a. slice(iStart[,iEnd]) :节选数组中的一段,(相当于拷贝了一份出来)原数组不受影响(从start开始到end前一个结束)//[,iEnd]可有可不有,即等价于slice(iStart,iEnd)/slice(iStart)
var arr = [0,1,2,3,4,5,6,7,8];
console.log('slice截取后:' + arr.slice(6) + '<br>');(未设置end)//[6,7,8]
console.log('slice截取后:' + arr.slice(3,6) + '<br>');(设置了end)//[3,4,5]
console.log('slice截取后:' + arr.slice(-3) + '<br>');
(负数表示从1开始倒数第几位)//[6,7,8]
b. splice(iIndex[,iHowmany][,item1][,item2][,item3]...):从Index开始删除元素(原数组受到影响,占位和内容一起删除--length改变),删除几个由Howmany决定,item:要插入的元素
var arr = [0,1,2,3,4,5,6,7,8];
var arr1 = arr.splice(6);
console.log('splice删除后:' + arr + '<br>');(未设置end)//[0,1,2,3,4,5]
console.log('splice删除后:' + arr1 + '<br>');(未设置end)//[6,7,8]
var arr = [0,1,2,3,4,5,6,7,8];
var arr1 = arr.splice(-3);
console.log('splice删除后:' + arr + '<br>');(未设置end)//[0,1,2,3,4,5]
console.log('splice删除后:' + arr1 + '<br>');(未设置end)//[6,7,8]
var arr = [0,1,2,3,4,5,6,7,8];
var arr1 = arr.splice(2,3);
console.log('splice删除后:' + arr + '<br>');(未设置end)//[0,1,5,6,7,8]
console.log('splice删除后:' + arr1 + '<br>');(未设置end)// [2,3,4,]
var arr = [0,1,2,3,4,5,6,7,8];
var arr1 = arr.splice(2,3,-1,-2,-3,-4);
console.log('splice删除后:' + arr + '<br>');(未设置end)//[0,1,-1,-2,-3,-4,5,6,7,8]
console.log('splice删除后:' + arr1 + '<br>');(未设置end)//[2,3,4,]
c. delete(哪个数组的哪个索引),返回true或false
var arr = [0,1,2,3,4,5,6,7,8];
var arr1 = delete(arr[4]);
console.log('delete删除后:' + arr + '<br>');//delete删除后:0,1,2,3,,5,6,7,8
console.log('delete删除后:' + arr1 + '<br>');//delete删除后:true
(5) 清空一个数组:
a. 通过length(位置还在,内容清空)
var arr = [1,2,3];
console.log(arr);
arr.length = 0;
console.log(arr); //[,,]
b. 通过splice(相当于堆内存里申请了一个新变量)
var arr = [1,2,3];
console.log(arr);
arr.splice(0);
console.log(arr);
c.直接赋值( )
var arr = [1,2,3];
console.log(arr);
arr = [];
console.log(arr);
23. 遍历数组(三种轮询数组的方法)
arr = [0,1,2,3,4,5]
(1)for(i)方法:按照数组的方式改动数组
for(var i = 0; i < arr.length; i++){ // 索引从0开始,所以i从0开始,小于arr.length,数组名.length:动态检测数组元素的个数
console.log(arr[i]);
}
例题:求数组[2,6,1,8,54,68,14,9,3]的最大值,
var arr = [2,6,1,8,54,68,14,9,3];
var max = arr[1]; // 默认最大值取数组中的第1个元素
for (var i = 1; i <= arr.length; i++) { // 第一个值赋给了max,则从第二个元素开始遍历
if(arr[i] > max) {
max = arr[i]; // 如果遍历到比max值大的元素,就将这个元素的值赋值给max
}
}
console.log('该数组的最大值是' + max);
例题:将数组['red','green','blue','black','yellow']内容反过来存放,
var arr = ['red','green','blue','black','yellow'];
var newArry = [];
for(var i = arr.length-1; i >= 0; i--) {
newArr[newArry.length] = arr[i];
}
console.log(newArry);
(2)for(in)方法:使用map的方式返回所有非稀疏的节点的key
for(var i in arr ){
console.log(arr[i]);
}
(3)forEach方法:返回所有数字的且非稀疏的节点的value
arr.forEach(function(x) {
console.log(x);
}
);
24 . 数组的操作
concat:两个数组合并成一个数组
join:把数组打开并将所有元素连接成一个字符串
sort:对数组里面的值(字符串)进行排序并返回(字符串比较ASCII码值:0-30h/a-41h/A-61h)
(1)concat:
var arr =[3,2,1];
var arr2 =[4,5,6];
var arr3 = arr.concat(arr2);
console.log(arr3);//[3,2,1,4,5,6]
(2)join:
var arr =[3,2,1];
var arr2 =[4,5,6];
console.log(arr3.join('-'));//3-2-1-4-5-6
(3)sort:
arr3 = [1,5,3,2,9,4,0,8];
console.log(arr3.sort());//[0,1,2,3,4,5,8,9]
arr3 = ['bbb','aaa','ccc'];//字符串排序(从第一位开始比较)
arr3.sort();
console.log(arr3);//['aaa','bbb','ccc']
25. 数组排序
冒泡排序:一种算法,把一系列的数据按照一定的顺序进行排列显示(从小到大或从大到小),每轮两两进行比较调换位置,
var arr = [4,1,2,3,5];
for (var i =0; i <= arr.length -1; i++) { //外层循环管趟数,趟数为arr.length-1
for (var j = 0; j <=arr.length - i -1; j++) { //内层循环管每一趟,同一个数需要交换的次数:arr.length - i - 1
if (arr[j] > arr[j+1]) { //内部交换两个变量的值,前一个元素和后一个元素相比较
var temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
console.log(arr);
26. 多维数组(可以把数组作为一个元素给另外的数组)
(1) 二维数组
var matrix = [[1,2,3],[4,5,6],[7,8,9]];
等价于 matrix = [];
matrix[0] = [1,2,3];
matrix[1] = [4,5,6];
matrix[2] = [7,8,9];
console.log(matrix[1][1]);访问matrix数组一行一列//5
(2) 三维数组
var cubic = [];
cubic[0] = [];
cubic[0][0] = [[1,2,3]];
cubic[0][1] = [[4,5,6],];
cubic[0][2] = [[7,8,9]];
cubic[1] = [];
cubic[1][0] = [[10,11,12]];
cubic[1][1] = [[13,14,15],];
cubic[1][2] = [[16,17,18]];
cubic[2] = [];
cubic[2][0] = [[19,20,21]];
cubic[2][1] = [[22,23,24],];
cubic[2][2] = [[25,26,27]];
console.log(cubic[1][1][1]);访问cubic数组一行一列一层//14
27. 类数组
(1)有下标,有length,有push和splice方法,就是一个类数组
(2)类数组既可以当数组用又可以当对象用
(3)DOM里的数组都是类数组
var arr = [1,2,3];//数组
var obj = {//类数组
'0':1,
'1':2,
'2':3,
'length':3,
'push':Array.prototype.push,
'splice':Array.prototype.splice
};
28. 函数
封装一段可以被重复执行调用的代码块,方便大量代码重复利用
函数使用:首先声明函数,然后调用函数,函数不调用自己不执行
(1)函数的声明:
函数可以有形参,也可以没有,可以有返回值,也可以没有,若没有返回undefined
a. 利用函数关键字自定义函数(命名函数):
function 函数名 (形参){
函数体/语句
}
例1:x = [1,2,3,4,5,NaN,undefined,'abc'],数组求和
function sum(arr){
var iRet = 0;
for(var i = 0; i < arr.length; i++){
if (typeof(arr[i]) == 'number'){
if(!isNaN(arr[i]) && isFinite(arr[i])){
iRet += arr[i] ;
}
}
}
return iRet;
}
console.log(sum(x));//15
例2:[1,2,3,4,5]--[5,4,3,2,1] 用函数实现
function reverseNumber(num){
if(typeof num != 'number'){
return '';
}
if(!isFinite(num) || isNaN(num)){
return '';
}
var tem + num.toString().split('').reverse().join('')
}
console.log(reverseNumber(12345));
b. 函数表达式(匿名函数):函数表达式声明和声明变量差不多,只不过变量里面存的是值,而函数表达式里面存的是函数//系统不会存储函数名,所以不需要函数名,且表达式需要加分号
( function f(){ } );
//相当于 var f = function(){ };
例题:阶乘
var f = function(x){//等价于function fact(x)
if (x <= 1){
return 1;
} else{
return x * arguments.callee(x-1)//等价于return x* fact(x-1)
};
};
console.log(f(5));
c. 函数可以作为参数传递给另一个函数
//f2()是函数声明
function f2(){
console.log('I am f2()');
return 'f2 say hello!';
}
f1(f2);
//f3是函数表达式
var f3 = function(){
console.log('I am f3()');
return 'f3 say hello!';
};
f1(f3);
d. 立即执行函数:不需要调用,立马能够自己执行的函数,只能用一次//引用数变为0,即被GC释放(function是一个引用类型的对象)
写法: ( function ( ) { } ) ( )
或 ( function( ) { } ( ) ) //第二个小括号可以看作是调用的意思
--优点:独立创建了一个作用域,里面所有的变量都是局部变量,不会有命名冲突,用完释放,减少内存压力
--只有函数表达式可以,函数声明不可以
--函数表达式中的名字不会被放在GO或者AO中
console.log(
(
function(x){
console.log('I am f3()');
return 'f3 say hello to ' + x '!';
}
)
(wangli)
);
e. 嵌套函数:重复过程要提炼出函数
function hypotenuse(a,b){
return Math.sqrt(a*a + b*b);
}
console.log(hypotenuse(3,4))
改为嵌套函数后:
function hypotenuse(a,b){
function square (x){
return x*x;
}
}
console.log(hypotenuse(3,4))
(2)函数的调用:
a. 函数的方式
f2(f1)
b. 作为对象的方法
var obj = {
add:function(a,b){
return a + b;
}
}
console.log(obj.add(1,2));
c. 构造函数
var arr = new Array();
d. 间接调用:间接调用和直接调用唯一的区别就是可以绑定this指针,如果不考虑this,这三种调用方式完全一样
function hypotenuse(a,b){
return Math.sqrt(a*a + b*b);
}
this.hypotenuse(3,4);//直接调用
hypotenuse.call(this,3,4);//间接调用
hypotenuse.apply(this,[3,4]);//间接调用
e. 函数之间可以相互调用
例题:用户输入年份,输出当前年份2月份的天数
function isRunYear (year) { //如果是闰年返回true,否则返回false
var flag = false;
if (year % 4 == 0 && year % 100 == 0 || year % 400 == 0){
flag = true;
}
return flag;
}
function backDay(){
var year = prompt('请输入要查询的年份')
if(isRunYear (year)){ //调用函数需要加小括号
alert(year + '年是闰年,2月份有29天');
} else{
alert(year + '年是平年,2月份有28天');
}
}
backDay();
29. 函数的形参和实参
在函数内部某些值不固定,可以通过参数在调用函数时传递不同的值进去,参数可以实现执行不重复的代码(形参和实参数量和类型都可以不一样,形参和实参会绑定,动态关联)
function 函数名 (形参1,形参2) { //函数声明小括号里的是形参(形式上的参数) }
函数名(实参1,实参2)
函数调用小括号里的是实参(实际的参数),函数调用的时候把值传递给形参
注意:
a.如果实参和形参个数一致,则正常输出结果
b.如果实参的个数多于形参的个数,会依次取够形参的个数即不会再往后取后面的值
c.如果实参的个数小于形参的个数,没有接收到实参传递的值得形参可以看作是不用声明的变量,即undefined,相加结果为NaN
arguments的使用
不确定有多少个参数传递的时候,可以完全不写形参,因为所有函数都内置了一个arguments对象,且只有函数中有arguments,arguments对象中存储了传递的所有实参,传递的实参可以通过arguments来获取
arguments展示形式是一个伪数组,可以进行遍历,具有length属性,按索引方式存数数据,但不具有数组的push和pop等方法
function foo(){
console.log(foo.length);//6
console.log(arguments); //arguments是实参的数组 [1,2,3,4,5,6]
console.log(arguments[2]);//3
for (i = 0; i < arguments.length; i++){ //遍历arguments中的参数
console.log(arguments[i]);//1 2 3 4 5 6
}
}
foo(1,2,3,4,5,6);
30. 函数的返回值
return语句:通过returen将函数将值返回给调用者,同时return也是终止语句,return后面的语句不再执行
return只能返回一个值,有多个值时只返回最后一个值,若要返回多个值,可以一个数组的形式返
错误示范:
function cook (aru) {
console.log(aru);//函数内部一般不书写输出语句输出最终结果
}
cook('大猪肘子');
正确语法:
function 函数名 (形参) {
return 需要返回的结果;//只要函数遇到return就把后面的结果返回给函数的调用者,若函数没有return则返回的结果是undefined
}
函数名(实参); //等同于return后面的结果
console.log(函数名(实参));
正确示范:
function getSum (num1,num2) {
return num1 + num2;
}
console.log(getNum (1,2));//输出3
31. 作用域
是在运行时代码中某些特定部分的变量、函数、和对象的可访问性。作用域决定了代码区块中变量和其它资源的可见性,分为局部作用域和全局作用域
就是代码名字在某个范围内起作用和效果,目的是为了提高程序的可靠性,减少命名冲突,在不同作用域下变量名不会冲突
全局作用域:整个script标签内或一个单独的js文件
局部作用域(函数作用域):在函数内部,代码名字只在函数内部起效果和作用
变量作用域:根据作用域的不同,变量分为全局变量和局部变量
全局变量:在全局作用域下的变量,在全局下都可以使用
***如果在函数内部没有声明直接赋值的变量也属于全局变量
局部变量:在局部作用域下的变量,即在函数内部的变量就是几部变量
***函数的形参也可以看作是局部变量
函数作用域:
外部对内部可见
var scope = 'g';
function t (){
console.log(scope);//g
console.log(scope);//g
}
t();
内部对外部不可见
function t (){
var scope = 'l';
}
t();
console.log(scope);//报错
都可见时,内部优先
var scope = 'g';
function t (){
console.log(scope);//undefined
var scope = '1';
console.log(scope);//1
}
t();
js中只有函数级别的作用域,没有块级别的作用域(es6时新增块级作用域);
换句话说,只有在进入或者退出函数的时候,作用域会发生变化
32. 执行环境和作用域链
(1)执行环境
定义了执行期间可以访问的变量和函数
全局执行环境:全局变量只有浏览器关闭时才会销毁,比较占内存资源
Global Object (window)
从见到JS代码开始创建
到网页关闭时销毁
函数执行环境:局部变量当程序执行完毕就会销毁,比较节约内存资源
Activation Object
从函数调用开始创建
到函数调用结束时销毁
(2)
a.作用域链(scope),每个函数都有,内部函数访问外部函数的变量(函数嵌套),采取的是链式查找(一级一级往上,就近原则)的方式来决定取那个值,这种结构称为作用域链
b.作用域是私有属性,只能由JS引擎访问;
c.作用域链,是AO和GO构成的链
d.所谓执行环境,就是根据作用域链依次查找变量和函数
找到即停
全部找完无果,报错
e.作用域链,每个函数都有
33. 生成作用域链
每个函数在定义(函数声明/函数表达式)时会拷贝其父亲函数的作用域链
在函数被调用时,生成AO然后将AO压入作用域链的栈顶
(函数在被多次调用时产生不同的AO,函数在被多次递归调用时产生不同的AO)
(with,生成新的with variable object,放在作用域链表顶端)
var name = 1;
var person = {
name:2
}
with(person){
console.log(name);//2
}
34. 作用域链的应用
(1) 尽量少使用靠近上层的变量,多使用自己的局部变量,不然效率低
(2) 尽量减少不同层次函数使用相同的变量名,避免函数名与变量名一样,不然重名,容易出错
35. 预编译(预解析)(第一次/前置扫描)
(1) js引擎运行js分为两步,先预解析后代码执行
a. 预解析:js引擎会把js里面所有的变量声明(var)和函数声明(function)提升到当前作用域的最前面(只是声明不赋值)
b. 代码执行:按照代码书写的顺序从上往下执行
(2) 预解析分为变量预解析(变量提升)和函数预解析(函数提升)
a. 变量提升就是把所有变量声明提升到当前作用域的最前面(不提升赋值操作)
console.log(num); // undefined
var num = 0;
执行顺序:var num; //预解析
console.log(num); // undefined
num = 0;
b. 函数提升就是把所有函数声明提升到当前作用域的最前面(不调用函数)
fun(); // 报错
var fun = function(){
console.log(222);
}
执行顺序:var fun; //预解析
fun(); // 报错
fun = function(){
console.log(222);
}
36. 函数(预编译)
(1):创建全局对象GO(window)(上下文)
(2):加载函数文件
(3):预编译
a.找出所有的变量声明,按照变量名加入全局对象,如果已存在,忽略
b.找出所有的函数声明,按照函数名加入全局对象,如果已经存在同名变量或者函数,替换
c.非声明不予理睬
(4): 解释执行
a. 没有var的变量,都不是变量声明,全部认为是window的全局变量,不参与编译
console.log(aa);
aa = 5;
console.log(aa);
b. 即使在函数中,aa也是全局变量,是运行时,不是定义时;
text();
function text (){
a = 5;
}
console.log(a);
c.函数中,所有变量声明,在函数的预编译阶段完成,所有变量的声明与实际的书写位置无关
console.log(aa);
var aa = 5;
console.log(aa);
d. 函数中,所有函数声明,在函数的预编译阶段完成,所有函数的声明与实际的书写位置无关
console.log(haha);
function haha (){
console.log('h1');
}
e.函数中,如果变量与函数同名,那么,函数将覆盖变量
console.log(haha);
var haha = 123;
function haha (){
console.log('h1');
}
f. 函数中,只有函数能够覆盖变量,变量无法覆盖函数
console.log(haha);
function haha (){
console.log('h1');
}
var haha = 123;
g.函数中,函数如果同名,后面的函数声明会覆盖前面的函数声明,并且忽略参数
haha(1);
console.log(haha);
function haha (a){
console.log('haha1');
}
function haha (b){
console.log('haha2');
}
37. 函数(预编译)调用
(1)创建活动对象AO(Active Object)(上下文)
(2)预编译
scope chain
初始化arguments
初始化形参,将arguments中的值赋值给形参
找出所有的变量声明,按照变量名加入AO,如果已存在忽略
找出所有函数声明,按照函数名加入AO,如果已存在同名变量或者函数,替换
this初始化
(3)解释执行
a. 函数中,所有变量声明,在函数的预编译阶段完成,所有变量的声明与实际的书写位置无关
function f(){
console.log(aa);
var aa = 5;
console.log(aa);
}
b. 函数中,所有函数声明,在函数的预编译阶段完成,所有函数的声明与实际的书写位置无关
function f(){
console.log(haha);
function haha (){
console.log('h1');
}
}
c. 函数中,如果变量与函数同名,那么,函数将覆盖变量
d. 函数中,只有函数能够覆盖变量,变量无法覆盖函数
function f(){
console.log(haha);
function haha (){
console.log('h1');
}
var haha = 123;
e. 函数中,函数如果同名,后面的函数声明会覆盖前面的函数声明,并且忽略参数
haha(1);
console.log(haha);
function haha (a){
console.log('haha1');
}
function haha (b){
console.log('haha2');
}
f. 当函数编译后,遇到需要访问的变量或函数,优先考虑自己AO( 局部作用域)中定义的变量和函数,如果找不到,才会在其定义的上一层中寻找,直到到达GO(全局作用域)
38. 递归
一个函数在内部可以调用其本身,那么这个函数就是递归函数(简单理解就是函数内部自己调用自己)
递归函数作用和循环效果类似
由于递归很容易发生'栈溢出'错误(stack overflow),所以必须加退出条件 return
递归可用于遍历多层对象
案例:利用递归求1-n的阶乘1*2*3*...n
function fn(n){
if (n === 1){
return 1;
}
return n * f(n-1);
}
fn();
案例:利用递归求斐波那契数列(兔子序列:指定的那一项是他前两项的和) 1,1,2,3,5,8,13...
function fb(n){
if (n === 1 || n === 2){
return 1;
}
return f(n-2) + f(n-1);
}
console.log(fb(3));//2
递归构造:
找到已知f(x-1)情况下(找到递归的基石,比如f(0)、f(1)的值)求解f(x)的通式
递归的例子:
有一台阶,阶数为n,可以一次跨1阶、2阶、3阶,一共有多少种走法?
function step(n){
switch(n){
case 1:return 1;
break;
case 2:return 2;
break;
case 3:return 4;
break;
default:return (step(n-3)+step(n-2)+step(n-1));
break;
}
return '';
}
console.log(step(5));
39. 闭包
闭包是一个函数加上创建函数的函数的作用域的连接,闭包关闭了函数的自由变量
function fun(){
var a=10;
return function fun(){
console.log(a)//a是自由变量
}
}
(1) 变量作用域: 函数内部可以使用全局变量,函数外部不可以使用局部变量,函数执行完毕,本作用域内的局部变量会销毁
(2)闭包: 指有权访问另一个函数作用域中变量的函数(简单理解就是:一个作用域可以访问另一个函数内部的局部变量) ,//被访问的拥有局部变量的那个函数就是闭包函数
闭包的主要作用:访问其他函数内部变量,延长变量的生命周期,避免定义全局变量所造成的污染
function fn (){ //fn是闭包函数
var num = 10; //num是局部变量
function fun(){
console.log(num);
}
return fun();
}
var f = fn();
f();
fn外面的作用域也可以访问fn内部的局部变量
案例:使用闭包的方式得到当前li的索引号
for(var = i = 0; i < lis.length; i++){
(function(i){ //立即执行函数也称为小闭包,
因为立即执行函数里面任何一个函数都可以使用他的变量
lis[i].onclick = function(){
console.log(i); //调用立即执行函数传进来的参数i
}
})()
}
函数的AO通过scope chain相互连接起来,使得函数体内的变量都可以保存在函数的AO,这样的特性称为“闭包”
闭包的危险:闭包会造成原有AO不释放,造成内存泄露
如何避免闭包引起的内存泄漏:
在退出函数之前,将不使用的局部变量赋值为null
闭包的应用:实现公有变量
缓存存储结构
封装,实现属性私有化
练习1:
function outer (){
var num = 100;
function add() {
num ++;
console.log(num);
}
return add;
}
var fn = outer();
fn();//101 //这里fn = add(){}这个方法 num = 101
fn();//102
fn();//103
var fn2 = outer();
fn2();//101
练习2:
function outer(){
var result = new Array();
for (var i = 0; i < 2; i++){
result[i] = function (){ //result存的是两个函数[f,f]
return i;
}
} //跳出循环的时候 i 已经等于 2 了
return result;
}
var fn = outer();
console.log(fn[0]());//2 这里相当于调用第一个f
console.log(fn[1]());//2 这里相当于调用第二个f
40. this的用法
(1) 在脚本中,this初始化为window
console.log(this);
(2) 在普通函数中,this初始化为window
function f (){
console.log(this);
}
f();
(3) 在object调用的函数中,this被指定为object,谁调用,指向谁
var obj = {
name: 'a',
f2: fuction(){
console.log(this);
}
};
obj.f2();
(4) 在call/apply中,this可以被指定,被指定为第一参数
var obj = {
name: 'a',
f2: fuction(x,y){
console.log(this);
}
};
obj.f2();
var f = obj.f2;
f.call(obj,1,2);//object
(5) 在new的构造函数中,this被指向正在创建的对象
function F(){
console.log(this); //F
}
var obj = new F();
41. 对象
对象是一个具体的事物,js中对象是一组无序的相关属性和方法的集合,所有事物都是对象,例如字符串,数值,数组,函数等
对象是由属性和方法组成的 (js中的对象表达机构更清晰,更强大)
javascript中的对象分为三种:自定义对象,内置对象(Math,Date,Array,String等),浏览器对象
42. (自定义)对象的创建
(除最后一行外用逗号分割)
(1) 对象里面的属性或者方法采取见值对的形式,键(属性名) : 值(属性值)
(2) 多个属性或者方法中间用逗号隔开
(3) 方法冒号后面跟的是一个匿名函数
对象的三种命名规则
(1)匈牙利命名规则:属性+类型+对象描述
属性:成员变量m_、静态成员s_
类型:整形i、数组a、字符串str
对象描述:单词
(2)小驼峰命名规则:对象描述(第一个单词首字母小写,其他单词首字母大写)
(3)大驼峰命名规则:对象描述
(1) 利用字面量创建对象{ }
var obj1 = {};
例子:
var objBook = {
name :'Book Name',//原始成员
pageNumber:300,
author:{ //引用成员
firstname:'aaa',
lastname:'bbb',
},
query: function(){//成员函数表达式
console.log('query');
}
};
(2) 利用new Object创建对象
var obj2 = new Object(); // 两个方法一样
利用 = 赋值的方法添加对象的属性和方法
每个属性和方法之间用分号结束
obj2.name = '张三';
obj2.age = 18;
(3) 构造函数(创建对象)
把对象里面一些相同的属性和方法抽象出来封装到函数里面
能够每次以相同的方式构造对象,同时改动这个函数,所有的对象都能跟着改动
与前两个创建对象的方法(一次只能创建一个对象)相比,构造函数可以一次创建多个对象//构造函数名称以大写字母开头(约定俗成)
构造函数不需要return就能返回结果
调用构造函数必须使用new
属性和方法前面必须添加this
new在执行时的步骤:
a. 在内存中创建一个新的空对象
b. 让this指向这个新的空对象
c. 执行构造函数里的代码,给这个对象添加属性和方法
d. 返回这个新对象(所以构造函数不需要return就能返回结果)
function 构造函数函数名(){
this.属性 = 值;
this.方法 = function(){};
}
new 构造函数名();
function Student(name,age,gender ){
this.name = name;
this.age = age;
this.gender = gender;
}
var std = new Student('wangli',30,'male');
43. 对象的属性
对象属性可以增删改查,与数组类似,但不需要声明变量
枚举:自己的东西可以枚举出来,继承别人来的东西不能枚举出来
var obj = {};
(1) 增加一个属性
obj.a = 'aaa';
obj['1'] = 'bbb'
obj.f1 = function(){
console.log('ff........')
};
(2)查询一个属性
a. in
console.log('a' in obj);
b. hasOwnProperty()
console.log(obj.hasOwenProperty('a'));
(3)删除一个属性
delete
delete obj.a;
(4)修改一个属性
obj.a = 123;
44. 对象的使用(调用对象属性或方法)
(1)调用对象的属性1:对象名.属性名
console.log(objBook.name);
(2)调用对象的属性2:对象名['属性名']
等价于:
console.log(objBook['age']);
(3)调用对象的方法:属性名.方法名()
objbook.query();
45. 对象的遍历
for in
for (变量 in 对象){
}
for (var k in obj){ //for in里面的变量名,最常用的是k 和 key
console.log(k); //k变量输出,得到的是属性名 name
console.log(obj[k]); // obj[k] ,得到的是属性值 张三
}
46. Math 对象
(1)常数
console.log(Math.E);//2.71828
console.log(Math.LN2);//0.9
console.log(Math.LN10);//2.3
console.log(Math.PI);//3.1415926535
console.log(Math.SQRT1_2)//根号下1/2
(2)方法
绝对值
console.log(Math.abs(-5));
三角函数
console.log(Math.sin(30));//0.5
反三角函数
console.log(Math.asin(1));
四舍五入以及取整
console.log(Math.round(10.4));//10
console.log(Math.round(10.5));//11
console.log(Math.floor(10.6));//10 向下取整
console.log(Math.ceil(10.4));//11 向上取整
指数和对数
console.log(Math.exp(2));//7.3 e^2
console.log(Math.pow(2,2));//4 2^2
console.log(Math.log(25));//3.2 e^3.2=25
console.log(Math.sqrt(25));//5 根号下25
最大值最小值
console.log(Math.max(1,5,25));//25
console.log(Math.min(1,5,25));//1
随机数
console.log(Math.random());//0-1之间的均匀分布 [0,1)
随机数公式:得到两个数之间的随机整数且包含这两个数:
function getRandom(min,max){
return Math.floor(Math.random()*(max-min+1))+min;
}
console.log(getRandom(1,10));
案例:
a. 猜1-10之间的一个数(只有3次机会)
var getRandom = function (min,max){
return Math.floor(Math.random()*(max-min+1))+min;
}
var random = getRandom(1,10);
console.log(random)
for(var i = 1; i<= 3; i++){
var num = prompt('请猜一个1-10之间的数')
if (num < random){
alert('对不起,你猜小了')
} else if(num > random){
alert('对不起,你猜大了')
} else{
alert('恭喜你,你猜对了')
break;
}
}
b.随机点名:
var getRandom = function (min,max){
return Math.floor(Math.random()*(max-min+1))+min;
}
var arr = ['张三丰','霍元甲','李小龙'];
console.log(arr[getRandom(0,arr.length-1)]);
47. Date日期对象(是一个构造函数,必须new)
(1) 如何获取当前时间
var d1 = new Date();//必须先实例化,才能调用其他(年月日时分秒)对象
console.log(d1);
(2) 数:1970年1月1日到现在的毫秒数(时间戳,永远不会重复,一直在增加)
var d3 = Date.parse(d1.toString());//获取总的毫秒数
console.log(d3);
console.log(d1.getTime());//获取总的毫秒数
var x = d1.getTime();
x = x / (1000*3600*24*365);
console.log(x);//1970年到现在有几年
console.log(d1.valueOf());//获取总的毫秒数
var date1 = +new Date(); // +new Date()返回的就是总的毫秒数
console.log(Date.now()); //获取总的毫秒数(h5新增)
(3) setFullYear()//月份设定特殊,从0-11
d1.setFullYear(2017,0,12);//2017年1月12日
(4) 有参数的构造函数
var d2 = new Date(2017,0,12);//2017年1月12日
(5) UTC(协调世界时间)和GMT (格林尼治时间)
console.log(d1.toUTCString());
console.log(d1.toGMTString());
(6) 设定年setFullYear和getFullYear
d1.setFullYear(2015);
console.log(d1);
d1.setFullYear(d1.getFullYear() +1);
console.log(d1);
(7) 设定月(0-11)
console.log(d1.getMonth() + 1 );//设定月
(8) 设定日
console.log(d1.getDate());//设定日
(9) 设定星期几(0:星期日,1-6:周一到周六)
console.log(d1.getDay());//设定日
(10) Date克隆
var d4 = new Date(d1);
console.log(d4);
48. 定时器:也称回调函数callback
(1) setTimeout(调用函数,延迟的毫秒数) 只调用一次函数
clearTimeout(定时器名) 停止定时器
(2) setInterval(调用函数,延迟的毫秒数)一直调用函数
clearInterval(定时器名) 停止定时器
(3) this:this的指向在函数定义的时候是确定不了的,只有函数执行的时候才能确定this到底指向谁,一般情况下this的最终指向是那个调用他的对象
a.全局作用域或者普通函数中this指向全局对象window(注意定时器里的this指向window)
b.方法调用中谁调用this指向谁
c.构造函数中this指向构造函数的实例
function Fun(){ //this指向fun
console.log(this);
}
var fun = new Fun();
练习:做一个时钟(可终止)
var timer;
function startTime(){
var today = new Date();//获取当前时间
var h = to2bit(today.getHours());//获取小时
var m = to2bit(today.getMinutes());//获取分钟
var s = to2bit(today.getSeconds());//获取秒
var div = document.getElementById('txt');//找到输出时间的div
div.innerHTML = h + ':' + m + ':' + s;//将div中原有内容替换为时间
timer = setTimeout('startTime()',20);//隔20毫秒运行一次setTimeout这个函数
}
timer = setTimeout('startTime()',20);//隔20毫秒运行一次startTime这个函数
function to2bit(num){//当时分秒数字显示小于10时,在时间前补0占位
if(num < 10){
return '0' + num;
}else{
return '' + num;
}
}
function stopTime(){//清除定时器
clearTimeout(timer);
}
练习:做一个定时器
var time = prompt('请输入什么时间点结束(时间格式:2020-09-10 18:00:24)')
function countDown (time){
var nowTime = +new Date(); //返回当前时间的总豪秒数
var inputTime = +new Date(time); //用户输入的时间(未来时间,即到什么时候结束)的总豪秒数
var timeDown = (inputTime - nowTime)/1000; //现在时间到未来时间剩余时间的总秒数
var d = parseInt(timeDown/60/60/24); //计算天数
d = d < 10 ? '0' + d : '' + d; //不足10则补0占位
var h = parseInt(timeDown/60/60%24); //计算小时
h = h < 10 ? '0' + h : '' + h; //不足10则补0占位
var m = parseInt(timeDown/60%60); //计算分钟
m = m < 10 ? '0' + m : '' + m; //不足10则补0占位
var s = parseInscrot(timeDown%60); //计算秒数
s = s < 10 ? '0' + s : '' + s; //不足10则补0占位
return d + '天' + h + '时' + m + '分' + s + '秒' ;
}
function show(){
console.log('距离结束还有' + countDown(time));
var timer = setTimeout('show()',1000);//每隔20毫秒运行一次countDown这个函数
}
show();
练习:做一个可以变长的进度条
function startLonger(){
var div = document.getElementById('divid');
var width = Number.parseInt(div.style.width);
if(width < 300){
width += 5;
div.style.width = width + 'px';
setTimeout(startLonger(),20);
}else{
div.style.width = '300px';
}
}
setTimeout(startLonger(),20);
49. Array数组对象
var arr = new Array();//创建了一个空数组
var arr = new Array(2);//表示数组长度为2,里面有2个空的数组元素
var arr = new Array(2,3);//等价于var arr = [2,3];里面有2个数组元素,2和3
(1) 检测是否为数组
a. instanceof Array //运算符
var arr = [2,3];
var obj = {name:'张三'};
console.log(arr instanceof Array);//true
console.log(obj instanceof Array);//false
b. Array.isArray() //h5新增
var arr = [2,3];
var obj = {name:'张三'};
console.log(Array.isArray(arr));//true
console.log(Array.isArray(obj));//false
(2) 添加数组元素
a. push(推):在数组末尾添加一个或多个数组元素
push()参数直接写数组元素就行,多个元素用英文逗号隔开
push之后,返回的结果是新数组的长度,原数组也会发生变化
var arr = [1,2,3];
arr.push(4); //arr = [1,2,3,4]
arr.push(4,'pink'); //arr = [1,2,3,4,'pink']
b. unshift():在数组前面添加一个或多个数组元素
unshift()参数直接写数组元素就行,多个元素用英文逗号隔开
unshift之后,返回的结果是新数组的长度,原数组也会发生变化
var arr = [1,2,3];
arr.unshift(4); //arr = [4,1,2,3]
arr.unshift(4,'pink'); //arr = [4,'pink',1,2,3]
(3) 删除数组元素
a. pop(推):删除数组的最后一个元素
pop()没有参数
pop之后,返回的结果是新数组的长度,原数组也会发生变化
var arr = [1,2,3];
arr.pop(); //arr = [1,2]
b. shift():删除数组的第一个元素
shift()没有参数
shift之后,返回的结果是新数组的长度,原数组也会发生变化
var arr = [1,2,3];
arr.shift(); //arr = [2,3]
(4) 数组排序
a. 翻转数组:reverse()
var arr = [1,2,3];
arr.reverse(); //arr = [3,2,1]
b. 冒泡排序:sort()
var arr = [4,3,1];
arr.sort(); //arr = [1,3,4]
sort只能个位排序,超过个位从左往右依次比较排序
var arr = [4,3,11,1,35];
arr.sort(); //arr = [1,11,3,,35,4]
解决超过两位数的sort排序:
var arr = [4,3,11,1,35];
arr.sort(function(a,b){
return a-b; //升序排列
return b-a; //降序排列
}); //arr = [1,3,4,11,35]
(5) 数组索引
a. indexOf(数组元素):从前面开始查找,返回该数组满足条件的第一个元素的索引号,找不到该数组元素则返回负一(-1)
var arr = [4,3,11,3,35];
console.log(indexOf(3));//1
console.log(indexOf(8));//-1
b.lastIndexOf(数组元素):从后面开始查找,返回该数组满足条件的第一个元素的索引号,找不到该数组元素则返回负一(-1)
var arr = [4,3,11,3,35,5];
console.log(lastIndexOf(3));//3
console.log(lastIndexOf(8));//-1
(6) 数组去重
遍历旧数组,拿着旧数组得元素去查询新数组,如果该元素不在新数组中就添加进新数组,如果新数组已经由相同元素就不添加
function unique(arr){
var newArray = []; //声明一个新数组
for (var i = 0; i < arr.length; i++){ //遍历旧数组
if (newArray.indexOf(arr[i]) === -1){ //在新数组里查询旧数组的该元素,若不能查到
newArray.push(arr[i]);// 则把旧数组的这个元素添加到新数组里
}
}
return newArray;
}
var demo = unique(['3','5','6','5','6','3','1']);
console.log(demo);
(a)//时间复杂度,与x元素个数成正比-----(number/number、boolean、string都适用)
var x = [1,3,4,5,1,3,2,1,2,4];
var y = [];
for (var i = 0;i < x.length; i++){ //遍历旧数组
for (var j = 0; j < y.length; j++){ //遍历新数组
if(x[i] === y[j]){//看看这个元素y里是不是有,没有的话push ,等于说明重复
break;
}
}
if(j >= y.length){
y.push(x[i]);
}
}
console.log(x); //[1,3,4,5,1,3,2,1,2,4];
console.log(x); //[1,3,4,5,2];
(b)//时间复杂度,生成y的时间复杂度O(N),x排序,时间复杂度O(N*logN)(基于比较的排序,堆排序,时间都是N*logN)--------(只适用于number)
var x = [1,3,4,5,1,3,2,1,2,4];
var y = [];
x.sort();//x排序,不用每次翻找y
console.log(x);//[1,1,1,2,2,3,3,4,4,5];
for(var i = 0; i < x.length; i++){
if(y.length != 0 || y[y.length - 1] !== x[i]){
y.push(x[i]);
}
}
console.log(y);//[1,2,3,4,5]
(c)//取决于arrMap的时间复杂度
var arrMap = [];
for(var i = 0; i < x.length; i++){
arrMap[x[i]] = true;
}
for(var j in arrMap){
y.push(+j);//[1,2,3,4,5]
}
console.log(y);
(7) 数组转换为字符串
a. toString()
var arr = [1,2,3]
console.log(arr.toString()); // 1,2,3
b. join(分隔符) //可以添加分隔符
var arr = [1,2,3]
console.log(arr.join()); // 1,2,3 默认逗号
console.log(arr.join(-)); // 1-2-3
50. 字符串对象
(1)基本包装类型
把简单的数据类型(String,Number,Boolean)包装成为复杂数据类型,这样基本数据类型也就同对象一样有了属性和方法,
var str = 'andy';
console.log(str.lengthg);//4
类包装执行过程
a.生成临时变量,把简单数据类型包装成为复杂数据类型
temp = new String('andy');
b.赋值给我们声明的字符变量
str = temp; //str = new String('andy');
c.销毁临时变量
temp = null;
(2) 字符串的不可变
指的是里面的值不可变,虽然可以给一个字符串的变量重新赋一个新的值,看上去内容可以改变,但其实是新开辟了一个内存空间把新的值存进去,在让声明的变量指向新空间,本质上地址已经变了,因此不要大量拼接字符串和给字符串变量重新赋值,这样会占用大量内存空间
(3)根据字符返回位置
字符串所有方法,都不会修改字符串本身(字符串不可变特性),操作完成会返回一个新的字符串,
indexOf/lastIndexOf('查找的字符',查找的起始位置)
例题:查找字符串'abcdeohofgoasdocvoxcoadopp'中所有o出现的位置以及出现的次数
var str = 'abcdeohofgoasdocvoxcoadopp';
var index = str.indexOf('o');//查找o首次出现的位置
var count = 0;
while (index !== -1){ //如果查找到有o
console.log('o出现的位置是字符串的' + index + '号索引位置');
count += 1;
index = str.indexOf('o',index + 1);
}
console.log('o出现的次数一共是' + count + '次');
(4)根据位置返回字符
a.charAt(index):返回指定位置的字符,
var str = 'andy';
console.log(str.charAt(3));//y
遍历字符串
for (var i = 0; i < str.length; i++){
console.log(str.charAt(i))
}
b.charCodeAt(index):获取指定位置处字符的ASCILL码,可以判断用户按了哪个键
var str = 'andy';
console.log(str.charCodeAt(0));//97
c.str[index]:获取指定位置处字符 //html新增 ,IE8以上支持
var str = 'andy';
console.log(str[3]);//y
案例:判断一个字符串'abcodofodoxcfopp'中出现次数最多的字符,并统计其出现的次数
var str = 'abcodofodoxcfopp';
var obj = {};//声明一个空对象
for (var i = 0; i < str.length; i++){ //遍历字符串
var property = str.charAt(i); //把遍历的字符复制给属性
if(obj[property]){ //如果空对象里有该字符这个属性
obj[property]++; //那么这个属性值+1
} else{ //不存在
obj[property] = 1;//则属性值为1
}
}
console.log(obj);
var max = 0; //声明一个最大值
var theChar = ''; //声明一个变量接收最大属性
for (var k in obj){ //遍历对象
if (obj[k] > max){ //如果对象里的属性值大于声明的最大值
max = obj[k]; //那么最大值就被赋值=属性值
theChar = k; //最大属性值的属性就是该字符
}
}
console.log('出现次数最多的字符是' + theChar);
console.log('一共出现的次数是' + max + '次');
(5)字符串操作方法
a.concat(str1,str2,str3):用于连接两个或多个字符串,等效于+,
b.substr(start,length):从start位置开始(索引号),length取的个数
c.slice(start,end):从start位置开始,截取到end位置,end不包含在内
d.substring(start,end):从start位置开始,截取到end位置,end不包含在内,基本和slice相同,但是不接受负值
var str = "hello world"
console.log(str.substring(0,5));//hello
(6) 其他方法
a.replace('被替换的字符','替换为的字符') //只会替换第一个字符
var str = "hello world"
console.log(str.replace(o,a));//hella world
b.split('分隔符')
c.toUpperCase() 转换大写
var str = "hello world"
console.log(str.toUpperCase());//HELLO WORLD
d.toLowerCase 转换小写
var str = "hello world"
console.log(str.toLocaleLowerCase());//hello world
e.trim()去除空格
var str = "hello world"
trimleft();去除左空格/trimright();去除右空格·
console.log(str.trim()); 去除全部空格//helloworld
51. 包装类
var n1 = new Number(123);
var s1 = new String('aaa');
var b1 = new Boolean(true);
console.log(typeof(n1),typeof(s1),typeof(b1));//object
52. 数据结构(堆和栈)
堆栈(内存)空间分配:
(1) 栈stac
由操作系统自动分配释放存放函数的参数值,局部变量的值
简单数据类型(值类型:number、string、Boolean、undefined、null)存放到栈内存里面(栈里开辟空间存放值,变量名指向栈里的对象)
原则:后进先出,进栈:push,出栈:pop(尾进尾出)(进栈:unshift,出栈:shift(头进头出))
(2) 堆
一般由程序员分配释放,若程序员不释放,由垃圾回收机制回收
复杂类型(引用类型:array、 function、object)(对象)存放到堆里面 (变量名先指向栈里存放的地址/引用,地址再指向堆里的值)
push-pop(尾进尾出)
堆:一块存储空间,没有任何原则
栈:
push-pop(尾进尾出)
var arr = ['a','b','c'];
console.log(arr.join(','));
arr.push('z');
console.log(arr.join(','));//['a','b','c','z']
console.log(arr.pop());
console.log(arr.join(','));//['a','b','c']
unshift-shift(头进头出)
var arr = ['a','b','c'];
console.log(arr.join(','));
arr.unshift('z');
console.log(arr.join(','));//['z','a','b','c']
console.log(arr.shift());
console.log(arr.join(','));//['a','b','c']
队列queue:原则:先进先出
push-shift(尾进头出)
var arr = ['a','b','c'];
console.log(arr.join(','));
arr.push('z');
console.log(arr.join(','));//['a','b','c','z']
console.log(arr.shift());
console.log(arr.join(','));//['b','c','z']
unshift-pop(头进尾出)
var arr = ['a','b','c'];
console.log(arr.join(','));
arr.unshift('z');
console.log(arr.join(','));//['z','a','b','c']
console.log(arr.pop());
console.log(arr.join(','));//['z','a','b',]
栈的应用 ://6.1动手实验(暂时看不懂,以后研究)
例题:a = '2+3*5-4' ,应用四则运算,先乘除后加减,数0-9,+-*/没有括号,不调用系统解释器
思路:
算法思路:
不同优先级,右侧操作符优先级高于左侧,则右侧优先
不同优先级,左侧操作符优先级高于右侧,则左侧优先
相同优先级,左侧优先
软件工程思路:
(1)测试用例
2+3*5-4
1+2+3+4
1*2+3*4/5-4/2
1+2
(2)debug的工具和手段(Debug、log)
//栈:push-pop(后进先出)
var nArray = [];
var oArray = [];
var nRet = 0;
//first number
var sN Token = x.charCodeAt(0) - 48;
栈和堆
var a = 5;//key是变量名a, value就是5
var arr = [];//key是变量名arr,value是[]的地址/引用
栈内存中的对象,有一个引用计数
GC:垃圾收集,内存回收机制,MC,COM---(谁的引用数为0,就摧毁谁)
GC时间:堆内存达到一定使用门槛或定期
当代码运行到栈内变量的作用域之外时,变量将被摧毁,如果变量被摧毁,引用数减1,如果变量被赋值,指向其他值变量或引用变量,引用数减1
循环引用:变量将永远不会被摧毁
var a1 = [];
var a2 = [a];
a1[0] = a2;
a2[0] = a1;
var a = 5;
var b = a;
var arr = [];
var arr1 = [];
var s ='abc';
栈内存:
ST001:a,5
ST002:b,5
ST003:arr,HP001
ST004:arr1,HP001
ST005:s,'abc'
堆内存
HP001:[]
js垃圾回收机制
js内存分为栈内存和堆内存,栈JavaScript用于存储静态数据,静态数据是引擎在编译时知道大小的数据。这包括指向对象和函数的引用和原始值(strings, numbers, booleans, undefined 和 null);由于JS引擎知道大小不会变,所以它们将为每个值分配固定大小的内存。堆是用于存储JavaScript对象和函数。与栈不同,JS引擎不会为这些对象分配固定大小的内存空间。栈包含对堆中对象数据的引用。
栈垃圾回收的方式非常简便,当一个函数执行结束之后,JavaScript 引擎销毁该函数保存在栈中的执行上下文。当函数直接结束,栈空间处理完成了,但是堆空间的数据还是存储在堆空间中,需要垃圾回收器将堆空间中的垃圾数据回收。在 V8 中会把堆分为新生代和老生代两个区域,新生代中存放的是生存时间短的对象,老生代中存放的生存时间久的对象。 新生区中使用Scavenge算法,老生区中使用标记-清除算法和标记-整理算法。
Scavenge算法:
(1) 标记:对对象区域中的垃圾进行标记
(2) 清除垃圾数据和整理碎片化内存:副垃圾回收器会把这些存活的对象复制到空闲区域中,并且有序的排列起来,复制后空闲区域就没有内存碎片了
(3) 角色翻转:完成复制后,对象区域与空闲区域进行角色翻转,也就是原来的对象区域变成空闲区域,原来的空闲区域变成了对象区域
标记-清除算法:
(1)标记:标记阶段就是从一组根元素开始,递归遍历这组根元素,在这个遍历过程中,能到达的元素称为活动对象,没有到达的元素就可以判断为垃圾数据。
(2) 清除:将垃圾数据进行清除。
(3)产生内存碎片:对一块内存多次执行标记 - 清除算法后,会产生大量不连续的内存碎片。而碎片过多会导致大对象无法分配到足够的连续内存。
标记-整理算法
(1) 标记:和标记 - 清除的标记过程一样,从一组根元素开始,递归遍历这组根元素,在这个遍历过程中,能到达的元素标记为活动对象。
(2) 整理:让所有存活的对象都向内存的一端移动
(3) 清除:清理掉端边界以外的
53. 特殊字符
\r\n 字符表示换行(文件都是一行的,编辑器为了照顾人类感受,见到\r\n,显示为换行)
\r 0x0d CR carrage return 回车
\n 0x0a LF line feed 换行
转义字符:
如果在字符串中涉及到一些特殊字符如“\” “'” “""” 等,这些字符无法直接使用,需要采用转义字符的方式。
\b 退格符
\t 水平制表符(空格)
\f 换页符
\n 换行符
\r 回车符
\' 单引号
\" 双引号
\\ 反斜线
54. 编码与解码
编码:用户输入字符,计算机记录对应二进制的值
解码:计算机根据记录的二进制值,显示为人类认识的字符图像
字符集:是一个系统支持的所有抽象字符的集合
字符编码:字符集的编码规则
常见字符集:
ASCLL字符集
GB字符集族:
GB2312、GBK、GB18030
Unicode字符集族:
UTF-8、UTF-16、UTF-32
USC字符集族:
USC-2、USC-4
55. json
json指的是javascript对象表示法
json对象:
{ }中书写
名称/值对,"key":'value'
json数组:
[ ]中书写
对象间,用逗号分隔
json字符串
var strJSON='{"name":"ALbad",\n'+'
"age":"25"}';
console.log(strJSON);
string-->Object
(1) evel() 自己产生一个string当做js脚本,交由js引擎运行
var strJSON2 = '('+ strJSON +')';
console.log(strJSON2)
var obj1 = eval(strJSON2);
(2) JSON.parse()
var obj2 = JSON.parse(strJSON);
(3) $().parseJSON()
var obj3 = jQuery.parseJSON(strJSON);
Object-->string
(1) JSON.stringify()
var strJSON3 = JSON.stringify(obj1);
console.log(strJSON3);
(2) jQuery.toJson()
var strJSON4 = jQuery.toJson(obj1);
console.log(strJSON4);
练习1:把一张九九乘法表放入json,然后解析
{
'0':['1x1=1'],
'1':['1x2=2','2x2=4'],
...
}
var ONNTable = {};
for (var i = 1;i <= 9;i++){
var line = [];
for(var j = 1;j <= i;j++){
var str = i + 'x' + j + '=' + (i*j);
line.push(str);
}
ONNTable.[i-1] = (line);
}
var sNNTable = JSON.stringify(ONNTable);
var jsonTable = JSON.parse(sNNTable);
for(var x in jsonTable){
var line = jsonTable[x];
for(var y in line){
var text = line[y];
console.log(text);
}
}
56. 正则表达式
用于匹配字符串中字符组合的模式(正则表达式也是对象)
正则表达式的创建:
(1)正则表达式类 RegExp(exp,param)
var regExp = new RegExp(/[0-9]+/,'g');//正整数
//[ ]指代一个字符,里面的字符表示被指代的字符的选择范围
//+ 表示前面的字符,1次或者1次以上
//g 代表global,全局
var str1 = '123a123';
console.log(str1.match(regExp));\
//在match这个函数里
g,匹配所有的字符串
没有g,匹配第一个然后停止
(2)直接量(通过字面量创建)(用两个撇斜杠,/expression/param)
var regExp2 = /[0-9]+/g;
string对象的match:
console.log(str1.match(/[0-9]+/g));
console.log(str1.match(/\d+/g));
console.log(str1.match(/123/g));只匹配123,不重复
console.log(str1.match(/(123)+/g));匹配123,至少循环一次,允许重复
RegExp对象的test方法:用来检测字符串是否符合正则表达式的规范
var regExp2 = /[0-9]+/g;
console.log(typeof(regExp2));//object
console.log(regExp2.test(str1));//true(能匹配上则返回true,否则返回false)
57. 正则表达式详解
^表示以什么开头,$表示以什么结尾
(1)简单类
单词本身
var str = 'hello world!';
console.log(str.match(/hello/g));
(2)范围类
[ ]指代一个字符,内部指出字符的范围
[0123456789] [0-9] [a-z] [A-Z]
十六进制 [0-9a-fA-F]
(3)负向类
[^0-9]//^在括号里面表示取反,表示不能是数字,排除数字
str = '546456;adfar145;Bz4156;';
console.log(str.match(/[0123456789]/g));//等价
console.log(str.match(/[0-9]/g));//等价
console.log(str.match(/\d/g));//等价
console.log(str.match(/[0-9][0-9]/g));//匹配字符串中的两位数
(4)量词一个前面单位出现的次数
{n} //出现次数
console.log(str.match(/[0-9]{3,4}/g));
{m,n} //至少m次,至多n次
{m,} //至少m次,至多不限
贪婪量词:? + * //找到匹配的最大串
? //0次或者1次 = {0,1}
+ //1次或者多余1次 = {1,}
* //0次或者多余0次 = {0,}
惰性量词:
*? +? //找到匹配的最小串
(5)通配符 预定义类
[^\n\r] //除了回车换行以外,都可以匹配
\d = [0-9] //数字
\D = [^0-9] //非数字
\w = [a-zA-Z_0-9] //数字、字母、下划线
变量名:/[a-zA-Z_]\w //变量名只能是字母数字和下划线且不能以数字开头
\W = [^a-zA-Z_0-9] //非数字、字母、下划线
\s = [\t\n\x0B\f\r] //空格或者空白
\S = [^\t\n\x0B\f\r] //非空格或者非空白
\b //表示边界(两个字符中间的位置)一边是\w,另一边是\W
\B //不是边界
^ //表示开始
$ //表示结束
(6)正则表达式参数/表达式/[switch](switch也称为修饰符,按照什么样的模式来匹配,有三中值)
g : 全局匹配(不会出现匹配到第一个值就停止继续往后匹配的情况)
i : 忽略大小写
gi : 全局匹配+忽略大小写
(6)分组
一个正则表达式,不但可以对整个匹配进行操作,还可以对()中的子串进行匹配分组
(pattern) 匹配 pattern 同时捕获结果,自动设定好组
\1 RegExp.$1 :反向引用
\k<name> :反向引用
(?<name>pattern) 匹配pattern同时捕获结果,设定name为组名
str = 'mom and dad';
console.log(str.match(/mom( and dad)?/))
console.log(regExp.$1);
str = 'word excel excel hello word word!';
console.log(str.match(/(\b[a-zA-Z]+\b)\s+\1/g));//excel excel word word
console.logstr.match(/(?<n1>\b[a-zA-Z]+\b)\s+\k<n1>/));
零宽(正负向)先行/后行断言
零宽(正负向)先行/后行:如通配符,不占用字符宽度//正向:是 负向:非
断言:判断是否符合要求进行匹配
str = 'reaaa;rcaaa=bbb;';
//零宽先行断言(?<):寻找四个字符,以ea开头
console.log(str.match((/(?<=ea).{4}/g);
//零宽后行断言(?):寻找两个字符,两字符以分号结尾
console.log(str.match(/.{2}(?=;)/g));
//零宽负向先行断言(<?!):寻找不以re开头的三个字母
console.log(str.match((/(<?!re)[a-zA-Z]{3}/g);
//零宽负向后行断言(?!):寻找三个个字母,不以分号结尾
console.log(str.match((/[a-zA-Z]{3}(?!;)/g);
(7)或 |
str = 'var aaadf1';
str2 = 'function f(){}';
console.log(str.match(/^var\s+\[a-zA-Z]\w*;|^funxtion\s+\/[a-zA-Z]\w*\s*\(\))\{\};);
console.log(str2.match(/^var\s+\[a-zA-Z]\w*;|^funxtion\s+\/[a-zA-Z]\w*\s*\(\))\{\};);
(8)匹配:
匹配手机号 /^1[34579]\d-?\d{4}-?\d{4}$/;//137-4561-1546
匹配邮箱号 /^[\w\-\.]+@[\w\-]+(\.[a-zA-Z]{2,5}){1,3}$/; //23d-1vhgv.@163.com.cn
匹配整数 /^[+-]?\d+$/; //-1
匹配浮点数 /^[+-]?\d+(\.\d+)?([Ee][+-]?\d+)?$/; //1.2E5
匹配中国汉子 /^[\u4e00-\u9fa5\uff0c\u3002]+$/; //中文。
58. String支持的正则函数
(1)search(regExp) 直接量和对象,返回整数,即开始的位置,-1表示没有找到
g :search不支持全局搜索,直接忽略参数g,同时忽略regExp的lastIndex
str = 'I word excel word ok';
console.log(str.search(/\b word \b/));//2
console.log(str.search(/\b worda \b/));//-1
(2)match(regExp) 直接量和对象
没有g:返回数组,0:第一个匹配的最大串,其他是分组的子串;index:最大串开始的位置;input:原始的串
有g: 返回数组,所有匹配的最大串,但是没有分组的子串;index和input无效
console.log(str.match(/\b excel \b/));
console.log(str.match(/\b excel \b/g));
(3)replace(regExp/substring,replacement)
regExp:直接量,对象,substring:子串
replacement:替换进去的串,或者函数
// $1-$99:分组,
// $&:代表全部子串,
// $`:子串左侧
// $':子串右侧
把小写变成大写:
str = 'I word excel word ok';
console.log(str.replace(/\b word \b/,'WORD'));
等价于
console.log(str.replace('word','WORD'));
全部替换
console.log(str.replace(/\b word \b/g,'WORD'));
颠倒位置:
str = 'aaa-bbb cc-ddd';
console.log(str.replace(/([a-z]+)-([a-z]+)/g,'$2-$1')) //bbb-aaa ddd-cc
(4)split(separetor,hoemany) //字符串分割成数组
separetor:正则表达式/字符串,
str = 'I word excel word ok';
console.log(str.split(/\s+/));
console.log(str.split(/\s+/,4));
console.log(str.split(''));
(5)regExp test() //返回true是否匹配
str = 'I word excel word ok';
console.log(str.test(/\b word \b/.test(str)));//true
(6)regExp exec() //返回数组,找不到:null
没有g: 数组的0位:最大匹配串,后面是数组
index:开始匹配的位置
input:原串
有g :lastIndex中放入子串结束位置的下一位,每次调用后移
str = 'I word excel word ok';
var regExp2 = /\b word \b/g;
regExp2.lastIndex = 6;
var aRst = regExp2.exec(str);
console.log(aRst);
console.log(regExp2.lastIndex);//17
59. Web API
浏览器提供的一套操作浏览器功能和页面元素的API(接口),包括BOM 和DOM
60. DOM
文档对象模型,是w3c组织推荐的处理可扩展标记语言的标准编程接口
(有了这些DOM接口就可以改变网页的内容,结构和样式),DOM把所有内容看作对象,即文档对象模型
*文档:一个页面就是一个文档,DOM中用document表示(document对象:console.log(window.document);)
*元素:页面中所有的标签都是元素,DOM中用element表示
*节点:网页中所有内容都是节点(标签,属性,文本,注释等),DOM中使用node表示
获取元素:文档页面从上往下加载,得先有标签才能获取,因此script语句写在获取元素标签的下面
console.dir(); 打印返回的元素,更好的查看里面的属性和方法
var p1 = document.getElementById('c1');
console.dir(p1);
(1) 根据ID名获取element元素
getElementById(string);//id名 返回一个匹配ID的元素
var p1 = document.getElementById('c1');
console.log(p1);
(2)根据class类名获取element元素(h5新增,IE9以下不支持)
a. getElementsByClassName(string);//class名 返回获取过来的所有满足条件元素的集合,以伪数组的形式存储
var p2 = document.getElementsByClassName('c2');
console.log(p2);
b. element.classList 返回元素的类名(返回多个以数组的形式)
console.log(div.classList);
添加类名:element.classList.add('类名'); //不会覆盖以前的类名
div.classList.add('two');
切换类名:element.classList.toggle('类名');//有就删除,没有就加上,即从无到有,从有到无变化
btn.addEventListener('click',function(){
div.classList.toggle('bgc');//点击一下添加bgc的类名,再点击一下取消类名
})
删除类名:element.classList.remove('类名');
div.classList.remove('one');
(3)根据标签名TagName获取element元素
getElementsByTagName(string)//标签名 返回获取过来的所有满足条件元素的集合,以伪数组的形式存储
Var lis = document.getElementsByTagName('li');
console.log(lis);
console.log(lis[0]);//返回指定的一个
for(var i = 0; < lis.length; i++){//遍历每一个符合的标签元素
console.log(lis[i]);
}
(4)根据标签名Name获取element元素
getElementsByName(string)//返回数组 ,name属性只对部分元素有效(form表单、img、ifram)
Var inputs= document.getElementsByName('c4');
console.log(inputs);
(5) querySelector() //返回指定css选择器的第一个元素
querySelector() //返回指定css选择器的第一个元素
var firstbox = document.querySelector('.box')
Var p5= document.querySelector('#c5').style.color = 'red';
(6)querySelectorAll() //返回指定css选择器的所有元素
querySelectorAll() //返回指定css选择器的所有元素
Var s= document.querySelectorAll('form input');
console.log(s); //输出所有的form下所有的input元素
s[0].style.color = 'green';//form下的第1个input内容颜色为绿色
s[2].style.color = 'red';//form下的第3个input内容颜色为红色
(7)获取特殊元素
a.获取body元素
document.body //返回body元素对象
b.获取html元素
document.documentElement //返回html元素对象
61. DOM 增删替 (操作元素)
(1)增加:
a. 创建节点:document.createElement('p')
添加节点:
appendChild(父亲元素把儿子元素放到所有当前儿子元素的最后)
insertBefore(要添加的节点,添加到谁的前面)(父亲元素把儿子元素放到指定元素的前面)
var p1 = document.createElement('p');//生成一个p标签(element)
var txt1 = document.createTextNode('aaa');//生成一个aaa的节点
p1.appendChild(txt1);//将aaa挂载到p标签下
document.body.appendChild(p1);//将p标签挂载到html的body标签下所有子元素的最后
var p2 = document.createElement('p');//生成一个p标签(element)
var txt2 = document.createTextNode('bbb');//生成一个bbb的文本节点
p.appendChild(txt2);//将aaa挂载到p标签下
document.body.insertBefore(p2,p1);//将p标签挂载到html的body标签下p1标签的前面
复制节点(克隆节点):node.cloneNode()
括号为空或者里面是false,则执行浅拷贝,只复制标签,不复制里面的内容
括号里为true,则执行深拷贝,同时复制标签和里面的内容
var li = ul.children[0].cloneNode(true);
ul.appendChild(li);
b.document.write 直接将内容写入页面内容流,但是页面文档流执行完毕再调用,则他会导致页面全部重绘(即重新绘制了一个新页面,之前的内容不存在) //不常用
document.write('<div>123</div>')
c.innerHtml //本质上是拼接字符串,当大量创建时效率会降低,但如果用数组方式拼接效率反而会比ceratElement更高
div.innerHtml = '<a href = "javascript:;">123</a>'
数组形式拼接(解决效率低问题)
var arr = [];//声明一个新数组
for (i = 0; i<= 100; i++ ){ //循环
arr.push('<a href = "javascript:;">123</a>');//将超链接字符串放到数组中
}
div.innerHtml = arr.join('');//将数组转化为字符串形式复制给div
d. 直接把字符串格式元素添加到父元素中
element.insertAdjacentHTML(position,text);
position:
beforebegin 插入元素自身的前面
afterend 插入元素自身的后面
afterbegin 插入元素内部第一个子节点之前
beforeend 插入元素内部最后一个子节点之后
<div id = "one">one</div>
var d1 = document.queryselector('#one');
d1.insertAdjacentHTML('afterend','<div id = "two">two</div>')
(2)删除:
removeChild (父亲删除儿子--不是不存在,只是不显示)
var p3 = document.createElement('p');//生成一个p标签(element)
var txt3 = document.createTextNode('bbb');//生成一个aaa的节点
p.appendChild(txt3);//将aaa挂载到p标签下
document.body.removeChild(p3)//html的body标签下p3标签
等价于 p3.remove();//删除html的body标签下p3标签
(3)替换:
a. replaceChild (父亲将后面子元素替换成前面的子元素)
var p4 = document.createElement('p');//生成一个p标签(element)
var txt4 = document.createTextNode('bbb');//生成一个bbb的节点
p.appendChild(txt4);//将aaa挂载到p标签下
document.body.replaceChild(p4,p3)//将p3标签替换成p4标签
b. node.innerText //所见即所得,不识别html标签(非标准),可读写,会去除空格和换行
console.log(p.innerText);
p.innerText = '<a href="http://www.taobao.com>淘宝</a>"';//<a href="http://www.taobao.com>淘宝</a>"
c. elment.innerHTML //会转译,识别html标签(w3c标准),可读写,保留空格和换行
console.log(p.innerHtml);
p.innerHTML = '<a href="http://www.taobao.com>淘宝</a>"';//淘宝-超链接
d. node.textContent //所见即所得
p.textContent = '<a href="http://www.taobao.com>淘宝</a>"';
//<a href="http://www.taobao.com>淘宝</a>"
e. element.setAttribute
p.setAttribute('class','cls2');//将p标签的class名设置为cls2
f. element.getAttribute
console.log(p.getAttribute('class'));//得到p标签的class名
案例:动态生成表格:
<table border="1px solid black" cellspacing="0" cellpadding="10px" style="margin: 0 auto; text-align: center;">
<thead>
<tr>
<th>姓名</th>
<th>学号</th>
<th>成绩</th>
<th>操作</th>
</tr>
</thead>
<tbody>
// <tr>
// <td>李星云</td>
// <td>201905006401</td>
// <td>90</td>
// <td>删除</td>
// </tr>
// <tr>
// <td>萧炎</td>
// <td>201905006402</td>
// <td>92</td>
// <td>删除</td>
// </tr>
// <tr>
// <td>石昊</td>
// <td>201905006403</td>
// <td>94</td>
// <td>删除</td>
// </tr>
// <tr>
// <td>张小凡</td>
// <td>201905006404</td>
// <td>96</td>
// <td>删除</td>
// </tr>
</tbody>
</table>
<script type="text/javascript">
//首先声明一个数组,把数据以对象的形式存放到数组里面
var datas = [{
name: '李星云',
ID: 201905006401,
score: 90
},
{
name: '萧炎',
ID: 201905006402,
score: 92
},
{
name: '石昊',
ID: 201905006403,
score: 94
},
{
name: '张小凡',
ID: 201905006404,
score: 96
}
];
//往tbody创建行 ,通过数组长度确定创建几行
var tbody = document.querySelector('tbody'); //找到tbody
for (var i = 0; i < datas.length; i++) { //遍历,有几个数据就创建几行
var tr = document.createElement('tr');//创建行
tbody.appendChild(tr);//将行插入到tbody里
//行里创建单元格,有几个取决于属性个数
for (k in datas[i]) { //遍历对象,datas[i]是对象
var td = document.createElement('td');//创建行
tr.appendChild(td);//将单元格插入到行里里
td.innerHTML = datas[i][k];//将数组里面的每个对象里的属性值给单元格
}
//创建删除列,个数和数组length有关系
var td = document.createElement('td');
tr.appendChild(td);//将单元格插入到行里里
td.innerHTML = '<a href = "javascript:;">删除</a>';//将数组里面的每个对象里的属性值给单元格
//点击删除对应行
var a = document.querySelectorAll('a');//找到a
for (var i = 0; i < a.length; i++) {//遍历a
a[i].onclick = function(){
//点击a连接tbody删除他的子节点行
tbody.removeChild(this.parentNode.parentNode);
}
}
}
</script>
62. 操作Element元素的属性
(1)修改图片元素属性
a.修改图片元素属性src
img.src = '图片的链接地址';-
b.修改图片元素属性title
img.title = '刘德华';
(2)修改表单元素属性
a.修改表单元素属性value
input.value = '请输入内容';
b.修改表单元素属性disabled(禁用)
button.onclick = function(){
this.disabled = true;//this指向的是事件函数的调用者button
}
c..修改表单元素属性type(文本类型)
input.type = 'password';
input.type = 'text';
(3)修改样式属性:Element.style(产生的是行内样式,权重较高,属性采取驼峰命名法)
a.修改backgroundColor
div.onclick = function(){
this.style.backgroundColor = 'pink';//this指向的是事件函数的调用者div
}
b.修改显示/隐藏
button.onclick = function(){
div.style.disply = 'none';//盒子隐藏
}
案例:修改精灵图背景
var lis = doucument.querySelectorAll('li'); //获取所有的li元素
for (var i = 0; i < lis.length; i++){//遍历每一个li元素
var index = i * 44; //44是精灵图每个图片的大小(竖向排列是高,横向排列是宽)
lis[i].style.backgroundPosition = '0 - ' + index + ' px';
}
案例:文本框获得焦点时隐藏内容(onfocus 获得焦点/onblur 失去焦点)
var input = doucument.querySelector('input'); //获取文本框
txt.onfocus = function(){//获得焦点时
if(this.value === '手机'){
input.value = '';
}
this.style.color = #333 ;
}
txt.onblur = function(){//失去焦点时
if(this.value === ''){
input.value = '手机';
}
}
(4)修改Element元素名
a.修改元素的className //直接更改元素类名,会覆盖原先的类名(如果样式修改较多可用这种操作)
div.className = 'change';
div.className = 'first change';//多类名选择器,可保留原先的类名
(5)排他思想:tab标签切换,点击谁谁的样式改变,且始终只有一个元素样式改变了
案例:
var btns = document.getElementByTagName('button'); //获取所有的按钮元素
for (var i = 0; i < btns.length; i++){//遍历所有的按钮元素
btns[i].onclick = function(){
for (var i = 0; i < btns.length; i++){//遍历所有的按钮元素
btns[i].style.backgroundColor = ''; //先把所有button背景颜色去掉
}
this.style.backgroundColor = 'pink';//再设置当前元素的背景颜色
}
}
(6)获取元素属性值
a.Element.属性(只能获取内置属性值)
console.log(div.id);//box
console.log(input.checked);//true/false
b.Element.getAttribute('属性')(还可获取自定义属性值)
console.log(input.getAttribute('index'));
c.Element.dataset.属性(h5新增获取自定义属性)
console.log(input.dataset.index);
console.log(input.dataset[index]);
自定义属性:为了保存并使用数据,有些数据可以保存到页面中而不用保存到数据库中
设置h5自定义属性:以属性data开头作为属性名并且赋值
<div data-index='1'> </div>
(7)设置元素属性值
a.Element.属性 = '值';(直接赋值)
div.id = 'box';
b.Element.setAttribute('属性','值’)(主要针对自定义属性)
console.log(input.setAttribute('index',2));
(8)移除属性 Element.removeAttribute('index');
案例:全选与取消全选(全选按钮选中状态,则下面所有其他按钮都是选中状态,反之亦然,但只要下面的其他按钮至少有一个不是选中状态,全选按钮就是未选中状态)
var checkAll = document.getElementById('checkAll');//获取全选按钮(复选框)
var checks = document.getElementById('checkbody').getElementByTagName('input');
//获取下面所有复选框
checkAll.onclick = fuction(){
for (var i = 0; i < checks.length; i++){//遍历其他所有复选框
checks[i].checked = this.checked;
//把所有复选框改为全选按钮的选中状态
}
}
for (var i = 0; i < checks.length; i++){//遍历其他所有复选框
checks[i].onclick = function (){ //给下面所有复选框遍历都添加点击事件
var flag = true;//控制全选按钮是否被选中
for (var i = 0; i < checks.length; i++){
//在遍历一遍看是否有至少一个没被选中
if(!checks[i].checked){
flag = false;
}
}
checkAll.checked = flag;
}
}
63. 操作Node节点
节点至少拥有nodeType(节点类型),nodeName(节点名称),nodeValue(节点值)这三个基本属性
元素节点nodeType为1 (最常用)
属性节点nodeType为2
文本节点nodeType为3(包含文字,空格,换行等)
(1)parentNode 父节点 (得到离元素最近的父节点,找不到则返回null)
console.log(li.parentNode);
(2)
a. children 子节点(获取所有子元素节点,比较常用)
console.log(ul.children);
console.log(ul.children[1]);//ul里的第二个子元素
console.log(ul.children[ul.length-1]);//ul里的最后一个子元素
b. childNode 子节点(得到包含文本在内的节点,不常用)
console.log(ul.childNode);
(3)
a. nextElementSibling 兄弟节点(返回当前元素下一个兄弟元素节点,找不到返回null) //IE9以下不支持
console.log(nav.nextElementSibling);
b. previousElementSibling 兄弟节点(返回当前元素上一个兄弟元素节点,找不到返回null)
console.log(nav.previousElementSibling);
64. HTML DOM 事件
事件由三部分组成:事件源(被触发对象(按钮)),事件类型(如何触发(点击onclick)),事件处理程序(通过函数赋值完成)
var d = document.getElementById('button'); //获取事件源
d.onclick = function(){ //绑定/注册事件(事件类型单击事件)
console.log(我被点击了); //添加事件处理程序
};
(1)鼠标事件
onclick 当用户点击某个对象时调用的事件句柄。
oncontextmenu 在用户点击鼠标右键打开上下文菜单时触发
ondblclick 当用户双击某个对象时调用的事件句柄。
onmousedown 鼠标按钮被按下。
onmouseenter 当鼠标指针移动到元素上时触发(鼠标只是经过自身盒子触发)
onmouseleave 当鼠标指针移出元素时触发
onmousemove 鼠标被移动。
onmouseover 鼠标移到某元素之上(鼠标经过自身盒子触发,经过子盒子还会触发)。
onmouseout 鼠标从某元素移开。
onmouseup 鼠标按键被松开。
(2)键盘事件
属性 描述 DOM
onkeydown 某个键盘按键被按下。
onkeypress 某个键盘按键被按下并松开。
onkeyup 某个键盘按键被松开。
(3)框架/对象(Frame/Object)事件
属性 描述 DOM
onabort 图像的加载被中断。 ( <object>)
onbeforeunload 该事件在即将离开页面(刷新或关闭)时触发
onerror 在加载文档或图像时发生错误。 ( <object>, <body>和 <frameset>)
onhashchange 该事件在当前 URL 的锚部分发生修改时触发。
onload 一张页面或一幅图像完成加载。
onpageshow 该事件在用户访问页面时触发
onpagehide 该事件在用户离开当前网页跳转到另外一个页面时触发
onresize 窗口或框架被重新调整大小。
onscroll 当文档被滚动时发生的事件。
onunload 用户退出页面。 ( <body> 和 <frameset>)
(4)表单事件
属性 描述 DOM
onblur 元素失去焦点时触发
onchange 该事件在表单元素的内容改变时触发( <input>, <keygen>, <select>, 和 <textarea>)
onfocus 元素获取焦点时触发
onfocusin 元素即将获取焦点时触发
onfocusout 元素即将失去焦点时触发
oninput 元素获取用户输入时触发
onreset 表单重置时触发
onsearch 用户向搜索域输入文本时触发 ( <input="search">)
onselect 用户选取文本时触发 ( <input> 和 <textarea>)
onsubmit 表单提交时触发
(5)剪贴板事件
属性 描述 DOM
oncopy 该事件在用户拷贝元素内容时触发
oncut 该事件在用户剪切元素内容时触发
onpaste 该事件在用户粘贴元素内容时触发
(6)打印事件
属性 描述 DOM
onafterprint 该事件在页面已经开始打印,或者打印窗口已经关闭时触发
onbeforeprint 该事件在页面即将开始打印时触发
(7)拖动事件
事件 描述 DOM
ondrag 该事件在元素正在拖动时触发
ondragend 该事件在用户完成元素的拖动时触发
ondragenter 该事件在拖动的元素进入放置目标时触发
ondragleave 该事件在拖动元素离开放置目标时触发
ondragover 该事件在拖动元素在放置目标上时触发
ondragstart 该事件在用户开始拖动元素时触发
ondrop 该事件在拖动元素放置在目标区域时触发
(8)多媒体(Media)事件
事件 描述 DOM
onabort 事件在视频/音频(audio/video)终止加载时触发。
oncanplay 事件在用户可以开始播放视频/音频(audio/video)时触发。
oncanplaythrough 事件在视频/音频(audio/video)可以正常播放且无需停顿和缓冲时触发。
ondurationchange 事件在视频/音频(audio/video)的时长发生变化时触发。
onemptied 当期播放列表为空时触发
onended 事件在视频/音频(audio/video)播放结束时触发。
onerror 事件在视频/音频(audio/video)数据加载期间发生错误时触发。
onloadeddata 事件在浏览器加载视频/音频(audio/video)当前帧时触发触发。
onloadedmetadata 事件在指定视频/音频(audio/video)的元数据加载后触发。
onloadstart 事件在浏览器开始寻找指定视频/音频(audio/video)触发。
onpause 事件在视频/音频(audio/video)暂停时触发。
onplay 事件在视频/音频(audio/video)开始播放时触发。
onplaying 事件在视频/音频(audio/video)暂停或者在缓冲后准备重新开始播放时触发。
onprogress 事件在浏览器下载指定的视频/音频(audio/video)时触发。
onratechange 事件在视频/音频(audio/video)的播放速度发送改变时触发。
onseeked 事件在用户重新定位视频/音频(audio/video)的播放位置后触发。
onseeking 事件在用户开始重新定位视频/音频(audio/video)时触发。
onstalled 事件在浏览器获取媒体数据,但媒体数据不可用时触发。
onsuspend 事件在浏览器读取媒体数据中止时触发。
ontimeupdate 事件在当前的播放位置发送改变时触发。
onvolumechange 事件在音量发生改变时触发。
onwaiting 事件在视频由于要播放下一帧而需要缓冲时触发。
(9)动画事件
事件 描述 DOM
animationend 该事件在 CSS 动画结束播放时触发
animationiteration 该事件在 CSS 动画重复播放时触发
animationstart 该事件在 CSS 动画开始播放时触发
(10)过渡事件
事件 描述 DOM
transitionend 该事件在 CSS 完成过渡后触发。
(11)其他事件
事件 描述 DOM
onmessage 该事件通过或者从对象(WebSocket, Web Worker, Event Source 或者子 frame 或父窗口)接收到消息时触发
onmousewheel 已废弃。 使用 onwheel 事件替代
ononline 该事件在浏览器开始在线工作时触发。
onoffline 该事件在浏览器开始离线工作时触发。
onpopstate 该事件在窗口的浏览历史(history 对象)发生改变时触发。
onshow 该事件当 <menu> 元素在上下文菜单显示时触发
onstorage 该事件在 Web Storage(HTML 5 Web 存储)更新时触发
ontoggle 该事件在用户打开或关闭 <details> 元素时触发
onwheel 该事件在鼠标滚轮在元素上下滚动时触发
scrool 页面滚动事件
(12)触屏事件(移动端事件)
touchstart 手指触摸到一个DOM元素时触发
touchmove 手指在一个DOM元素上滑动时触发
touchend 手指在一个DOM元素上移开时触发
触摸事件对象(touchEvent)
touches 正在触摸屏幕的所有手指的一个列表
targetTouches 正在触摸当前DOM元素上的手指的一个列表
changedTouches 手指状态发生了改变的列表,从无到有,从有到无变化
移动端click事件: 移动端click事件会有300ms的延时(移动端双击可以缩放屏幕,点击之后300ms之内没有点击第二次,则判断为点击事件)
解决方法:a. 禁用缩放(视口标签添加 <meta neme = "viewport" content = "user-scalable = no">)
b. 利用头出事件自己封装这个事件解决300ms延迟(手指离开屏幕的时间 - 手指触摸屏幕时的时间,间隔时间小于150ms,且没有滑动屏幕就定义为点击)
鼠标/键盘事件对象
属性 描述 DOM
altKey 返回当事件被触发时,"ALT" 是否被按下。
button 返回当事件被触发时,哪个鼠标按钮被点击。
clientX 返回当事件被触发时,鼠标指针的水平坐标。
clientY 返回当事件被触发时,鼠标指针的垂直坐标。
ctrlKey 返回当事件被触发时,"CTRL" 键是否被按下。
Location 返回按键在设备上的位置
charCode 返回onkeypress事件触发键值的字母代码。
key 在按下按键时返回按键的标识符。
keyCode 返回onkeypress事件触发的键的值的字符代码,或者 onkeydown 或 onkeyup 事件的键的代码。
which 返回onkeypress事件触发的键的值的字符代码,或者 onkeydown 或 onkeyup 事件的键的代码。
metaKey 返回当事件被触发时,"meta" 键是否被按下。
relatedTarget 返回与事件的目标节点相关的节点。
screenX 返回当某个事件被触发时,鼠标指针的水平坐标。
screenY 返回当某个事件被触发时,鼠标指针的垂直坐标。
shiftKey 返回当事件被触发时,"SHIFT" 键是否被按下。
65. 事件绑定(注册事件)
(1) ontype
传统注册方式,兼容性好,但只能绑定一个函数,后面覆盖前面,等同于写在DOM上的attribute
var d = document.getElementById('button');
d.onclick = function(){
console.log('我被点击了,onclick1');
};
d.onclick = function(){
console.log('我被点击了,onclick2'); //只显示我被点击了,onclick2
};
(2)addEventListener()
方法监听注册方式,可以绑定多个函数,但同一函数只能绑定一次,且IE9以下不支持
var d = document.getElementById('button');
d.addEventListener('click', function() {
console.log('我被点击了,onclick1'); //正常显示
});
d.addEventListener('click',function(){
console.log('我被点击了,onclick2'); //正常显示
});
(3)attachEvent()
附加事件方式,可以绑定多个函数,同一函数可以绑定多次,但仅IE9以前浏览器支持
function divclick(){
console.log(我被点击了,onclick1);
}
d.attachEvent('onclick',function(){
divclick.call(d)
});
练习:可以同时兼容IE和Chrome的点击函数
a. 函数的方式:
function addEvent(emlt,type,func){
if(elmt.addEventListener){
elmt.addEventListener(type,func,false);
}else if(emlt.attachEvent){//IE
emlt.attachEvent('on' + type ,function(){
func.call(elmt);
});
}else{
elmt['on'+type] = func;
}
}
function.divclick(){
console.log('我被点击了')
};
addEvent(d,'click',divclick);
b. Element.prototype
Element.prototype.addEvent = function (type,func){
var elmt = this;
if(elmt.addEventListener){
elmt.addEventListener(type,func,false);
} else if(elmt.attachEvent){
elmt.attachEvent('on' + type,function(){
func.call(elmt);
});
} else{
elmt['on' + type] = func;
}
};
function divclick(){
console.log(this);
console.log('我被点击了');
}
66. 事件解绑定
(1)直接删除法:适用于行内直接绑定的事件
<h1 id="h1" onclick="_click();">送你一朵花</h1>
(2)传统解除法
var h1 = document.getElementById('h1');
h1.onclick =function (){
alert('11');
h1.onclick= false; // 或者 h1.onclick= null ;
}
(3)先有绑定函数,再次解除法
先使用 addEventListener 绑定事件
var h1 = document.getElementById('h1');
h1.addEventListener('click',clickx,false);//调用函数不需要小括号
function clickx(){
alert("点击到了");
unclick();
}
再使用 removeEventListener 删除绑定事件
function unclick(){
var h1 = document.getElementById('h1');
h1.removeEventListener('click',clickx,false);
}
(4)d.attachEvent('onclick',fn1);
function fn1 (){
alert(33);
div.attachEvent('onclick',fn1);
}
67. DOM事件流
事件发生时会在元素节点之间按照特定的顺序传播,这个传播过程即DOM事件流
DOM事件流分为三个阶段
a.捕获阶段(事件捕获:网景最早提出,由DOM最顶层节点开始,然后逐级向下传播到最具体元素的接收位置)
b.当前目标阶段
c.冒泡阶段(事件冒泡:最早由IE提出,事件开始由最具体的元素接收,然后逐级向上传播到DOM最顶层节点的过程)
注意: (1) js代码中只能执行捕获或者冒泡其中的一个阶段
(2)onclick 和 attachEvent 只能得到冒泡阶段
(3)addEventListener(type,listenner,true/false)第三个参数如果是true,表示在事件捕获阶段调用事件处理程序;如果是false(不写默认就是false),表示在事件冒泡阶段调用事件处理程序
(4) focus,blur,change,submit,reset,select不冒泡
68. 事件对象
事件对象只有有了事件才会存在,他是系统自动创建的,不需要我们传递参数
div.onclick = function (event){};
console.log(event);
event就是一个事件对象,写到监听函数的小括号里,可以自定义命名(常用:event evt e)(IE8之前不兼容,需window.event)
事件对象是事件一系列相关数据的集合,比如鼠标点击事件里面就包含了鼠标的相关信息(比如:鼠标坐标),键盘事件(判断用户按下了哪个键)
鼠标对象事件:
e.clientX/Y :返回鼠标相对于浏览器窗口可视区的X坐标/Y坐标
e.pageX/Y :返回鼠标相对于文档页面的X/Y坐标 IE+支持
e.screenX/Y:返回鼠标相对于电脑屏幕的X/Y坐标
案例:图片跟随鼠标移动 :
img{
position:absolute;
}
var img = document.QuerySelectoe('img');
document.addEventListener('mousemove',function(e){鼠标移动文档就会触发这个事件
var x = e.pageX;
var y e.pageY;
img.style.left = x - 图片宽度的一半 + 'px'; //鼠标在图片中心位置
img.style.top = y -图片高度得一半 + 'px';
}
键盘事件 :执行顺序 onkeyup--onkeydown--onkeypress
onkeyup : 某个键盘按键被松开时触发 (不区分大小写)
onkeydown : 某个键盘按键被按下时触发 (不区分大小写)
(事件触发的时候,文字还没有落入文本框中)
onkeypress: 某个键盘按键被按下时触发(但不能识别功能键 ctrl shift 箭头等)(区分大小写)
(事件触发的时候,文字还没有落入文本框中)
键盘事件对象 :
keyCode : 返回按键的ASCSLL码值(判断判断用户按下了哪个键)
案例:用户按下s键,输入框自动获得焦点
var search = document.querySelector('input');
document.addEventListener('keydown',function(e){
//console.log(e.keyCode);
if(e.keyCode === 83 ){
search.focus();
}
})
案例:文本框输入内容,弹出一个更大的盒子显示以文本框输入内容相同,但字号更大的内容
var div = document.querySelector('div');
var input = document.querySelector('input');
input.addEventListener('keyup',function(){//input输入内容时触发事件
if(this.value == ''){
div.style.disply = 'none'; //值为空则不显示大盒子
}else{
div.style.disply = 'block';//值不为空则显示
div.innerText = this.value;//文本替换为input的值
}
})
//当文本框失去焦点时,隐藏大盒子
input.addEventListener('blur',function(){
div.style.disply = 'none';
})
//当文本框有焦点且值不为空时,显示大盒子
input.addEventListener('focus',function(){
if(this.value !== ''){
div.style.disply = 'blcok';
}
})
事件对象属性方法:
e.target 返回触发事件对象(标准) //this返回的是绑定事件对象
e.srcElement 返回触发事件对象(非标准,ie6-8使用)
e.stopPropagation() 阻止冒泡(标准)
e.cancelBubble 阻止冒泡(非标准)
e.type 返回事件类型(比如 click mouseover)
e.preventDefault() 阻止默认事件(比如 不让链接跳转,标准)
//return false;但是return后面的代码不执行
e.returnValue 阻止默认事件(比如 不让链接跳转,非标准)
69.事件处理
(1)事件冒泡:
同一事件,子元素的事件触发后再触发父元素
addEventLIstener('click',function(){
console.log('冒泡');
} false);
(2)事件捕获:
同一事件,先触发父元素,再触发子元素(IE没有事件捕获)
addEventLIstener('click',function(){
console.log('捕获');
} true);
(3)触发顺序:
首先捕获,然后时间执行,最后是冒泡
(4)取消事件冒泡:
w3c标准:eventstopPropagation()
IE&Chrome:e.cancelBubble = true
var dg = document.getElementsByClassName('clsg')[0];
var df = document.getElementsByClassName('clsf')[0];
var ds = document.getElementsByClassName('clss')[0];
ds.addEventlistener('click',function(e){
console.log('ds click 冒泡');
e = e || window.event;
if(e.stopPropagation){
e.stopPropagation();//w3c标准
}else{
e.cancelBubble = true;//IE和谷歌
}
},false);
(5)阻止默认事件:
默认事件:表单提交、a标签的跳转、右键菜单
ontype:return false
w3c:event.preventDefault()
IE:event.returnValue = false
禁用鼠标右键方法1:
document.oncontextmenu = function(){
console.log('right click');
return false;
};
禁用鼠标右键方法2:
document.addEventlistener('contextmenu',function(e){
console.log('addEventlistener contextmenu');
e = e || window.event;
if(e.preventDefault){
e.preventDefault();
}else{
e.returnValue = false;
}
},false);
禁用鼠标选中:
document.addEventListener('selectstart',function(e){
e.preventDefault();
})
(6)事件源对象:
事件委托:利用冒泡事件,在父节点上相应事件,而不是在子节点上相应事件
优点:性能更高,灵活,不需要大量操作element
FireFox&Chrome: event.target
IE&Chrome:event.srcElement
var ul = document.getElementById('uid');//子元素li获得点击事件
ul.addEventlistenner('click',function(){
e = e || window.event;
var target = e.target || e.srcElement;
terget.innerHTML = target.innerHTML + 'click';
console.log(target.innerHTML);
console.log(target);
},false);
等价于:
var li = ul.getElementByTagName('li');
for (var i = 0;i < li.length;i++){
li[i].addEventListener('click',function(){
this.innerHTML += 'click';
console.log(this.innerHTML);
},false)
}
70. 事件委托
也称事件代理
事件监听器不再设置在每一个子节点上,而是设置在其父节点上,然后利用冒泡原理影响设置每个子节点
var ul = document.querySeletor('ul');
ul.addEventListener('click',function(e){
e.target.style.backgroundColor = 'pink';//e.target 获取到当前点击的子对象
})
71. js执行机制
同步任务执行完毕再执行异步任务
同步任务:同步任务都在主线程上执行,形成一个执行线
异步任务:js的异步任务是通过回调函数实现的,异步任务相关回调函数添加到任务队列中(也称消息队列)
一般而言,异步任务有以下三种类型:
a.普通事件:如click,resize等
b.资源加载,如load,error等
c.定时器,包括setinteval,setTimeout
72. BOM浏览器对象模型
提供了独立于内容而与浏览器窗口进行交互的对象,window(浏览器窗口对象,成员包括所有的全局变量,函数和对象)
window.name是浏览器下的一个特殊属性
(1)窗口加载事件:
a. widow.onload是窗口(页面)加载事件,当文档内容完全加载完成会触发该事件(包括图像,脚本文件,css文件等),就调用处理函数
widow.onload = function(){ };
或widow.addEventListener('load',function( ){ });
b.document.DOMContentLoaded是DOM加载完毕,不包含图像,脚本文件,css文件等就可以执行,加载速度比load更快
document.addEventListener('DOMContentLoaded',function( ){ });
(2)调整窗口大小事件
widow.onresize是窗口调整大小事件,当触发时就调用处理函数
widow.onresize = function(){};
或widow.addEventListener('resize',function(){});
var w = window.innerWidth //获取浏览器的宽
||document.documentElement.clientWidth
||document.body.clientWidth;
console.log(w);
var h = window.innerHeight //获取浏览器的高
||document.documentElement.clientHeight
||document.body.clientHeight;
console.log(h);
screen:(屏幕对象,通常用于获取用户可用屏幕的宽和高)
var ws = screen.availWidth; //获取屏幕的宽
var hs = screen.availHeight //获取屏幕的高
console.log(ws);
console.log(hs);
location:(位置对象,用于获得当前页面的url地址,还可以吧浏览器重新定向到新的指定页面)
console.log(location.hostname);//访问的(主机)域名
console.log(location.pathname);//路径地址
console.log(location.port);//端口
console.log(location.protocol);//协议
console.log(location.href);//获取或设置整个URL
console.log(location.search);//获取参数
location.href = 'http://www.taobao.com';//跳转链接
location.assign('http://www.taobao.com');
//跳转页面链接,记录浏览历史,可以实现后退功能
location.replace('http://www.taobao.com');
//跳转页面链接,不记录浏览历史,不可以实现后退功能
location.reload();//重新加载页面,刷新
history:(历史记录对象,其中包含了浏览器的浏览历史记录)
history.back();//返回上一个页面
history.forward;//前进一个页面
history.go(-1);//返回上一个页面
history.go(2);//前进2个页面
history.length;//获取当前窗口历史记录个数
navigator:(浏览器对象,通常用于获取用户浏览器的相关信息)
console.log(navigator.appCodeName);//
console.log(navigator.appName);//
console.log(navigator.appVersion);//版本
console.log(navigator.cookieEnabled);//
console.log(navigator.platform);//
console.log(navigator.userAgent);//手机端或oc端信息
console.log(navigator.systemLanguage);//
案例:动画函数封装
function animate(obj,target,time){ //将函数里具体的元素/数值用形参代替,等待不同的实例传参进来
clearInterval(obj.timer); //保证只有一个定时器,每调用一次定时器首先清除上一个定时器(避免连续调用多个定时器,会加速)
obj.timer = setInterval(function(){//优化:obj.timer代替var timer,能避免开辟多个内存空间,浪费资源
if(obj.offsetLeft >= target){
clearInterval(obj.timer);
}
obj.style.left = obj.offset + 1 + 'px';
},time)
}
var div = document.querySelector('div');
btn.addEventListener(function(){
animate(div,400,30);
})
73. 窗口操作
open()新开窗口
close()关闭
moveTo()移动
resizeTo()调整大小
var mywindow = window.open('','','width = 200,height = 100');
mywindow.document.write('我是新开的窗口');
function closewindow(){
mywindow.close();//关闭窗口
}
function movewindow(){
mywindow.moveTo(200,300);//移动窗口到坐标x=200,y=300
mywindow.focus();//获得焦点
}
function resiz ewindow(){
mywindow.resizeTo(500,400);//重置窗口大小
}
跳转到页面窗口特定位置: window.scroll(x,y);
返回顶部: window.scroll(0,0);
74. 弹窗
function clk1(){
window.alert('我是警告框');
}
function clk1(){
var r = prompt('提示文字','输入的内容');//输入框,输入的内容会返回控制台
return r;
}
console.log(r);
function clk1(){
var r = window.confirm('提示文字');//确认对话框,确定或取消,会返回true或false回控制台
return r;
}
console.log(r);
75. offset、client、scroll
(1) offset:元素偏移量,offset相关属性可以动态得到该元素的位置(偏移),大小等
a.获得元素距离带有定位父元素的位置
b.获得元素自身的大小(宽度和高度)
c.返回数值不带单位
element.offsetParent:返回该元素相对带有定位的父元素的偏移量(如果父级元素都没有定位则返回body)
element.offsetTop/Left:返回元素相对带有定位父元素上边框/左边框的偏移量
element.offsetWidth/Height:返回自身包括padding,边框,内容区的宽度/高度,返回数值不带单位
offset和style的区别:
offset可以得到任意样式表中的样式值,style只能得到行内样式表中的样式值
offset获得的数值没有单位,style获得的是带有单位的字符串
offsetWidth包含width + padding + border,style.width不包含padding和border的值
offsetWidth是只读属性,只能获取不能赋值,style.width是可读写属性,可以获取也可以赋值
获取元素位置offset合适,更改元素值需要style
案例:鼠标拖拽移动盒子
var div = document.querySelector('div');
div.addEventListener('mousedown',function(e){ //鼠标在盒子内按下触发事件
var mouseX = e.pageX - div.offsetLeft; //获得鼠标在盒子内的坐标
var mouseY = e.pageY - div.offsetTop; //鼠标在盒子内的坐标 = 鼠标在页面中的坐标 - 盒子在页面中的偏移量
//(2)
document.addEventListener('mouseover',move);
function move (e){ //鼠标移动事件需写在鼠标按下事件的下面,鼠标按下的前提下,当鼠标在页面中移动时触发事件
div.style.left = e.pageX - mouseX + 'px'; //获得盒子在页面中的偏移量
div.style.top = e.pageY - mouseY + 'px'; //盒子在页面内的坐标 = 鼠标在页面中的坐标 - 鼠标在盒子内的坐标
}
//(3)鼠标弹起,就让鼠标移动事件解除
document.addEventListener('mouseup',function(){
document.removeEventListener('mousemove',move);
})
})
(2) client 元素可视区: client相关属性可以动态得到该元素的大小,边框大小等
element.clientTop/Left:返回元素上/左边框大小
element.clientWidth/Height:返回自身包括padding,内容区的宽度/高度(不包括边框),
返回数值不带单位
(3) scroll 元素滚动: scroll相关属性可以动态得到该元素的大小,滚动距离等
element.scrollTop/Left:返回元素被卷去的上侧/左侧距离,
返回数值不带单位(window.pageYOffset:页面被卷去的头部,侧边栏固定)
element.scrollWidth/Height:返回自身(包括padding)实际内容区的宽度/高度(不含边框),
返回数值不带单位
76. 节流阀
当上一个函数动画内容执行完毕,再去执行下一个函数动画,让时间无法连续触发
利用回调函数,添加一个变量来控制,锁住函数和解锁函数.
回调函数:当动画函数执行完毕才会执行
案例:轮播图
// 1. 获取元素
var arrow_l = document.querySelector('.arrow-l');
var arrow_r = document.querySelector('.arrow-r');
var focus = document.querySelector('.focus');
var focusWidth = focus.offsetWidth;
// 2. 鼠标经过focus 就显示隐藏左右按钮
focus.addEventListener('mouseenter', function() {
arrow_l.style.display = 'block';
arrow_r.style.display = 'block';
clearInterval(timer);
timer = null; // 清除定时器变量
});
focus.addEventListener('mouseleave', function() {
arrow_l.style.display = 'none';
arrow_r.style.display = 'none';
timer = setInterval(function() {
//手动调用点击事件
arrow_r.click();
}, 2000);
});
// 3. 动态生成小圆圈 有几张图片,我就生成几个小圆圈
var ul = focus.querySelector('ul');
var ol = focus.querySelector('.circle');
// console.log(ul.children.length);
for (var i = 0; i < ul.children.length; i++) {
// 创建一个小li
var li = document.createElement('li');
// 记录当前小圆圈的索引号 通过自定义属性来做
li.setAttribute('index', i);
// 把小li插入到ol 里面
ol.appendChild(li);
// 4. 小圆圈的排他思想 我们可以直接在生成小圆圈的同时直接绑定点击事件
li.addEventListener('click', function() {
// 干掉所有人 把所有的小li 清除 current 类名
for (var i = 0; i < ol.children.length; i++) {
ol.children[i].className = '';
}
// 留下我自己 当前的小li 设置current 类名
this.className = 'current';
// 5. 点击小圆圈,移动图片 当然移动的是 ul
// ul 的移动距离 小圆圈的索引号 乘以 图片的宽度 注意是负值
// 当我们点击了某个小li 就拿到当前小li 的索引号
var index = this.getAttribute('index');
// 当我们点击了某个小li 就要把这个li 的索引号给 num
num = index;
// 当我们点击了某个小li 就要把这个li 的索引号给 circle
circle = index;
// num = circle = index;
console.log(focusWidth);
console.log(index);
animate(ul, -index * focusWidth);
})
}
// 把ol里面的第一个小li设置类名为 current
ol.children[0].className = 'current';
// 6. 克隆第一张图片(li)放到ul 最后面
var first = ul.children[0].cloneNode(true);
ul.appendChild(first);
// 7. 点击右侧按钮, 图片滚动一张
var num = 0;
// circle 控制小圆圈的播放
var circle = 0;
// flag 节流阀
var flag = true;
arrow_r.addEventListener('click', function() {
if (flag) {
flag = false; // 关闭节流阀
// 如果走到了最后复制的一张图片,此时 我们的ul 要快速复原 left 改为 0
if (num == ul.children.length - 1) {
ul.style.left = 0;
num = 0;
}
num++;
animate(ul, -num * focusWidth, function() {
flag = true; // 打开节流阀
});
// 8. 点击右侧按钮,小圆圈跟随一起变化 可以再声明一个变量控制小圆圈的播放
circle++;
// 如果circle == 4 说明走到最后我们克隆的这张图片了 我们就复原
if (circle == ol.children.length) {
circle = 0;
}
// 调用函数
circleChange();
}
});
// 9. 左侧按钮做法
arrow_l.addEventListener('click', function() {
if (flag) {
flag = false;
if (num == 0) {
num = ul.children.length - 1;
ul.style.left = -num * focusWidth + 'px';
}
num--;
animate(ul, -num * focusWidth, function() {
flag = true;
});
// 点击左侧按钮,小圆圈跟随一起变化 可以再声明一个变量控制小圆圈的播放
circle--;
// 如果circle < 0 说明第一张图片,则小圆圈要改为第4个小圆圈(3)
// if (circle < 0) {
// circle = ol.children.length - 1;
// }
circle = circle < 0 ? ol.children.length - 1 : circle;
// 调用函数
circleChange();
}
});
function circleChange() {
// 先清除其余小圆圈的current类名
for (var i = 0; i < ol.children.length; i++) {
ol.children[i].className = '';
}
// 留下当前的小圆圈的current类名
ol.children[circle].className = 'current';
}
// 10. 自动播放轮播图
var timer = setInterval(function() {
//手动调用点击事件
arrow_r.click();
}, 2000);
})
77. 本地存储
数据存储在用户浏览器中,本地存储只能存储字符串的数据格式
数组和对象必须使用JSON.stringify();方法转换为字符串格式才能进行本地存储,取的时候再用JSON.psrse();方法把字符串转换为原来的对象格
优点:
设置、读取方便,甚至页面刷新不丢失数据
容量较大,sessionStorage可以存储约5M的数据,localStorage可以存储约20M的数据
只能存储字符串,可以将对象JSON.stringify()编码后存储
(1)window.sessionStorage
生命周期为关闭浏览器窗口
在同一个窗口(页面)数据可以共享
以键值对的形式存储使用
存储数据:sessionStorge.setItem(key,value)
获取数据:sessionStorge.getItem(key,value)
删除数据:sessionStorge.removeItem(key,value)
删除所有数据:sessionStorge.clear()
(2)window.localStorage
生命周期永久生效,除非手动删除否则关闭页面也会存在
可以多窗口(页面)共享(同一浏览器可以共享)
以键值对的形式存储使用
存储数据:localStorge.setItem(key,value)
获取数据:localStorge.getItem(key,value)
删除数据:localStorge.removeItem(key,value)
删除所有数据:localStorge.clear()
78. javascript面向对象编程
面向对象的特性:
封装性
继承性
多态性
ES6中的类和对象:
类class(抽象的/泛指): 抽取抽象对象共用的属性和行为组织封装成一个类(模板)
对象(具体的/特指): 对类进行实例化,获取类的对象(对象是由属性和方法来表示)
类constructor构造函数:
constructor ()是类的构造函数(默认方法),用于传递参数,返回实例对象,通过new命令
生成对象实例时,自动调用该方法,如果没有显示定义,类内部会自动给我们创建一个constructor()
注意: 类里的所有函数不需要写function, 多个函数方法之间不需要填写逗号分隔
注意: 在ES6中类没有变量提升,所以必须先定义类,才能通过类实例化对象
类里面的共有属性和方法一定要加this使用
类里面this的指向:constructor方法中this指向实例化的对象,其他函数则是被谁调用了就指向谁
创建类:
class neme {
class body
}
创建实例:
var xx = new name(); // 类必须使用new实例化对象
举例:
class async method_name(){
}tar {
constructor(uname){
this.uname = uname;//谁创建了对象,this指向谁(this指向ldh)
}
sing(){
console.log('我唱歌');
}
}
var ldh = new Star('刘德华');
console.log(ldh.uname)
类的继承: 子类可以继承父类的一些属性和方法
继承中,如果实例化子类输出一个方法,先看子类有没有这个方法,如果有就先执行子类的
继承中,如果子类里没有,就去查找父类有没有这个方法,如果有就执行父类的这个方法
super关键字:用于访问和调用父类对象上的函数,可以调用父类的构造函数,也可以调用父类的普通函数(super必须在子类this之前调用)
class Father{}//父类
class Son extends Father{} //子类继承父类
例子:
class Father{
constructor(x,y){
this.x = x;
this.y = y;
}
money(){
console.log(100);
}
sum(){
console.log(this.x + this.y);
}
}//父类
class Son extends Father{
constructor(x,y){
super(x,y );//调用父类中的构造函数
}
} //子类继承父类
var son = new Son();//子类实例化
son.money();
79 .利用构造函数创建对象
构造函数有两类成员:实例成员和静态成员
实例成员:构造函数内部通过this添加的成员,实例成员只能通过实例化的对象访问,不能通过构造函数访问 //uname,age,sing ,ldh.sing()
静态成员:在构造函数本身上添加的成员,静态成员只能通过构造函数来访问,不能通过
对象访问 //star.sex = ‘男’ ;
function Star (uname,age){ //创建对象
this.uname = uname;
this.age = age;
this.sing = function (){
console.log('我会唱歌');
}
}
var ldh = new Star('刘德华',18) ;//对象实例化
构造函数存在的问题: 构造函数方法很好用,但是存在浪费内存的问题(不同的对象调用同一个函数(sing),会开辟不同的内存空间)
解决方法:构造函数原型 prototype (构造函数通过原型分配的函数是所有对象所共享)
80. 原型(对象)
(1) 每个函数都有一个内置属性,叫做prototype(原型), prototype是对象
可以把不变的方法直接定义在prototype对象上,这样所有对象实例就可以共享这些方法
(2)如果一个对象是通过new产生的,那么这个对象将有缺省的属性,_proto_, 这个属性指向函数的原型prototype
原型的用处:产生的对象将获得prototype的属性和方法
在构造函数中,里面this指向的是对象实例
原型对象里面的this,指向的也是对象实例
(3)对象原型 __proto__ : 对象都会有一个属性__proto__指向函数的 prototype 原型对象,之所以我们对象可以使用构造函数 prototype原型对象 的属性和方法,就是因为对象有__proto__原型的存在 (ldh.__proto__ === Star.prototype)
(4)constructor构造函数:对象原型__proto__和构造函数原型对象prototype里面都有一个属性constructor属性,constructor我们称为构造函数,因为他指向构造函数本身
(5)javascript的成员查找机制(通过原型链查找):
__proto__对象原型的意义就在于为对象成员查找机制提供一个方向,或者说一条路线
当访问一个对象属性方法时,首先查找这个对象自身有没有该属性
如果没有就查找它的原型(也就是__proto__指向的prototype原型对象)
如果还没有就查找它原型对象的原型(Object的原型对象)
依次类推一直找到Object为止(null)
Star.prototype.sing = function(){
console.log('我会唱歌');
}
ldh.sing();
(6)子构造函数继承父构造函数:
Son.prototype = Father.prototype;
这样直接赋值虽然也能继承,但如果修改了子原型对象,父原型对象也会跟着一起变化,方法不可取
正确继承方法:
Son.prototype = new Father();//子原型对象指向父的实例化对象
Son.prototype.constructor = Son;// 利用对象形式修改了子原型对象,
必须让子原型对象的constructor指会原来的原型对象,不然是指向父原型对象
81. 原型链
(1)原型对象:每一个函数在创建时都会被赋予一个prototype属性,它指向函数的原型对象,
这个对象可以包含所有实例共享的属性和函数。
(2)原型链:对象的每个实例都具有一个___proto__属性,指向的是构造函数的原型对象,
而原型对象同样存在一个_proto__属性指向上一级构造函数的原型对象,
就这样层层往上,直到最上层某个原型对象为null。
(3)是null而不是object.prototype
原型链上的所有节点都是对象,不能是字符串、数字、布尔值等原始类型。
规范要求原型链必须是有限长度的。你没法访问null的属性,所以起到了终止原型链的作用;
另一方面,null在某种意义上也是一种对象,即空对象,
因为null一开始就是为表示一个“空”的对象存在的
82. ES5中的新增方法
(1)数组方法 : forEach(), map(), filter(), some(), every();
a. forEach(); 遍历数组
array.forEach(function(每个数组元素Value,数组当前索引index,数组对象本身arr){});
var arr = [12,6,4,88];
sum = 0;
arr.forEach(function(value,index,arr){
sum += value;
console.log(sum);
})
b. map()与forEach类似
c. filter(); 遍历数组,创建一个新数组,主要用于筛选数组,返回一个数组,把所有满足条件的都返回回来
array.filter(function(每个数组元素Value,数组当前索引index,数组对象本身arr){});
var arr = [12,6,4,88];
var newArry = arr.filter(function(value,index){
return value >= 20;
})
console.log(newArry);
d. some(); 遍历数组,用于检测数组中的元素是否满足指定条件,返回布尔值,满足条件就返回true,找不到就返回false,如果找到第一个满足条件的就终止循环,不再继续查找
array.some(function(每个数组元素Value,数组当前索引index,数组对象本身arr){});
var arr = ['pink','yellow','white','red'];
var newArry = arr.some(function(value,index,arr){
return value == 'pink';
})
console.log(newArry);
e. every()与some()类似
(2)字符串方法
a. trim() 从字符串的两端删除空白字符(不会影响原字符串本身,他返回的是一个新的字符串)
str.trim()
(3)对象方法
a. Object.keys() 用于获取对象自身所有的属性(返回一个由属性名组成的数组,效果类似于for...in)
Object.keys(obj)
b. Object.defineProperty() 定义对象中新属性或修改原有属性(以前写法:object.属性名,如:obj.name = '张三';)
Object.defineProperty(obj,prop,descriptor)
obj:修改哪个对象
prop:添加或修改哪个属性
descriptor:以对象形式{}书写,里面有四个选项选择
value:设置属性值,默认为undefined
writable:值是否可以重写,true/false,默认为false,即不允许再修改该属性值
enumberable:目标属性是否可以被枚举,true/false,默认为false,
即不允许再遍历该属性值
confingurable:目标属性是否可以被删除或是否可以再次修改特性,true/false,
默认为false,即不允许再删除该属性值,和修改以上三个特性
83. 函数的定义和调用方式
(1)函数的定义:
a. 自定义函数(命名函数):function fn (){};
b. 函数表达式(匿名函数):var fun = function(){};
c. var fun = new Function('形参1','形参2','函数体');
参数必须是字符串,执行效率低
由此,所有函数都是Function的实例(对象),函数也属于对象
fun();调用函数
(2)函数的调用方式与this指向:
a. 普通函数
function fn(){
console.log('人生的巅峰' + this);
}
调用: fn();或fn.call(); // this指向函数的调用者window
b. 对象的方法
var o = {
sayHi:function(){
console.log('人生的巅峰' + this);
}
}
调用: o.sayHi(); // this指向函数的调用者对象o
c. 构造函数
function Star(){};
调用: var ldh = new Star() // this指向构造函数的实例化对象ldh(原型对象prototype也指向实例化对象)
d. 绑定事件函数
调用: btn.onclick = function(){}; //点击按钮就调用函数,this指向函数的调用者btn按钮对象
e. 定时器函数
调用: setInterval(function(){},1000); / / 定时器自动1秒调用一次,this指向函数的调用者window对象
f. 立即执行函数
调用: (function(){ //自动调用,this指向函数的调用者window对象
console.log('人生巅峰')
}
)();
d. 箭头函数中:this->声明时所在作用域下的this
(3)this
this的意思为“这个;当前”,是一个指针型变量,它动态指向当前函数的运行环境
改变函数内部this指向:call(), apply(), bind()三种方法
call()方法调用一个对象,可以改变函数的this指向,同时可以实现继承
fun.call(thisArg,arg1,arg2,...)
var o = {
name:'andy';
}
function fn(a,b){
console.log(this); //{name: 'andy'}
console.log(a+b); //3
}
fn.call(o,1.2);
继承:
function Father(uname,age,sex){
this.uname = uname;
this.age = age;
this.sex = sex;
}
function Son()(uname,age,sex){
Father.call(this,uname,age,sex);
//调用father的同时this指向father,实现继承father的方法
}
var son = new Son('刘德华',18,'男');
console.log(son);
apply()方法调用一个函数,也可以改变函数的this指向
fun.apply(thisArg,[argsArray])
thisArg:在fun函数运行时指定的this值
argsArray:传递的值,必须包含在数组里面
返回值就是函数的返回值,因为它就是调用函数
apply()主要用于借助数学内置对象求最大最小值:
var arr = [1,66,3,99,4];
var max = Math.max.apply(Math ,arr);
console.log(max);
bind()方法不会调用函数,但是能改变函数内部this指向
fun.bind(thisArg,arg1,arg2,...)
thisArg:在fun函数运行时指定的this值
ar1,ar2:传递的其他参数
返回值由指定的this值和初始化参数改造的原函数拷贝(返回修改后的一个新函数)
适用于,有一个按钮,当我们点击了之后,就禁用这个按钮,3秒之后再开启:
var btn = document.querySelector('button');
btn.onlick = function(){
this.disabled = true; // 绑定事件this指向调用对象,此时指向btn,禁用btn
setTimeout(function(){ //定时器里的this指向window,需要修改this指向
this.disabled = false;
}.bind(this),3000) //给里面不需要立即调用执行的函数绑定bind方法,
bind在定时函数外,此时的this指向btn
}
84. 严格模式
ES5的严格模式采用具有限制性Javascript变体的一种方式,即在严格条件下运行JS代码,严格模式在IE10以上的浏览器才支持,旧版本依然使用正常模式
为整个脚本文件开启严格模式,需要在所有语句之前放一个特定语句'use strict';
严格模式对正常的Javascript语意做了一些更改:
(1)消除了Javascript语意的一些不合理,不严谨之处,减少了一些怪异行为(比如,未声明变量赋值使用)
(2)消除代码运行的一些不安全之处,保证代码运行的安全
(3)提高编译器效率,增加运行速度
(4)禁用了在ECMAScript的未来版本中可能会定义的一些语法,为未来新版本的Javascript做好铺垫,比如一些保留字如:class,enumexport,extends,import,super不能做变量名
严格模式中的变化:
(1)变量名必须先声明再使用
(2)不能随意删除已经声明好的变量
(3)严格模式下this的指向问题:
a.在严格模式下全局作用域中的函数this指向由window变为undefined
b.在严格模式下构造函数,如果不加new来调用,this指向会报错,加new调用一样还是指向实例化对象(以前构造函数不加new调用,当普通函数,this指向全局对象)
c.在严格模式下定时器this还是指向window
d.在严格模式下事件,对象还是指向调用者
(4)函数变化:
a.在严格模式下事件,函数里面参数不允许重名
b.函数必须声明在顶层,ES6中引入块级作用域,不允许在非函数的代码快内声明函
85. 高阶函数
对其他函数进行操作,他接受其他函数作为参数或将函数作为返回值输出
function fn (callback) {
callback&&callback();
}
fn(function(){alert('Hi)});
function fn () {
return function (){}
}
fn();
86. 浅拷贝和深拷贝
浅拷贝只拷贝一层,更深层次对象级别只拷贝引用
深拷贝拷贝多层,每一级别的数据都会拷贝
var obj = {
id:1,
name:'andy',
color:['red','pink']
}
var o = {};
for in循环实现浅拷贝(拷贝复杂数据类型时,修改新对象的值,被拷贝对象的值也会被修改)
for(k in obj){
o[k] = obj[k]
}
console.log(o);
封装函数实现深拷贝(拷贝复杂数据类型时,修改新对象的值,被拷贝对象的值不会受影响)
function deepCopy (newObj,oldObj){
for(k in oldObj){ //遍历对象
var item = oldObj[k]; //获取对象的属性值
if(item instanceof Arry){ //判断对象是否是数组
newObj[k] = []; //
deepCopy(newObj[k],item)
} else if(item instanceof Object){ //判断对象是否是对象
newObj[k] = []; //
deepCopy(newObj[k],item)
} else { //普通数据类型则直接赋值
newObj[k] = item;
}
}
}
deepCopy(o,obj) ;
87.ES6新增语法
(1) let 声明的变量具有块级作用域,只在所处于的块级有效,不存在变量提升,只能先声明再使用, //一对大括号内部就是一个块级作用域(var 声明的变量没有块级作用域,是函数级作用域)
使用let关键字声明的变量具有暂时性死区特性 //使用let声明变量后,变量就和当前块级作用域绑定,和块级作用域外部的同名变量没有联系
var num = 10;
if(true){
console.log(num);// 报错,因为使用了let关键字声明变量,
此块级作用域中出现暂时性死区,无法向上一级查找变量
let num = 20;
}
(2)const 声明常量,常量就是值(内存地址),不能变化的量,const声明的常量具有块级作用域,在块级作用域之外无法使用
const声明常量时必须赋初始值,否则会报错,赋值后,简单数据类型的值不能修改 ,复杂数据类型值对应的内存地址不能修改(值可以更改)
var、const、let对比
(1)var是ES5提出的,let和const是ES6提出的。
(2)var声明的变量存在变量提升现象,let和const声明的变量不存在变量提升现象
(遵循:“先声明,后使用”的原则,否则会报错)。
(3)var允许重复声明同一个变量,let和const在同一作用域下不允许重复声明同一个变量。
(4)var声明的变量不存在块级作用域(是函数级作用域),let和const声明的变量存在块级作用域
并且声明的变量只在所声明的块级作用域内有效。
(5)var不存在暂时性死区,let和const存在暂时性死区。
(let/const 命令会使区块形成封闭的作用域。若在声明之前使用变量,就会报错。
总之,在代码块内,使用 let 命令声明变量之前,该变量都是不可用的)
(6)var声明的变量在window上,而let和const不在
ES6新特性
(1)新增关键字 let const
(2)箭头函数:this 是静态的,始终指向函数声明所在作用域下的this的值
(3)函数默认参数值(function sum(a,b=5){ return a+b})
(4) 运算符…(分散、合并)
(5)对象词法扩展(简写变量,忽略function关键字)
(6)支持二进制,八进制字面量
(7)解构赋值
(8)对象超类
(9)模板语法
(10)for 0f(遍历值) 和for in(遍历键)
(11)Map 和WeakMap 一个对象由多个 key-val 对构成,在 Map 中,
任何类型都可以作为对象的 key()
(12)set 和SetMap :Set对象是一组不重复的值,重复的值将被忽略,
值类型可以是原始类型和引用类型:
(13)类
(14)Symbol:新数据类型,他的值唯一,不可变
(15)迭代器iterator
(16)Gnerators
(17)Promise
(18)模块:export import
88. 解构赋值
按照一定的模式,从数组或对象中取值,将提取出来的值赋值给另外的变量
(1)数组解构:ES6允许我们按照一 一对应的关系从数组中取值,然后将数组赋值给变量
let arr = [1,2,3];
let [a,b,c,d] = arr;
console.log(b); // 2
console.log(d); //未找到则返回 undefined
(2)对象解构:允许我们使用变量的名字匹配对象的属性,匹配成功将对象属性赋值给变量
(a) let person = {neme:'张三',age:20};
let [name,age] = person;
console.log(name); // 张三
console.log(age); // 20
(b) let person = {neme:'张三',age:20};
let [name:myname,age:myage] = person;
console.log(myname); // 张三
console.log(myage); // 20
89. 箭头函数:ES6中新增的定义函数的方式
箭头函数和普通函数区别
(1)箭头函数都是匿名函数
(2)箭头函数不能用于构造函数,不能使用new
(3)this的指向不同:在普通函数中,this总是指向调用它的对象,
如果用作构造函数,this指向创建的对象实例。
箭头函数中的this是静态的,this的指向永远是声明时所在作用域下this的值,
它并没有自己的this,call apply bind也没有办法改变this的指向
(4)箭头函数不具有prototype原型对象。
(5)箭头函数不绑定arguments,取而代之用rest参数…解决
(1) () => {}
const fn = () => { // 将函数赋值给一个变量
// 函数体
console.log()
}
fn(); //调用箭头函数
(2)若函数体中只有一句代码,且代码执行结果就是返回值,可以省略大括号
const fn = (num1,num2) => num1 + num2;
(3)在箭头函数中,如果形参只有一个,形参外侧的小括号也可以省略
const fn = num => {
alert(num);
}
fn(20);
(4)箭头函数不绑定this关键字,箭头函数中的this,指向的是函数定义位置的上下文this
function fn(){
console.log(this);
return () => {
console.log(this); // 箭头匿名函数中的this不是指向window,
而是指向函数定义fn位置中的this obj
}
}
const obj = {name:'张三'}
fn.call(obj); // 通过call()方法调用函数改变this指向为obj
案例题:
var age = 100;
var obj = {
age:20,
say:() => {
alert(this.age);// 100,箭头函数定义在obj对象中,对象中没有作用域,
因此此箭头函数定义在全局作用域中,this指向全局
}
}
obj.say();
90. 剩余参数
允许我们将一个不定数量的参数表示为一个数组
const num = (first,...args) => { 在箭头函数中不能使用arguments接受所有实参参数,实参的数量多余形参的数量,在形参前面加...表示剩余的实参都由它以数组的形式接收
案例:
const sum = (...args) => {
let total = 0;
args.forEach(item => total += item);
return total;
}
console.log(sum(10,20));//30 ;
91. 剩余参数和解构配合使用
let students = ['张三','李四','王二'];
let [s1,...s2] = students;
console.log(s1);//张三
console.log(s2);//['李四’,‘王二]’
92. ES6内置对象扩展
(1)Array的扩展方法
a. 扩展运算符(展开语法):在数组或对象的前面加...,可以将数组或对象转为用逗号分隔的参数序列
let ary = [1,2,3];
...ary // 1,2,3
console.log(...ary); //1 2 3,输出时逗号被当做log的参数分隔符,因此不显示
扩展运算符可以运用于合并数组
方法1:
let ary1 = [1,2,3];
let ary2 = [3,4,5];
let ary3 = [...ary1,...ary2];//[1,2,3,3,4,5]
方法2:
let ary1 = [1,2,3];
let ary2 = [3,4,5];
arr1.push(...arr2);//[1,2,3,3,4,5]
将类数组或可遍历对象转换为真正的数组(方便使用数组的方法)
let oDivs = document.getElementsByTagName('div');
oDivs = [..oDivs];
b. find()方法,用于找出第一个符合条件的数组成员,如果没有找回返回undefined
let ary = [{
id:1,
name:'张三'
},
{
id:2,
name:'李四'
}]
let target = ary.find(item => item.id == 2);
console.log(target); //{id:2, name:'李四' }
c. findIndex()方法,用于找出第一个符合条件的数组成员的位置的索引号,如果没有找回返回-1
let ary = [10,20,30]
let index = ary.findIndex(item => item > 15);
console.log(index); //1
d. includs()方法,表示某个数组是否包含给定的值,返回布尔值
[1,2,3].includs(2);//true
[1,2,3].includs(4);//false
(2) 构造函数方法:Array.from()
a. 将类数组或可遍历对象转换为真正的数组(方便使用数组的方法)
let arrayLike = {
'0':'a',
'1':'b',
'2':'c',
length:3
}
let arr = Array.from(arrayLike);*//['a','b','c']
b. Array.from()方法还可以接收第二个参数,作用类似于数组的map方法,用来对每个元素进行处理,将处理后的值放入返回的数组
let arrayLike = {
'0':'1',
'1':'2',
'2':'3',
length:3
}
let newArray = Array.from(arrayLike,item => item*2);*//['2','4','6']
(3)String的扩展方法
a. 模板字符串:${} 可以解析变量,可以换行(美观)
let name = '张三';
let sayHello = 'hello,my name is ${name}';
console.log(sayhello); // hello,my name is 张三
let result = {
name:'张三',
age:20
};
let html = '
<div>
<span>${result.name}</span>
<span>${result.age}</span>
</div>
';
document.write(html);
在模板字符串中可以调用函数
const sayHello = function(){
return '来追我阿';
}
let greet = '${sayHello()}哈哈哈';
console.log(greet);
b. startsWith()方法和 endssWith()方法
startsWith():表示参数字符串是否在原字符串的头部,返回布尔值
endsWith():表示参数字符串是否在原字符串的尾,返回布尔值
let str = 'hello world';
str.startsWith('hello') //true
str.endsWith('!') //false
c. repaet()方法,表示将原字符串重复n次,返回一个新字符串
console.log('y'.repeat(2));//yy
93. Set数据结构
ES6提供的新的数据结构,它类似于数组,但是成员的值都是唯一的,没有重复的值
Set本身是一个构造函数,用来生成Set数据结构
const s = new Set();
Set函数可以接受一个数组作为参数,用来初始化.
const set = new Set ([1,2,3,4,4]);
console.log(set.size);//4
(1)Set可以用来进行数组去重
const set = new Set (['a','a','b','b']);
const arry =[...set] ;
console.log(arry); //['a','b']
(2)Set实例方法:
add(value):添加某个值,返回Set结构本身
delete(value):删除某个值,返回一个布尔值,表示删除是否成功
has(value):返回一个布尔值,表示该值是否为Set成员
clear():清除所有成员,没有返回值
const s = new Set ();
s.add(1).add(2).add(3);//123
s.delete(2);//true 13
s.has(1);//true
s.clear();//
(3)遍历Set数据结构,从中取值
s.forEach(value => console.log(value))
const s5 = new Set(['a','b','c']);
s5.forEach(value => console.log(value))
94. 防抖节流
节流是为了限制函数的执行次数,而防抖是为了限制函数的执行时机。
防抖:在事件被触发n秒后再执行回调,如果在这n秒内又被触发,则重新计时,重新出发定时器。search搜索联想,用户在不断输入值时,用防抖来节约请求资源。
function debounce(fn,denay,immediately=false){
let timer =null;// //使用闭包,缓存变量
var this=_this;
const args=arguments;
return function(){
if(timer) clearInterval(timer);
if(immediately){
let callNow=!timer;
timer=setTimeout(()=>{
timer=null;
},denay);
if(callNow){
fn.apply(this,args)
}
}else{
timer=setTimeout(()=>{
fn.apply(this,args);
},denay)
}
}
}
节流:规定在一个单位时间内,只能触发一次函数。如果这个单位时间内触发多次函数,只有一次生效。鼠标不断点击触发,mousedown(单位时间内只触发一次)监听滚动事件,比如是否滑到底部自动加载更多,用throttle来判断
function throttle(fn,denay,immediately=false){
let timer=0;//使用闭包,缓存变量
const args=arguments;
var this=_this
return function(){
if(!timer){
time=Date.now();
if(immediately){
fn.apply(_this,args)
}
return ;
}
if(Date.now()-time>denay){
fn.apply(this,args);
time=Date.now();
}
}
}
95. promise
(1)promise是为解决异步处理回调金字塔问题而产生的
(2)两个特点:Promise状态不受外界影响,(pending,fulfilled,rejuected);Promise状态一旦改变,就不会再变
(3)三个缺点:
a.无法取消Promise,一旦新建它就会立即执行,无法中途取消;
b.如果不设置回调函数,Promise内部抛出的错误,不会反映到外部
c.当处于pending状态时,无法得知目前进展到哪一个阶段,是刚刚开始还是即将完成
(4)Promise在哪存放成功回调序列和失败回调序列?
a .onResolvedCallbacks 成功后要执行的回调序列 是一个数组
b. onRejectedCallbacks 失败后要执行的回调序列 是一个数组
96. EventLoop 事件循环
JS是单线程的,为了防止一个函数执行时间过长阻塞后面的代码,所以会先将同步代码压入执行栈中,依次执行,将异步代码推入异步队列,异步队列又分为宏任务队列和微任务队列,因为宏任务队列的执行时间较长,所以微任务队列要优先于宏任务队列。
宏任务:setTimeout setInterval I/O UI渲染 setimmediate
微任务:process.nextick promise MutaionObserver
process.nextick会再下次事件循环的开始前被调用;而setTimeout 和 setInterval是在下一次事件循环队列中
GUI线程就是渲染页面的,他解析HTML和CSS,然后将他们构建成DOM树和渲染树就是这个线程负责的。
JS引擎线程就是负责执行JS的主线程,前面说的"JS是单线程的"就是指的这个线程。Chrome V8引擎就是在这个线程运行的。需要注意的是,这个线程跟GUI线程是互斥的。