zoukankan      html  css  js  c++  java
  • 【建议】记录一次BAT一线互联网公司前端JavaScript面试

    前沿

    置身世外只为暗中观察!!!Hello大家好,我是魔王哪吒!

    面试内容

    你需要一些HTML和css的基础知识,掌握JavaScript和ES6的基本语法,对事物的好奇心。

    • 初级JS面试题
    • JS Web API,开发环境,运行环境
    • 原型,作用域,异步,Ajax,事件,webpack等

    观察所有面试题的考点

    1. 学习梳理知识体系图
    2. 找准知识体系,刻意掌握
    3. 善于总结观点,原理

    typeof能判断哪些类型

    typeof运算符:

    • 可以识别所有值的类型
    • 可以识别函数
    • 可以用力判断是否为引用类型

    考点:JS变量类型

    代码:

    // 判断所有值的类型
    let a; typeof a // 'undefined'
    const str = 'abc'; typeof str // 'string'
    const n = 10; typeof n // 'number'
    const b = true; typeof b // 'boolean'
    const s = Symbol('s'); typeof s // 'symbol'
    
    // 能够判断函数
    typeof console.log // 'function'
    typeof function(){} // 'function'
    
    // 能够识别引用类型
    typeof null // 'object'
    typeof ['a','b'] // 'object'
    typeof {x:10} // 'object'
    

    什么时候使用===,什么时候使用==

    考点:强制类型转换

    字符串拼接的类型转换

    const a = 10 + 1 // 11
    const b = 10 + '10' // '1010'
    const c = true + '1' // true1
    

    解决方法:

    10 + parseInt('1') // 11
    

    == 运算符的类型转换

    100 == '100' // true
    0 == '' // true
    0 == false // true
    false == '' // true
    null == undefined // true
    

    类型转换

    1. 除了0之外的所有数字,转换为布尔型都为true
    2. 除了“ ”之外的所有字符,转换为布尔型都为true
    3. nullundefined转换为布尔型为false

    什么时候使用===,什么时候使用==

    除了==null外,其他地方用===

    const obj = {a:1}
    if(obj.b==null){}
    // 相当于
    //if(obj.b === null || obj.b === undefined){}
    

    window.onload和DOMContentLoaded的区别

    考点:页面加载过程

    创建10个标签,点击后弹出对应的序号

    考点:JS作用域

    手写节流 throttle,防抖 debounce

    考点:性能,体验优化

    Promise解决了什么问题

    考点:JS异步

    值类型和引用类型的区别

    值类型的表示:

    // 值类型
    let a = 10
    let b = a
    a= 20
    console.log(b); // 10
    

    引用类型的表示:

    // 引用类型
    let a = { age:12 }
    let b = a
    b.age = 13
    console.log(a.age) // 13
    

    手写深拷贝

    1. 判断是值类型还是引用类型
    2. 判断是数组还是对象
    3. 递归

    代码:

    // 浅拷贝
    const obj1 = {
        age: 12,
        name: 'web',
        address: {
            city: 'beijing'
        },
    }
    
    const obj2 = obj1
    obj2.address.city = 'shanghai'
    console.log(obj1.address.city)
    

    结果:

    shanghai
    

    深拷贝:定义要拷贝的对象

    const obj2 = deepClone(obj1)
    obj2.address.city = 'shanghai'
    console.log(obj1.address.city)
    
    function deepClone(obj = {}){
        if(typeof obj !== 'object' || obj == null){
            // obj是null,或者不是对象和数组情况,直接返回
            return obj
        }
        
        // 初始化返回结果
        let result
        if(obj instanceof Array){
            result = []
        }else{
            result = {}
        }
        
        for (let key in obj) {
            // 保证Key不是原型的属性
            if(obj.hasOwnProperty(key)){
                // 递归调用
                result[key] = deepClone(obj[key])
            }
        }
        
        // 返回结果
        return result
    }
    

    深拷贝结果

    beijing
    

    原型,原型链

    1. 如何判断一个变量是不是数组?
    • a instanceof Array
    1. 手写一个简易的jquery,考虑插件和扩展性?

    代码:

    class jQuery {
        constructor(selector){
            const result = document.querySelectorAll(selector)
            const length = result.length
            for(let i=0; i<length; i++){
                this[i] = result[i]
            }
            this.length = length
        }
        get(index){
            return this[index]
        }
        each(fn){
            for(let i=0;i<this.length;i++){
                const elem = this[i]
                fn(elem)
            }
        }
        on(type,fn){
            return this.each(elem=>{
                elem.addEventListener(type,fn,false)
            })
        }
    }
    

    插件的扩展性

    jQuery.prototype.dialog = function(info) {
        alert(info)
    }
    

    复写机制:

    class myJQuery extends jQuery {
        constructor(selector) {
            super(selector)
        }
        // 扩展自己的方法
        study(){
            
        }
    }
    
    1. class的原型本质,如何理解?
    • 原型和原型链的图示

    class类似模板

    • constructor
    • 属性
    • 方法

    代码:

    // 类
    
    class Person {
        constructor(name, age) {
            this.name = name
            this.age = age
        }
        study() {
            console.log('study')
        }
    }
    
    // 通过类 new 对象,实例
    const web = new Person('哪吒', 10)
    

    class实际上是函数,可见的语法糖

    typeof Student
    结果:
    "function"
    typeof People
    结果:
    "function"
    

    继承

    • extends
    • super
    • 扩展,重写方法

    原型

    原型关系:

    1. 每个class都有显示原型prototype
    2. 每个实例都有隐式原型__proto__
    3. 实例的__proto__指向对应classprototype

    原型链

    console.log(Student.prototype.__proto__)
    console.log(People.prototype)
    console.log(People.prototype === Student.prototype.__proto__)
    

    类型判断 instanceof

    [] instanceof Array // true
    [] instanceof Object // true
    {} instanceof Object // true
    

    作用域,闭包

    • this的不同应用场景下,如何取值?
    • 手写bind函数
    • 实际开发中闭包的应用场景,举例说明

    创建10个a标签,点击弹出对应序号

    let i, a
    for(i=0; i<10; i++) {
        a=document.createElement('a')
        a.innerHTML = i + '<br>'
        a.addEventListener('click', function(e){
            e.preventDefault()
            alert(i)
        })
        document.body.appendChild(a)
    }
    

    作用域

    作用域分:

    1. 全局作用域
    2. 函数作用域
    3. 块级作用域(es6新增)

    代码:

    let a = 0
    function fn1() {
        let a1 = 100
        function fn2() {
            let a2 = 200
            function fn3() {
                let a3 = 300
                return a+a1+a2+a3
            }
            fn3()
        }
        fn2()
    }
    fn1()
    

    块级作用域

    if(true) {
        let x=100
    }
    console.log(x) // 会报错
    

    自由变量

    如果一个变量在当前作用域没有定义,但被使用了,向上级作用域去找,一层一层一次寻找,直到找到为止,如果到了全局作用域都没有找到,就会报错xx is not defined

    闭包

    闭包的表现:

    1. 函数作为参数被传递
    2. 函数作为返回值被返回

    做一个简单的cache工具

    闭包隐藏数据

    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'))
    

    函数作为返回值

    function create(){
        let a = 100
        return function(){
            console.log(a)
        }
    }
    let fn = create()
    let a = 200
    fn()
    

    结果:

    100
    

    函数作为参数

    function print(fn) {
        let a = 200
        fn()
    }
    
    let a = 100
    function fn(){
        console.log(a)
    }
    print(fn)
    

    结果:

    100
    

    所有自由变量的查找是在函数定义的地方,向上级作用域查找,不是执行的地方。

    this

    1. 作为普通函数被调用
    2. 使用call,apply,bind被调用
    3. 作为对象方法被调用
    4. class方法中被调用
    5. 箭头函数

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

    call指向,bind会返回新的函数

    代码:

    function fn1(){
        console.log(this)
    }
    fn1() // window
    
    fn1.call({x:10}) // {x:10}
    
    const fn2 = fn1.bind({x:20})
    fn2() // {x:20}
    

    代码:

    const Jeskson = {
        name: 'web',
        sayHi() {
            // this是指当前对象
            console.log(this)
        },
        wait(){
            setTimeout(function(){
                // this === window
                console.log(this)
            }
        }
    }
    

    代码:

    const Jeskson = {
        name: 'web',
        sayHi() {
            // this是指当前对象
            console.log(this)
        },
        wait() {
            setTimeout(()=>{
              // this指当前对象
              console.log(this)
            })
        }
    }
    

    代码:

    class People {
        constructor(name){
            this.name = name
            this.age = 20
        }
        sayHi(){
            console.log(this)
        }
    }
    const Jeskson = new People('web')
    Jeskson.sayHi() // Jeskson 对象
    

    手写bind函数

    Function.prototype.bind1 = function(){
        // 将参数解析为数组
        const args = Array.prototype.slice.call(arguments)
        // 获取this
        const t = args.shift()
        const self = this // 当前函数
        // 返回一个函数
        return function() {
            // 执行原函数,并返回结果
            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.bind ({x:100},10,20,30)
    const res = fn2()
    console.log(res)
    

    Chrome

    fn1.hasOwnProperty('bind')
    //false
    fn1.__proto__ == Function.prototype
    // true
    Function.prototype.apply/call/bind
    

    异步,单线程

    1. 同步和异步的区别是什么?
    2. 手写用Promise加载一张图片
    3. 前端使用异步的场景有哪些?

    代码:

    // setTimeout笔试题
    console.log(1)
    setTimeout(function(){
        console.log(2)
    },1000)
    console.log(3)
    setTimeout(function(){
        console.log(4)
    },0)
    console.log(5)
    
    1. 单线程和异步
    2. 应用场景
    3. callback hellPromise

    JS是单线程语言,同时只能做一件事
    浏览器和nodejs支持js启动进程,如web worker
    JS和dom渲染共用同一线程。

    异步:

    console.log(100)
    setTimeout(function(){
        console.log(200)
    },1000)
    console.log(300)
    

    同步

    console.log(100)
    alert(200)
    console.log(300)
    

    异步和同步的区别

    1. 基于js是单线程语言
    2. 异步不会阻塞代码的执行
    3. 同步会阻塞代码的执行

    手写用Promise加载一张图片

    function loadImg(src) {
        return 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
            }
        )
    }
    
    const url = ''
    loadImg(url).then(img => {
        console.log(img.width)
        return img
    }).then(img => {
        console.log(img.height)
    }).catch(ex=>console.error(ex))
    

    异步-单线程

    1. 单线程和异步,异步和同步区别
    2. 前端异步的应用场景
    3. Promise解决callback hell

    异步-callback

    异步,以回调callback函数形式

    异步-应用场景

    1. 网络请求
    2. 定时任务

    第一网络请求,如ajax图片加载,第二定时任务,如setTimeout

    //ajax
    console.log('start')
    $.get('./data.json', function(data1){
      console.log(data1)  
    })
    console.log('end')
    

    图片的加载

    console.log('start')
    let img = document.createElement('img')
    img.onload = function() {
        console.log('loaded')
    }
    img.src="/xxx.png"
    console.log('end')
    

    代码应用场景:

    // setTimeout
    console.log(100)
    setTimeout(function(){
        console.log(200)
    },1000)
    console.log(300)
    
    // setInterval
    console.log(100)
    setInterval(function(){
        console.log(200)
    },1000)
    console.log(300)
    

    异步-Promise

    Promise代码:

    function getData(url){
        return new Promise((resolve, reject) => {
            $.ajax({
                url,
                success(data) {
                    resolve(data)
                },
                error(err) {
                    reject(err)
                }
            })
        }
    }
    

    Promise

    const url1 = '/data1.json'
    const url2 = '/data2.json'
    const url3 = '/data3.json'
    getData(url1).then(data1=>{
        console.log(data1)
        return getData(url2)
    }).then(data2 => {
        console.log(data2)
        return getData(url3)
    }).then(data3 => {
        console.log(data3)
    }).catch(err => console.error(err))
    

    calback hell,回调地狱

    代码:

    // 获取第一个数据
    $.get(url1, (data1) => {
        console.log(data1)
        
        // 获取第二个数据
        $.get(url2, (data2) => {
            console.log(data2)
            
            // 获取第三个数据
            $.get(url3, (data3) => {
                console.log(data3)
                // 还可能获取更多的数据
            })
        })
    })
    

    DOM,BOM

    Dom操作,操作网页上的Dom元素,浏览器上的文本,图片

    Bom操作,操作浏览器上的一些事情,浏览器的导航,地址等

    事件绑定,ajax,存储

    DOM,Document Object Model

    1. DOM是哪种数据结构
    2. DOM操作的常用API
    3. attr和property的区别

    DOM的本质,节点操作,结构操作,DOM性能

    xml是一种可扩展的标记语言,可以描述任何结构的数据,html是一种特定的xml

    DOM的本质,它就是一颗树。

    • 获取Dom节点
    • attribute
    • property

    获取DOM节点

    const div1 = document.getElementById('div1') // 元素
    
    const divList = document.getElementsByTagName('div') // 集合
    
    console.log(divlist.length)
    console.log(divList[0])
    
    const containerList = document.getElementsByClassName('.container') // 集合
    
    const pList = document.querySelectorAll('p') // 集合
    

    DOM节点的property

    const pList = document.querySelectorAll('p');
    const p = pList[0]
    console.log(p.style.width) // 获取样式
    p.style.width='100px' // 修改样式
    console.log(p.className) // 获取class
    p.className='p1' // 修改class
    // 获取nodeName 和 nodeType
    console.log(p.nodeName)
    console.log(p.nodeType)
    

    propertyattribute

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

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

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

    DOM结构操作

    1. 新增/插入节点
    2. 获取子元素列表,获取父元素
    3. 删除子元素

    新增,插入节点

    const div1 = document.getElementById('div1')
    // 添加新节点
    const p1 = document.createElement('p')
    p1.innerHTML = 'this is p1'
    div1.appendChild(p1)
    const p2 = document.getElementById('p2')
    div1.appendChild(p2)
    

    获取子元素列表和获取父元素

    //获取子元素列表
    const div1 = document.getElementById('div1')
    const child = div1.childNodes
    
    //获取父元素
    const div1 = document.getElementById('div1')
    const parent = div1.parentNode
    

    删除节点

    const div1 = document.getElementById('div1')
    const child = div1.childNodes
    div1.removeChild(child[0])
    

    DOM性能

    DOM操作会耗费cpu,避免频繁,对DOM查询做缓存

    DOM查询做缓存

    // 不缓存DOM查询结果
    for(let=0; i<document.getElementsByTagName('p').length;i++) {
        // 每次循环,都会计算length,频繁进行dom查询
    }
    // 缓存dom查询结果
    const pList = document.getElementsByTagName('p')
    const length = pList.length
    for(let i = 0; i<length; i++){
        // 缓存length,只进行一次dom查询
    }
    

    讲频繁的操作修改为一次性操作

    const listNode = document.getElementById('list')
    
    // 创建一个文档片段,此时没有插入到dom树中
    const frag = document.createDocumentFragment();
    
    // 执行插入
    for(let x=0; x<10; x++) {
        const li = document.createElement("li")
        li.innerHTML="List item"+x
        frag.appendChild(li)
    }
    
    // 都完成后,再插入到dom树中
    listNode.appendChild(frag)
    

    问题:

    1. dom是哪种数据结构
    2. dom操作的常用api
    3. attrproperty的区别
    4. 一次性插入多个dom节点,考虑性能问题

    propertyattribute的区别

    1. property修改对象属性,不会体现到html结构中
    2. attribute修改html属性,会改变html结构

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

    • dom本质
    • dom节点操作
    • dom结构操作
    • dom性能

    BOM操作browser object model

    问题:如何识别浏览器的类型

    问题:分析拆解url各个部分

    BOM navigator

    代码:

    //navigator
    const ua = navigator.userAgent
    const isChrome = ua.indexOf('Chrome`)
    console.log(isChrome)
    

    事件绑定和事件冒泡

    1. 事件监听函数
    2. 描述事件冒泡的流程

    BOM screen

    代码:

    //screen
    console.log(screen.width)
    console.log(screen.height)
    

    BOM location

    BOM history

    ajax

    1. 手写一个简易的ajax
    2. 跨域的常用实现方式

    ajax XMLHttpRequest

    代码:

    //get请求
    const xhr = new XMLHttpRequest()
    xhr.open('GET','/api', true)
    xhr.onreadystatechange = function() {
        // 这里的函数异步执行
        if (xhr.readyState === 4){
            if(xhr.state === 200) {
                alert(xhr.responseText);
            }
        }
    }
    xhr.send(null)
    

    ajax 状态码

    xhr.readyState

    • 0为还没有调用send()方法
    • 1为已调用send()方法,正在发送请求
    • 2为send()方法执行完成,已经接收到全部响应内容
    • 3为正在解析响应内容
    • 4为响应内容解析完成,可以在客户端调用

    xhr.status

    • 2xx表示成功处理请求
    • 3xx表示需要重定向,浏览器直接跳转
    • 4xx表示客户端请求错误
    • 5xx表示服务器端错误

    ajax 跨域

    1. 什么是跨域,同源策略
    2. JSONP
    3. CORS,服务器端支持

    同源策略-跨域

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

    同源就是:协议,域名,端口,一致

    代码

    function ajax(url) {
        const p = new Promise((resolve, reject)=>{
            const xhr = new XMLHttpRequest()
            xhr.open('GET',url,true)
            xhr.onreadystatechange=function(){
                if(xhr.readyState === 4){
                    if(xhr.status===2000) {
                        resolve(
                            JSON.parse(xhr.responseText)
                        )
                    }else if(xhr.status === 404) {
                        reject(new Error('404 not found'))
                    }
                }
            }
            xhr.send(null)
        }
    }
    

    事件

    • 事件绑定
    • 事件冒泡
    • 事件代理

    事件-绑定

    代码:

    const btn = document.getElementById('btn1')
    btn.addEventListener('click',event=>{
        console.log('clicked')
    })
    

    代码:

    //通用的绑定函数
    function bindEvent(elem, type, fn)
        elem.addEventListener(type,fn)
    }
    
    const a = document.getElementById('web')
    bindEvent(a, 'click', e=>{
        e.preventDefault()
        alert('clicked')
    })
    

    事件-冒泡

    代码:

    <body>
    <div id="div1">
    <p id="p1">web</p>
    <p id="p2">it</p>
    <p id="p3">it</p>
    <p id="p4">it</p>
    </div>
    <div id="div2">
    <P id="p5">it</p>
    <p id="p6">it</p>
    </div>
    </body>
    
    const p1 = document.getElementById('p1')
    const body = document.body
    bindEvent(p1, 'click', e=>{
        e.stopPropagation() // 阻止冒泡
        alert('web')
    })
    bindEvent(body, 'click', e=>{
        alert('it')
    })
    

    事件-代理

    代码:

    <div id="div1">
    <a href="#">a1</a>
    <a href="#">a2</a>
    </div>
    <button>点击
    </button>
    
    const div1 = document.getElementById('div1')
    div1.addEventListener('click', e=>{
     const target = e.target
     if(e.nodeName === 'A') {
         alert(target.innerHTML)
     }
    })
    

    事件代理,可以减少浏览器内存占用

    通用的事件绑定函数

    const btn1 = document.geteElementById('btn1')
    bindEvent(btn1,'click',event=>{
        // console.log(event.target) // 获取触发的元素
        event.preventDefault() // 阻止默认行为
        alert('clicked')
    })
    
    const div2 = document.getElementById('div2')
    bindEvent(div2,'click',event=>{
        event.preventDefault()
        const target = event.target
        if(target.nodeName === 'A') {
            alert(target.innerHTML)
        }
    })
    

    代码:

    function bindEvent(elem, type, selector, fn) {
        if(fn==null) {
            fn = selector
            selector = null
        }
        elem.addEventListener(type, e=>{
            let target
            if(selector) {
                // 需要代理
                target = e.target
                if(target.matches(selector)){
                    fn.call(target, e)
                }
            } else {
                // 不需要代理
                fn(e)
            }
        })
    }
    

    存储

    cookielocalStoragesessionStorage区别

    1. localStorage数据会永远存储,除非代码回手动删除
    2. sessionStorage数据只存在于当前会话,浏览器关闭则清空

    一般用localStorage会更多一些

    存储-cookie

    • 存储大小,最大4kb
    • http请求时需要发送到服务器端,增加请求数据量
    1. localStorage 和 sessionStorage
    2. html5专门为存储而设计的,最大可为5M
    3. api简单易用setItemgetItem

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

    存储-localStorage

    代码:

    localStorage.setItem('a',100)
    localStorage.getItem('a')
    

    存储-sessionStorage

    代码:

    sessionStorage.setItem('a',100)
    sessionStorage.getItem('a')
    

    开发环境

    1. git
    2. 调试工具
    3. 抓包
    4. webpack babel
    5. linux常用命令

    git

    • 大型项目需要多人协作开发,必用git

    代码:常用git命令

    git add .
    git checkout xxx
    git commit -m 'xxx'
    git push origin master
    git pull origin master
    
    git branch
    git checkout -b xxx / git checkout xx
    git merge xxx
    

    调式

    chrome调试工具

    1. Elements
    2. Network
    3. Console
    4. Application
    5. debugger

    调式,抓包

    移动端h5页,查看网络请求,需要用工具抓包

    windows一般用fiddler

    webpck和babel

    代码:

    const path = require('path')
    
    module.exports = {
        mode: 'development',
        entry: path.join(__dirname, 'src', 'index.js'),
        output: {
            filename: 'bundle.js',
            path: path.join(__dirname,'dist')
        }
    }
    

    linux命令

    线上机器一般都是linux

    代码:

    ssh work
    ls
    
    rm -rf abc
    cd dist
    mv index.html index1.html
    
    rm a1.js
    touch d.js
    rm -rf d.js
    

    运行环境

    1. 运行环境既浏览器server端有nodejs
    2. 网页加载过程
    3. 性能优化
    4. 安全

    页面加载和渲染过程

    1. 从输入url到渲染出页面的整个过程
    2. window.onLoad和DOMContentLoaded的区别
    • 加载资源的形式
    • 加载资源的过程
    • 渲染页面的过程

    资源的形式:html代码,媒体文件,如图片,视频等,javascriptcss

    加载过程:dns解析,域名,ip地址。浏览器根据ip地址向服务器发起http请求。

    服务器处理http请求,并返回给浏览器。

    渲染过程,根据html代码生成dom tree,根据css代码生成cssom。讲dom treecssom整合行程render tree

    根据render tree渲染页面,遇到script暂停渲染,优先加载并执行js代码,完成再继续。

    页面渲染

    window.onLoadDOMContentLoaded

    代码:

    window.addEventListener('load',function(){
        // 页面的全部资源加载完才会执行,包括图片,视频等
    })
    document.addEventListener('DOMContentLoad',function(){
      // dom渲染完既可执行,此时图片,视频还可能没有加载完 
    })
    
    1. window.onload资源全部加载完才能执行,包括图片
    2. DOMContentLoaded DOM渲染完成即可,图片可能还没下载

    性能优化

    手写防抖,节流

    原则:

    1. 多使用内存,缓存或其他方法
    2. 减少cpu计算量,减少网络加载耗时

    让加载更快,渲染更快,减少资源体积。减少访问次数,合并代码,ssr服务器端渲染,缓存。

    dom查询进行缓存,频繁dom操作,合并到一起插入dom结构,节流throttle防抖debounce

    ssr

    服务器端渲染,讲网页和数据一起加载,一起渲染

    非ssr,先加载网页,再加载数据,再渲染数据

    懒加载

    代码:

    <img id="img1" src="preview.png" data-realsrc="abc.png"/>
    <script type="text/javascript">
    var img1 = document.getElementById('img1')
    img1.src = img1.getAttribute('data-realsrc')
    </script>
    

    防抖debounce

    1. 监听一个输入框,文字变化后触发change事件
    2. 直接用keyup事件,则会频繁触发change事件

    防抖,用户输入结束或暂停时,才会触发change事件。

    代码:

    const input1 = document.getElementById('input1')
    input1.addEventListener('keyup', function(){
        console.log(input1.value)
    }}
    

    代码:

    const input1 = document.getElementById('input1')
    let timer = null
    input1.addEventListener('keyup', function(){
        if(timer){
            clearTimeout(timer)
        }
        timer = setTimeout(()=>{
            // 模拟触发change事件
            console.log(input1.value)
            // 清空定时器
            timer = null
        },500)
    })
    

    debounce防抖代码:

    function debounce(fn, delay = 500) {
        // timer 是闭包中的
        let timer = null
        return function() {
            if(timer) {
                clearTimeout(timer)
            }
            timer = setTimeout(()=>{
                fn.apply(this, arguments)
                timer = null
            },delay)
        }
    }
    
    input1.addEventListener('keyup', debounce(()=>{
        console.log(input1.value)
    }),600)
    

    节流throttle

    拖拽一个元素时,要随时拿到该元素被拖拽的位置

    代码:

    const div1 = document.getElementById('div1')
    let timer = null
    div1.addEventListener('drag',function(e){
        if(timer){
            return
        }
        timer = setTimeout(()=>{
            console.log(e.offsetX, e.offsetY)
            timer = null
        },100)
    })
    

    节流代码:

    function throttle(fn, delay = 100) {
        let timer = null
        return function() {
            if(timer) {
                return
            }
            timer = setTimeout(()=>{
                fn.applay(this,arguments)
                timer = null
            },delay)
        }
    }
    
    div1.addEventListener('drag',throttle(function(e){
        console.log(e.offsetX,e.offsetY)
    },200))
    

    web安全

    1. 前端web常见的攻击方式有哪些?

    2. xss跨站请求攻击

    3. xsrf跨站请求伪造

    运行环境

    1. 页面加载:加载,渲染
    2. 性能优化:加载资源优化,渲染优化
    3. 安全:xss,csrf

    总结

    什么是变量提升

    1. var 和 let const 的区别
    2. typeof返回哪些类型
    3. 举例强制类型转换和隐式类型转换

    var和let const 的区别

    var是es5的语法,let const 是es6语法,var有变量提升

    var和Let是变量,const是常量,不可修改

    let const有块级作用域,var没有

    object和function

    强制类型转换和隐式类型转换

    强制:parseInt,parseFloat,toString等

    isEqual

    1. 手写深度比较
    2. split()和join()的区别
    3. 数组,pop,push,unshift,shift

    lodash.isEqual

    //实现如下效果
    const obj1 = {a:10, b:{x:100,y:200}}
    const obj2 = {a:10, b:{x:100,y:200}}
    isEqual(objq,obj2) === true
    

    判断是否是对象或数组

    代码:

    function isObject(obj){
        return typeof ojb === 'object' && obj !== null
    }
    // 全相等
    function isEqual(obj1,obj2){
        if(!isObject(obj1) || !isObject(obj2)) {
            // 值类型
            return obj1===obj2
        }
        if(obj1===obj2){
            return true
        }
        // 两个都是对象或数组,而且不相等
        const obj1key = Object.keys(obj1)
        const obj2key = Object.keys(obj2)
        
        if(obj1key.length !== obj2key.length){
            return false
        }
        for(let key in obj1){
            const res = isEqual(obj1[key],obj2[key])
            if(!res){
                return false
            }
        }
        return true
    }
    

    split()和join()的区别

    '1-2-3'.split('-') // [1,2,3]
    [1,2,3].join('-') // '1-2-3'
    

    数组的pop,push,unshift,shift

    功能分别是什么,返回值是什么,有什么影响。

    pop返回删除的最后一个值

    push返回追加后元素的长度

    unshift插入到最前面,返回长度length

    shift删除最前面的,返回删除的值

    • pop,shift-》返回值
    • unshift, push-》length

    1. 数组slice和splice的区别
    2. [10,20,30].map(parseInt)返回结果
    3. ajax请求get和post的区别

    • get用于查询,post用于提交
    • get参数拼接在url上,post放在请求体内

    call和apply的区别

    代码:

    fn.call(this, p1,p2,p3)
    fn.apply(this, argument)
    

    事件代理是什么

    代码:

    const p1 = document.getElementById('p1')
    const body = document.body
    
    bindEvent(p1, 'click', e=>{
        e.stopProgation();
        alert()
    })
    bindEvent(body, 'click', e=>{
        alert()
    })
    

    闭包是什么,有什么特性,有什么负面影响

    1. 作用域和自由变量

    自由变量的查找,要在函数定义的地方,不是执行的地方

    闭包不要乱用,变量会常驻内容,不会释放

    闭包:

    function create() {
        let a=100
        return function(){
            console.log(a)
        }
    }
    let fn = create()
    let a=200
    fn() // 100
    
    1. 如何阻止事件冒泡和默认行为?
    2. 查找,添加,删除,移动dom节点的方法
    3. 如何减少dom操作
    • event.stopPropagation()
    • event.preventDefault()

    代码:

    // 新建节点
    const newP = document.createElement('p')
    newP.innerHTML = 'this is newP'
    // 插入节点
    div1.appendChild(newP)
    // 移动节点
    const p1 = document.getElementById('p1')
    div2.appendChild(p1)
    //获取父元素
    console.log(p1.parentNode)
    

    如何减少dom操作

    1. 缓存dom查询结果
    2. 多次dom操作,合并到一次插入

    代码:

    const list = document.getElementById('list')
    const frag = document.createDocumentFragment()
    
    for(let i=0; i<20; i++){
        const li = document.createElement('li')
        li.innerHTML = 'list'
        frag.appendChild(li)
    }
    list.appendChild(frag)
    

    jsonp的原理,为什么它不是真正的ajax

    浏览器的同源策略和跨域

    document load 和 ready的区别

    load:

    页面的区别资源加载完才会执行

    ready:

    dom渲染完既可执行,图片,视频等还没有加载完

    函数声明和函数表达式的区别

    函数声明

    function fn(){...}

    函数表达式:

    const fn = function(){...}

    函数声明会预加载,而函数表达式不会

    new Object()和Object.create()的区别

    • {}等同new Object(),原型Object.prototype

    场景题1

    代码:

    let i 
    for (i = 1; i <= 3; i++) {
        setTimeout(function(){
            console.log(i)
        },0)
    }
    

    场景2

    代码:

    let a = 100
    function test() {
        alert(a)
        a = 10
        alert(a)
    }
    test()
    alert(a)
    

    场景3

    手写字符串trim方法,保证浏览器兼容性

    用Js实现继承

    场景4

    1. 如何捕获Js程序中的异常
    2. 什么是json
    3. 获取当前页面url参数

    代码:

    try {
        // todo
    }catch(error) {
        console.error(error)
    }finally{
        
    }
    

    json是一种数据格式,本质是一段字符串,json格式和js对象结构一致。

    window.JSON是一个全局对象,JSON。stringifyJSON.parse

    获取当前页面url参数

    传统方式,查找location.search

    api,URLSearchParams

    场景5

    1. 讲url参数解析为js对象
    2. 手写数组flatern
    3. 数组去重

    代码:

    // 1
    function queryToObj(){
        const res={}
        const search = location.search.substr(1)
        search.split('&').forEach(res => {
            const arr = res.split('=')
            const key = arr[0]
            const key = arr[1]
            res[key] = val
        })
    }
    

    使用URLSearchParams

    function queryToObj() {
        const res = {}
        const pList = new URLSearchParams(location.search)
        pList.forEach((val,key) => {
          res[key] = val  
        })
        return res
    }
    

    手写数组flatern

    代码:

    function flat(arr) {
        const isDeep = arr.some(item=> item instanceof Array)
        if(!isDeep) {
            return arr
        }
        const res = Array.prototype.concat.apply([],arr)
        return flat(res)
    }
    

    数组去重

    代码:

    function unique(arr) {
        const res=[]
        arr.forEach(item => {
            if(res.indexOf(item) < 0) {
                res.push(item)
            }
        })
        return res
    }
    

    使用Set

    function unique(arr) {
        const set = new Set(arr)
        return [...set]
    }
    

    场景6

    1. 手写深拷贝
    2. 使用RAF requestAnimateFrame
    3. 前端性能的优化

    代码:

    function deepClone(obj = {}) {
        if(type of obj !== 'object' || obj == null){
            return obj
        }
        let result
        if(obj instanceof Array) {
            result = []
        }else{
            result = {}
        }
        for(let key in obj){
            if(obj.hasOwnProperty(key)){
                result[key] = deepClone(obj[key])
            }
        }
        return result
    }
    

    Object.assign不是深拷贝!

    性能优化

    原则:多使用内存,缓存,减少计算,网络请求

    加载页面,页面渲染,页面操作等多多思考问题。

  • 相关阅读:
    flash中网页跳转总结
    as3自定义事件
    mouseChildren启示
    flash拖动条移出flash无法拖动
    需要一个策略文件,但在加载此媒体时未设置checkPolicyFile标志
    Teach Yourself SQL in 10 Minutes
    电子书本地转换软件 Calibre
    Teach Yourself SQL in 10 Minutes
    Teach Yourself SQL in 10 Minutes
    Teach Yourself SQL in 10 Minutes – Page 31 练习
  • 原文地址:https://www.cnblogs.com/dashucoding/p/12633809.html
Copyright © 2011-2022 走看看