JS基础课程总结 - 题目

变量类型和计算

  • typeof能判断哪些类型
  1. 能判断所有的值类型
      字符串(string)、数值(number)、布尔值(boolean)、undefined、null、symbol
  2. 能判断函数
  3. 能判断是不是引用类型(无法细分),想细分怎么办?使用instanceof
  • 何时使用 === 何时使用 ==
  1. 除了==null外  其余的都要用===
  • 值类型引用类型的区别
  1. 堆栈的角度去判断
  • 手写深拷贝
  function deepClone(obj) {
      if(obj == null || typeof obj !== 'object'){
        return obj
      }
      let result = null;
      if(obj instanceof Array){
          result = []
      }else{
          result = {}
      }
      for(let key in obj){
          result[key] = deepClone(obj[key])
      }
      return result
  }

原型和原型链

  • instanceof 的本质
  对象a由函数A创建,当使用```a instanceof A```的时候,会出现:a顺着__proto__往上找,A顺着prototype往上找,如果两者在同一个地方相遇,即返回true。
  • 如何判断一个变量是不是数组
  1. arr instaceof Array
  2. Array.isArray
  3. Object.prototype.toString.call(arr)
  • 手写一个简易的jQuery,考虑插件,考虑拓展性
  class jQuery {
    constructor(selector){
        let dom = document.querySelectorAll(selector)
        const length = dom.length

        for(let i=0;i<length;i++){
            this[i] = dom[i]
        }
        this.length = length
        this.selector = selector
    }

    get(index){
        return this[index]
    }

    each(fn){
        for(let i=0;i<this.length;i++){
            const elem = this[i]
            fn(elem)
        }
    }

    on(type,fn){
        this.each(elem=>{
            elem.addEventListener(type,fn,false)
        })
    }

    // 拓展api
}

/**
 let divs = new jQuery('div')
 divs.each(div=>{
        console.log(div);
    })
 divs.on('click',(e)=>{
        console.log(e);
    })
 console.log(divs.get(1));
 */

// 插件机制
jQuery.prototype.dislog = function () {
    // 添加插件
}

// 复写(造轮子)
class myJQuery extends jQuery{
    constructor(selector) {
        super(selector);

    }
    // 拓展自己的方法
    // ...
}
  • extends的本质,怎么理解
例如:B extends A
  实现:B._ _ _proto_ _ _ = A       B.prototype._ _ _proto_ _ _ = A.prototype  

作用域和闭包

  • this的不同应用场景,如何取值
    1. 构造函数(this -> 当前对象)
    2. 函数作为对象的一个属性
    let obj = {
        aa:111,
        b:function () {
            console.log(this);
        }
    }
    obj.b()
  • 手写bind/call/apply函数
    function a(args) {
        console.log(this);
        console.log('aaa',args);
    }

    let b = {
        aaa:111
    }

    Function.prototype.myBind = function () {
        let args = [...arguments]
        let context = args.shift()

        let _this = this
        return function () {
            _this.apply(context,arguments)
        }
    }

    // a.myBind(b,[1,2,3])()

    Function.prototype.myCall = function () {
        let args = [...arguments]
        let context = args.shift()
        context.fn = this
        const result = context.fn(...args)
        delete context.fn
        return result
    }
    a.myCall(b,1,2,3)

    // a.apply(b,[1,2,3])
    Function.prototype.myApply = function () {
        let args = [...arguments]
        let context = args.shift()

        context.fn = this
        let result = context.fn(args)
        delete context.fn
        return result
    }
    a.myApply(b,[1,2,3])
  • 实际开发中闭包的应用场景,举例说明
    1. 封装变量,将其封装到函数内部,不让拿到原值进行修改(绑到函数的原型上面,感觉更好)
    2. 模拟块级作用域(立即执行函数)
    3. 缓存数据
     let numCache = function () {
        var cache = {}
        return function () {
            var arg = JSON.stringify(arguments)
            if(cache[arg]){
                // 从cache中取出
                console.log('从cache中取出');
            }else{
                // 计算
                console.log('计算');
                let sum = 0
                for(let i=0;i<arguments.length;i++){
                    sum+=arguments[i]
                }
                cache[arg] = sum
            }
        }
    }

    let sum = numCache()
    sum(1,2,3) // 计算
    sum(1,2,3) // 从cache中取出
  • 创建10个<a>标签,点击的时候弹出来对应的序号(let,闭包)
    let dom = document.getElementById('myDiv')
    let df = new DocumentFragment()
    for (var i=0;i<10;i++){
        let node = document.createElement('a')
        node.innerHTML = 'node '+i
        let fn = function(){
            var index = i
            return function () {
                console.log(index);
            }
        }

        /**
         * 如果不使用闭包,在onclick的时候,此时执行的函数中的i不存在当前函数执行上下文中,就会向父级作用域(global)查找,找到的i=10
         * 使用闭包,在onclick的时候,此时执行函数中的i不存在当前执行函数上下文中,就会向父级作用域(fn)查找,找到了,此时父级作用域中
         * 的i是在fn函数声明的时候就创建了,因为作用域是词法环境导致,即函数声明时得到作用域,所以fn中的i就是循环时候的每次都不一样的i,
         * 所以找到父级作用域(fn)时,得到的i就是想要的结果。
         * */

        node.onclick = fn()

        /**
         * 简单写法
         * node.onclick = (function(i){
            return function () {
                console.log(i);
            }
        })(i)
         */

        df.append(node)
    }
    dom.append(df)

异步和单线程

    function loadImg() {
        return new Promise((resolve, reject) => {
            let img = document.createElement('img')
            img.src = url
            img.onload = function () {
                resolve(img)
            }
            img.onerror = function (err) {
                reject(err)
            }
        })
    }
  • 前端使用异步的场景有哪些
  1. 网络请求  
  2. 定时任务

DOM

  • DOM是哪种数据结构
  - 树
  • DOM本质是什么
  - 一棵html树
  • DOM操作常用的API
- 节点操作  
  - 查询节点 
    getElementById / getElementsByTagName / getElementsByClassName / querySelectorAll
  - 插入节点 
     appendChild...
  - 移动节点 
    appendChild() 将现有节点插入到其他内部,会出现移动的效果
  • attributeproperty区别是什么
  property : p1.style.width...... // 修改js变量属性,不会体现到html中 (建议)
  attribute : p1.setAttribute('data-name',111)   // 修改html属性,直接改变html结构
  • 一次性插入多个DOM节点,考虑性能
  - DocumentFragement
  - length的时候,赋给一个变量

BOM

  • 如何识别浏览器的类型
  - 获取浏览器的信息
    - navigator      / userAgent   

  - 获取屏幕的信息
    - screen  / width height

  - 获取地址栏的信息
    - location  href/protocol/pathname/search/hash

  - 前进后退的信息
    - history  /forward() back() 
  • 拆解URL的各个部分

事件

  • 编写一个通用事件监听函数
    function bindEvent(element, type, selector, fn) {
        // selector是事件代理时候用
        if(fn == null){
            fn = selector
            selector = null
        }

        element.addEventListener(type,function(event){
            const target = event.target

            if(selector){
                // 代理绑定
                if(target.matches(selector)){
                    // dom是否符合selector选择器
                    fn.call(target,event)
                }
            }else{
                //普通绑定
                fn.call(target,event)
            }
        })
    }
  • 描述事件冒泡的流程
 以点击事件为例,用户点击div的时候,会触发div的点击事件监听函数,接着会触发div的父级dom的点击事件监听函数,
一直往上冒,冒到document为止
  • 无限下拉的图片列表,如何监听每个图片的点击
  - 使用事件代理,使用父级监听点击函数,然后判断即可。不要每个元素都添加。
  • 事件代理
子元素比较多,不需要挨个进行事件绑定,将其事件绑定到父元素上,然后做一些判断即可。
  - 优点
    代码简洁 、 减少浏览器内存占用 、 不要滥用

ajax

  • 手写一个简易的ajax
    let xhr = new XMLHttpRequest()
    xhr.onreadystatechange = function () {
        // 0: 请求未初始化
        // 1: 服务器连接已建立
        // 2: 请求已接收
        // 3: 请求处理中
        // 4: 请求已完成,且响应已就绪
        if(xhr.readyState === 4){
            if(xhr.status === 200){
                console.log(JSON.parse(xhr.responseText));
            }
        }
    }
    xhr.open('get','./data.json')
    xhr.send()
  • 跨域常用的实现方式
  - jsonp 和 CROS(server做)
  • 哪些标签不跨域
  -<img>  <link>  <script>
  • fetch是什么
  一种异步请求的工具,不同于XMLHttpRequest,是一种新的api,底部实现Promise,
有一个缺点就是返回的promise不会被标记为reject,即使HTTP相应的状态码是404或者500.

存储

  • 描述cookie,localStorage,sessionStorage的区别(cookie的本质
cookie的本质:
  - 本身是用于浏览器和server通讯,不是用于本地存储,只是被借用来本地存储。前后端都可以修改cookie。

webpack相关

- 为什么使用webpack
  ES6的模块化,浏览器是暂时不支持的。
  ES6语法,浏览器并不完全支持
  压缩代码,整合代码,以让网页加载更快(缓存机制)

- 安装webpack
  ```npm install webpack webpack-cli -D  --代理```

- babel的用处
  将ES6的语法转移为ES5的语法

- 模块化的规范是什么

// 多导出
export function f() {}
export const a = 111

import {fn,a} from './a'

// 单导出
function f1() {}
const aa = 222
export {
    f1,aa
}
import {fn,aa} from "./a";

// 导出多个,导入时不解构
let aaa222 = {}
export default aaa222

import aaa222 from './'

页面加载过程

  • 从输入url到渲染出页面的整个过程
# 加载过程
1. DNS(Doman name server)解析:域名 -> IP
2. 浏览器根据IP地址向服务器发起HTTP请求
3. 服务器接处理HTTP请求,并返回给浏览器

# 渲染过程
1. 根据HTML代码生成DOM Tree
2. 根据CSS代码生成CSSOM (CSS 对象模型)
3. 将DOM Tree和CSSOM 整合生成 Render Tree
4. 根据Render Tree渲染页面
5. 遇到script的话 就暂停渲染  有限执行js代码  因为js线程和渲染线程是一个线程
6. 直至把Render Tree渲染完成
  • 为何把css放在head中
扫描到head后,发现没有css,此时就去渲染页面,到后面发现css后,可能会修改之前渲染的,
所以会做重复工作,甚至造成页面跳动等情况。
  • window.onloadDOMContentLoaded的区别
    window.addEventListener('load',function () {
        // 网页上所有资源都加载完才会触发,包括耗时的图片等...
    })
    window.addEventListener('DOMContentLoaded',function () {
        // DOM渲染完毕即可执行,此时的图片、视频还可能都没有加载完
    })

性能优化

  • 前端常见的性能优化的方案
# 性能优化
原则: 空间换时间
    1. 多使用内存,缓存或者其他方法
    2. 减少CPU的计算量,减少网络加载耗时
从何入手:
    让加载更快
        减少资源体积 - 压缩代码
        减少访问次数
            合并代码(合并js等资源)
            SSR服务端渲染(把资源在后端一次组装完成,减少客户端的请求次数)
            缓存(静态资源加hash后缀,根据文件计算hash。文件不变,则会自动触发http缓存机制,返回304)
        使用更快的网络 - CDN
    让渲染更快
        CSS放在head,JS放在body最下面
        尽早开始执行JS,在DOMContentLoaded触发
        懒加载(图片懒加载,上滑加载更多...)
        对DOM查询进行缓存
        避免频繁的DOM操作,使用DocumentFragement片段
        节流、防抖
  • 手写节流防抖
    防抖,节流的意思
  防抖:频繁操作,最后XXms后触发
  节流:频繁操作,按一定频率触发
    // 节流
    // 主要思想: 在频繁的回调函数中,每XXms就执行一次,平均速率
    function throttle(fn,delay = 500) {
        let timer = null
        return function () {
            if(timer){
                return
            }

            timer = setTimeout(()=>{
                fn.apply(this,arguments)

                timer = null
            },delay)
        }
    }
    input.oninput = throttle(function (e) {
        console.log(e);
    },1000)

    // 防抖 - 最后触发
    // 主要思想: 在用户停止输入的那一瞬间开始,倒数XXms
    function debounce(fn, delay = 1000) {
        let timer = null

        return function () {
            if(timer){
                clearTimeout(timer)
            }
            timer = setTimeout(()=>{
                fn.apply(this,arguments)
                timer = null
            },delay)
        }
    }
    input.oninput = debounce(function (e) {
        console.log(e);
    },1000)

    

安全

  • Web前端常见的安全攻击方式预防
常见的攻击方式
  - XSS  &  XSRF
  - XSS 发表博客,嵌入<script>脚本,拿到别人的cookie信息
    解决方案:更换特殊字符<、>等等
  - XSRF 跨站请求伪造  <img src=xxx.com/pay...../>
    解决方案:使用post请求  &  增加验证(密码,验证码等等)

面试真题

1. var let const的区别

    var 是ES5的语法  , let const是ES6的语法 ; var 有变量提升
    var和let是变量 可以修改,const是常量,不可修改
    let const有块级作用域,var没有

2. typeof 能判断那些数据类型

    值类型:string,number,boolean,undefined,symbol
    引用类型:object(注意,typeof null === 'object' <- null是值类型,但是浏览器bug)
    函数:function

3. 列举 强制类型转换 & 隐式类型转换

    强制:parseInt parseFloat toString等
    隐式:if 、 逻辑运算 、 == 、 +拼接字符串

4. 手写深度比较 isEqual

    function isObject(obj) {
        return typeof obj === 'object' && obj !== null
    }

    function isEqual(obj1,obj2) {
        // 不是对象  就直接返回两者三等
        if(!isObject(obj1) || !isObject(obj2)){
            return obj1 === obj2
        }
        if(obj1 === obj2){
            return true
        }

        // 两个都是对象或者数组 而且不相等
        let obj1keys = Object.keys(obj1)
        let obj2keys = Object.keys(obj2)

        if(obj1keys.length !== obj2keys.length){
            return false
        }

        // 两个都是对象或者数组 而且keys个数一致
        // 以obj1为基准  进行比较
        for(let key in obj1){
            let result =  isEqual(obj1[key],obj2[key])
            if(!result){
                return false
            }
        }
        return true
    }

5. split() 和 join() 的区别

    split是拆分字符串,join是拼接数组,两者完全背离

6. 数组常见API

---1.连接两个或更多的数组。
---2.检测数组元素的每个元素是否都符合条件。
---3.检测数组元素中是否有元素符合指定条件。
---4.反转数组的元素顺序。
---5.检测数组元素,并返回符合条件所有元素的数组。
---6.删除并返回数组的第一个元素。
---7.搜索数组中的元素,并返回它所在的位置。
---8.用于插入、删除或替换数组的元素。
---9.对数组的元素进行排序。
---10.删除数组的最后一个元素并返回删除的元素。
---11.把数组的所有元素放入一个字符串。
---12.把数组转换为字符串,并返回结果。
---13.向数组的末尾添加一个或更多元素,并返回新的长度。
---14.返回一个指定的字符串值最后出现的位置,在一个字符串中的指定位置从后向前搜索。
---15.通过指定函数处理数组的每个元素,并返回处理后的数组。
---16.选取数组的的一部分。
---17.返回数组对象的原始值。
---18.向数组的开头添加一个或更多元素,并返回新的长度。
---19.数组求和
    1.concat() 不
    2.every() 不
    3.some() 不
    4.reverse() 会
    5.filter() 不
    6.shift() 会
    7.indexOf() 不
    8.splice() 会
    9.sort() 会
    10.pop() 会
    11.join() 不
    12.toString() 不
    13.push() 会
    14.lastIndexOf() 不
    15.map() 不
    16.slice() 不
    17.valueOf() 不
    18.unshift() 会
    19.reduce((total,currentValue)=>{})

7. 数组slice和splice的区别是什么

    slice是 切片
    splice是 剪切
        splice(1,3,x,x,x)
        从下标为1开始,后截三位,替换成x,x,x,并返回截出来的值,为数组

8. [10,20,30].map(parseInt)

    将其拆分开,就得到:
    [10,20,30].map((item,index)=>{
        // 注意parseInt的第二个参数,是代表进制
        return parseInt(item,index)
    })

9. 函数call和apply的区别

    就在参数上是否是数组

10. 事件代理(委托)是什么

    冒泡事件,无限下拉的点击事件监听

11. 闭包是什么?有什么特性?有什么负面影响?
    闭包就是能够读取其他函数内部变量的函数

    函数作为参数被传入,函数作为返回值被返回
    理解 " 自由变量 " 是什么?
        就是当前函数内找不到定义的变量,在父级作用域内,该变量就是自由变量
    影响:
        变量常驻内存,得不到释放,但不一定是内存泄漏(内存泄漏是不用的内存无法被回收,闭包占用的内存可能会使用)

12. 如何阻止" 事件冒泡 " 和 " 默认行为 "

    阻止冒泡:e.stopPropagation()
    取消默认行为:e.preventDefault()

13. jsonp的原理

    通过script标签实现的
    1. 在window上绑定一个逻辑函数
    window.callback = function(data){
        console.log(data)
    }
    2. 请求一个js资源,js资源里面有一个执行函数,callback({一些数据作为入参传递进来})

14. load 和 ready的区别

    load 是所有资源加载完毕
    ready => DOMContextLoaded 是dom加载完毕就会触发

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

    函数声明:在执行前预加载,类似代码提升
    函数表达式:不会

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

    {} === new Object({}) 原型是Object.prototype
    Object.create(null) 没有原型
    Object.create({xxx}) 可以指定原型
    Object.create({xxx}) 把传入的对象赋值到__proto__上

17. 正则表达式
    // 字符串 字母开头,后面数字字母下划线,长度6-30

    /^[a-zA-Z]\w{5,29}$/

    // 邮政编码

    /^\d{6}$/

    // 小写英文字母

    /^[a-z]+$/

    // 英文字母

    /^[a-zA-Z]+$/

    // 日期格式

    /^\d{4}-\d{1,2}-\d{1,2}$/

    // 用户名

    /^[a-zA-Z]\w{5,29}$/

    // 简单ip地址匹配

    /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/

18. 手写trim方法,保证兼容性

    String.prototype.trim = function () {
        return this.replace(/^\s+/,'').replace('/\s+$/','')
    }

19. 获取数组的最大值
    1. 将arguments变为数组,除了解构外,还有什么办法
        let arr = Array.prototype.slice.call(arguments)
    2. 使用原生获取最大值的方法
        Math.max(n1,n2,n3...)
        Math.min(n1,n2,n3...)

20. 如何使用JS实现继承

    class继承
    prototype继承(不推荐)

21. 如何捕获JS程序的异常

    手动:try{}catch(e){}
    自动:window.onerror = function(){
        // 对跨域的js,cdn等,不会有详细
        // 对于压缩的js,还需要配合 sourceMap 去反查询压缩的行列
    }

22. 什么是JSON

    就是一种数据格式,本身是一段字符串
    格式和js对象解构一致,比较友好

23. 怎么得到浏览器参数

    function query(name) {
        let search = location.search.substr(1)
        let reg = new RegExp(`(^|&)${name}=([^&]*)($|&)`)
        let res = search.match(reg)
        if(!res){
            return null
        }
        return res[2]
    }

24. 数组拍平
    let arr = [1,2,[1,2]]
    Array.prototype.concat.apply([],arr)  // 两层
    function flat(arr) {
        let isDeep = arr.some(item=>item instanceof Array)
        if(!isDeep){
            return arr
        }
        let res = Array.prototype.concat.apply([],arr)
        return flat(res)
    }

    console.log(flat([1, 2, [3, 4, [1, 2, [3, 4]]]]));

25. 数组去重

    传统方式
        挨个比较
        function clear(arr){
            let result = []
            arr.forEach(a=>{
                if(!result.includes(a)){
                    result.push(a)
                }
            })
            return result
        }

    使用Set
        new Set([1,1,2,3,1])  // 1,2,3

26. 介绍一下RAF requestAnimationFrame
    动画的一个api,动画流畅的必要条件:60帧/s,使用setTimeout的话,需要手动控制,但是RAF是自动计算的
    window.requestAnimateFrame(fn)
    切换tab页面的时候,动画是暂停的,浏览器做了优化

27. Object.assign()是深拷贝吗?
    不是的,只是浅层级被拷贝了,深层级没有
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,686评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,668评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,160评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,736评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,847评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,043评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,129评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,872评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,318评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,645评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,777评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,470评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,126评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,861评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,095评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,589评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,687评论 2 351

推荐阅读更多精彩内容