JS中的数组类型转换汇总
[基本数据类型]
- number 数字
- string 字符串
- boolean 布尔
- null 空
- undefined 未定义
[引用数据类型]
- object类型
- 普通对象
- 数组对象 (Array)
- 正则对象 (RegExp)
- 数学对象 (Math)
- 日期函数 (Date)
- function 函数
把其它数据类型转换为number类型
1、发生的情况
- isNaN检测的时候:当检测的值不是数字类型,浏览器会自己调用Number方法把它先转换为数字,然后再检测是否为非有效数字
isNaN('3')=>false
Number('3')->3
isNaN(3)->false
- 基于parseInt/parseFloat/Number去手动转换为数字类型
- 数学运算:+ - * / %,但是"+"不仅仅是数学运算,还可能是字符串拼接
'3'-1 =>2
Number('3')->3
3-1=2
'3px'-1 =>NaN
'3px'+1 =>'3px1' 字符串拼接
var i='3';
i=i+1; =>'31'
i+=1; =>'31'
i++; =>4 i++就是单纯的数学运算,已经摒弃掉字符串拼接的规则
- 在基于"=="比较的时候,有时候也会把其它值转换为数字类型
2、转换规律
//=>转换为数字类型的时候浏览器默认都是用Number()方法
【把字符串转换为数字】
只要遇到一个非有效数字字符,结果就是NaN
''->0
' '->0(空字符串里面是空格)
'\n' ->0 换行符(Enter)
'\t' ->0 制表符(Tab)
【把布尔转换为数字】
true ->1
false ->0
【把没有转换为数字】
null ->0
undefined ->NaN
【把引用类型值转换为数字】
首先都先转换为字符串(toString),然后再转换为数字(Number)
把其他类型值转换为字符串
1、发生的情况
- 基于alert/confirm/prompt/document.write等方法输出内容的时候,会把输出的值转换为字符串,然后再输出
alert(1) =>'1'
- 基于"+"进行字符串拼接的时候
- 把引用类型值转换为数字的时候,首先会转换为字符串,然后再转换为数字
- 给对象设置属性名,如果不是字符串,首先转换为字符串,然后再当做属性存储到对象中(对象的属性只能是数字或者字符串)
- 手动调用toString/toFixed/join/String等方法的时候,也是为了转换为字符串
var a=Math.PI;
Math.PI.toFixed(2) ->'3.14'
var ary=[1,45,2,34,34];
ary.join("+")
'1+45+2+34+34';//返回值,原有数组不变
2、转化规律
//=>调用toString方法
【除了对象,都是你理解的转换结果】
1 ->'1'
NaN ->'NaN'
null ->null
[] ->''
[12] ->'12'
[12,23] ->'12,23'
【对象】
{name:"zhufneg",age:9} ->'[object Object]'
{} ->'[object Object]'
把其它值转换为布尔类型
1、发生的情况
- 基于!/!!/Boolean等方法转换
- 条件判断中的条件最后都会转换为布尔类型
- ...
if(n){
//=>把n的值转换为布尔验证条件真假
}
if('3px'+3){
//=>先计算表达式的结果'3px3',把结果转换为布尔true,条件成立
}
2、转换的规律
只有0/NaN/""/null/undefined五个值转换为布尔的false,其余都是转换为true
特殊情况:数学运算和字符串拼接
//=>当表达式中出现字符串,就是字符串拼接,否则就是数学运算
1+true //=>2 数学运算
'1'+true //=>'1true' 字符串拼接
[12]+10 //=>'1210' 虽然现在没看见字符串,但是引用类型转换为数字,首先会转换为字符串,所以变为了字符串拼接
({})+10 //=>'[object Object]10'
[]+10 //=>'10'
{}+10 =>10
{} 代表一个代码块(块级作用域)
+10 才是我们的操作
严格写法:
{};
+10;(+10就相当于0+10)
思考题
12+true+false+null+undefined+[]+'wy'+null+undefined+[]+true //=>'NaNwynullundefinedtrue'
特殊情况:"=="在进行比较的时候,如果左右两边的数据类型不一样,则先转换为相同的类型,再进行比较
对象==对象:不一定相等,因为对象操作的是引用地址,地址不相同,则不相等
{name:"xxx"}=={name:"xxx"}
[]==[] =>false
var obj1={};
var obj2=obj1;
obj1=obj2 =>true
对象==数字:把对象转换为数字,然后再比较
对象==布尔:把对象转换为数字,把布尔也转换为数字
对象==字符串:把对象转换为字符串
[]=='' //->true
[12,23]=='12,23' -//>true,如果按照下面的规律对象和字符串进行比较的时候转换为数字,两边都为NaN,那么返回的结果应该是false
字符串==数字:字符串转换为数字 字符串==布尔:都转换为数字 布尔==数字:把布尔转换为数字
规律:
1、不同情况的比较,都是把其它值转换为数字,然后再进行比较的
2、对象和对象比较的时候,比较的是地址
3、null==undefined //->true
4、null === undefined ->false
5、null、undefined和其它值都不相等
6、null==null
7、undefined==undefined
8、null===null
9、undefined===undefined
1==true =>true
1==false =>false
2==true =>false
[]==true:false 都转换为数字 0==1
![]==true:false
[]==false: true 都转换为数字 0==0
![]==false: true ![]把数组转换为布尔类型再取反 false==false
关于JS数组常用方法的剖析
数组也是对象数据类型的,也是由键值对组成的
var ary = [12,23,34];
/*
*结构:
* 0:12
* 1:23
* 2:34
* length:3
*/
1.以数字作为索引(属性名),索引从零开始增加
2.有一个length属性存储的是数组长度
ary[0] 获取第一项
ary[ary.length-1] 获取最后一项
数组中每一项的值可以是任何数据类型的
//=>多维数组
var ary=[
{
name:'xxx',
age:20
}
{
name:'xxx',
age:20
}
]
数组的常用方法
按照四个维度记忆:
- 方法的作用
- 方法的参数
- 方法的返回值
- 原有数组是否改变
push
- 语法:ary.push()
- 作用:向数组末尾追加新的内容
- 参数:追加的内容,可以是一个或多个,可以是各种数据类型
- 返回值:新增后数组的长度
- 原有数组改变
pop
- 语法:ary.pop()
- 作用:删除数组末尾的最后一项
- 参数:无
- 返回值:被删除的那一项内容
- 原有数组发生改变
shift
- 语法:ary.shift()
- 语法:删除数组中的第一项
- 参数:无
- 返回值:被删除的那一项内容
- 原有数组发生改变
unshift
- 语法:ary.unshift()
- 作用:向数组开始位置追加新的内容
- 参数:追加的内容,可以是一个或多个,可以是各种数据类型
- 返回值:新增后数组的长度
- 原有数组改变
splice
删除
- 语法:ary.splice(n,m)
- 作用:从索引n开始,删除m个内容
- 参数:索引 删除项数
- 返回值:删除的部分用新数组返回
- 原有数组发生改变
增加
- 语法:ary.splice(n,0,x)
- 作用:从索引n开始删除零项(没删除),把x或者更多需要插入的内容放到数组中索引N的"前面"
- 参数:索引 删除项数 插入内容
- 返回值:删除的部分用新数组返回
- 原有数组发生改变
修改
- 语法:ary.splice(n,m,x)
- 作用:把原有内容删除掉,然后用新的内容替换这部分信息即可
- 参数:索引 删除项数 插入内容
- 返回值:删除的部分用新数组返回
- 原有数组发生改变
以上所有方法总结
1、在最前面追加内容的方法
- ary.unshift(参数)
- ary.splice(0,0,参数)
2、在末尾追加内容的方法
- ary.push(参数)
- ary.splice(ary.length,0,参数)
- ary[ary.length]=参数
3、删除数组中第一项的方法
- ary.shift()
- ary.splice(0,1)
4、删除数组中最后一项的方法
- ary.pop()
- ary.splice(ary.length-1)
- ary.length--
slice
- 语法:ary.slice(n,m)
- 作用:从索引n截取到索引m处(不包含m)
- 返回值:以新数组存储截取的内容
- 原有数组不会发生改变
concat
- 语法:ary.concat()
- 作用:实现多个数组(或者值)的拼接
- 参数:数组或者值
- 返回:拼接后的新数组
- 原有数组不变
toString
- 语法:ary.toString()
- 作用:把数组转换为字符串
- 参数:无
- 返回:数组中的每一项用逗号分隔的字符串
- 原有数组不变
join
- 语法:ary.join()
- 作用:和toString类似,也是把数组转换为字符串,但是我们可以设置变为字符串后,每一项之间的连接符
- 参数:指定的链接符返回
- 原有数组不变
reverse
- 语法:ary.reverse()
- 作用:把数组倒过来排列
- 参数:无
- 返回:排列后的新数组
- 原有数组改变
sort
- 语法:ary.sort()
- 作用:给数组排序
- 参数:无或者回调函数
- 返回:排序后的新数组
- 原有数组改变
不传递参数的情况下:可以给10以内的数字进行升序排列,但是超过10的就无法处理了(多位数只识别第一位,按照第一位的大小进行排列)
升序:ary.sort(function(a,b){return a-b;})
降序:ary.sort(function(a,b){return b-a;})
验证数组中是否包含某一项
indexOf && lastIndex0f
这两个方法不兼容IE6-8
- 语法:ary.indexof(要检测的值)
- 作用:获取当前项在数组中第一次或者最后一次出现位置的索引
- 参数:要检测的值
- 返回值:被检测项的索引
- 原有数组不变
基于indexOf检测,如果数组中有这一项,返回一个大于等于零的索引 如果没有这一项,返回的索引为-1
//=>验证数组中是否包含某一项
if(ary.indexOf(100)>-1){
//=>ARY中包含100这一项
}
案例:数组去重
方案一 indexOf方法
Array.prototype.myUnique=function(){
for(let i=0;i<this.length-1;i++){
let item=this[i],
curary=this.slice(i+1);
if(curary.indexOf(item)>-1){
this[i]=this[this.length-1];
this.length--;
i--;
}
}
}
方案二 利用对象属性名的唯一性
Array.prototype.myUnique=function(){
let obj={};
for(var i=0;i<this.length;i++){
if(typeof obj[this[i]]!=='undefined'){
this[i]=this[this.length-1];
this.length--;
i--;
continues;
}
obj[this[i]]=this[i];
}
obj=null;
}
方案三 利用双循环
Array.prototype.myUnique=function(){
for(var i=0;i<this.length-1;i++;){
var item=this[i];
for(var j=i+1;j<this.length;j++){
if(item===this[j]){
this[i]=this[this.length-1];
this.length--;
j--;
}
}
}
}
方案四 排序后相邻去除法
Array.prototype.myUnique=function(){
let ary=[];
let _this=[...this];
for(let i=0;i<_this.length;i++){
let item=_this[i],
nextItem=_this[i+1];
if(item!==nextItem[i+1]){
ary.push(item);
}
}
return ary;
}
map
- 语法: ary.map(function(item,index){})
- 参数:函数
- 作用: 遍历数组中的每一项,每遍历一次执行一次函数,并把当前项替换成函数执行的返回值,如果函数没有返回值,或者return后面没有值,那么返回的是undefined,统一放在一个新数组中返回
- 返回值:替换后的新数组
- 原有数组不变
let ary=[12,23,34,25,36,47];
let curary=ary.map(function(item,index){
return item+1;
});
console.log(curary);//=>[13,24,35,26,37,48]
console.log(ary);//=>[12,23,34,25,36,47]
----------------
let ary=[12,23,34,25,36,47];
let curary=ary.map(function(item,index){
});
console.log(curary);//=>[undefined,undefined,undefined,undefined,undefined,undefined]
重写map方法
Array.prototype.myMap = function myMap(callback, context) {
var ary = [];
if([].map){
return this.map(callback,context);
}
for(var i=0;i<this.length;i++){
var res = callback.call(context,this[i],i);
ary.push(res);
}
return ary;
}
filter
- 语法:ary.filter(function(item,index){})
- 参数:函数
- 作用:过滤数组,把符合条件的当前项放到新的数组中
- 返回值:符合条件的新数组
- 原有数组不变
let ary=[12,23,34,25,36,47];
let curary=ary.filter((item,index)=>{
return item>20&&item<40;
})
console.log(curary);//=>[23,34,25,36]
find
- 语法:ary.find(function(item,index){})
- 参数:回调函数
- 作用:过滤数组,把第一次符合条件的当前项返回
- 返回值:第一次符合条件的那一项
- 原有数组不变
let ary=[1,2,3,4,5];
let a=ary.find((item)=>{
return item>1;
});
console.log(a);//=>2
console.log(ary);//=>[1,2,3,4,5]
findIndex
- 语法:ary.findIndex(function(item,index){})
- 参数:回调函数
- 作用:过滤数组,返回第一次符合条件的当前项对应的索引
- 返回值:第一次符合条件的当前项的索引
- 原有数组不变
let ary=[1,2,3,4,5];
let a=ary.find((item)=>{
return item>1;
});
console.log(a);//=>1
console.log(ary);//=>[1,2,3,4,5]
some
- 语法:ary.some(function(item,index){});
- 参数:回调函数
- 作用:数组中有几项,回调函数执行几次,直到回调函数中return结果为true时,停止遍历
- 返回值:true 或者false,看return后面的值是什么(最终会被转换为布尔类型)
- 原有数组不变
let ary=[1,2,"string",NaN,3,4];
let a=ary.some((item)=>{
console.log(item);//=>1,2,string
return typeof item ==="string";
});
console.log(a);//=>true
---------------
let ary=[1,2,"string",NaN,3,4];
let a=ary.some((item)=>{
console.log(item);//=>1,2,string,NaN,3,4
return 0;
});
console.log(a);//=>false,因为Boolean(0)=>false
every
- 语法:ary.every((item,index)=>{});
- 参数:回调函数
- 作用:遍历数组中的每一项,每遍历一次,就执行一次回调函数,只要遇到回调函数返回值的结果为false立马停止遍历
- 返回值:true 或者false
- 原有数组不变
let ary=[1,2,NaN,3,4];
let a=ary.every((item)=>{
console.log(item);//=>1,2,NaN,3,4
return typeof item==="number";
});
console.log(a);//=>true
------------
let ary=[1,2,"string",NaN,3,4];
let a=ary.every((item)=>{
console.log(item);//=>1,2,string
return typeof item==="number";
});
console.log(a);//=>false
reduce
- 语法:ary.reduce((prev,item)=>{},init);
- 参数:回调函数(第一个参数是前一次回调函数的返回结构,第二项是当前项),init代表了传递给函数的初始值
- 作用:作为累加器,数组中的每个值(从左到右开始缩减),最终计算为一个值
- 返回值:最终得到的结果
- 原有数组不变
let ary=[1,1,1,1,1,,1,1,1,1,1];
let a=ary.reduce((prev,item)=>{
console.log(prev, item);
return prev-item;
},10);
console.log(a);//=>0,遇到空位直接跳过
console.log(ary);//=>[1,1,1,1,1,,1,1,1,1,1];
reduceRight
- 语法:ary.reduceRight((prev,item)=>{},init)
- 参数:回调函数(第一个参数是前一次回调函数的返回结构,第二项是当前项),init代表了传递给函数的初始值
- 作用:作为累加器,数组中的每个值(从右到左开始缩减),最终计算为一个值
- 返回值:最终得到的结果
- 原有数组不变
let ary=[-1,1,-1,1];
let b=ary.reduceRight((prev,item)=>{
console.log(prev, item);
return prev*item;//=>10*1*-1*1*-1
},10);
console.log(b);//=>10
JS中关于字符串的一些细节知识
在JS中所有用单引号或者双引号包起来的都是字符串,每一个字符串是由零到多个字符组成
var str = 'zhufengpeixun';
str.length ->字符串长度
str[0] ->'z'
str[str.length-1] ->'n'
str[100] ->undefined
//=>字符串中的每一个字符,都有一个自己对应的索引,也有类似于数组一样的length代表自己的长度
//=>循环遍历字符串,输出每一项字符
for(var i=0;i<str.length;i++){
console.log(str[i]);
}
字符串的常用方法
字符串是基本数据类型,字符串的每一次操作都是值直接的进行操作,不像数组一样是基于空间地址来操作的,所以不存在原有字符串是否改变这一说,肯定都是不变的
charAt && charCodeAt
- 语法:str.charAt(索引)
- 作用:charAt根据索引获取指定位置的字符,charCodeAt不仅仅获取字符,它获取的是字符对应的Unicode编码值(ASCII)
- 参数:索引
- 返回值:字符或者对应的编码


indexOf && lastIndexOf
基于这两个方法,可以获取字符在字符串中第一次或者最后一次出现位置的索引,有这个字符,返回大于等于零的索引,不包含这个字符,返回的结果是-1,所以可以基于这两个方法,验证当前字符串中是否包含某个字符
var str = 'zhufengpeixun';
if(str.indexof('@')>-1){
//=>条件成立说明包含@符号
}
slice
- 语法:str.slice(n,m)
- 作用:str.slice(n,m)从索引n开始找到索引为m处(不包含m),把找到的字符当做新字符串返回
- 返回值:截取的字符串
substring
- 语法:str.substring(n,m)
- 作用:str.substring(n,m)从索引n开始找到索引为m处(不包含m),把找到的字符当做新字符串返回,
不支持负数作为索引 - 返回值:截取的字符串
substr
- 语法:str.substr(n,m)
- 作用:从索引n开始截取m个字符
toUpperCase && toLowerCase
- 语法:str.toUpperCase()/str.toLowerCase()
- 作用:实现字母的大小写转换
- toUpperCase 小写转化为大写
- toLowerCase 大写转换为小写
- 参数:无
split
- 语法:str.split()
- 作用:和数组中的join相对应,数组中的join是把数组每一项按照指定的连接符变为字符串,而split是把字符串按照指定的分隔符,拆分成数组中每一项

replace
- 语法:str.replace(原有字符,要替换的新字符)
- 作用:替换字符串中的原有字符
- 返回:替换后的字符串
真实项目中的需求
1.时间字符串格式化
需求:有一个时间字符串"2018-4-4 16:26:8".我们想基于这个字符串获取到"04月04日 16时26分"
var str="2018-4-4 16:26:8",
ary=str.split(' '),
aryLeft=ary[0],
aryRight=ary[1];
month=aryLeft.split('-')[1],
day=aryLeft.split('-')[2],
hour=aryRight.split(':')[0],
minute=aryRight.split(':')[1];
function fn(n){
return n<10?"0"+n:n;
}
var date=fn(month)+'月'+fn(day)+'日 '+fn(hour)+'时'+fn(minute)+'分'
console.log(date);
--------------正则
let reg=/^(\d{4})[-/](0?[1-9]|1[0-2])[-/](0?[1-9]|[12]\d|3[01])\s+(0?[1-9]|1\d|2[0-3]):(0?\d|[1-5]\d):(0[1-9]|[1-5]\d)$/;
console.log(reg.exec(str));
2.URL地址问号传参解析
有一个URL地址"www.zhufengpeixun.cn/stu/?1x=1&n…"地址问号后面的内容是我们需要解析出来的参数信息 { 1x:1, name:'AA', sex:'man' }
var str="http://www.zhufengpeixun.cn/stu/?1x=1&name=AA&sex=man#teacher";
var indexASK=str.indexOf("?");
var indexWell=str.indexOf('#');
if(indexWell>-1){
str=str.substring(indexASK+1,indexWell);
}else{
str=str.substr(indexASK+1);
}
var ary=str.split('&');
var obj={};
for(var i=0;i<ary.length;i++){
var curary=ary[i].split('=');
obj[curary[0]]=curary[1];
}
案例:获取URL问号后传参值--函数封装
function queryParameter(URL){
var indexASK=URL.indexOf("?"),
indexWell=URL.indexOf('#');
indexWell>-1?URL=URL.substring(indexASK+1,indexWell):URL=URL.substring(indexASk+1);
var ary=URL.split('&'),
obj={};
for(var i=0;i<ary.length;i++){
var item=ary[i],
curary=item.split("=");
obj[curary[0]]=curary[1];
}
return obj;
}
JS中的数学函数
Math称为数学函数,但是属于对象类型的
typeof Math//=>'object'
之所以叫做数学函数,是因为Math这个对象中提供了很多操作数字的方法
Math中提供的常用方法
abs
- 语法:Math.abs()
- 作用:取绝对值

ceil && floor
- 语法:Math.ceil()
- 作用:向上/向下取整

round
- 语法:Math.round()
- 作用:四舍五入

sqrt
- 语法:Math.sqrt()
- 作用:开平方

pow
- 语法:Math.pow(n,m)
- 作用:n的m次方

max && min
- 语法:Math.max()
- 作用:获取最大值或最小值
PI
- 语法:Math.PI()
- 作用:获取圆周率

random
- 语法:Math.random()
- 作用:获取[0,1)的随机小数
获取[n,m]之间的随机整数
Math.round(Math.random()*(m-n)+n)

获取四位随机验证码--封装函数
function queryCode(str){
var result="";
while(result.length<4){
var mathIndex=Math.round(Math.random()*(str.length-1));
result.indexOf(str.charAt(mathIndex))===-1?result+=str.charAt(mathIndex):null;
}
return result;
}
queryCode();
函数
function fn(n,m){//=>形参:入口
//=>函数体
var total = 0;
total=n+m;
console.log(total)
}
fn(10,20);//=>实参:给形参传递的具体值
/*
* var a =12;
* fn(a,1===1?10:0);//=>实参一定是值,即使我们写的是变量或者表达式,也是把变量或者表达式计算的结果作为值传递给形参变量
*/
函数执行的时候,都会形成一个全新的私有作用域(私有的栈内存),目的是:
- 1.把原有堆内存中存储的字符串变为JS表达式执行
- 2.保护里面的私有变量不受外界的干扰(和外界是隔离的) 我们把函数执行的这种保护机制,称之为**"闭包"**

function fn(n,m){
var total=0; //=>total:私有变量
total=n+m;
}
fn(10,20);
console.log(total);//=>Uncaught ReferenceError: total is not defined TOTAL是私有变量,我们无法在函数的外面直接获取这个值(闭包)
函数中的return
- 函数的入口:形参
- 函数的出口:返回值 return
- 把函数运行的结果(或者函数体中的部分信息)拿到函数外面去使用
function fn(n,m){
var total = 0;
total = n+m;
return total;//=>并不是把total变量返回,返回的是变量存储的值,return返回的永远是一个值
}
fn //=>代表的是函数本身(函数本身也是个值,本身代表的是堆内存中的代码字符串)
fn(10,20);//=>代表的是函数执行(不仅如此,它代表的是函数执行后,返回的结果[return返回的值])
function fn(n,m){
var total = 0;
total = n+m;
}
var res = fn(10,20);//=>如果当前函数没有return结果出来(或者return;啥也没返回),函数执行在外面拿到的结果都是 undefined
function fn(n,m){
//=>如果n/m有一个没有传递值,我们返回零
if(n===undefined || m===undefined){
return 0;//=>return还有一个作用:类似于循环中的break,能够强制结果函数体中代码的执行(return后面的代码不在执行)
}
var total=0;
total=n+m;
return total;
}
fn(10);//=>此时n=10,m为undefined,相当于10+undefined,Number(undefined)=>NaN,10+NaN=>NaN
===知识点1===
1、n===undefined 经常这样判断n的值是否为undefined,这种方式可以
2、n==undefined 这种模式不好,因为null==undefined也是相等的(===比较才不相等)
3、typeof n=='undefined' 项目中开发者更喜欢这种判断模式
===知识点2===
1、total=0; 0是有值的,值时0,从内存方面来说,会在栈内存中占一个位置
2、total=null; 开发者更喜欢用null来作为初始值,null是空对象指针,是不占内存的;
函数中的arguments
形参是有局限性的:我们需要具体的知道用户和执行的时候传递实参数量、顺序等,才可以使用形参变量定义对应的入口
arguments:函数内置的实参集合(内置:函数天生就存在的机制,不管你是否设置了形参,也不管你是否传递了实参,arguments都有,始终存在的)
function sum(){
console.log(arguments);
}
argument是一个类数组,不是数组,不能直接使用数组中的方法

即使设置形参变量,形参该是什么值还是什么值,但是arguments使用存储的是所有传递进来的实参,所以它被称为实参集合

function sum(){
console.log(arguments.callee===sum);//=>true
}

案例:任意数求和
//=>思路:先将实参中的每一项都转换为数字类型,如果是有效数字则累加,否则,不累加
function sum(){
var total=null;
for(var i=0;i<arguments.length;i++){
var item=arguments[i];
if(!isNaN(Number(item))){
total+=Number(item);
}
}
return total;
}
实名函数和匿名函数
- 实名函数:有函数名的
- 匿名函数:没有函数名的
- 函数表达式:把函数当做值赋值给变量或者元素的事件
- 自执行函数:创建和执行一起完成的,自执行函数正常写法是"(function(){...})();"但是,JS中会将以”function“ 开头的语句认定为函数声明语句,而自执行函数得到的是一个值,所以不可以直接这样写。所以要以运算符或者括号开头~func...或者以变量接收var a=function(){...}();
===函数表达式===
var fn=function (){
}
oBox.onclick=function (){
}
===自执行函数
(function (){})();
//=>因为自执行函数不能以function做开头,所以function前加上运算符。
~function(){
console.log(this) //=>Window
}();
//return 值为~undefined=>-1
//二进制否运算符(~)将每个二进制位都变为相反值(0变为1,1变为0)。
!function(){
console.log(this) //=>Window
}();
//return 值为!undefined=>true
-function(){
console.log(this) //=>Window
}();
//return 值为-undefined=>NaN
+function(){
console.log(this) //=>Window
}();
//return 值为+undefined=>NaN
DOM
DOM树
dom tree 当浏览器加载HTML页面的时候,首先就是DOM结构的计算,计算出来的DOM结构就是DOM树(把页面中的HTML标签像树状结构一样,分析出之间的层级关系)

获取DOM元素的方法
getElementById
通过元素的ID获取指定的元素对象,使用的时候都是
document.getELementById("")此处的document是限定了获取元素的范围,把它称之为"上下文(context)"1、getElementById的上下文只能是document 因为严格意义上一个页面的id是不能重复的,浏览器规定在整个文档中既可以获取这个唯一的ID
2、 如果页面中的ID重复了,我们基于这个方法只能获取到第一个元素,后面相同ID元素无法获取
3、在IE6-7浏览器中,会把表单元素(input)的name属性值当做ID来使用(建议:以后使用表单元素的时候,不要让name和id的值有冲突)
getElementsByTagName
[context].getElementsByTagName在指定的上下文中,根据标签名获取到一组元素集合(HTMLCollection)1、 获取的元素集合是一个类数组(不能直接使用数组的方法) 2、它会把当前上下文中,子子孙孙(后代)层级内的标签都获取到(获取的不仅仅是儿子级的) 3、基于这个方法获得的结果永远都是一个集合(不管里面是否有内容,也不管有几项,它是一个容器或者集合),如果想操作集合中具体的某一项,需要基于索引获取到才可以

案例:寻找所有id为HAHA的元素
思路:先获得id为box的div下的所有元素,得到一个类数组,创建一个新数组,再遍历类数组中的每一项,如果id等于HAHA则存储到数组中
第一种方案
<div class="box" id="box">
<ul>
<li class="item1 item2">新闻</li>
<li class="item1">电影</li>
<li class="item2">音乐</li>
</ul>
<div id="HAHA" name="hobby">最新新闻</div>
<div>最新电影</div>
<div id="HAHA">最新音乐</div>
</div>
var oBox=document.getElementById('box'),
nodeList=oBox.getElementsByTagName('*'),
ary=[];
function queryAllById(id){
for(var i=0;i<nodeList;i++){
var item=nodeList[i];
item.id===id?ary.push(item):null;
}
return ary;
}
第二种方案
console.log(HAHA);
//=>HAHA为id名
在JS中,默认会把元素的ID设置为变量(不需要再获取设置),而且ID重复,获取的结果就是一个集合,包含所有ID项,不重复就是一个元素对象(类似于ById获取的结果)
getELementsByClassName
[context].getELementsByClassName()在指定的上下文中,基于元素的样式类名(class="xxx")获取到一组元素集合
1、真实项目中,我们经常是基于样式类来给元素设置样式,所以在JS中,我们也会经常基于样式类来获取元素,但是此方法在IE6-8下不兼容
兼容处理方案
Node.prototype.queryElementsByClassName = function queryElementsByClassName() {
if (arguments.length === 0) return [];
var strClass = arguments[0],
nodeList = utils.toArray(this.getElementsByTagName('*'));
strClass = strClass.replace(/^ +| +$/g, '').split(/ +/);
for (var i = 0; i < strClass.length; i++) {
var reg = new RegExp('(^| +)' + strClass[i] + '( +|$)');
for (var k = 0; k < nodeList.length; k++) {
if (!reg.test(nodeList[k].className)) {
nodeList.splice(k, 1);
k--;
}
}
}
return nodeList;
};
getElementsByName
document.getElementsByName()它的上下文也只能是document,在整个文档中,基于元素的name属性值获取一组节点集合(也是一个类数组)
1、在IE9及以下版本浏览器当中,只对表单元素的name属性起作用(正常来说,我们项目中只会给表单元素设置name,给非表单元素设置name,其实是一个不太符合规范的操作)

querySelector
[context].querySelector()在指定的上下文中基于选择器(类似于CSS选择器)获取到指定的元素对象(获取到的是一个元素,哪怕选择器匹配了多个,我们只获取第一个)
querySelectorAll
>在querySelector的基础上,我们获取到选择器匹配到的所有元素,结果是一个节点集合(NodeList)

querySelector/querySelectorAll都是不兼容IE6-8浏览器的,不考虑兼容的情况下,我们能用byid或者其它方式获取的,也尽量不要用这两个方法,这两个方法性能消耗较大
<div id="div1" class="fon mark" name="div1">01</div>
<div id="div2" class="mark">02</div>
<div id="div1" class="fon">03</div>
<script>
console.log(document.querySelectorAll("#div1"));/*ID选择器*/
console.log(document.querySelectorAll('.mark'));/*类选择器*/
console.log(document.querySelectorAll('body>div'));/*子级选择器*/
console.log(document.querySelectorAll('div'));/*标签选择器*/
console.log(document.querySelectorAll("body div"));/*后代选择器*/
console.log(document.querySelectorAll("div[name='div1']"));/*标签属性选择器*/
console.log(document.querySelectorAll("*"));/*通配符选择器*/
</script>
document.head
获取head元素对象 直接调取document这个实例上的head属性
document.body
获取body元素对象 直接调取document这个实例上的body属性
document.documentElement
获取HTML元素对象 直接调取document这个实例上的documentElement属性
//=>需求:获取浏览器一屏幕的宽度和高度(兼容所有的浏览器)
document.documentELement.clientWidth ||
document.body.clientWidth
document.documentELment.clien
tHeight ||
document.body.clientHeight
节点(node)
在一个HTML文档中出现的所有东西都是节点
- 元素节点(HTML标签)
- 文本节点(文字内容、空格、换行)
- 注释节点(注释内容)
- 文档节点(document)
- ...
每一种类型的节点都会有一些属性区分自己的特性和特征
- nodeType:节点类型
- nodeName:节点名称
- nodeValue:节点值
元素节点
- nodeType:1
- nodeName:大写标签名
- nodeValue:null
文本节点
- nodeType:3
- nodeName:"#text"
- nodeValue:文本内容
注释节点
- nodeType:8
- nodeName:"#comment"
- nodeValue:注释内容
文档节点
- nodeType:9
- nodeName:"#document"
- nodeValue:null
描述节点之间关系的属性
parentNode
获取当前节点唯一的父亲节点
childNodes
获取当前节点的所有子节点
- 子节点:只获得儿子级别的
- 所有:包含元素节点、文本节点等
children
获取当前节点所有的元素子节点 在IE6-8中会把注释节点也当做元素节点获取到,所以兼容性不好
firstChild
获取当前节点的第一个子节点(可能是元素或者文本)
firsrElementChild
获取当前节点的第一个元素子节点 在IE6-8中不兼容
lastChild
获取当前节点的最后一个子节点(可能是元素或者文本)
lastElementChild
获取当前节点的最后一个元素子节点 在IE6-8中不兼容
previousSibling
获取当前节点的上一个哥哥节点(获取的哥哥可能是元素也可能是文本等)
previousElementSibling
获取上一个哥哥元素节点(不兼容IE6-8)
nextSibling
获取当前节点的下一个弟弟节点(获取的哥哥可能是元素也可能是文本等)
nextElemnentSibling
获取下一个弟弟元素节点(不兼容IE6-8)
兼容处理1--获取当前元素的所有元素子节点
基于children不兼容IE低版本浏览器(会把注释当做元素节点)
//=>思路:首先获取当前元素下所有的子节点,然后遍历这些节点,筛选出元素的(nodeType===1),把筛选出来的结果单独存储起来即可
/*
*children:get all the element nodes of the current element
*@parameter
* curEle:[object] current element
*@return
* [Array] all the element nodes
*by team on 2018/04/07 12:36
*/
function children(curEle){
var childEle=curEle.childNodes;
var aryEle=[];
for(var i=0;i<childEle.length;i++){
var item=childEle[i];
if(item.nodeType===1){
aryEle.push(item);
}
}
return aryEle;
}
兼容处理2--获取当前元素的上一个哥哥元素节点
基于previousElementSibling不兼容IE低版本浏览器
//=>先找当前元素的哥哥节点,看是否为元素节点,不是的话,基于哥哥,找哥哥的上一个哥哥节点...一直到找到元素节点或者已经没有哥哥了(说明我就是老大)则结束查找
/*
*prev:get the last elder brother element node of the current element
*@parameter
* curEle:[object] current element
*@return
* [object] last elder brother element
*by team on 2018/04/07 12:48
*/
function prev(curEle){
var item=curEle.previousSibling;
while(item && item.nodeType!==1){//=>item是为了验证当前这个元素是否还设有,如果没有item会为null,Boolean(item)=>false
item=item.previousSibling;
}
return item;
}
兼容处理3---获得当前项下一个弟弟元素节点
思路:先找到当前项的弟弟节点,判断该弟弟节点的nodeType===1,如果不等于就继续往前找 一直找到末尾
/*
* next:get the next element of the current element
* @parameter:[object] the current element
* return:the next element
* by Fay on 17:58
*/
function next(curEle){
var item=curEle.nextSibling;
while(item && item.nodeType!==1){
item=item.nextSibling;
}
return item;
}
兼容处理4--获取当前项所有的弟弟元素节点
思路:先获取当前元素的弟弟节点,创建一个空数组,如果当前元素的弟弟节点的nodeType===1则存起来,一直往下找找到末尾
/*
* nextAll:get all of the next elements by the current element
* @parameter:[object] the current element
* return: all of the next elements
* by Fay on 18:06
*/
function nexAll(curEle){
var item=curEle.nextSibling,
ary=[];
while(item){
item.nodeType===1?ary.push(item):null;
item=item.nextSibling;
}
return ary;
}
兼容处理5--获取当前项所有的哥哥元素节点
/*
* preAll:get all of the previous elements by the current element;
* @parameter:[object] the current element
* return:[object] all of the previous elements
* by Fay on 18:11
*/
function preAll(curEle){
var item=curEle.previousSibling,
ary=[];
while(item){
item.nodeType===1?ary.push(item):null;
item=item.previousSibling;
}
return ary;
}
兼容处理6--获得当前项的所有兄弟元素节点
思路:将所有的哥哥元素节点与所有的弟弟元素节点相加
/*
* siblings:get all siblings elements by the current element
* @parameter:[object] the current element
* return:all siblings elements
* by Fay on 18:25
*/
function siblings(curEle){
var preItem=curEle.previousSibling,
nextItem=curEle.nextSibling,
ary=[];
while(preItem){
preItem.nodeType===1?ary.push(preItem):null;
preItem=preItem.previousSibling;
}
while(nextItem){
nextItem.nodeType===1?ary.push(nextItem):null;
nextItem=nextItem.nextSibling;
}
return ary;
}
兼容处理7--获得当前项的索引
思路:当前元素有多少个哥哥节点,索引就是多少,即获取当前项哥哥节点的个数
/*
* index:get the index of the current element
* @parameter:[object] the current element
* return:[number] the index of the current element
* by Fay on 18:17
*/
function index(curEle){
var item=curEle.previousSibling,
ary=[];
while(item){
ary.push(item);
item=item.previousSibling;
}
return ary.length;
}
关于DOM的增删改
createElement
创建一个元素标签(元素对象)
document.createELement([标签名])Document类原型上的属性,属性值是函数
案例:通过动态创建元素快速获取queryURLParameter
a元素有一些内置属性:
- hash:存储了哈希值 '#teacher'
- search:问号传递参数值,没有传递是空字符串 '?name=bd&age=10'
- hostname:域名 'www.baidu.cn'
- pathname:路径 '/stu/'
function queryURLParameter(url){
var link=document.createElement('a');
link.href=url;
var search=link.search;
if(search.length===0) return;
url=search.substr(1);
var ary=url.split('&'),
obj={};
for(var i=0;i<ary.length;i++){
var curary=ary[i].split('=');
obj[curary[0]]=curary[1];
}
link=null
return obj;
}
appendChild
把一个元素对象插入到指定容器的末尾
[container].appendChild([newELe])Node类原型上的属性,属性值是函数
var newP = document.createElement('p');
document.body.appendChild(newP);
newP.innerHTML = '我是P!!';
newP.style.backgroundColor = 'red';
insertBefore
把一个元素对象插入到指定容器中某一个元素标签之前
[container].insertBefore([newEle],[oldEle])Node类原型上的属性,属性值是函数
<div id="box2" class="box">2</div>
//=>创建
var oDiv = document.createElement('div');
oDiv.id='div1';
oDiv.className='box';
oDiv.innerText='1';
document.body.insertBefore(oDiv,document.getElementById('box2'));
//也可以这样写,直接用id名
document.body.insertBefore(oDiv,box2);
cloneNode
把某一个节点进行克隆
[curELe].cloneNode():浅克隆,只克隆当前的标签[curELe].cloneNode(true):深克隆,当前标签及其里面的内容都一起克隆了
Node类原型上的属性,属性值是函数
<div class="box" id="box1">1</div>
<div class="box" id="box2">2</div>
<script>
var box1=document.getElementById('box1'),
box2=document.getElementById('box2');
var box3=box2.cloneNode();
document.body.appendChild(box3);
var box4=box2.cloneNode(true);
document.body.appendChild(box4);
</script>

removeChild
在指定容器中删除某一个元素
[container].removeChild([curEle])Node类原型上的属性,属性值是函数
set/get/removeAttribute
设置/获取/删除 当前元素的某一个自定义属性 设置的自定义属性值最终都会以字符串的形式展现
[container].setAttribute([key],[value])Element类原型上的属性,属性值是函数
使用xxx.index=0和xxx.setAttribute('index',0)这两种设置自定义属性的区别?
xxx.index:是把当前操作的元素当做一个普通对象,为其设置一个属性名(和页面中的HTML标签没关系)


xxx.setAttribute:把元素当做特殊的元素对象来处理,设置的自定义属性和页面结构中的DOM元素映射在一起的



使用DOM方法设置的自定义属性与对象设置自定义属性无法互相获取,两种方法是独立的
- 第一种是基于对象键值对操作方式,修改当前元素对象的堆内存空间来完成
- 第二种是直接修改页面中的HTML标签的结构来完成(此种方法设置的自定义属性可以在结构上呈现出来)
