JavaScript面试题

1.      js基础知识

1.1.       变量类型和计算

1.1.1.    常见值类型

·            

desc

         

desc

1.1.2.    常见引用类型

·            

desc

         

desc

1.1.3.    if 语句和逻辑运算

·        truly变量 : !!a === true的变量

      truely  变量在   中直接返回 

·        falsely变量:  !!a=== false的变量

      falsely变量在&&直接返回

·            

desc

1.1.4.    面试题和答案

·        typeof能判断那些类型

      识别所有值类型

      参考代码图片

         

desc

      识别函数

      参考代码图片

         

desc

      判断是否是引用类型(不可再细分)

      参考代码图片

         

desc

·        何时使用===   何时使用==

         

desc

·        值类型和引用类型的区别

         

desc

·        手写深拷贝(递归)

      参考代码图片

         

desc

      参考代码

      // 深克隆
const obj1 = {
    age: 20,
    name: 'xxx',
    address: {
        city: 'beijing'
    },
    arr: ['a', 'b', 'c']
}

const obj2 = deepClone(obj1)
obj2.address.city = 'shanghai'
console.log('obj1+' + obj1.address.city);
console.log('obj2+' + obj2.address.city);

//
深拷贝
function deepClone(obj = {}) {
    if (typeof obj !== 'object'  obj == null) {<w:br/>        // obj
null,或者不是对象和数组,直接返回<w:br/>        return obj<w:br/>    }<w:br/>    // 初始化返回结果<w:br/>    let result<w:br/>    if (obj instanceof Array) {<w:br/>        result = []<w:br/>    } else {<w:br/>        result = {}<w:br/>    }<w:br/>    for (let key in obj) {<w:br/>        // 保证key不是原型的属性<w:br/>        if (obj.hasOwnProperty(key)) {<w:br/>            // 递归调用<w:br/>            result[key] = deepClone(obj[key])<w:br/>        }<w:br/>    }<w:br/>    return result<w:br/>}

      深拷贝个人理解

      判断是否(对象&数组)或空

      typeof obj !=="object"

      判断是不是数组

      obj  instanceof Array

      保证objkey不是原型的属性

      obj.hasOwnpropery(key)

      递归调用

1.2.       原型和原型链

1.2.1.    class和继承

·        class

      参考代码

      class Student {
    constructor(name, number) {
        this.name = name
        this.number = number
    }
    sayHi() {
        console.log(
            `
姓名${this.name},学号${this.number}`
        );
    }
}

//
通过类 new 对象/实例
const xialuo=new Student('
夏洛',100)
console.log(xialuo.name)
console.log(xialuo.number)
xialuo.sayHi()

const madongmei=new Student('
马冬梅',200)
console.log(madongmei.name)
console.log(madongmei.number)
madongmei.sayHi()

·        继承

      参考代码

      // 父类
class People {
    constructor(name) {
        this.name = name
    }
    eat() {
        console.log(
            `${this.name} eat something`
        );
    }
}

//
子类
class Student extends People {
    constructor(name, number) {
        // super 
发给父类处理
        super(name)
        this.number = number
    }
    sayHi() {
        console.log(`
我叫${this.name},是一名学生,这次考试${this.number}`);
    }
}
//
子类
class Teacher extends People {
    constructor(name, major) {
        // super
发给父类处理
        super(name)
        this.major = major
    }
    teach() {
        console.log(`
我叫${this.name},是一名${this.major}老师`);
    }
}


//
通过类new对象/实例
const xialuo = new Student('
夏洛', 100)
console.log(xialuo.name);
console.log(xialuo.number);
xialuo.sayHi()

const madongmei = new Teacher('
马冬梅', '语文')
console.log(madongmei.name);
console.log(madongmei.major);
madongmei.teach()

1.2.2.    原型和原型链

·        原型

         

desc

         

desc

      原型

      每个class 都有显示原型prototype

      每个实例都有隐式原型__proto__

      实例的_proto__指向对应classprototype

      基于原型的执行规则

      获取属性xialuo.name 或执行方法xialuo.sayhi()

      先在自身属性和方法寻找

      如果找不到则自动去_proto_中查找

·        原型链

         

desc

         

desc

      hasOwnProperty() 是否有这个属性

·        类型判断-instanceof

      判断变量属于哪个class(包括父类)

·        重要提示

      class ES6语法规范,由ECMA 委员会发布

      ECMA只规定语法规则,即我们代码的书写规范,不规定如何实现

      以上实现方式都是V8引擎的实现方式,也是主流的

1.2.3.    面试题和答案

·        如何准确判断一个变量是不是数组?

      类型判断-instanceof

      判断变量属于哪个class(包括父类)

         

desc

·        手写一个简易的jQuery ,考虑插件和扩展性

·        class的原型本质,怎么理解?

      参考代码

      // 父类
class People {
    constructor(name) {
        this.name = name
    }
    eat() {
        console.log(
            `${this.name} eat something`
        );
    }
}
//
子类
class Student extends People {
    constructor(name, number) {
        // super 
发给父类处理
        super(name)
        this.number = number
    }
    sayHi() {
        console.log(`
我叫${this.name},是一名学生,这次考试${this.number}`);
    }
}
//
子类
class Teacher extends People {
    constructor(name, major) {
        // super
发给父类处理
        super(name)
        this.major = major
    }
    teach() {
        console.log(`
我叫${this.name},是一名${this.major}老师`);
    }
}


//
通过类new对象/实例
const xialuo = new Student('
夏洛', 100)
console.log(xialuo.name);
console.log(xialuo.number);
xialuo.sayHi()

const madongmei = new Teacher('
马冬梅', '语文')
console.log(madongmei.name);
console.log(madongmei.major);
madongmei.teach()




1.3.       作用域和闭包

1.3.1.    面试题和答案

·        this的不同应用场景,如何取值?

      this取什么样的值,是在函数执行的时候,不是函数定义的时候确定的

·        手写bind函数

      参考代码

      // 模拟 bind
Function.prototype.bind1 = function () {
    //
将参数拆解为数组
    // Array.prototype.slice.call()
是一个固定的用法,可以将类数组转成数组
    const args = Array.prototype.slice.call(arguments)

    //
获取 this(数组第一项),就是调用bind1方法时,传递的第一个参数:{x: 100}
    const t = args.shift()

    //
谁调用函数,函数中的this指向的就是谁,fn1.bind1(...) 这样写,bind1函数中的this指向fn1
    const self = this
    //
返回一个函数
    return function () {
        //
调用fn1函数,并把fn1中的this改为{x: 100},此处的args就是10,20,30
        return self.apply(t, args)
    }
}

function fn1(a, b, c) {
    console.log('this', this)
    console.log(a, b, c)
    return 'this is fn1'
}

const fn2 = fn1.bind1({ x: 100 }, 10, 20, 30)
const res = fn2()
console.log(res)

·        实际开发中闭包的应用场景,举例说明

      闭包的作用:隐藏数据,只提供api

      参考代码

      // 闭包隐藏数据,只提供API
function createCache() {
    const data = {}   //
闭包中的数据,被隐藏,不被外界访问
    return {
        set: function (key, val) {
            data[key] = val
        },
        get: function (key) {
            return data[key]
        }
    }
}

const c = createCache()
c.set('a', 100)
console.log(c.get('a'));

1.3.2.    作用域和自由变量

·        作用域

         

desc

      全局作用域

      函数作用域

      块级作用域(es6

         

desc

         

desc

·        自由变量

      一个变量在当前作用域没有定义,但被使用了

      向上级作用域,一层一层依次寻找,直至找到为止

      如果到全局作用域都没找到,则报错xx is not defined

         

desc

1.3.3.    闭包

·        作用域应用的特殊情况,有两种表现:

      函数作为返回值被返回

         

desc

      函数作为参数被传递

         

desc

·        自由变量的查找规则:是在函数定义的地方,向上级作用域查找 ,而不是在执行的地方!!!

1.3.4.    this的几种赋值情况

·        this取什么样的值,是在函数执行的时候,不是函数定义的时候确定的

·        this的应用场景

      作为普通函数

         

desc

      使用call apply bind

         

desc

      作为对象方法被调用

         

desc

      箭头函数

      箭头函数只取上面一层作用域的值

         

desc

      class方法中调用

         

desc

1.4.       异步和单线程

1.4.1.    单线程

·        JS是单线程语言,只能同时做一件事

·        浏览器和nodeJS已支持JS启动进程,如Web Worker

·        JSDOM渲染共用同一个线程,因为JS可修改DOM结构

·        遇到等待(网络请求,定时任务)不能卡住

·        需要异步

·        回调callback函数形式

1.4.2.    异步

·        异步和同步

      基于JS是单线程语言

      异步不会阻塞代码执行

      同步会阻塞代码执行

      参考代码图片

         

desc

1.4.3.    应用场景

·        网络请求,如ajax图片加载

·        定时任务,如setTimeout

1.4.4.    callback hellPromise

·        callback hell

      参考代码图片

         

desc

·        Promise

      参考代码图片

         

desc

      参考代码

      function loadImg(src) {
    const p = new Promise(
        (resolve, reject) => {
            const img = document.createElement('img')
            img.onload = () => {
                resolve(img)
            }

            img.onerror = () => {
                const err = new Error(`
图片加载失败${src}`)
                reject(err)
            }
            img.src = src
        }
    )
    return p
}
//
加载一张图片
// const url = 'https://im-jason.top/images/avatar.jpg'
// loadImg(url).then(img => {
//     console.log(`
${img.width}`)
//     return img
// }).then(img => {
//     console.log(`
${img.height}`);
// }).catch(ex => console.error(ex))


//
加载两张图片
const url1 = 'https://im-jason.top/images/avatar.jpg'
const url2 = 'https://im-jason.top/images/money.jpg'
loadImg(url1).then(img1 => {
    console.log(`img1
${img1.width}`)
    return img1
}).then(img1 => {
    console.log(`img1
${img1.height}`)
    return loadImg(url2)
}).then(img2=>{
    console.log(`img2
${img2.height}`)
}).catch(ex => console.error(ex))

1.4.5.    面试题

·        同步和异步的区别是什么?

      异步基于JS是单线程语言

      异步不会阻塞代码执行

      同步会阻塞代码执行

·        手写用Promise加载一张图片?

      参考代码

      function loadImg(src) {
    const p = new Promise(
        (resolve, reject) => {
            const img = document.createElement('img')
            img.onload = () => {
                resolve(img)
            }

            img.onerror = () => {
                const err = new Error(`
图片加载失败${src}`)
                reject(err)
            }
            img.src = src
        }
    )
    return p
}
//
加载一张图片
// const url = 'https://im-jason.top/images/avatar.jpg'
// loadImg(url).then(img => {
//     console.log(`
${img.width}`)
//     return img
// }).then(img => {
//     console.log(`
${img.height}`);
// }).catch(ex => console.error(ex))


//
加载两张图片
const url1 = 'https://im-jason.top/images/avatar.jpg'
const url2 = 'https://im-jason.top/images/money.jpg'
loadImg(url1).then(img1 => {
    console.log(`img1
${img1.width}`)
    return img1
}).then(img1 => {
    console.log(`img1
${img1.height}`)
    return loadImg(url2)
}).then(img2=>{
    console.log(`img2
${img2.height}`)
}).catch(ex => console.error(ex))

·        前端使用异步的场景?

      网络请求,如ajax图片加载

      定时任务,如setTimeout

1.4.6.    小结

·        单线程和异步,异步和同步区别

·        前端异步的应用场景︰网络请求&定时任务

·        Promise解决callback hell

2.      JS Web API

2.1.       JS基础知识到JS Web API

2.1.1.    JS基础知识,规定语法(ECMA 262标准)

2.1.2.    JSWeb API ,网页操作的 API ( W3C标准)

2.1.3.    前者是后者的基础,两者结合才能真正实际应用

2.2.       Web-API_DOM

2.2.1.    DOM是哪种数据结构

·        DOM

2.2.2.    DOM操作的常用API

·        DOM节点操作

      节点操作

      参考代码图片

         

desc

      DOM节点的property

      参考代码图片

         

desc

·        DOM结构操作

      新增/插入节点

      参考代码图片

         

desc

      移动节点

      参考代码图片

         

desc

      获取父元素

      参考代码图片

         

desc

      获取子元素列表

      参考代码图片

         

desc

      如果是需要正常的dom节点 ,不是需要text,所以要操作过滤

      参考代码图片

         

desc

      删除子元素

      参考代码图片

         

desc

2.2.3.    attributeproperty的区别

·        property : 修改对象属性,不会体现到html结构中

·        attribute : 修改html属性,会改变html结构

·        两者都有可能引起 DOM重新渲染

2.2.4.    一次性插入多个DOM节点,考虑性能

·        DOM操作非常昂贵,避免频繁的DOM操作

      DOM查询做缓存

      参考代码图片

         

desc

      将频繁操作改为一次性操作

      参考代码图片

         

desc

2.3.       Web-API_BOM

2.3.1.    bom

·        Brower Object Model浏览器对象模型

2.3.2.    面试题

·        如何识别浏览器的类型

      navigator

·        分析拆解url各个部分

      location

2.3.3.    知识点

·        navigator

      浏览器信息

·        screen

      屏幕信息

·        location

      地址信息 分析url

·        history

      上一页,下一页的信息

·        参考代码图片

         

desc

2.4.       Web-API_事件

2.4.1.    面试题

·        编写一个通用的事件监听函数

      参考代码图片

         

desc

      参考代码

      function bindEvent(elem, type, selector, fn) {
    if (fn == null) {
        fn = selector
        selector = null
    }
    elem.addEventListener(type, event => {
        const target = event.target
        if (selector) {
            //
代理绑定
            if (target.matches(selector)) {
                fn.call(target, event)
            }
        } else {
            //
普通绑定
            fn.call(target, event)
        }
    })
}

·        描述事件冒泡的流程

      基于DOM树形结构

      事件会顺着触发元素往上冒泡

      应用场景代理

·        无限下拉的图片列表,如何监听每个图片的点击?

      事件代理

      e.target 获取触发元素

      matches来判断是否是触发元素

2.4.2.    知识点

·        事件绑定

      参考代码图片

         

desc

·        事件冒泡

      参考代码图片

         

desc

·        事件代理

      优点

      代码简洁

      减少浏览器内存占用

      但是,不要滥用

      参考代码图片

         

desc

2.5.       Web-API_Ajax

2.5.1.    面试题

·        手写一个简易的Ajax

      参考代码图片

         

desc

      参考代码

      function ajax(url) {
    const p = new Promise((resolve, reject) => {
        const xhr = new XMLHttpRequest()
        xhr.open('get',url, true)
        xhr.onreadystatechange = function () {
            // console.log(xhr.readyState);
            if (xhr.readyState === 4) {
                if (xhr.status === 200) {
                    resolve(
                        JSON.parse(xhr.responseText)
                    )
                    alert(xhr.responseText)
                } else if (xhr.status === 404) {
                    reject(new Error('404 not found'))
                }
            }
        }
        xhr.send(null)
    })
    return p
}

const url = './test.json'
ajax(url).then(res => console.log(res))
    .catch(err => console.error(err))

·        跨域的常见实现方式

      JSONP

      访问https://imooc.com/,服务端一定返回一个html文件吗?

      服务器可以任意动态拼接数据返回,只要符合html格式要求

      同理于<script src= "https://imooc.com/getData.js”>

      跨域

      <script>可绕过跨域限制

      服务器可以任意动态拼接数据返回

      所以,<script>就可以获得跨域的数据,只要服务端愿意返回

      参考代码图片

         

desc

      jQuery实现 jsonp

      参考代码图片

         

desc

      CORS(服务端支持)

      CORS-服务器设置http header

      参考代码图片

         

desc

2.5.2.    知识点

·        XMLHttpRequest

·        状态码

·        跨域:同源策略,跨域解决方案

2.5.3.    XMLHttpRequest

·        get

      参考代码图片

         

desc

·        post

      参考代码图片

         

desc

2.5.4.    xhr.readyState

·        0 - (未初始化)还没有调用send()方法

·        1 - (载入)已调用send()方法,正在发送请求

·        2 - (载入完成)send()方法执行完成,已经接收到全部响应内容

·        3 -(交互)正在解析响应内容

·        4 - (完成)响应内容解析完成,可以在客户端调用

2.5.5.    xhr.status

·        2xx–表示成功处理请求,如200

·        3xx–需要重定向,浏览器直接跳转,如301 302 304

·        4xx–客户端请求错误,如404 403

·        5xx –服务器端错误

2.5.6.    跨域

·        什么是跨域(同源策略)

      同源:协议、域名、端口,三者必须一致

      ajax请求时,浏览器要求当前网页和server必须同源(安全)

      所有的跨域,都必须经过server端允许和配合

      未经server端允许就实现跨域,说明浏览器有漏洞,危险信号

      加载图片css js可无视同源策略

      <img src=跨域的图片地址/>

      <img />可用于统计打点,可使用第三方统计服务

      <link href=跨域的css地址/>

      <link /> <script>可使用CDN CDN一般都是外域

      <script src=跨域的js地址></script>

      <script>可实现JSONP

2.5.7.    实际项目中ajax的常用插件

·        jQuery

·        fetch

      参考代码图片

         

desc

·        axios

2.6.       存储与节流防抖

2.6.1.    面试题

·        存储

      描述cookie localStorage sessionStorage 区别

      cookie

      本身用于浏览器和sever通讯

      借用到本地存储来

      可用document.cookie=‘...’来修改

      cookie的缺点

      存储大小,最大4kb

      http请求是需要发送到服务端,增加请求数据量

      只能用document.cookie=‘...’来修改,太过简陋

      localStoragesessionStorage

      localStorage

      数据会永久存储,除非代码或手动删除

      sessionStorage

      数据只存在于当前会话,浏览器关闭则清空

      一般用localStorage会多一些

      优点

      HTML5专门为存储而设计,最大可存5M

      API简单易用

      存入数据

      getItem

      取出数据

      setItem 

      参考代码图片

         

desc

      不会随着http请求被发送出去

2.6.2.    防抖debounce

·        监听一个输入框的,文字变化后触发change 事件,直接用keyup事件,则会频发触发change 事件,防抖:用户输入结束或暂停时,才会触发change事件

      参考代码图片

         

desc

      参考代码

      // 防抖
function debounce(fn, delay = 500) {
    // timer
是闭包中的
    let timer = null

    return function () {
        if (timer) {
            clearTimeout(timer)
        }
        timer = setTimeout(() => {
            fn.apply(this, arguments)
            timer = null
        }, delay)
    }
}
const input1 = document.getElementById('input1')
input1.addEventListener('keyup', debounce(function (e) {
    console.log(e.target)
    console.log(input1.value)
}, 600))

·        传入的参数在返回的function中通过apply被调用,故function中调用fn时,传入的参数默认为event对象,即arguments中的值为键盘事件对象event

2.6.3.    节流throttle

·        函数频繁触发,内部的逻辑频繁执行,导致消耗大,内部有请求时甚至容易卡死,故需设置不论函数多么频繁的触发,但只有间隔指定的时间才会执行一次函数内容,不是每次触发都执行函数内容

      参考代码图片

         

desc

      参考代码

      // 节流
function throttle(fn, delay = 100) {
    let timer = null

    return function () {
        if (timer) {
            return
        }
        timer = setTimeout(() => {
            fn.apply(this, arguments)
            timer = null
        }, delay)
    }
}

div1.addEventListener('drag', throttle(function (e) {
    console.log(e.offsetX, e.offsetY)
}))

·        传入的参数在返回的function中通过apply被调用,故function中调用fn时,传入的参数默认为event对象,即arguments中的值为键盘事件对象event