# 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对象上,letconst不会
var varValue = 1;
let letValue = 2;
const constValue = 3;
console.log(window.varValue, window.letValue, window.constValue);
// 1 undefined undefined
  • var存在变量提升,被提升到作用域顶部,letconst不会
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):变量在用letconst声明之前,他们是不可以访问的。如果在声明语句之前就访问变量,会抛出一个ReferenceError
//TDZ开始
temp = "abc";//ReferenceError 
console.log(temp)//ReferenceError 
let temp;//TDZ结束
  • letconst声明的变量只在其代码块内有效
{
    const constValue = 18;
    let letValue = 20;
    var varValue = 22
}
constValue;//// Uncaught ReferenceError: constValue is not defined
letValue;//// Uncaught ReferenceError: letValue is not defined
22
  • constlet不允许在同一个作用域内重复声明一个变量,且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

# 箭头函数和普通函数的区别

  1. 箭头函数不能作为构造函数,不能使用new。
  2. 箭头函数没有原型属性。
  3. 箭头函数的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