前端学习总结篇(一)

Rinsann 2021年09月03日 575次浏览

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

1.闭包就是声明一个变量,声明一个函数,在函数内部访问外部的变量,那么这个函数加这个变量叫做闭包。

2.闭包的用途

  • 从外部读取内部的变量
  • 将创建的变量的值始终保持在内存中

3.闭包的缺点是

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

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

  • call、apply和bind的作用都是改变this的指向,其中call和apply的区别在于它们的传参方式不同
  • call可以传多个形参
  • apply只能传一个数组形参
  • 而bind和call、apply的区别在于bind不会立即调用,而是返回一个函数对象

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

常见http状态码:

  • 200 请求成功,一般用于GET与POST请求
  • 204 表示客户端发送给客户端的请求得到了成功处理,但在返回的响应报文中不含实体的主体部分(没有资源可以返回)
  • 206 表示客户端进行了范围请求,并且服务器成功执行了这部分的GET请求
  • 301 永久性重定向,表示请求的资源被分配了新的URL,之后应使用更改的URL
  • 302 临时性重定向,表示请求的资源被分配了新的URL,希望本次访问使用新的URL
301与302的区别:前者是永久移动,后者是临时移动(之后可能还会更改URL)
  • 303 请求的资源被分配了新的URL,应使用GET方法定向获取请求的资源
302与303的区别:后者明确表示客户端应当采用GET方式获取资源
  • 304 客户端发送附带条件(是指采用GET方法的请求报文中包含if-Match、If-Modified-Since、If-None-Match、If-Range、If-Unmodified-Since中任一首部)的请求时,服务器端允许访问资源,但是请求为满足条件的情况下返回改状态码
  • 307 临时重定向,与303有着相同的含义,307会遵照浏览器标准不会从POST变成GET;(不同浏览器可能会出现不同的情况)
  • 400 语义有误,当前请求无法被服务器理解或者请求参数有误
  • 401 未经许可,需要通过HTTP认证
  • 403 服务器拒绝该次访问(访问权限出现问题)
  • 404 服务器无法根据客户端的请求找到资源(网页)
  • 500 服务器错误,导致了无法完成对请求的处理
  • 503 服务器暂时处于超负载或正在进行停机维护,无法处理请求

著名面试题:

如何实现数组去重?
假设有数组 array = [1,5,2,3,4,2,3,1,3,4]
你要写一个函数 unique,使得
unique(array) 的值为 [1,5,2,3,4]
也就是把重复的值都去掉,只保留不重复的值。

要求写出两个答案:

  1. 一个答案不使用 Set 实现
  2. 另一个答案使用 Set
  3. (附加分)使用了 Map / WeakMap 以支持对象去重的,额外加
  4. 说出每个方案缺点的,再额外每个方案加

不使用Set

unique = (array) => {
	const hash = []
	for(let i=0;i<array.length;i++){
		hash[array[i]] = true
	}
	const result = []
	for(let k in hash){
		result.push(k)
	}
	return result;
}
缺点:此法只支持数字或者字符串数组,如果数组里面有对象,比如 array = [{number:1}, 2],就会出错。

使用Set

unique = (array)=>{
	return [...new Set(array)]
}
缺点:API 太新,旧浏览器不支持

使用Map

unique = (array)=>{
	let map = new Map();
	let result = [];
	for (let i=0;i<array.length;i++){
		if(map.has(array[i])){//判断map中是否有这个key
			continue
		}else{ //如果没有,就加入result中
			map.set(array[i],true);
			result.push(array[i])
		}
	}
	return result;
}
缺点:API 太新,旧浏览器不支持

DOM 事件相关

  1. 什么是事件委托
    1. 事件委托也称之为事件代理。是JavaScript中常用绑定事件的常用技巧。顾名思义,即是把原本需要绑定在子元素的响应事件委托给父元素,让父元素担当事件监听的职务。事件代理的原理是DOM元素的事件冒泡。
  2. 怎么阻止默认动作
    1. w3c 的方法是 e.preventDefault(),IE 则是使用 e.returnValue = false
    2. 在支持 addEventListener() 的浏览器中,也能通过调用时间对象的 preventDefault()方法取消时间的默认操作。不过,在 IE9 之前的 IE 中,可以通过设置事件对象的 returnValue 属性为 false
  3. 怎么阻止事件冒泡
    1. w3c 的方法是 e.stopPropagation(),IE 则是使用 e.cancelBubble = true
    2. 在支持 addEventListener() 的浏览器中,可以调用事件对象的一个 stopPropagation()
      方法已阻止事件的继续传播。如果在同一对象上定义了其他处理程序,剩下的处理程序将依旧被调用,但调用 stopPropagation()
      方法可以在事件传播期间的任何时间调用,它能工作在捕获阶段、事件目标本身中和冒泡阶段。
      IE9 之前的IE不支持 stopPropagation() 方法。相反,IE事件对象有一个 cancleBubble 属性,设置这个属性为
      true 能阻止事件进一步传播。( IE8 及之前版本不支持事件传播的捕获阶段,所以冒泡是唯一待取消的事件传播。)当前的 DOM 事件规范草案在 Event 对象上定义了另一个方法,命名为stopImmediatePropagation()。类似
      stopPropagation(),这个方法组织了任何其他对象的事件传播,但也阻止了在相同对象上注册的任何其他事件处理程序的调用。

如何理解JS的继承

  • 基于原型的继承给

    基本原理是:将父类的实例赋值给子类的原型。

     // 父类
     function Staff() {    
       this.company = 'tianchuang';
       this.list = [];
     }
     // 父类的原型
     Staff.prototype.getComName = function() {
      return this.company;
     };
    
    // 子类
         function Coder(name, skill) {
           this.name = name;
           this.skill = skill;
         }
    
    // 继承 Staff
         Coder.prototype = new Staff();
    
     // 因为子类原型的指向已经变了,所以需要把子类原型的co         ntructor指向子类本身
         Coder.prototype.constructor = Coder;
    
    // 给子类原型添加属性
         Coder.prototype.getInfo = function() {
           return {
             name: this.name,
            skill: this.skill
           };
         };
    
         let coder = new Coder('小明', 'javascript');
    
         coder.getInfo(); // {name: '小明', skill: 'javascript'}
         coder.getComName(); // 'tianchuang'
    
    //这种继承方式的缺点:子类的实例可以访问父类的私有属性,子类的实例还可以更改该属性,这样不安全。
    
  • 基于 class 的继承给

    ES6 中有了类的概念,可以通过 class 声明一个类,通过 extends 关键字来实现继承关系。
    class 与 ES5 构造函数的主要区别:

    class 只能通过 new 来调用,而构造函数则可以直接调用;

    class 内部所有定义的方法,都是不可枚举的(non-enumerable)

class Parent {
  constructor(name) {
    this.name = name;
  }
  static say() {
    return 'hello';
  }
}

class Child extends Parent {
  constructor(name, age) {
    super(name); // 调用父类的 constructor(name)
    this.age = age;
  }
}

let child1 = new Child('kevin', '18');

console.log(child1);

数组排序

给出正整数数组 array = [2,1,5,3,8,4,9,5]
请写出一个函数 sort,使得 sort(array) 得到从小到大排好序的数组 [1,2,3,4,5,5,8,9]
新的数组可以是在 array 自身上改的,也可以是完全新开辟的内存。

不得使用 JS 内置的 sort API

let array = [2,1,5,3,8,4,9,5]
function sort(arr){
    let max = arr.length - 1;
    for(let j = 0;j<max;j++){
        let done = true;
        for(let i = 0;i < max - j;i++){
            if(arr[i] > arr[i + 1]){
                let temp = arr[i];
                arr[i] = arr[i+1];
                arr[i+1] = temp;
                done = false;
            }
        }
        if(done){
            break;
        }
    }
    return arr;   
}
sort(array)

你对 Promise 的了解?

答题要点:

  • Promise 的用途

    • Promise对象是JavaScript的异步操作解决方案,为异步操作提供统一接口,可以让异步操作写起来就像再写同步操作的流程,不必一层层的嵌套回调地狱,让回调地狱变得可控。
  • 如何创建一个 new Promise

    • new Promise((resolve,reject) => {})
  • 如何使用 Promise.prototype.then

    const p1 = new Promise( (resolve,reject) => {
      resolve('success!')
    } )
    
    p1.then(value => {
      console.log(value)
    }, reason => {
      console.error(reason)
    })
    
  • 如何使用 Promise.all

    let p = Promise.all([1,2,3,Promise.resolve(444)]);
    console.log(p); // Promise { <state>: "fulfilled", <value>: Array[4] }
    
    let p2 = Promise.all([1,2,3,Promise.reject(555)]);
    console.log(p2); // Promise { <state>: "rejected", <reason>: 555 }
    
  • 如何使用 Promise.race


let p1 = new Promise((resolve, reject) => {
    setTimeout(resolve, 500, "one");
})
let p2 = new Promise(() => {
    setTimeout(resolve, 200, "two")
})
Promise.all([p1, p2]).then((value) => {
    console.log(value);
}, (reason) => {
    console.log(reason)
})
//"two" 两个都完成,
// 但是p2更快
let p3 = new Promise((resolve, reject) => {
    setTimeout(reject, 100, "three")
})
Promise.all([p3, p2]).then((value) => {
    console.log(value);},(reason) => {console.log(reason)
}
) 
// three 因为p3更快,
// 所以失败了//使用Promise.race: Promise.race()方法返回一个 promise,一旦迭代器中的某个promise解决或拒绝,返回的 promise就会解决或拒绝。

说说跨域。

要点:

  • 什么是同源
    • 同源策略(Same origin policy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。
    • 非同源受到的限制:cookie不能读取、dom无法获得、ajax请求不能发送
  • 什么是跨域
  • JSONP 跨域
    • 由于同源策略的限制,XmlHttpRequest只允许请求当前源(域名、协议、端口)的资源。而动态添加一个