总结JS基础及面试题

55 阅读12分钟

值类型:赋值后不会相互干扰;数据对应存储在栈里面,修改时相互不干扰

引用类型:赋值后b变化a也会随之变化;数据存储在堆中,栈中存放内容地址,修改后对应的内存地址里的值也会发生变化

常见值类型:undefined、string、number、boolean、Symbol

常见引用类型:object、array;特殊类型:null、函数

typeof运算符:识别所有值类型;识别函数;判断是否是引用类型(不可细分)

浅拷贝:拷贝后的数据发生变化后原数据也会发生变化

// 深拷贝
const obj ={
    name'xxx',
    age21,
    address: {
        city'beijing',
    },
    arr: [1,2,3,4]
}

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)) {
            // 递归使用
            retult[key] = deepClone(obj[key])
        }
    }

    return result
}

==运算符:除了== null之外,其他都一律用===。

truly变量:!!a === true变量;

falsely变量:!!a === false 变量。

if判断判断的是truly和falsely变量。

class:constructor(构造);属性;方法。

继承:extends;super继承父类函数;扩展或重写方法。

class 实际上是函数,可见是语法糖(简单的说,语法糖就是一种便捷写法)。

隐式原型:_proto_;

显示原型:prototype;

每个class都有显示原型prototype;每个实例都有隐式原型__proto__;实例里的__proto__对应class里的prototype。

基于原型的执行规则:获取属性(例:XXX.name)或执行方法(例:XXX.sayhi())时,先在自身属性和方法寻找,如果找不到则自动去__proto__中查找。

hasOwnProperty:是否是自己的属性。

Object:Object.prototype包含:toString;hasOwnProperty;__proto__ (null)。

原型:任何javascript对象都有一个prototype或__proto__属性指向它的构造函数的prototype;
原型链:其实就是javascript的继承机制,是指获取JavaScript对象的属性会顺着其__proto__的指向寻找,直至找到Object.prototype上。

// 实现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
        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) {
        return this.each(elem => {
            elem.addEventListener(type, fn, false)
        })
    }
}

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

// “造轮子”
class myJQuery extends jQuery {
    constructor(selector) {
        super(selector)
    }
    // 扩展自己的方法
    addClass(className) {

    }
}

作用域:全局作用域;函数作用域;块级作用域。

自由变量:一个变量在当前作用域没有定义但被使用;向上级作用域一层一层一次寻找,直到找到为止;如果全局作用域都没有找到,则报错not defined

闭包:作用域应用的特殊情况,有两种表现:函数作为参数被传递;函数作为返回值被返回。
所有的自由变量的查找是在函数定义的地方向上级作用域查找,不是执行的地方!

// 闭包
// 函数作为返回值
function create() {
    const a = 100
    return function () {
        console.log(a)
    }
}

const fn = create()
const a = 200
fn() // 100

// 函数作为参数被传递
function print(fn) {
    const a = 200
    fn()
}
const a = 100
function fn() {
    console.log(a)
}

this 的应用场景

1、作为普通函数去调用

2、使用 call apply bind 去调用

3、作为对象方法被调用

4、在 class 方法中调用

5、在箭头函数中被调用

注意:this 在各个场景中取什么值,是在函数执行的时候确认的,不是在函数定义的时候确认的。

call 是直接改变 this 的指向,bind 它会返回一个新的函数去执行,二者都会改变 this 的指向。

注意:箭头函数里面的 this 永远是取它上级作用域里面的 this,它自己本身不会决定 this 的值。

this:在class方法中调用;箭头函数。this取什么值是在函数执行的时候确认的,不是在函数定义的时候确认的!
call直接调用;bind会返回一个新的函数去执行。
箭头函数永远是取它上级作用域的值。

JS是单线程语言,只能同时做一件事儿。
浏览器和nodejs已支持JS启动进程,如Web Worker。并不能影响js是单线程的
JS和DOM渲染共用同一个线程,因为JS可修改DOM结构。
异步:遇到等待不能卡住;需要异步;回调callback函数形式。
异步应用场景:网络请求,如ajax图片加载;定时任务,如SetTimeout。
异步和同步的区别:基于JS是单线程语言;异步不会阻塞代码执行;同步会阻塞代码执行;

// 手写promise加载一张图片
function loadImg () {
    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 url1  = '200.jpg'
const url2 = '100.jpg'

loadImg(url1).then(img1 => {
    console.log(img1.width)
    return img1 // 普通对象
}).then(img1 => {
    console.log(img1.height)
    return loadImg(url2) // promise 实例
}).then(img2 => {
    console.log(img2.width)
    return img2
}).then(img2 => {
    console.log(img2.height)
}).catch(ex => console.error(ex))

event loop (事件循环/事件轮询)
JS 是单线程运行的;异步要基于回调来实现;event loop 就是异步回调的实现原理。 JS 如何执行:从前到后,一行一行执行;如果某一行执行报错,则停止下面代码的执行;先把同步代码执行完,载执行异步。 

总结 event loop 过程

    同步代码,一行一行放在 Call Stack 执行;

    遇到异步,会先“记录”下,等待时机(定时、网络请求等);

    时机到了,就移动到 Callback Queue;

    如 Call Stack 为空(即同步代码执行完)Event Loop 开始工作;

    轮询查找 Callback Queue,如有则移动到 Call Stack 执行;

    然后继续轮询查找(永动机一样);

DOM事件和event loop

    JS是单线程的;

    异步(setTimeout、ajax等)使用回调,基于 event loop;

    DOM事件也使用回调,基于 event loop;(DOM事件不是异步)

Promise 三种状态:peding;resolved;rejected。
pending ---> rsolved 或者 pending ---> rejected (变化不可逆)

状态的表现:

    pending状态,不会触发then和catch

    resolved状态,会触发后续的then回调函数

    rejected状态,会触发后续的catch回调函数

then 和 catch 改变状态:

    then 正常返回 resolved,里面有报错则返回 rejected

    catch 正常返回 resolved,里面有报错则返回rejected

async/await 是消灭异步回调的终极武器;async/await 和Promise 并不互斥;两者相辅相成。

执行async 函数,返回的是Promise 对象;

awiat 相当于Promise的 then;

try...catch 可捕获异常,代替了Promise 的 catch。

宏任务: setTimeout,setInterval,Ajax,DOM事件;DOM渲染后触发

微任务:Promise,async/await;DOM渲染前触发

微任务执行时机比宏任务要早。微任务是 ES6 语法规定的;宏任务是浏览器规定的。

event loop 和 DOM 渲染:

    每次 Call Stack 清空(即每次轮询结束),即同步任务执行完;

    都是 DOM 重新渲染的机会,DOM 结构如有改变则重新渲染;

    然后再去触发下一次的 Event Loop。

63f49a0f0001c9f819201080.jpg

63f499f90001aabd19201080.jpg

63f58bd4090a45ae10730600.jpg await相当于是then,b是100;async返回的是Promise。

await 报错,后面的代码都不执行。await后面的都作为回调内容 -- 微任务

初始化 promise 时,传入的函数会立刻被执行。

  1. 同步代码执行完毕( event loop  - call stack 被清空)
  2. 执行微任务
  3. 尝试触发 DOM 渲染
  4. 触发 Event Loop,执行宏任务

微信图片_20230302171658.png

微信图片_20230302171729.png

阻止冒泡:event.stopPropagation().

// 通用的事件绑定函数
// function bindEvent(elem, type, fn) {
//     elem.addEventListener(type, fn)
// }
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)
        }
    })
}

// 普通绑定
const btn1 = document.getElementById('btn1')
bindEvent(btn1, 'click', function (event) {
    // console.log(event.target) // 获取触发的元素
    event.preventDefault() // 阻止默认行为
    alert(this.innerHTML)
})

// 代理绑定
const div3 = document.getElementById('div3')
bindEvent(div3, 'click''a', function (event) {
    event.preventDefault()
    alert(this.innerHTML)
})

ajax 核心API XMLHttpRequest

const xhr = new XMLHttpRequest()
xhr.open('GET''/data/test.json'true)
xhr.onreadystatechange = function () {
    if (xhr.readyState === 4) {
        if (xhr.status === 200) {
            // console.log(
            //     JSON.parse(xhr.responseText)
            // )
            alert(xhr.responseText)
        } else if (xhr.status === 404) {
            console.log('404 not found')
        }
    }
}
xhr.send(null)

63f82c77000119f719201080.jpg

跨域

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

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

实现跨域的常见方式:

63f84b6f000111f719201080.jpg

63f84b8a0001a01019201080.jpg

63f84c6700019bb819201080.jpg

63f84cde00013e9019201080.jpg

// 手写一个简易的ajax
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 === 200) {
                    resolve(
                        JSON.parse(xhr.responseText)
                    )
                } else if (xhr.status === 404 || xhr.status === 500) {
                    reject(new Error('404 not found'))
                }
            }
        }
        xhr.send(null)
    })
    return p
}

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

实际项目中ajax的常见插件:

    $.ajax

    feach

    axios

微信截图_20230302174611.png

微信图片_20230302174719.png

http 状态码

分类: 1xx服务器收到请求;2xx请求成功;3xx重定向;4xx客户端错误;5xx服务端错误。**
**

    200:成功

    301:永久重定向(配合location,浏览器自动处理)

    302:临时重定向(配合location,浏览器自动处理)

    304:资源未被修改

    404:资源未找到

    403:没有权限

    500:服务器错误

    504:网关超时

微信截图_20230302174808.png

http headers:

    Request Headers : 

        Accept 浏览器可接受的数据类型

        Accept-Encoding 浏览器可接收的压缩算法,如 gzip

        Accept-Languange 浏览器可接收的语言,如 zh-CN

        Connection:keep-alive一次TCP连接重复使用

        cookie

        Host

        User-Agent(简称UA)浏览区信息

        Content-type发送数据的格式,如 application/json

    Response Headers : 

        Content-type 返回数据的格式,如 application/json

        Content-length 返回数据的大小,多少字节

        Content-Encoding 返回数据的压缩算法,如 gzip

        Set-cookie

63f87e3400012b1919201080.jpg

强制缓存:强缓存是利用http头中的Expires和Cache-Control两个字段来控制的,用来表示资源的缓存时间。如果浏览器判断所请求的目标资源有效命中,则可以直接从强制缓存中返回请求响应和Status Code 200 OK,无须与服务器进行任何通信。

协商缓存:协商缓存就是在使用本地缓存之前,需要向服务器端发起一次get请求,由服务器确定缓存资源是否可用(是否过期)。如果未过期,直接使用缓存在资源并返回Status Code 304 OK

Cache-Control:Response Headers中;控制强制缓存的逻辑;例如 Cach-Control:max-age=32536000(单位是秒)缓存一年。

Cache-Control: max-age 缓存时间;no-cache 不用本地缓存向服务器请求;no-store 不用本地缓存也不用服务器缓存,直接请求服务端返回数据;private 只能最终用户进行缓存;public 允许中间路由或者代理进行缓存。

Expires:同在Response Headers中;同为控制缓存过期;已被 Cach-Control代替。

协商缓存: 服务器端缓存策略;服务器判断客户端资源,是否和服务器资源一样;一致则返回304,否则返回200和最新的资源。

63f82c77000119f719201080.jpg

资源标识:

    在 Response Headers 中,有两种

    Last-Modified 资源的最后修改时间

    Etag 资源的唯一标识(一个字符串,类似人类的指纹)

63f884e10001138219201080.jpg

63f885a80001973619201080.jpg

63f885650001cc5e19201080.jpg

63f886170001df9419201080.jpg

63f88626000146c919201080.jpg

微信图片_20230302174719.png

63f88626000146c919201080.jpg

git 常用命令:

     git add .  把修改的所有文件都git上 后面加文件名,add单个文件

    git checkout .  把文件还原到原先的状态  后面加文件名,撤回单个文件

    git commit -m “xxx”  提交代码

    git push origin master  提交代码到git仓库

    git pull origin master   下拉代码到本地

    git branch  新建分支

    git checkout -b xxx / git checkout xxx  切换分支

    git merge xxx  合并分支

抓包工具:windows 一般用 fiddler ;Mac OS 一般用 charles。

linux 常用命令:

    ssh work@192.168.10.21  work:用户名;192...:ip地址  登录到linux测试机或线上

    ls  查看文件夹

    ls -a  查看全部文件(隐藏文件)

    ll  查看文件(列表形式)   

    clear  清屏

    mkdir  创建文件夹

    rm -rf 文件名   删除文件

    cd  去到某个文件夹

    mv 旧文件名 新文件名   修改文件名

    mv 文件名 路径   移动文件至某个路径下

    cp 现有文件 新文件名  将现有的文件进行拷贝

    touch 文件名   新建文件

    vi 文件名  新建文件(编辑文件)

    vim 文件名  (没有则新建,有则打开文件)

    cat 文件名   查看打印出文件所有内容

    head 文件名  打印出文件的前一部分

    tail 文件名   打印出文件的末尾部分

    grep 关键字 文件名   在某个文件里查找相关内容

    vimtutor 查看命令教程

微信图片_20230302185614.png

微信图片_20230302185905.png

63fa302b00017fc319201080.jpg

性能优化: 让加载更快;让渲染更快

63fc3e540001ac8219201080.jpg

微信图片_20230302190117.png

前端性能优化:缓存;CDN;SSR;懒加载;缓存DOM查询;多个DOM操作仪器插入到DOM结构中;今早开始执行JS。

63fc3f510001cadd19201080.jpg

63fc400c0001584f19201080.jpg

63fc40200001a2f819201080.jpg

防抖和节流的相同点:防抖和节流都是为了阻止操作高频触发,从而浪费性能。

防抖和节流的区别:防抖是触发高频事件后n秒内函数只会执行一次,如果n秒内高频事件再次被触发,则重新计算时间。适用于可以多次触发但触发只生效最后一次的场景。节流是高频事件触发,但在n秒内只会执行一次,如果n秒内触发多次函数,只有一次生效,节流会稀释函数的执行频率。

// 防抖
function debounce (fn, delay = 500) {
    let timer = null
    return function () {
        if (timer) {
            clearTimeout(timer)
        }
        timer = setTimeout(function () {
           fn.apply(thisarguments)
           timer = null
        }, delay)
    }
}

input1.addEventListerner('keyup'debounce(() => {
    console.log(input1.value)
}),600)
// 节流
function throttle (fn, delay = 100) {
    let timer = null
    return function () {
        if (timer) {
            return
        }
        timer = setTimeout(() => {
            fn.apply(thisarguments)
            timer = null
        }, delay)
    }
}

div.addEventListerner('drag'throttle(function (e) {
    console.log(e.offsetX, e.offsetY)
}))

web前端攻击方式:

    XSS跨站请求攻击:通过

63fc6a850001da9a19201080.jpg

63fc6b820001b9d719201080.jpg

var 和 let const的区别

    var 是ES5的语法,let、const是ES6的语法;var 有变量提升;

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

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

typeof 返回类型:Boolean,string,undefined,number, symbol;object;function

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

    强制:parseInt、parseFloat、toString等;

    隐式:if、逻辑运算、==、+拼接字符串;

// 判断数据类型
function isObject (obj) {
  return typeof obj == 'object' && obj != null
}

// 深度判断数据是否相等
function isEqual (obj1, obj2) {
  if (obj1 === obj2) {
    return true
  }

  if (!isObject(obj1) || !isObject(obj2)) {
    return obj1 === obj2
  }

  const objKey1 = Object.keys(obj1)
  const objKey2 = Object.keys(obj2)
  if (objKey1.length != objKey2.length) {
    return false
  } else {
    for (let key in obj1) {
      const res = isEqual(obj1[key], obj2[key])
      if (!res) {
        return false
      }
    }
  }
  return true
}

const obj1 = {
  a100,
  b: {
    x100,
    y200
  }
}

const obj2 = {
  a100,
  b: {
    x100,
    y200
  }
}

console.log(isEqual(obj1, obj2))

split()和 join()的区别

    split 是拆分字符串转换成数组;

    join是拼接数组成字符串;

数组的pop、push、unshift、shift 分别做什么

    pop:截取数组的最后一个元素,返回新数组为最后一个元素,改变原数组;

    push:在数组的最后添加元素,返回length,改变原数组;

    unshift:把元素插入最前面,返回length,改变原数组;

    shift:截取数组第一个元素,返回新数组为第一个元素,改变原数组;

//  纯函数:1,不改变原数组(没有副作用吧);2,返回一个数组

    concat 拼接数组

    map 遍历数组

    filter 过滤数组

    slice 类型于深拷贝

//  非纯函数

    push、pop、shift、unshift、forEach、some、every、reduce

数组 slice 和 splice 区别:

    slice:切片;纯函数;(startIndex,endIndex);截取第二个往后所有只填一个参数,如:(2);截取最后两个参数以负数的形式展示,如(-2)。

    splice:剪接;非纯函数(开始位置,个数,剪接的内容(可以为空)))

[10, 20, 30].map(parseInt) 输出什么?

答案:[10,NaN,NaN]

拆解:[10, 20 , 30 ].map((num, index) => {

    return parseInt(num, index)

})

parseInt(string, radix):

string    必需。要被解析的字符串。    

radix    可选。表示要解析的数字的基数。该值介于 2 ~ 36 之间。    

    

ajax 请求 get 和 post 区别

    get:一般用于查询;参数拼接在url上;

    post:一般用于提交;参数放在请求体内(数据体积可更大);安全性,易于防止CSRF;

函数 call 和 apply 的区别

    fn.call(this, p1, p2, p3) call后面的参数是一个一个传进去的

    fn.apply(this, arguments) apply 后面传的是数组或者类数组

    传参方式不同;

事件代理(委托)是什么

    事件委托就是利用事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。

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

    应用场景:作为参数被传入,作为返回值被返回;

    自由变量的查找,要在函数定义的地方(而非执行的地方);

    影响:变量会常驻内存,得不到释放。闭包不要乱用;

如何阻止事件冒泡和默认行为:

    event.stopPropagation()

    event.preventDefault()

查找、添加、删除、移动DOM节点的方法:

    查找:getElementById; getElementsByTagName; getElementsByClassName; querySelectorAll;

    添加:createElement

    移动:appendChild

    删除:removeChild

如何减少DOM操作

    缓存DOM查询结果

    多次DOM合并,一次处理

解释 jsonp 的原理,为何它不是真正的ajax

    ajax 是通过xml-http 实现;jsonp 是通过

    img、script可以绕过跨域

== 和 === 的不同

    == 会转换数据类型进行比较

    === 要求数据类型、值都相等

63fe09810001ba8b19201080.jpg

微信图片_20230302192613.png

字符串   字母开头,后面字母数字下划线,长度6-30

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

    ^ 字符串开头;[] 选择;\w 命中字母数字下划线;{} 长度集合,大于等于5,小于等于29;$ 结尾。

如何用JS实现继承

    class 继承;propotype 继承(不推荐)

微信图片_20230302193004.png

微信图片_20230302193036.png

63fedd0e0001b08519201080.jpg

什么是JSON:

    json 是一种数据格式,本质是一段字符串;

    json 格式和 JS 对象结构一致,对 JS 语言更友好;

    window.JSON 是一个全局对象:JSON.stringify JSON.parse

获取当前页面url参数:

    传统方式,查找 location.search;

    新API,URLSearchParams;

63fee15c0001d8db19201080.jpg

63ff09030001258319201080.jpg

有序和无序

    有序:操作慢;无序:操作快,但无序;

    如何结合两者优点:二叉树、及其变种;

Map 和 Object 区别

     API 不同,Map 可以以任意类型为 key;

    Map 是有序结构(重要);

    Map 操作同样很快;

    set:新增或修改;delete:删除;has:获取值;forEach:循环,参数(value,key);size:长度;

Set 和 数组 的区别:

    API 不同;

    Set 元素不能重复;

    Set 是无序结构,操作很快;

    add:新增;delete:删除;has:是否存在;size:长度;forEach:循环(参数:val)

    Set 去重:[... new Set([1,1,23,4,5,5])]

63ff13d100018d9c19201080.jpg

数组 reduce 的用法:

63ff17b20001657819201080.jpg

63ff18c60001390c19201080.jpg

63ff18140001d1c219201080.jpg