前端十题01

159 阅读13分钟

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

什么是闭包?

在一个函数中能读取其他函数内部变量的这个函数就称为闭包。
在js中,变量的作用域分为:全局作用域和局部作用域。 对一个函数而言,它内部的变量作用域仅仅是函数之内,如果它读取函数之外的变量,这个函数它就是闭包了。

function f1(){
    let n=9;
    addF = ()=>{return  n += 1;}
    function f2(){
    console.log(n)
    return 'hi,i am '+n
    }
    return f2;
}
    const result=f1();
    result(); // 9 'hi,i am 9'
    addF();//10
    result(); // 10 'hi,i am 10'

对f2而言,其中的变量n是f2函数之外的变量,而n的作用域是整个f1函数的内部,f2就是闭包。

闭包的用途?

闭包可以用在许多地方。它的最大用处有两个,一个是可以读取函数内部的变量,例如f2读取f1的变量;另一个就是让这些变量的值始终保持在内存中,不会在f1调用后被自动清除,在addF使用n后,n依然保留着。

闭包的优缺点?

优点:
变量保护,使得能跨作用域访问变量,让函数外部也可以访问到函数内的变量,内部函数有权访问外部函数作用域中的变量。函数可以嵌套使用,子函数可以访问到父函数中的变量和参数。

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

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

call、apply、bind都是改变this指向的方法

fn.call([this],[param1],[param2]...)

call中,如果不传参数,或者第一个参数是nullnudefinedthis都指向window,如果传参数,就是第一个参数默认为this的指向。

apply:和call基本上一致,唯一区别在于传参方式,call是将第一个参数后的参数一个一个读入作为argumens中的一项,apply则是读取后面的一个数组作为arguments。

fn.call(obj, 1, 2); 
fn.apply(obj, [1, 2]);

bind:语法和call一模一样,区别在于立即执行还是等待执行,bind不兼容IE6~8

fn.call(obj, 1, 2); // 改变fn中的this,并且把fn立即执行 
fn.bind(obj, 1, 2); // 改变fn中的this,fn并不执行

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

HTTP状态码

HTTP Status Code是用以表示网页服务器超文本传输协议响应状态的3位数字代码。

状态码分类表

标题类别原因短语
1xxInformational(信息性状态码)接受的请求正在处理
2xxSuccess(成功状态码)请求正常处理完毕
3xxRedirection(重定向)需要进行附加操作以完成请求
4xxClient error(客户端错误)客户端请求出错,服务器无法处理请求
5xxServer Error(服务器错误)服务器处理请求出错

各类别常见状态码:

2xx

200 OK:表示从客户端发送给服务器的请求被正常处理并返回;

204 No Content:表示客户端发送给客户端的请求得到了成功处理,但在返回的响应报文中不含实体的主体部分(没有资源可以返回);

206 Patial Content:表示客户端进行了范围请求,并且服务器成功执行了这部分的GET请求,响应报文中包含由Content-Range指定范围的实体内容。

3xx

301 Moved Permanently:永久性重定向,表示请求的资源被分配了新的URL,之后应使用更改的URL;

302 Found:临时性重定向,表示请求的资源被分配了新的URL,希望本次访问使用新的URL;

  301与302的区别:前者是永久移动,后者是临时移动(之后可能还会更改URL)

303 See Other:表示请求的资源被分配了新的URL,应使用GET方法定向获取请求的资源;

  302303的区别:后者明确表示客户端应当采用GET方式获取资源

304 Not Modified:表示客户端发送附带条件(是指采用GET方法的请求报文中包含if-Match、If-Modified-Since、If-None-Match、If-Range、If-Unmodified-Since中任一首部)的请求时,服务器端允许访问资源,但是请求为满足条件的情况下返回改状态码;

307 Temporary Redirect:临时重定向,与303有着相同的含义,307会遵照浏览器标准不会从POST变成GET;(不同浏览器可能会出现不同的情况);

4xx

400 Bad Request:表示请求报文中存在语法错误;

401 Unauthorized:未经许可,需要通过HTTP认证;

403 Forbidden:服务器拒绝该次访问(访问权限出现问题)

404 Not Found:表示服务器上无法找到请求的资源,除此之外,也可以在服务器拒绝请求但不想给拒绝原因时使用;

5xx

500 Inter Server Error:表示服务器在执行请求时发生了错误,也有可能是web应用存在的bug或某些临时的错误时;

503 Server Unavailable:表示服务器暂时处于超负载或正在进行停机维护,无法处理请求;

4. 著名面试题:如何实现数组去重?

假设有数组 array = [1,5,2,3,4,2,3,1,3,4]
你要写一个函数 unique,使得
unique(array) 的值为 [1,5,2,3,4]
也就是把重复的值都去掉,只保留不重复的值。
要求写出两个答案:
一个答案不使用 Set 实现(6分)
另一个答案使用 Set (4分)
(附加分)使用了 Map / WeakMap 以支持对象去重的,额外加 5 分。
说出每个方案缺点的,再额外每个方案加 2 分。

使用Set

const array = [1,5,2,3,4,2,3,1,3,4]
const unique = (arr) => new Set(arr)
unique(array)

使用Map

const array = [1,5,2,3,4,2,3,1,3,4]
const unique = (arr)=>{
    const map=new Map();
    return arr.filter(item=>!map.has(item)&&map.set(item,1))
}
unique(array)

5. DOM 事件相关

什么是事件委托?4分

JS里的事件委托:就是当事件触发时,把要做的事委托给父元素来处理。

再通俗点:就是自己的事不想干,叫它爸爸,甚至爷爷、甚至祖先来干。

怎么阻止默认动作?3分

w3c 的方法是 e.preventDefault(),IE 则是使用 e.returnValue = false;
在支持 addEventListener() 的浏览器中,也能通过调用时间对象的 preventDefault()
方法取消时间的默认操作。不过,在 IE9 之前的 IE 中,可以通过设置事件对象的 returnValue 属性为 false来达到同样的效果。

var $a = document.getElementsByTagName("a")[0];
$a.onclick = (e)=>{
alert("跳转动作被我阻止了")
e.preventDefault();
//return false;//也可以
}

怎么阻止事件冒泡?3分

w3c 的方法是 e.stopPropagation(),IE 则是使用 e.cancelBubble = true

function stopBubble(e){
    if(e&&e.stopPropagation){//非IE 
    e.stopPropagation(); 
    } else{//IE 
    window.event.cancelBubble=true; 
    }
}

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

答出基于原型的继承给 5 分
答出基于 class 的继承给 5 分

构造函数继承

function person(){this.kind="person";}

person.prototype.eat=function(food){console.log(this.name+" is eating "+food);}

function student(name) {
    person.apply(this,arguments)
    this.name=name;
}

利用call或者apply方法将父类构造函数的this绑定为子类构造函数的this

缺点:这种方式的继承只能继承父类构造函数中的属性和方法,对于原型对象无法继承

原型实例继承

student.prototype=new person()
student.prototype.construct=student

原型直接继承

student.prototype=person.prototype;
student.prototype.constructor=student;

缺点:这种方式无法继承父类构造函数中的属性与方法,但是可以继承父类构造函数的原型对象。还有因为子类原型student.prototype和父类原型person.prototype指向的是同一个地址,所以student.prototype任何的改变都会引起person.prototype的改变。

ES6中Class继承

Class可以通过extends关键字实现继承,这比ES5的通过修改原型链实现继承,要清晰和方便很多。

extends 子类的继承
super() 调用父类的构造方法,只能在子类中执行\

class的使用
//class的使用
class Person{
//本质上实例属性和方法是声明在Person.prototype中的
//constructor构造器,当我们用class类new一个对象时,会默认调用构造器
//里面声明实例属性
    constructor(name,age){
        this.name=name;
        this.age=age
  }

//实例方法
    sayName(){console.log(this.name)}

//而静态属性和方法要用关键字 static 来修饰,本质上他们都是放在构造函数Class上的,只能通过class构造函数来调用

//静态属性
static weight = '50KG'

//静态方法
static sayWeight(){
    console.log(this.weight);}
    
}

//class实例化
let tom =new Person('tom', 20, 'male')
class的继承
//父类class构造函数
class Animal {
  static animalAttr = 'Animal静态属性';
  constructor(name,age){
    this.name = name;
    this.age = age
    }

  animalFun(){console.log('Animal实例方法');}

  static animalStaFun(){console.log('animalStaFun静态方法');}

}

//子类构造函数        用extends关键字来表明继承的父类对象,
class Dog extends Animal{
  constructor(name,age,color,weight){
    // 显示调用super并传递继承的属性就会去寻找extends继承的父构造函数中的数据
    super(name,age)
    this.color = color;
    this.weight = weight;
  }
}

//子构造函数实例化,并验证是否继承了父类的实例属性和方法
 let dog = new Dog('二狗',4, 'black', '10kg')

//调用父构造函数的属性和方法
console.log(dog);        //Dog { name: '二狗', age: 4, color: 'black', weight: '10kg' }
dog.animalFun();        //Animal实例方法

//验证Dog是否继承了父类的静态属性和方法

console.log(Dog.animalAttr);  //Animal静态属性

Dog.animalStaFun()            //animalStaFun静态方法

//验证总结

//子类原型对象继承父类原型对象

console.log(Dog.prototype.__proto__ === Animal.prototype);   //true

//子类对象指向父类对象

console.log(Dog.__proto__ === Animal);      //true

7. 数组排序

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

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

8. 你对 Promise 的了解?

Promise 的用途

解决异步回调的问题。 它对比其余解决异步回调方法的优点是:能解决其余方法不规范,五花八门的命名、解决回调地狱、错误处理无法就行(try catch)等等的问题。

如何创建一个 new Promise(课堂里让大家背过)

return new Promise( (resolve,reject)=>{……})

如何使用 Promise.prototype.then(可查 MDN)

then()  方法返回一个 Promise 

它最多需要有两个参数:Promise 的成功和失败情况的回调函数。

它的语法是

p.then(onFulfilled, onRejected);

onFulfilled 可选

  • 当 Promise 变成接受状态(fulfilled)时调用的函数。该函数有一个参数,即接受的最终结果(the fulfillment  value)。如果该参数不是函数,则会在内部被替换为 (x) => x,即原样返回 promise 最终结果的函数

onRejected 可选

  • 当 Promise 变成拒绝状态(rejected)时调用的函数。该函数有一个参数,即拒绝的原因(rejection reason)。  如果该参数不是函数,则会在内部被替换为一个 "Thrower" 函数 (it throws an error it received as argument)。

返回值 当一个 Promise 完成(fulfilled)或者失败(rejected)时,返回函数将被异步调用(由当前的线程循环来调度完成)

如何使用 Promise.all(可查 MDN)

Promise.all() 方法接收一个 promise 的 iterable 类型(注:Array,Map,Set 都属于 ES6 的 iterable 类型)的输入,并且只返回一个Promise实例, 那个输入的所有 promise 的 resolve 回调的结果是一个数组。

Promise.all(iterable);

如何使用 Promise.race(可查 MDN)

Promise.race(iterable) 方法返回一个 promise,一旦迭代器中的某个 promise 解决或拒绝,返回的 promise 就会解决或拒绝。
语法

Promise.race(iterable);

返回值
一个待定的 Promise 只要给定的迭代中的一个 promise 解决或拒绝,就采用第一个 promise 的值作为它的值,从而异步地解析或拒绝(一旦堆栈为空)。

9. 说说跨域。

要点:

什么是同源

同源:当两个接口的协议、域名、端口号都相等时,这俩接口是同源的,只要有一个条件不相等,则非同源。不是同源的两个页面不能互相访问本地缓存、不能互相修改dom节点、不能使用ajax请求对方接口数据。

什么是跨域

在不同源的网页资源之间进行数据传输或者通信如:从 www.baidu.com 页面去请求 www.google.com 域:协议、域名、端口三者中有一者不同就是不同源,它们之间的访问就需要跨域。

JSONP 跨域

不同源的两个网站,以A方取请求B方的资源为例,jsonp跨域就是A在网页中创建一个script标签,去请求另一个网站的js,js会夹带一些数据,这些数据会在A的网站中调用一个函数去运行。

CORS 跨域

CORS跨域就是被请求的网站把请求的网站加入白名单。 CORS全称Cross-Origin Resource Sharing,也就是我们常说的跨域资源共享,CORS是通过新增一组HTTP头部字段,允许服务器声明那些源站有权限访问哪些资源。

response.setHeader('Access-Control-Allow-Origin','http://localhost:8889')

10.说说你对前端的 理解

评分标准:

  • 0 分:言之无物,看不下去
  • 5 分:人云亦云,流于平庸
  • 10 分满分:见解深刻,有自己的想法
  • 15 分:惊为天人 前端是将后台的程序以图形化的可交互的界面展示给用户,www = url + HTML + HTTP ,如果用收寄快递来理解,HTML是标记语言快递盒子,url相当于门牌号,HTTP是通信协议如何送件,前端的重点在于,如何让用户在收快递的时候更舒适,便捷,于是有了css和js、HTML的学习,HTML超文本标记语言,是一个界面内容的展示形式,决定了内容的结构是dom树,对内容进行操作,也是以树节点的方式操作,css是层叠样式表,让页面可以展示更多的样式,js是编程语言,可以让网页加载出更多的动态效果,以及与后台通信,AJAX用js发送请求和响应,操作HTML中的节点DOM,jQuery……。 在与其他服务器通信的时候也会遇到一些问题,例如:发送请求后不能直接拿到结果,对于处理异步和回调的问题,又到了Promise大显身手的时候了;又如,我发送的请求得不到服务器中的数据,解决跨域访问的问题,又有了Jsonp和CORS…… 总之,前端就是使通信正常顺畅的情况下,让页面更好地展示给用户。

`