前端重要知识总结

196 阅读8分钟

1.什么是闭包?闭包的用途是什么?闭包的缺点是什么?

什么是闭包:我对闭包的理解:

如果一个函数用到外部的变量,那么这个函数和这个变量就叫做 闭包

「函数」和「函数内部能访问到的变量」(也叫环境)的总和,就是一个闭包。
闭包是 JS 函数作用域的副产品。换句话说,正是由于 JS 的函数内部可以使用函数外部的变量,所以这段代码正好符合了闭包的定义。而不是 JS 故意要使用闭包。

闭包的用途:

隐藏局部变量,暴露操作函数

闭包常常用来「间接访问一个变量」。换句话说,「隐藏一个变量」。可以读取函数内部的变量,还能让这些变量的值始终保持在内存中。

闭包的缺点:

1.由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。
2.闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。

2.call、apply、bind 的用法分别是什么?

他们三个都可以改变this指向。
call和apply是直接调用函数,bind是改变this指向,但不立即调用函数

call:

call 接收多个参数,第一个为this要指向的函数,后边参数为函数本身的参数。
举例说明
let o = {
            name: '优秀的'
        }        
function fn(frist, last) { 
           console.log(this);
            console.log(`${frist}${this.name}${last}`);
        };        
fn.call(o, '我是', "前端"); //输出:我是优秀的前端

apply:

apply也是调用函数 第二个可以改变函数内部的this指向,但是他的参数必须是数组(伪数组)
let o = {
            name: 'andy'
        };        
function fn(arr) {
            console.log(this);
            console.log(arr); // 'pink'
        };        
fn.apply(o, ['pink']);

bind:

bind不会调用原来的函数 可以改变原来函数内部的this 指向, 返回的是原函数改变this之后产生的新函数
如果有的函数我们不需要立即调用,但是又想改变这个函数内部的this指向此时用bind

3.请说出至少 10 个 HTTP 状态码,并描述各状态码的意义。

  • 200 OK 请求成功。一般用于GET与POST请求
  • 204 No Content 无内容。服务器成功处理,但未返回内容。在未更新网页的情况下,可确保浏览器继续显示当前文档
  • 205 Reset Content 重置内容。服务器处理成功,用户终端(例如:浏览器)应重置文档视图。可通过此返回码清除浏览器的表单域
  • 206 Partial Content 部分内容。服务器成功处理了部分GET请求
  • 301 Moved Permanently 永久移动。请求的资源已被永久的移动到新URI,返回信息会包括新的URI,浏览器会自动定向到新URI。今后任何新的请求都应使用新的URI代替
  • 307 Temporary Redirect 临时重定向。与302类似。使用GET请求重定向
  • 400 Bad Request 客户端请求的语法错误,服务器无法理解
  • 403 Forbidden 服务器理解请求客户端的请求,但是拒绝执行此请求
  • 404 Not Found 服务器无法根据客户端的请求找到资源(网页)。通过此代码,网站设计人员可设置"您所请求的资源无法找到"的个性页面
  • 500 Internal Server Error 服务器内部错误,无法完成请求
  • 501 Not Implemented 服务器不支持请求的功能,无法完成请求

4.数组去重

//第一种  indexOffunction unique(array) {    if (Array.isArray(array)) {        let newArray = []        for (let i = 0; i < array.length; i++) {            if (newArray.indexOf(array[i]) === -1) {                newArray.push(array[i])            }        }        return newArray    } else {        console.log('type error');        return    }}array = [1, 8, 6, 5, 'red', 'green', 'pink', 'red', 'pink', 5, '5']unique(array) // [1, 8, 6, 5, "red", "green", "pink", "5"];

2.

//第二种 setfunction unique(array) {    // return array.from(new Set(arr))    return [...new Set(array)]}array = [1, 8, 6, 5, 'red', 'green', 'pink', 'red', 'pink', 5, '5']unique(array) // [1, 8, 6, 5, "red", "green", "pink", "5"];

3.

//第三种 mapfunction unique(array) {    let map = new Map()    let newArray = []    for (let i = 1; i < array.length; i++) {        if (map.has(array[i])) {            map.set(array[i], true)        } else {            map.set(array[i], false)            newArray.push(array[i])        }    }    return newArray}array = [1, 8, 6, 5, 'red', 'green', 'pink', 'red', 'pink', 5, '5']unique(array) // [1, 8, 6, 5, "red", "green", "pink", "5"];

5.DOM 事件相关

什么是事件委托:

事件委托就是利用冒泡原理,把处理任务委托给父级元素,然后通过目标事件确定事件源,并执行事件处理
优点:
1.不需要给每一个元素都添加监听事件,而是通过委托给父元素处理,减少内存消耗,提高性能
2.可以方便的动态添加元素不需要为新添加的元素重新绑定事件

怎么阻止默认动作

event.stopPropagation();

怎么阻止事件冒泡

event.preventDefault() 

6.你如何理解 JS 的继承?

基于原型的继承

function Parent(name1){
  this.name1 = name1
}
Parent.prototype.pMethod = function(){
  console.log(this.name1)
}

function Child(name2, name1){
    Parent.call(this, name1) // 得分点
    this.name2 = name2
}
Child.prototype.__proto__ = Parent.prototype 
//上面这句代码的古板写法应该是下面三句
//const empty = function(){}
//empty.prototype = Parent.prototype
//Child.prototype = new empty()

Child.prototype.cMethod = function(){
    console.log(this.name2)
}

基于 class 的继承

class Parent{
    constructor(name1){
        this.name1 = name1
    }
    pMethod(){
        console.log(this.name1)
    }
}
class Child extends Parent{
    constructor(name2, name1){
        super(name1) // 得分点
        this.name2 = name2
    }
    cMethod(){
        console.log(this.name2)
    }
}

7.数组排序

// 1.选择排序
// (1)选择排序-递归
let min = (numbers) => {
    if (numbers.length > 2) {
        return min([numbers[0], min(numbers.slice(1))])
    } else {
        return Math.min.apply(null, numbers)
    }
}
let minIndex = (numbers) => {
    let index = 0
    for (let i = 0; i < numbers.length; i++) {
        if (numbers[i] < numbers[index]) {
            index = i
        }
    }
    return index
}

let sort = (numbers) => {
    if (numbers.length > 2) {
        let index = minIndex(numbers)
        let minNum = numbers[index]
        numbers.splice(index, 1) //返回一个被切去最小数的数组
        return [minNum].concat(sort(numbers))
    } else {
        return numbers[0] > numbers[1] ? numbers.reverse() : numbers
    }
};
// (2)选择排序-for循环
//实现求最小值的下标
let minIndex = (numbers) => {
    let indexInt = 0
    for (let i = 1; i < numbers.length; i++) {
        if (numbers[i] < numbers[indexInt]) {
            indexInt = i
        }
    }
    return indexInt
};
//实现交换swap, 交换一个数组中的两个值
let swap = (numbers, i, j) => {
    let temp = numbers[i]
    numbers[i] = numbers[j]
    numbers[j] = temp
}

let sort = (numbers) => {
    for (let i = 0; i < numbers.length - 1; i++) {
        let index = minIndex(numbers.slice(i)) + i
            //minIndex里面的numbers和下面的numbers没关系,他是在minIndex函数里面运行的,即使切了也不影响下面的numbers,i最开始为零,所以numbers.slice(0)切零个,就是原数组,因为切掉了i个,所以他的下标就和原来的数组不相等了,切掉了i个,后面加上i
        if (index !== i) { swap(numbers, index, i) }
    }
    return numbers
};
// 2.快速排序

let quickSort = (numbers) => {
    if (numbers <= 1) { return numbers }
    let pivotIndex = Math.floor(numbers.length / 2)
    let pivot = numbers.splice(pivotIndex, 1)[0]
    let left = []
    let right = []
    for (let i = 0; i < numbers.length; i++) {
        if (numbers[i] < pivot) {
            left.push(numbers[i])
        } else {
            right.push(numbers[i])
        }
        return quickSort(left).concat(pivot, quickSort(right))
    }
};

// 3.归并排序
let merge = (a, b) => {
    if (a.length === 0) { return b }
    if (b.length === 0) { return a }
    return a[0] > b[0] ? [b[0]].concat(merge(a, b.slice(1))) : [a[0]].concat(merge(b, a.slice(1)))
}
let mergeSort = (numbers) => {
    if (numbers.length === 1) { return numbers }
    let left = numbers.slice(0, Math.floor(numbers.length / 2))
    let right = numbers.slice(Math.floor(numbers.length / 2))
    return merge(mergeSort(left), mergeSort(right))
};

// 4.计数排序

let countSort = arr => {
    let hashTable = {},
        max = 0,
        result = []
    for (let i = 0; i < arr.length; i++) {
        if (!(arr[i] in hashTable)) {
            hashTable[arr[i]] = 1
        } else {
            hashTable[arr[i]] += 1
        }
        if (arr[i] > max) { max = arr[i] }
    }
    for (let j = 0; j <= max; j++) {
        if (j in hashTable) {
            for (let i = 0; i < hashTable[j]; i++) {
                result.push(j)
            }
        }
    }
    return result
}作者:xiaoyuAnd链接:https://juejin.cn/post/6873394120639905800来源:掘金著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

8.你对 Promise 的了解?

Promise 的用途

Promise用于异步,避免回调地域,让代码看起来更同步

如何创建一个 new Promise

//new Promise((resolve,relsct)=>{.....})
function fn(){
    return new Promise((resolve, reject)=>{
        成功时调用 resolve(data)
        失败时调用 reject(reason)
    })
}

如何使用 Promise.prototype.then

then()方法返回一个promise,它最多需要两个参数,promise成功或失败情况的回调函数
let promise1 = new Promise((resolve,reject) => {   resolve('seccess')})promise1.then((value)=>{   console.log(value)   //seccess}

如何使用 Promise.all

promise.all()方法返回一个promise实例,Promise.all 等待所有都完成(或第一个失败),才算结束。Promise.all 当且仅当传入的可迭代对象为空时为同步。Promise.all 在任意一个传入的 promise 失败时返回失败。例如,如果你传入的 promise中,有四个 promise 在一定的时间之后调用成功函数,有一个立即调用失败函数,那么 Promise.all 将立即变为失败。

Promise.all([promise1, promise2]) 并行,等待所有 promise 成功。
如果都成功了,则 all 对应的 promise 也成功;如果有一个失败了,则 all 对应的 promise 失败。

const promise1 = Promise.resolve(3);const promise2 = 42;const promise3 = new Promise((resolve, reject) => {    setTimeout(resolve, 100, 'foo');});Promise.all([promise1, promise2, promise3]).then((values) => {    console.log(values);//Array [3, 42, "foo"]});

如何使用 Promise.race

race 函数返回一个 Promise,它将与第一个传递的 promise 相同的完成方式被完成。它可以是完成( resolves),也可以是失败(rejects),这要取决于第一个完成的方式是两个中的哪个。

Promise.race([promise1, promise2]),返回一个 promise,一旦数组中的某个promise解决或拒绝,返回的 promise就会解决或拒绝。

9.说说跨域

什么是同源:

同源策略/SOP(Same origin policy)是一种约定,所谓同源是指"协议+域名+端口"三者相同,即便两个不同的域名指向同一个ip地址,也非同源。

什么是跨域

浏览器同源策略限制下,向不同不同协议、不同域名或者不同端口)发送XHR请求,浏览器认为该请求不受信任,禁止请求,具体表现为请求后不正常响应

JSONP 跨域

在跨域时,当前浏览器不支持cors,我们创造一个script请求一个js文件,js文件会执行一个回调,回调里面就有数据。回调的名字可以随机生成
优点:jsonp可以支持ie,可以跨域
缺点:由于是script标签,拿不到状态码,只能发get请求,不支持post

* JSONP 是 json with padding 的缩写* 该技术通过 script 不受同源策略限制来达到跨域的目的* 该技术核心是前端构造 script 发起 get 请求,后端将数据放到 js 回调里,前端接受响应后执行回调拿到数据* 具体代码是 blablabla* 优点是通过简单的约定就能跨域* 缺点是不支持 get 以外的动词,而且存在 csrf 风险* 解决办法是 CORS 或 csrf token

CORS 跨域

简单请求:在响应头加Access-Control-Allow-Origin:允许跨域请求的源
非简单请求:在响应头加Access-Control-Allow-Origin:
Access-Control-Allow-Methods:
Access-Control-Allow-Headers:

* CORS 是跨域资源共享的缩写* 该技术通过在目标域名返回 CORS 响应头来达到获取该域名的数据的目的* 改技术核心就是设置 response header,分为简单请求和复杂请求两种* 简单请求只需要设置 Access-Control-Allow-Origin: 目标源 即可,复杂请求则分两步走,第一步是浏览器发起 OPTIONS 请求,第二步才是真实请求。OPTIONS 请求需要把服务器支持的操作通过响应头来表明,如 Access-Control-Allow-Methods: POST, GET, OPTIONS,另外一个重要的响应头是 Access-Control-Allow-Credentials: true 用来表明是否接受请求中的 Cookie。blablabla* 优点是通过简单的配置就能跨域* 缺点是某些古老浏览器不支持 CORS 或不支持 Credentials* 解决办法是用 JSONP 或 P3P 等技术