前端面试一:js相关

79 阅读4分钟

1.null/undefined

两者的区别

typeof null === 'object // null是表示为“无”的对象
typeof undefined === 'undefined' // undefined是表示为“无”的原始值

Number(null) === 0
Number(undefined) === NaN

什么时候会出现undefined

// 1. 已声明,未赋值
let o;
console;log(o) // undefined
// 2.对象某个属性不存在
let obj = {};
console.log(obj.a)
// 3.函数调用时少参数
function fn(a,b){
    console.log(a,b)
}
fn()
// 4.函数的默认返回值
function fn2(){
}
console.log(fn2())

null 的特点

// 1.手动释放内存
let obj={};
obj = null;
// 2.原型链的顶端

2.ES6-filter

    let a = [1,2,3,14,15]
    // current 当前值;index 当前值的下标;array a数组对象
    let b = a.filter((current, index, array) => {
        console.log(current, index, array)
        return current > 10
    })
    console.log(a)
    console.log(b)

3.forEach和map(如何终止稍等?)

    let arr = ['a','b','c']
    
    // forEach没有返回值
    const res = arr.forEach((ele, index, ary) => {
        console.log(ele) // a
        console.log(index) // 0
        console.log(ary) // ['a','b','c']
        return ele + '1'
    })
    console.log(arr) // ['a','b','c']
    console.log(res) // undefined
    
    // map 有返回值
    const res = arr.map((ele, index, ary) => {
        console.log(ele) // a
        console.log(index) // 0
        console.log(ary) // ['a','b, 'c]
        return ele + '1'
    })
    console.log(arr) // ['a,''b, 'c]
    console.log(res) // ['a1','b1, 'c1']

4递归求和1-100

    // 1+2+3+4+.....
    function fn(a, b) {
        const sum = a + b
        const c = b + 1
        if (c > 100) {
            return sum
        } else {
            return fn(sum, c)
        }
    }
    const res = fn(1, 2)
    console.log(res) // 5050

5 var、let、const

// var的特点(不足)
// 1.声明提升
console.log(num) // undefined
var num = 123

// 2.变量覆盖
var a1 = 12
var a1 = 34
console.log(a1) // 34

// 3.没有块级作用域
 function fn() {
       for(var i = 0; i < 3; i++) {
            console.log(i) // 0 1 2
       }
       console.log(i) // 3
   }
   fn()
const 定义的是常量,不允许被修改
const 声明之后必须赋值
支持块级作用域

let 声明后可以不赋值,允许被修改,支持块级作用域

6 通过es6的相关知识将下面的值互换

let a = 1;
let b = 2;
[a,b] = [b, a]
console.log(a,b) // 2,1

7 es6数组去重

   const ary = [12, 43, 23, 12, 43, 55]
   const newAry = [...new Set(ary)]
   console.log(newAry) // [12, 43, 23, 55]

8 Promise相关题

  // 构造函数是同步执行的
    const promise = new Promise((resolve, reject) => {
        console.log(1)
        resolve()
        console.log(2)
    })
    // 异步执行的
    promise.then(() => {
        console.log(3)
    })
    console.log(4)
    // 1243  

9 闭包

1 闭包是什么? --- 方法里面返回一个方法

2 为什么会出现?

当函数执行完后,函数内部的局部变量会被回收;为了延长局部变量的生命周期,出现了闭包;也正因为闭包会导致局部变量常驻内存,所以也要慎用闭包。

3 此外,闭包还可以创建私有环境

// 1 闭包是什么? --- 方法里面返回一个方法
    function a () {
        let a1 = 2;
        return function() {
            return a1;
        }
    }
// 2 存在的意思---延长变量的生命周期;创建私有环境;
    为什么会出现?:
    当函数执行完后,函数内部的局部变量会被回收;为了延长局部变量的生命周期,出现了闭包;也正因为闭包会导致局部变量常驻内存,所以也要慎用闭包。
     // 01 延长变量的生命周期:
        let a = '小明';  // a是全局变量(会污染局部变量,也不会被垃圾回收机制回收)
        function fn1() {
            let b = 1;
            console.log(a) //'小明'
        }
        fn1()
        console.log(b) // b is not defined 报错
        
        // 改动成闭包
         function fn1() {
            let b = 1;
            return function () {
                return b
            }
        }
        const res = fn1()()
        console.log(res) // 1
 // 02 创建私有环境
 Vue中的data() 为什么是一个函数?
 data() {
     return {}
 }
 // 回答: 这是一个闭包的设计,vue通过闭包给每个组件设计了一个私有域空间,保证每个组件都有私有的作用域,就不会造成数据之间的相互干扰。
 
 下面就验证了闭包可以实现每个对象都有独立的词法作用域
          let make = function() {
           let num = 0;
           function changeBy(val) {
                num += val
           }
           return {
               add: function() {
                   changeBy(1)
               },
               reduce: function() {
                   changeBy(-1)
               },
               value: function() {
                   return num
               }
           }
       }

       let counter1 = make()
       let counter2 = make()
       console.log(counter1.value()) // 0
       counter1.add()
       counter1.add()
       console.log(counter1.value()) // 2
       counter2.add()
       console.log(counter2.value()) // 1
 

10 防抖与节流

防抖:将多次操作变成一次

<input />
// 防抖函数
     function debounce(fn, delay) {
            let timer = null
            return function(...args) {
                if (timer) {
                    clearTimeout(timer)
                    timer = null
                }
                timer = setTimeout(() => {
                    fn.apply(this, args)
                    clearTimeout(timer)
                    timer = null
                }, delay)
            }
        }
   
     const inputEl = document.querySelector('input')
     function request (e) {
            console.log(this) // <input>  不是window
            console.log(e) // InputEvent 不是undefined
     } 
     inputEl.addEventListener('input', debounce(request, 1000))
     
     补充:
     (function fun(){
        console.log(arguments) // 非数组
        // 先变成数组
        const ary = Array.prototype.slice.apply(arguments) // [1,2]
        // 连接
        console.log(ary.concat([3]))//[1,2,3]
    })(1,2)

节流:一定时间内只调用一次函数

应用场景:比如手指在盒子上疯狂滑动时,会不停触发事件,但节流让它有节奏慢慢地触发事件

    <div class="box"></div>
        const boxEl = document.querySelector('.box')
        function boxFn(e) {
            console.log(e) // touchEvent
            console.log(this) // <div class="box"></div>
        }
        function throttle(fn, delay) {
            let timer = null
            return function(...args) {
                if (timer) return false
                timer = setTimeout(() => {
                    fn.apply(this, args)
                    clearTimeout(timer)
                    timer = null
                }, delay)
            }
        }
        boxEl.addEventListener('touchmove', throttle(boxFn, 1000))

另一种节流写法:

    function throttle(fn, delay) {
            let end_time = 0;
            return function(...args) {
                const current_time = +new Date() // 触发时的时间戳(尤其是第二次)
                if(current_time - end_time < delay) return false
                fn.apply(this, args)
                end_time = +new Date() // 执行结束后的时间
            }  
        }

11 原型链和继承

原型: prototype,是函数所特有的;在函数的原型上挂东西,就是为了实例的继承
原型链: __proto__,是大家都有的;

当实例对象要查找某个属性时,先从当前实例属性去查找,如果找到了就返回,否则就顺着原型链一层层往上找,直到找到null为止。如果没有找到,则报错。