十、数据类型的区别
- 基本数据类型:按值操作,值存储在栈内存中;
- 引用数据类型:按照堆内存的地址来操作,我们把对象的键值对存储在堆内存中,我们操作的是堆内存的引用地址;
let a={},b=Symbol('1'),c=Symbol('1');
a[b]='哈哈';
a[c]='呵呵';
console.log(a[b]);//='哈哈'
/:唯一值相同不会替换。
十一、js中创建变量的六种方式
var\function\let\const\class\import
十二、数据类型面试题
1、
2、考察parseInt原理
var str = 'abc123';
var num = parseInt(str);//=NaN数字类型
if (num == NaN) {
console.log('NaN');
} else if (num == 123) {
console.log(123);
} else if (typeof num == 'number') {
console.log('number');
}
else{
console.log('str');
}
//='number'
3、考察输出
console.log(alert(1));/=undefined
/=原理:先执行alert,把alert执行的返回结果输出,alert没有返回结果,所以输出:undefined
typeof undefined; /=undefined
consloe.log(parseInt(undefined));/=NaN
isNaN(undefined);/=true
4、考察isNaN、parseInt、Number
- parseInt、Number各自有规则;
- isNaN:参照Number规则,先转数据为数字类型;
isNaN(null);/=false,null转为数字类型为0;
parseInt(null);/=NaN
isNaN(parseInt(null));/=true,相当于isNaN(NaN);
Number(null);/=0
parseInt("");/=NaN
Number("");/=0
isNaN("");/=false
5、考察基本数据类型
- js中比较两个值:
== 、===、ES6的Object.is
规则:null==undefined为true,只要没有布尔、数字,都是转为字符串或对象比较;下方(十六、有详情)
==
:如果左右两边数据类型不同,则先转为相同类型,再进行比较;至于转为什么类型,谁转为谁有自己的规则;- 推测:只要有(Boolean)布尔类型、(Number)数字类型时,都转为数字类型比较;
- 有字符串或引用类型就转为字符串比较;
isNaN(NaN) == "";
==>true == "";/=false
有布尔类型转数字比较;
6、关于<script>
标签放在页面头部与尾部的区别,以及解决方法?
十三、条件判断、循环
1、js中常用判断语句
if 、else if 、else
- 三元运算符:处理最简单的
if else
情况:x>10? x++:x--;
情况1
:如果不想处理一些情况,就用null、undefined占位,不占位会报错;x>10 ? x++ : null;
情况2
:如果要处理的一种情况需要做多件事,则用小括号包裹,并且每件事之间用逗号分隔;x>10 ? (x++,console.log(x)) : null;
switch case break
:一个变量在不同情况下有不同操作时使用;- 每一种情况结束都要使用
break
不再向下执行,最后一个default
就不需要加了,因为没有能再执行的了; - 每一步
case
都是按照===
规则选择的; switch(x){ case1:x += 1;break; }
- 每一种情况结束都要使用
2、for循环、for in循环
- 循环:一轮一轮重复做某件事;循环一个集合,或控制循环次数;
- 循环体中可能出现的关键字:
break
:整个循环结束,当前循环有剩余代码也不执行,下一次i++
也不执行;continue
:直接结束本轮循环,当前循环体下面代码不再执行,下一次i++
正常执行;
for循环
可以遍历\循环\迭代一个数组;因为数组有索引、length
;for(var key in obj){}
:遍历对象;- 当前对象有多少可枚举属性就循环多少次;
- 每次循环属性名赋值给
key
;使用obj[key]
获取当前属性值;注意不要使用obj.key、obj['key']获取
for in 循环
的顺序会先按数字排序(如果属性名有数字),其他正常先后顺序;
var n = 10;
for (; n > 0;) {
if(n>5){
n-=2;
}
else{
n-=3
}
}
console.log(`${n}`);/= '-2 '
3、死循环,程序不输出
for(var i=3;i<10;i++){
i--;
}
console.log(i);/= 没有输出
4、i++、i+=1、i=i+1
i++
:默认为i=Number(i)+1;
也是与其他两者的区别;i+=1
==i=i+1
let a = '10';
a == 10 ? a++ : a--;
console.log(a);/= 11
十四、DOM操作
DOM概念
:文档对象模型,提供一系列属性和方法,让我们能操作页面中的dom元素;
1、获取DOM元素概念
- 基于JS获取到的DOM元素是
对象数据类型
的值,里面包含很多浏览器自带的用来操作元素的键值对;- 如:
id:'box';
存储元素的ID style:{};
存储当前元素的行内样式- 注意点:在设置行内样式的颜色时,使用基于16进制
#fff
、rgb表示写法时rgb(255 255 255)
,得到的是RGB形式,所以推荐写英文单词;
- 如:
let box = document.getElementById('box');
---方式一:
box.style.color='red';/=可以修改,因为是操作堆内存
---方式二:
let boxSty = box.style;//指向样式对象
boxSty.color='red';/=可以修改,也是操作堆内存
---方式三:
let text = box.style.color;//指向""空字符串
text = 'red';/=不能修改,相当于给text赋值;
2、获取DOM元素常用方法
document.getElementById('box');
:获取指定元素对象,在堆内存中;document.getElementByTagName('li');
:获取HTMLCollection元素集合(类数组),也是对象,内部每一项也是对象;
十五、函数
1、概念
- 本质:把实现一个功能的代码封装起来,以后实现功能只需要执行函数,无需再写一遍代码;
- 目的:封装;
- 创建函数:生产洗衣机
function 函数名(形参1,...){}
:参数规定放几件衣服,函数体规定怎么洗;
- 执行函数
函数名(实参1,...)
:放衣服开洗;
2、针对for循环产生的变量问题(var)
- 解决一:自定义属性编程思想:解决for循环后的
var i
值问题;(没有兼容性问题)
for (let i = 0; i < itemList.length; i++) {
var bgc = 'white',
item = itemList[i];
i%2 !== 0 ? bgc='pink':null;
item.style.backgroundColor = bgc;
item.myBg = bgc;//自定义属性
item.onmouseover = function () {
this.style.backgroundColor = 'green';
}
item.onmouseout = function () {
this.style.backgroundColor = this.myBg;
}
}
- 解决二:闭包思想(没有兼容性问题)
for (let i = 0; i < itemList.length; i++) {
(function (i) {
var bgc = 'white',
item = itemList[i];
i%2 !== 0 ? bgc='pink':null;
item.style.backgroundColor = bgc;
item.onmouseover = function () {
item.style.backgroundColor = 'green';
}
item.onmouseout = function () {
item.style.backgroundColor = bgc;
}
})(i)
}
---另一种闭包形式
[].forEach.call(btnList,(item,index)=>{
item.onclick = function(){
alert(`当前点击按钮的索引为${index}`)
}
})
- 解决三:let声明变量(es6)–与闭包原理相似
for (let i = 0; i < itemList.length; i++) {
let bgc = 'white',
item = itemList[i];
i%2 !== 0 ? bgc='pink':null;
item.style.backgroundColor = bgc;
item.onmouseover = function () {
item.style.backgroundColor = 'green';
}
item.onmouseout = function () {
item.style.backgroundColor = bgc;
}
}
- 定义函数变量、函数声明、事件绑定都是只创建函数,此时函数中代码只是在堆内存中存储的字符串;
3、函数创建与执行的堆栈运行机制
- 创建:形成新的堆内存
- 执行:形成新的执行上下文栈内存;
- 形参:创建函数时设定的变量,对应实参不传就是undefined;
- 实参:执行函数时给形参传递的具体值,可以是变量、具体值、表达式,最终传进去的都是最终值(js数据类型);
4、形参、实参
- 指定数求和:
function(x,y,z,...){}
有几个数,用几个形参接收; - 任意数求和:不确定实参个数,所以没法设置形参个数;
- 实参集合(箭头函数中没有):
function(){ console.log(arguments) }
;是类数组,与HTMLCollcetion
类似;通过for循环使用; - es6的剩余运算符:
function(...args){ console.log( args ) }
- 实参集合(箭头函数中没有):
5、函数返回值return
return;
:下面代码不再执行;return [变量、函数]
:返回函数内部私有的变量,外部得到的是这个变量的值,需要外部定义新的变量接收(或者直接使用);
function sum(){
let a = 1;
return a
}
var aa = sum();/函数中不写return,返回undefined
console.log(aa);/=1
6、函数类型
- 实名函数:
function func(){}
- 匿名函数:
- 函数表达式:
var func = function(){}
; - 立即执行函数:
(function(n){})(n)
; - 箭头函数:
var func = ()=>{}
- 函数表达式:
十六、数据类型间比较的规则
1、==
的比较规则
- 左右两边数据类型不同时,转为统一类型;
哪到底转成什么类型?
相同类型间:
NaN == NaN; /= false; NaN 跟任何值都不相等
Infinity == Infinity; /=true; 只和自己想等
Symbol(1) == Symbol(1); /=false; Symbol() 跟任何值都不相等
对象 == 对象; /= 比较地址,相等true,不等false
---
不同类型间:
null == undefined; /=true;除此之外,它们和任何值都不相等;
对象 == 字符串; /= 对象转为字符串
除此以上情况外,都转为 数字进行比较。
(只要没有布尔、数字,都是转为字符串或对象比较)
十七、数组
特殊的对象,属性名是索引;有length属性;
1、一维数组、多维数组
一维:let arr = [1,2,3];
二维数组:子项也可以展开
let arr = [1,{a:1}];
let arr = [1,[1,2]]
2、需要掌握
- 基础操作
arr[arr.length] = 1;
数组末尾追加;for( let i=0;i<arr.length;i++ ){}
;遍历索引for( let index in arr ){}
;遍历属性名(索引)for( let item of arr ){}
;遍历属性值- 删除:
delete arr[0]
;但是数组长度不变,删除项为空,所以一般不用这种方式; - 删除最后一项:
arr.length--
- 内置方法(浏览器自带的方法)
- 排序、去重(算法)
- 扁平化、深度克隆
- 扁平化:数组的扁平化指将一个多维数组变为一维数组
[1, [2, [3, 4]]]--->[1, 2, 3, 4]
如果只有两层:let array = [1, [2, 3, 4]];
方法一:
function flatten(arr) {
return Array.prototype.concat.apply([], arr); /= apply第二个参数就是数组形式
}
方法二:
function flatten(arr) {
while(arr.some(item=>Array.isArray(item))) {
arr = [].concat(...arr);
}
return arr;
}
console.log(flatten(array));
多层:let arrayDeeper = [1, [2, [3, 4]]];
方式一:toString&split&map方法
function flattenDeeper(arr){
return arr.toString().split(',').map((item)=>parseInt(item))
} /=map方法将字符串转为数字类型
方式二:reduce&concat方法
function flattenDeeper(arr) {
return arr.reduce((result, item)=> {
return result.concat(Array.isArray(item) ? flattenDeeper(item) : item);
}, []);
}
方法三:join&split&map方法
function flattenDeeper(arr){
return arr.join().split(',').map((item)=>parseInt(item))
}
let arr1 = [1,[2,3,[4,5]]]
console.log(flattenDeeper(arr1))//[1,2,3,4,5]
3、常见内置方法
- 记忆方式:
- 方法的意义和作用
- 参数
- 返回值
- 原始数组是否改变
- 1、关于数组的增删改
数组方法 | 作用 | 参数 | 返回值 | 原数组是否改变 |
---|---|---|---|---|
push | 数组末尾追加元素 | 参数个数、类型不固定 | 新增数组的长度 | 改变 |
pop | 数组末尾删除一个元素 | 无 | 被删除的末尾数组元素 | 改变 |
unshift | 数组开头追加元素 | 参数个数、类型不固定 | 新增数组的长度 | 改变 |
shift | 数组开头删除一个元素 | 无 | 被删除的开头数组元素 | 改变 |
splice | 实现数组指定位置的增删改 | 删:arr.splice(n,m),从索引n开始删m项 增: arr.splice(n,0,x,..) 改: arr.splice(n,m,x,..),从索引n开始删m项,用新元素替换 | 被删元素组成的数组;没有删除的返回[] | 都改变 |
- 2、关于查询和拼接
数组方法 | 作用 | 参数 | 返回值 | 原数组是否改变 |
---|---|---|---|---|
slice | 从数组中查询元素;浅克隆 | let newArr = arr.slice(n,m) 从索引n开始,查到索引m的前一项 | 返回一个新数组,包含查询到的元素 | 不改变 |
concat | 实现数组拼接;浅克隆 | let newArr = arr.concat('aa',arr1) 传具体值、要连接的数组 | 返回连接后的新数组 | 不改变,目标数组也不改变 |
- 3、关于数组转为字符串
数组方法 | 作用 | 参数 | 返回值 | 原数组是否改变 |
---|---|---|---|---|
toString | 将数组转为字符串,以逗号分隔 | 无 | 返回字符串 | 不改变 |
join | 将数组转为字符串,以指定符号分隔 | arr.join('-') ,指定分隔符,不写默认, 逗号 | 返回字符串 | 不改变 |
- 4、关于包含、排序
数组方法 | 作用 | 参数 | 返回值 | 原数组是否改变 |
---|---|---|---|---|
indexOf | 查询指定元素首次出现的索引 | arr.indexOf(item,start) 所查元素内容,开始查询的位置 | 返回Number类型,查到则为第一次出现位置的索引;没有查到返回-1 | 不改变 |
lastIndexOf | 查询指定元素最后出现的的索引 | array.lastIndexOf(item,start) 所查元素内容,开始查询的位置 | 返回Number类型,查到为最后一次出现位置的索引;没有查到返回-1 | 不改变 |
includes | 判断一个数组是否包含一个指定的值 | arr.includes(searchElement, fromIndex) 要查询的元素,开始位置 | 返回布尔类型值; 包含:true;不包含:false | 不改变 |
reverse | (排序)用于颠倒数组中元素的顺序 | 无 | 返回颠倒后的原数组 | 改变 |
sort | (排序)按规则排序数组 | 函数规则 | 返回改变后的原数组 | 改变 |
- 5、关于数组中迭代方法
数组方法 | 作用 | 参数 | 返回值 | 原数组是否改变 |
---|---|---|---|---|
forEach | 遍历数组中每一项,可得到当前项值与索引 | 函数参数arr.forEach(function(item,index){}) | undefined,处理函数中return无效 | 不改变 |
map | 遍历数组中每一项,可得到当前项值与索引 | 函数参数let newArr = arr.map(function(item,index){ return item*2 }) | 返回处理后新数组 每一项返回值由return决定; | 不改变 |
- 扩展:数组每项求和
let arr = [100,200,300];
let total = 0;
// --for循环
// for(var i=0;i<arr.length;i++){
// total += arr[i]
// }
// --for of循环
// for(let item of arr){
// total += item
// }
// --转字符串
// var str = arr.join('+');
// total = eval(str)
// --es6的reduce
total = arr.reduce((pre,item)=>{
return pre += item
},0)
console.log(total);
4、数组去重(12种)
- 双for循环(有问题)
- 问题:数组塌陷(数组元素减少时导致的后面元素前移)
- 原因:splice删除是直接删除原数组,所以每删除一项会导致后面数据前移,索引发生变化,如果不用
j--
,就会产生每次删除后再循环时就会隔过去一项数据元素; - 解决:每次删除后随即调用
j--
,用来与下次循环的j++
抵消; - 优化:
splice
删除优化(让后面每一项前移太耗性能),不在当前项删除,而是用最后一项替换这一项,把最后一项删除,因为本来就是为了去重,不考虑原有顺序问题;
let arr = [1,2,3,2,3,4,5,2,3,4,2,1,3,2,4];
for(let i=0;i<arr.length-1;i++){
let item = arr[i];
for(let j=i+1;j<arr.length;j++){
if(arr[j]===item){
arr.splice(j,1);==>改进:arr[j] = arr[arr.length-1];arr.length--;或arr.pop()
j--;/= 解决splice删除引发的数组塌陷问题
}
}
}
console.log(arr);
- 对象的键值对方式
- 弊端:去重数组中不能包含对象、函数,因为属性名不能为这些;而且数字与字符串一样的值也会认为重复;多个
undefined
不会去重; - 优点:单循环,性能好点
- 弊端:去重数组中不能包含对象、函数,因为属性名不能为这些;而且数字与字符串一样的值也会认为重复;多个
:将数组每一项当做对象的键与值;如果属性名存在那当前项就是重复的。
let arr = [1,2,3,1,1,4]
let obj = {}
for(let i=0;i<arr.length;i++){
let item = arr[i]
if(obj[item] !== undefined){
arr[i] = arr[arr.length-1] /=这是改进写法,将最后一项的值拿过来,删除最后一项
arr.length--; /= 属性值不是undefined,就删除数组中这项
i--;
continue;
}
obj[item] = item;/= 属性值是undefined就正常存对象里,数组中不删
}
console.log(arr);/= [ 1, 2, 3, 4 ]
- Set数据结构:类似数组
let arr = ['a','a','b',1,2,1];
const set = new Set(arr);/=伪数组 Set(4) {"a", "b", 1, 2};有size属性替换length;
转真正数组:
const newArr = [...set];
或者const newArr = Array.from(set);
console.log(newArr);//[ 'a', 'b', 1, 2 ]