1、js数据类型有哪些?
基本数据类型:String number boolean undefinrd null symbol
引用数据类型:Object 对象 (Array:数组 Function:函数)
NaN是一个数值类型,但是不是一个具体的数字(就类似与无穷大,是一个数字,但是你说这个数字是多少哪?)
2、类型转换(隐式转换)考题?
题目:下面代码输出结果是什么?
console.log(true + 1) // 2 这里把True转转成了1去做加法
console.log(false + 1)// 1 这里把false转转成了0去做加法
console.log('true' + 1) // true1 字符串遇见任何类型都会成为字符串
console.log(undefined + 1) // NaN NaN是一个数值类型,但是不是一个具体的数字
console.log(null + 1) // 1
console.log(typeof null ) // object
console.log(typeof undefined ) // undefined
console.log(typeof NaN ) // number
3、JS延迟加载有哪些?
一般有以下几种:
defer属性:等html全部执行完,才去执行js代码,按顺序执行脚本。
async属性:async是和html同步执行,无先后顺序执行JS(谁先加载执行谁)
4、== 与 === 区别?
== :是只比较值(开发不建议用)
原理:JavaScript在后台隐式调用valueOf隐式转换,它不显示出来。
从写了valueof就改变快了它的布尔值:原本是true,从写valueof后就成了false
// 从写valueof
Object.prototype.valueOf = function(){
let c = 123
}
let a = [1,2]
let b = '1,2'
console.log(a == b) // false
===:除了比较值,还比较类型
5、null与undefined的区别?
null其实就是对象没有
undefined就是数值没有
这就是历史问题:
因为在JavaScripts刚开始设计的时候,作者是借鉴Java语言里面的null,所以先有的null,但是使用一段时,发现null会被隐式转换成数值类型:0,很不容易发现错误;所以作者又设计出来undefined,因为undefined不会隐式转换成:0,而且undefined它是一个基本类型。
不同点:
当检测 null
或 undefined
时,注意相等(==)与全等(===)两个操作符的区别 (en-US) ,前者会执行类型转换:
typeof null // "object" (因为一些以前的原因而不是'null')
typeof undefined // "undefined"
null === undefined // false
null == undefined // true
null === null // true
null == null // true
!null //true
isNaN(1 + null) // false
isNaN(1 + undefined) // true
6、JS宏任务与微任务?
JavaScript是单线程语言,同一个时间只能做一件事情。
JS执行流程:首先执行同步任务(执行站里面的任务),接下来执行异步任务(事件循环)里面的的宏任务和微任务。
微任务:promise.then、
宏任务:setTimeout、setInterval、AJAX、DOM事件
流程:同步任务-->事件循环 (宏任务和微任务)-->微任务(执行完所有微再去执行宏)--> 宏任务 -->微任务--> 宏任务...一次循环执行直到执行完成。
7、JavaScript中var、let、const、作用域?(ES6)
var:
它没有块级作用域、
for (var i = 0; i < 3; i++) {
}
console.log(i)//3
变量的声明提升问题、
console.log(q) //undefined
var q = 123
声明覆盖问题
了解关于es6的更多知识可以看阮一峰——ES6 入门教程https://es6.ruanyifeng.com/
8、JS数组的方法
操作字符串方法:
charAr()方法返回指定位置的字符,当传入为负数或者超出长度的数值时就会返回一个空字符串。
indexOf()
和 lastIndexOf()
均返回 -1
indexOf()
方法返回字符串中指定文本首次出现的索引(位置)开发常用的方法
这个方法是用来判断字符串中是否又该字符,就返回索引,没有返回-1;还可以添加第二个参数就是从索引的那个位置开始的后面有没有该字符。
lastIndexOf()
方法返回指定文本在字符串中最后一次出现的索引
concat()
和并两个或多个字符串的方法,返回一个新字符串,不改变原字符串。该方法接受多个参数,
let s1='hello'
let s2='word'
let s3='!'
let b = s1.concat(s2,s3)
console.log(b); //helloword!
console.log(s1); //hello
console.log(s2); //word
console.log(s3); //!
如果传入的参数不是字符串,concat方法会将其会将其先转换成字符串,在进行拼接操作
let s1='hello'
let s2='word'
let s4= 100 //数值类型
let s3='!'
let b = s1.concat(s2,s4,s3)
console.log(b); //helloword100!
console.log(s1); //hello
console.log(s2); //word
console.log(s3); //!
console.log(s4); //100
除了上面的用concat方法外,还可以使用+来合并字符串,如下
let s1='hello'
let s2='word'
let s4= 100 //数值类型
let s3='!'
// let b = s1.concat(s2,s4,s3)
let a = s1+s2+s3+s4
console.log( a); //helloword!100
但是使用+要注意 :如果有多个数值类型的值先回进行加法运算才能变成字符串,达不到想要的效果,所以使用concat还是比较方便的。
let s2=12
let s4= 100 //数值类型
let s3='word!'
// let b = s1.concat(s2,s4,s3)
let a = s2+s4+s3
console.log( a); //112word!
substring()方法是用来截取字符串的,不会改变原字符串,第一个参数表是开始位置,第二个是结束位置(返回结果不包含该位置),结果可以看到没有返回索引为2的l。
let a = 'helloword!'
console.log( a.substring(0,2)); //he
如果省略第二个参数,则表是从索引为2参数到最后结束的所有字符串
let a = 'helloword!'
console.log( a.substring(2)); //lloword!
尽可能不要使用负数作为参数,因为substring方法会将负数装换成0,看下面操作:
let a = 'helloword!'
console.log( a.substring(-2,2)); //he
也不要在第一或者二个参数使用负数,还有第二个参数小于第一个参数:看操作:
let a = 'helloword!'
// 如果第一个参数是负数,substring会自动装换成(0,2)
// 如果第二个参数是负数,substring会自动装换成(0,2)
console.log( a.substring(-2,1)); //h
console.log( a.substring(2,-1)); //he
// 第二个参数小于第一个参数,substring会自动装换成(1,4)
console.log( a.substring(4,1)); //ell
console.log(a);//helloword!
数组的方法
push()
push方法会改变原数组,push():向数组的末尾添加元素
unshift()
unshift方法会改变原数组,unshift():向数开头加一个或多个元素,并且返回新的长度
shift()
shift():删除数组的第一位数据
,并且返回删除的数据,会改变原来的数组
。
//增:
let arr = [0,1,2,3,4,5,6],
Arr1 = arr.splice(arr.length-1,0,7); //从数组末尾开始向后截取0个元素,并在此插入新元素7
console.log(Arr1,arr) // => [],[0,1,2,3,4,5,6,7]
//删:
let arr2 = [0,1,2,3,4,5,6],
Arr3 = arr2.splice(0,1); //从索引0开始向后截取1个元素
console.log(Arr3,arr2)// => [0],[1,2,3,4,5,6]
//改:
let arr4 = [0,1,2,3,4,5,6],
Arr5 = arr4.splice(0,1,0.5) //从索引0开始向后截取1个元素,并在此插入元素0.5
console.log(Arr5,arr4) // => [0],[0.5,1,2,3,4,5,6]
slice()
slice()方法用来截取的,参数可写slice(3)、slice(1,3)、slice(-3)
该方法返回的是一个新数组,原数组不发生改变。
let arr = [1,2,3,4,5,6,7]
let ass = arr.slice(3) // 从前往后截取 从下标3开始截取数据
console.log(ass); // (4) [4, 5, 6, 7] 截取后返回的数据
console.log(arr); // (7) [1, 2, 3, 4, 5, 6, 7] 截取后对元数据并没有改变
let arr = [1,2,3,4,5,6,7]
let ass = arr.slice(1,3) // 从下边为1到下标为3 在一个范围截取
console.log(ass); // (2) [2, 3]
console.log(arr); // (7) [1, 2, 3, 4, 5, 6, 7]
let arr = [1,2,3,4,5,6,7]
let ass = arr.slice(-3) // 从后往前截取 那数组中的7就是下标为-1一直到下标为-3就是数字5
console.log(ass); // (3) [5, 6, 7]
console.log(arr); // (7) [1, 2, 3, 4, 5, 6, 7]
splice()
插入、删除、替换操作
该方法会改变原数组
//删除和修改插入
let arr = [0,1,2,3,4,5,6],
/* splice(1,3,'新插入')
一个参数:从下标1开始删除
二个参数:删除3个
三个参数:添加的新数据
*/
newArr = arr.splice(1,3,'新插入')
console.log(newArr)// (3) [1, 2, 3] 返回的是删除的内容
console.log(arr)// (5) [0, '新插入', 4, 5, 6] 这里可以看到改变了原来的数组
9、闭包?
什么是闭包:
闭包是一个函数加上到创建函数的作用的连接,闭包就是‘关闭’了函数的自由变量。
闭包的优点:
内部函数可以访问外部函数的局部变量
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
闭包来解决: onclick绑定的function中没有变量 i,解析引擎会寻找父级作用域,最终找到了全局变量 i,for循环结束时,i 的值已变成了4,所以onclick事件执行时,全都弹出 4。
var elements = document.getElementsByTagName('li');
var length = elements.length;
for (let i = 0; i < length; i++) {
elements[i].onclick = function (num) {
return function () {
alert(num);
};
}(i);
}
闭包的缺点:
函数体定义的的变量会一直存在内存中,造成内存的消耗问题。
解决:把闭包的函数使用完设置为null就行
10、原型与原型链?
先有原型再有原型链。
1、原型就可以解决什么问题?
对象共享属性和方法
2、谁有原型:
函数拥有:prototype属性,这个属性就是函数的原型对象。
对象拥有:__propto__原型链的形成是真正是靠__proto__ 而非prototype。
3、对象查找属性或者方法执行顺序:
先在对象自身查找-->构造函数-->对象的原型-->构造函数原型-->当前原型的原型查找-->...null
function Fun(){
// this.run = '1' // 构造函数
}
// Fun.prototype.run = '2' //构造函数原型
let obj = new Fun()
// obj.run = '3' //结果:3 先在对象自身查找
// obj.__proto__.run = '4' // 对象的原型
Object.prototype.run = '5' // 当前原型的原型查找
// 先在对象自身查找-->构造函数-->对象的原型-->构造函数原型-->当前原型的原型查找
console.log(obj.run) // 结果分别是:3 1 4 2 5
console.log(obj.__proto__) // Fun() 当前对象的原型就是Fun()
console.log(obj.__proto__.__proto__) // f Object() 当前对象的原型的原型就是Object()
console.log(obj.__proto__.__proto__.__proto__) // null 当前对象的原型的原型的原型就是null
4、原型链是什么?
原型链就是把原型串联起来,顶层链是一个null
11、call()、apply()、bind()的区别 (ES6)
call()apply()是立即执行函数,而bind()不是立即执行函数,因为它返回的是一个函数,要执行必须再加一个()才能执行
var str = 'woasdada'
var obj = {str: '我是内部的'}
function fu() {
console.log(this, this.str);
}
// fu.call(obj) // 立即执行而且改变了this指向obj里面的str
// fu.apply(obj) // 立即执行而且改变了this指向obj里面的str
fu.bind(obj)() // 不是立即执行,它返回值是一个函数所以要在加一个()才能调用
fu() // Window
字符串中出现最多的次的字符出现多少次数?(逻辑题):
let a ='aaaxxxxxllllllwwwwwlwwwwwwccc'
let q = {}
for (let i = 0; i < a.length; i++) {
const ax = a.charAt(i)
if (q[ax]) {
q[ax]++
}else{
q[ax] = 1
}
}
console.log(q); //{a: 3, x: 5, l: 7, w: 11, c: 3}
// 统计出来最大值
let max = 0
for (let key in q) {
if( max < q[key]) {
max = q[key]
}
}
console.log(max); //11
// 拿出最大值去比较
for(let key in q) {
if(q[key] === max){
console.log('出现次数最多字母是:' + key); // 出现次数最多字母是:w
console.log('出现的次数为:' + max + '次'); // 出现的次数为:11
}
}
12、箭头函数与普通函数区别?
1. this指向问题
箭头函数中的this只在箭头函数定义时候就决定的,而且不可以通过(call、apply、bind)修改它的指向
箭头函数的this指向定义时候,外层第一个函数。
2.箭头函数不能new (不能当做构造函数)
3.箭头函数没有prototype,普通函数有prototype
4.箭头函数么有arguments对象
13、Get和post的区别:
答:
Get是向服务器请求数据,post是向服务器提交数据。
数据层:
Get在数据传输时可见,post不可见。
Get传输数据量小,post比较大。
效率:
Get执行效率却比Post方法好。
提交:
Get是form提交的默认方法。
get推荐使用在查询信息时的使用,post适用于登录,注册等带有信息的请求。
14、深拷贝与浅拷贝区别?
数组与对象的赋值浅拷贝(藕断丝连)
解构赋值在一维数组属于深拷贝,多维数组属于浅拷贝
通过JSON.parse(JSON.stringify(xxx))可以实现深拷贝
手写标准深拷贝
<script>
// 引用数据类型(数组与对象)
function deepClone(source) {
// [] => Array(基类) {} => Object
const targetobj = source.constructor === Array ? [] : {};
for (let keys in source) {
if (source.hasOwnProperty(keys)) {
// 如果是引用数据类型
if (source[keys] && typeof source[keys] === 'object') {
//维护层代码
targetobj[keys] = source.constructor === Array ? [] : {};
// 递归
targetobj[keys] = deepClone(source[keys])
} else {
// 若基本数据类型,直接赋值
targetobj[keys] = source[keys];
}
}
}
return targetobj
}
let objC = {
ff: 'name',
gg: 1,
obj: { a: '111', age: 22 },
arr: [1, 2, 3, 45, 5]
}
let newobjC = deepClone(objC)
newobjC.ff = '修改数据'
newobjC.arr.push(12)
console.log(objC, newobjC);
</script>
懒加载实现
<!-- 懒加载原理 -->
<script>
let num = document.getElementsByTagName("img").length;
let img = document.getElementsByTagName("img");
let n = 0;
lazyload()
window.onscroll = lazyload
function lazyload(){
let seeHeight = document.documentElement.clientHeight;
let srollTop = document.documentElement.scrollTop || document.body.scrollTop;
for(let i = n; i< num;i++){
if(img[i].offsetTop < seeHeight + srollTop){
if(img[i].getAttribute("src") == "./assets/logo.png" ){
img[i].src = img[i].getAttribute("data-src")
}
n = i+1
}
}
}
</script>