# call和apply有什么区别?
# 箭头函数的作用
# 如何避免地狱回掉
# sass特性
# 组件component如何使用到父级页面的样式
# 优化前端性能总结
# 如何解决地狱回调
# ES5和ES6的区别
# 小程序打开页面的数量限制是多少
10个。在微信小程序中打开的页面不能超过10个,达到10个页面后,就不能再打开新的页面。
# npm i --save 和npm i --save -dev的区别
- 🍇 npm i --save:会把依赖安装到package.json中dependencies字段下,用于安装项目运行时需要的依赖,eg🥚:vue、JQuery
- 🍇 npm i --save -dev:会把依赖安装到package.json中devDependencies字段下,安装的依赖在项目运行的时候不需要,只是开发的时候需要,eg🥚:babel
# 离开了页面怎么访问原页面的数据
- 离开了页面,返回时还能获取该页面的数据 =》 keep-alive
- 离开了页面,让下一个页面还能访问上一个页面的数据 =》 用vuex存起来
# typeof null的结果是object,那null是对象吗?
答案:不是 解析:这是一个历史悠久的bug,在JS的最初版本中使用的是32位系统,为了性能考虑使用的是低位存储变量的类型信息,000开头的表示对象,而null代表是全零,所以才会出现object这个错误的结果
# var、let以及const的区别?
var在全局作用域下声明的变量会被挂载到window对象上,let、const不会
var varValue = 1;
let letValue = 2;
const constValue = 3;
console.log(window.varValue, window.letValue, window.constValue);
// 1 undefined undefined
var存在变量提升,被提升到作用域顶部,let、const不会
function sayHi() {
//var name;//伪代码,声明被提升了
console.log(name);//undefined
console.log(age);//age is not defined
console.log(like);//like is not defined
var name = "Banana";
let age = 21;
const like = 935
}
sayHi();
- 暂时性死区(TDZ):变量在用
let、const声明之前,他们是不可以访问的。如果在声明语句之前就访问变量,会抛出一个ReferenceError
//TDZ开始
temp = "abc";//ReferenceError
console.log(temp)//ReferenceError
let temp;//TDZ结束
let、const声明的变量只在其代码块内有效
{
const constValue = 18;
let letValue = 20;
var varValue = 22
}
constValue;//// Uncaught ReferenceError: constValue is not defined
letValue;//// Uncaught ReferenceError: letValue is not defined
22
const、let不允许在同一个作用域内重复声明一个变量,且const声明的是常量不可变(值是原始数据类型的不可改变,值是引用类型绑定的地址,地址不可变,但属性值能改变)。
const constValue = 18;
const constValue = 20//Identifier 'constValue' has already been declared
const constValue = 18;
constValue = 20//Assignment to constant variable.
let a = 5;
a = 7;//可以的
let a = 8;//Identifier 'a' has already been declared
⚠️ 最后我们来看一道老朋友:
for (var i = 0; i < 3; i++){
setTimeout(function(){
console.log(i)//3,3,3
},1000)
}
console.log("var定义的i",i)//3
在循环中,console.log(i)执行的都是同一个变量i,所以输出的是i最后的值。
那如果是let定义i又会是什么情况呢?
for (let i = 0; i < 3; i++){
setTimeout(function(){
console.log(i)//0,1,2
},1000)
}
console.log("let定义的i",i)//undefined,因为let声明的i只在for循环代码块里有效
因为for (let i = 0; i < 3; i++)中,圆括号形成了一个隐藏的作用域,每次都会创建一个新变量。为了更好的理解可以看一下代码:
// 伪代码
(let i = 0) {
setTimeout(function timer() {
console.log(i);
}, 0);
}
(let i = 1) {
setTimeout(function timer() {
console.log(i);
}, 0);
}
(let i = 2) {
setTimeout(function timer() {
console.log(i);
}, 0);
};
变量的赋值可以分为三个阶段:
- 创建变量,在内存中开辟空间
- 初始化变量,将变量初始化为undefined
- 真正赋值
关于let、var和function:
- let 的「创建」过程被提升了,但是初始化没有提升。
- var 的「创建」和「初始化」都被提升了。
- function 的「创建」「初始化」和「赋值」都被提升了。
# js的各种位置是怎样的
比如clientHeight,scrollHeight,offsetHeight ,以及scrollTop, offsetTop,clientTop的区别?
| 属性名 | 区别 |
|---|---|
| clientHeight | 表示元素的可视区域的高度,不包含border和滚动条.样式的height+上下padding |
| offsetHeight | 表示元素的可视的高度,包含了border和滚动条,还包含::before,::after这样的伪元素 |
| scrollHeight | 表示了所有区域的高度,包含了因为滚动被隐藏的部分 |
| offsetTop | 当前元素顶部距离最近父元素(position:relative;)顶部的距离,和有没有滚动条没有关系。 |
| scollTop | 在有滚动条时,为元素顶部被遮住部分的高度。在没有滚动条时scrollTop==0恒成立。 |
# 什么是回调地狱
# 什么是回调函数
回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。
function getMessage(msg, callback){
setTimeout(function(){
console.log(msg);
callback();
},2000);
}
function displayMessage(){
console.log('Happy happy!');
}
getMessage('Hi banana',displayMessage)
//两秒后
//Hi Banana
//Happy happy
在上面这个栗子中,我们给函数getMessage()传递了两个参数。第一个是msg变量,第二个是回调函数的引用(指针)。在getMessage()完成后,将调用回调函数displayMessage()。
# 什么是回调地狱
当多个异步函数一个接着一个地执行,就会产生回调地狱。
# 如何避免回调地狱
- 代码习惯
- 将功能移开,保证简洁
- 模块化
- 处理每个回调中的每个错误
- Promise
- async-await
# 箭头函数和普通函数的区别
- 箭头函数不能作为构造函数,不能使用new。
- 箭头函数没有原型属性。
- 箭头函数的this指向其上下文的this,没有办法改变其指向。普通函数的this指向调用它的对象。
# Ajax和Axios和Fetch的区别?
Ajax是一种快速创建动态网页的技术,他的核心是XHR(XMLHttpRequest)对象。多个请求之间如果有先后关系的话,就会出现回调地狱。
var xhr = new XMLHttpRequest();
xhr.open('get','banana.php',true)//三个参数:请求类型、请求路径、是否异步请求
xhr.send(null)//接受一个参数:要作为请求主体发送的数据。如果不需要,则传入null
if(xhr.status >= 200 && xhr.status<300 || xhr.status == 304){
alert(xhr.responseTest);
}else{
alert('unsuccessful'+xhr.status)
}
jQuery Ajax是对原生XHR的封装,使用起来更方便了。
$ajax({
methods:'post',
url:'./banana/6hz',
data:{
name:'nico'
},
dataType:dataType,
success:function(){},
error:function(){}
})
Axios是基于Promise上的,本质上也是对原生XHR的封装。不但可以在客户端使用,也可以在nodejs端使用。
axios.post('./banana/6hz',{
name:'banana',
age:18
}).then(function(response){
console.log(response);
}).catch(function(error){
console.log(error);
})
# 函数的节流和防抖是什么?
# 防抖(debounce)
- 理解:在短时间内大量触发同一事件,防抖的含义在于:在某个时间期限(如1000毫秒)内,只会执行一次函数。
- 实现原理:设置一个setTimeOut函数,约定在某一段时间后再触发事件处理,每次触发事件都会重新设置计时器,直到在X毫秒内无二次操作。(延迟执行)
function debounce(fn,delay){ let timer = null //借助闭包 return function() { if(timer){ clearTimeout(timer) } timer = setTimeout(fn,delay) } } function showTop () { var scrollTop = document.body.scrollTop || document.documentElement.scrollTop; console.log('滚动条位置:' + scrollTop); } window.onscroll = debounce(showTop,1000)- 如果在1s内没有再次触发滚动事件,那么就执行函数
- 如果在1ms内再次触发滚动事件,那么当前的计时取消,重新开始计时
- 应用:鼠标滚动事件
# 节流:事件触发后每隔一段时间触发一次,可触发多次。
- 原理:设计一种类似控制阀门一样定期开放的函数,也就是让函数执行一次后,在某个时间段内暂时失效,过了这段时间后再重新激活。
- 实现:如果短时间内大量触发同一事件,那么在函数执行一次之后,该函数在指定的时间期限内不再工作,直至过了这段时间才重新生效。
function throttle(func, wait) { let timeout = null return function() { let context = this let args = arguments if (!timeout) { timeout = setTimeout(() => { timeout = null func.apply(context, args) }, wait) } } } - 应用:搜索框input事件,例如要支持输入实时搜索可以使用节流方案
# 区别:
防抖和节流的区别:防抖是将多次执行变为最后一次执行,节流是将多次执行变为每隔一段时间执行。防抖每次触发事件都重置定时器,而节流在定时器到时间后再清空定时器。
# Promise和SetTimeout有什么区别?
Promise是微任务,setTimeout是宏任务。微任务的优先级比宏任务高。
# Json对象和字符串互转
- Json.parse(String):接受一个 JSON 字符串并将其转换成一个 JavaScript 对象
var b='{"name":"banana","age":"18"}';
var bToObj=JSON.parse(b);
console.log(typeof(bToObj));//object
- Json.stringify(obj):接受一个 JavaScript 对象并将其转换为一个 JSON 字符串
var a={"name":"banana","age":"18"};
var aToStr=JSON.stringify(a);
console.log(typeof(aToStr)); //string
# 如何将一个字符串倒序
- 字符串转数组,反转数组,数组转字符串。
var str = "http://localhost:8080/question/js/js01.html";
var strArr = str.split("");
var newStr = strArr.reverse();
console.log(newStr.join(""));//lmth.10sj/sj/noitseuq/0808:tsohlacol//:ptth
- 遍历字符串,再从末尾一个一个取出来放到新字符串
var str = "http://localhost:8080/question/js/js01.html";
var newStr = "";
for(let i = 0;i<str.length;i++){
let s = str.charAt(str.length-1-i);//从末尾一个一个取出来
newStr += s;//再一个个放到新字符串后面
}
- 字符串转数组,借助数组队列方法,数组转字符串。
var str = "http://localhost:8080/question/js/js01.html";
var strArr = str.split("");
var newArr = [];
for(let i = strArr.length-1;i >= 0;i--){
newArr.push(strArr[i])
}
// 或者
// while(strArr.length>0){
// newArr.push(strArr.pop())
// }
console.log(newArr.join(""))
# new操作符具体干了什么?
㊙:揭秘new背后的故事
- 创建一个空对象,并把这个空对象的原型指向构造函数的原型对象
- 使用 apply 把构造函数的作用域赋值给这个新对象(因此this就指向了这个新对象)
- 执行构造函数的代码,为空对象增加新的属性方法
- 返回这个新对象
# 数组去重
⭐set与展开运算符
function unique(arr) {
if (!Array.isArray(arr)) {
console.log('type error!')
return
}
return [...new Set(arr)]
}
⭐Array.from与Set
function unique(arr) {
if (!Array.isArray(arr)) {
console.log('type error!')
return
}
return Array.from(new Set(arr))
}
# onmousedown、onmouseup 以及 onclick 的执行顺序是怎么样的?
- onmousedown:当鼠标别被点击时触发
- onmouseup:当鼠标释放按钮时触发
- onclick:当完成整个鼠标点击事件时触发
$('.test').click(function(){
console.log('click')
})
$('.test').mouseup(function(){
console.log('mouseup')
})
$('.test').mousedown(function(){
console.log('mousedown')
})
//这里为了方便用了Jquery相应短的函数,输出顺序为mousedown、mouseup、click