前端面试题整理

1. 简述一下对HTML语义化的理解

答:

(1)用正确的标签做正确的事情;

(2)在没有CSS的情况下也能让页面呈现出清晰的结构,容易阅读;

(3)有利于SEO,和搜索引擎建立良好的沟通,有助于爬虫抓取更多的有效信息(爬虫依赖于HTML标记来确定上下文和各个关键字的权重);

(4)便于团队开发和维护,语义化更具有可读性,使开发者对网络更容易将网站分块。

2. new操作符具体干了什么?

(1)创建一个空对象,并且this变量引用该对象,同时还继承该函数的原型;

(2)属性和方法被加入到this引用的对象中;

(3)新创建的对象由this所引用,并且最后隐式的返回this.

function School(){
  var name = "Tom";
  var fn = function(){}
}
var obj = new School();
obj.name //undefined
obj.fn //undefined


//obj是一个空对象:School {}
//                  __proto__:
//                      constructor: ƒ School()
//                      __proto__: Object

 

3. 说说你对作用域链的理解

PS: [[scope]],作用域,[[scope]]中存储了运行期上下文的集合;作用域链:[[scope]]中所存的执行期上下文对象的集合,这个集合程链式结构,所以把它叫作用域链。

作用域链的作用是 保证执行环境里有权访问的变量和函数是有序的。作用域链的变量的访问只能向上访问,变量访问到window对象即被终止。作用域链向下访问是不被允许的。

 

4. HTTP和HTTPS

HTTP协议通常承载于TCP协议之上,在HTPP和TCP之间添加一个安全协议层(SSL或TLS),这个时候就变成了HTTPS。

默认HTTP的端口号为80,HTTPS的端口号为443.

为什么HTTPS更安全?

因为网络请求需要中间有很多的服务器、路由器的转发。中间的节点都有可能篡改信息,而如果使用HTTPS,密钥在你和终点站才有,https它利用ssl/tsl协议传输,它包含证书、卸载、流量转发、负载均衡、页面适配、浏览器适配,refer传输等,保证传输过程的安全性。

 

5. TCP与UDP

1)TCP(Transmission Control Protocol,传输控制协议),是基于连接的协议,即:在正式收发数据前,必须和对方建立可靠的连接。

一个TCP连接必须要经过三次“对话”才能连接起来。

2) UDP(User Data Protocol,用户数据报协议),是与TCP相对应的协议,它是面向非连接的协议,不与对方建立连接,直接把数据包传送过去。

UDP适用于一次只传少量数据,对可靠性要求不高的应用环境。

 

6. TCP传输的三次握手和四次挥手策略

为了准确无误地把数据送到目标处,TCP采用三次握手策略。握手过程中使用了TCP的标志:SYN(同步标志)和ACK(确认标志)。SYN=1代表这是一个连接请求,或连接接收报文;握手完成后,SYN标志位置0。ACK=1时,确认号字段才有效。

“三次握手”:

1) 发送端发一个带SYN标志的数据包给对方;

2) 接收端收到后,回传一个带SYN/ACK标志的数据包以示传送确认信息;

3) 最后,发送端再回传一个带ACK标志的数据包,代表“握手结束”。

 

断开一个TCP连接需要“四次挥手”:

第一次挥手:浏览器发送一个FIN(终止标志),用来关闭浏览器到服务器的数据传送,即告知对方,我不给你发数据了,但此时还可以接收数据;

第二次挥手:服务器收到FIN包后,发一个ACK给对方,确认序号为收到序号+1;

第三次挥手: 服务器方发一个FIN,关闭到浏览器的数据传送(不给你发了);

第四次挥手: 浏览器收到FIN后,发一个ACK给服务器,确认序号为收到序号+1.

 

为什么连接时三次,而关闭时要四次呢?

因为当Server端收到Client端的请求后,可直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。

但当关闭连接时,当Server端收到FIN报文时,很可能不会立即关闭SOCKET,所以只能先回复一个ACK报文,告知Client端“你的FIN已收到”。只有等到我Server端所有报文都发送完了,才能发送FIN报文。因此不能一起发送。

 

7. 前端存储的方式

1)cookies(在计算机上存储在小的文本文件中的数据,解决连接关闭后记住用户信息)

     a. 网站为标识用户身份而储存在用户本地终端上的数据,是经过加密了的;数据在http协议中携带,在浏览器和服务器之间来回传递

     b. 它兼容性好,请求头自带cookie方便,缺点是大小只有4k,自动请求头加入cookie浪费流量,每个domain限制20个cookie,使用起来麻烦需要自行封装

     c. 设置的cookie在过期之前一直有效,即使窗口或浏览器关闭

     d. 它有极高的扩展性和可用性

重要:通过JavaScript创建cookie:JS可用document.cookie创建,读取和删除cookie

           a.创建cookie: 

document.cookie="username=kbin; expires=Sun, 31 Dec 2017 12:00:00 UTC;path=/"

           b. 读取cookie:(同时会以一条字符串的形式返回所有cookie)

var x=document.cookie

          c. 改变cookie:

document.cookie="username=kbin2; expires=Sun, 31 Dec 2017 12:00:00 UTC;path=/"

          d. 删除cookie:(只需将expires参数的时间设置为过去的时间)

document.cookie = "username=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;";

 

2)localStorage

    HTML5加入的以键值对(key-value)为标准的方式。

    优点是操作方便,永久性储存,除非手动删除,大小为5M,兼容IE8+

    localStorage.setItem("key","value") //以"key"为名称存储一个值"value"

    localStorage.getItem("key")//获取名称为"key"的值

    localStorage.removeItem("key")//删除名称为"key"的信息

    localStorage.clear() //清空localstorage中所有的信息

3)sessionStorage

     与localStorage基本类似,但它的数据在页面关闭后会被清理,而且与cookie和localStorage不同,它不能在所有同源窗口中共享,是会话级别的储存方式

重要:localStorage和sessionStorage使用相同的API

4)IndexedDB

     NoSQL数据库,用键值对进行储存,可进行快速读取操作,非常适合web场景,同时用JavaScript进行操作会非常方便

 

PS:三者对比一览表:

 

8. 同源和跨域

1)同源

      a. 概念:也叫同源策略,是浏览器的一种安全机制。

      b. 规则:同源指‘三个相同’:协议名相同,域名相同,端口号相同(即,同一个网站)

      PS: https://www.github.com/80   其中,https是协议名,www.github.com是子域名,github.com是主域名,80是端口号

       c. 目的:保护用户信息安全,防止恶意网站窃取信息

 

2)跨域

一个网站的网址组成包括协议名,子域名,主域名,端口号。当在页面中一个url请求数据时,如果这个url的协议名,子域名,主域名,端口号有任意一个不相同,就会产生跨域问题。(同源策略:只能是同源的网站之间才可以请求数据)

为了实现不同源网站之间可以相互请求数据,所以要解决跨域问题,跨域解决方案:

a. jsonp(json with padding):把JSON填充到一个盒子里

    因为浏览器的script, img, iframe标签是不受同源策略限制的,所以通过动态插入script标签引入一个js文件,这个js文件载入成功后会执行url参数中指定的callback函数,并把我们需要的json数据作为参数传入。在服务器端,当req.params参数中带有callback属性时,则把数据作为callback的参数执行,并拼接成一个字符串后返回。

网页端代码:

<script>
    //json数据请求数据(ajax发送请求只用于同源网站之间)
    function tryJsonp(data){
         alert("receive");
         alert(data.name); //要从后台拿到数据
    }

</script>
//把函数名上传给后台,避免了前后台交互时,函数名不一致导致获取不到数据的问题
<script src="http://www.perguo.com/qy/test?callback=tryJsonp"></script>

服务器端代码(node.js写后端)

router.get("/test", function(req, res) {
  var data = { name: "Guo" };
  var query = req.query;
  if(query && query.callback) {
    var str = query.callback + '(' + JSON.stringify(data) + ')';
    res.send(str);
  } else {
    res.send('OK');
  }
})

优点:兼容性好,简单易用,支持浏览器与服务器双向通信。

缺点:只支持get请求,且只支持跨域HTTP这种情况(不支持HTTPS)。

 

b. 直接在服务器端设置跨域资源访问CORS(Cross-Origin Resource Sharing)

设置Request Header头中Access-Control-Allow-Origin为指定可获取数据的域名(一劳永逸)。

思想:在服务器端检查请求头部的origin,从而决定请求应该成功还是失败。Node.js实现CORS方法代码:

app.use(function (req, res, next) {
  res.header("Access-Contron-Allow-Origin", "*");//允许所有来源访问
  res.header("Access-Control-Allow-Origin", "Origin, X-Requested-With, Content-Type, Accept");
  next();
})
app.use('/', routes);
app.use('/users', users);

c. 找‘爸爸’,寻找相同主域document.domain. 通过修改document.domain来跨子域。

例如:http://www.example.com/a.html和http://bbs.example.com/b.html,这俩主域名都是example.com,相当于有同一个爸爸。此方法只适用于两个iframe之间的跨域。

document.domain = 'example.com'.

 

d. 通过window.name来跨域接收数据

不同域的框架把想要共享的信息放在window.name里,此方法只适用于两个iframe之间的跨域。

name属性有一个特征:在一个窗口(window)的生命周期内,窗口载入的所有页面都是共享一个window.name的,每个页面对window.name都有读写的权限。

window.name是持久存在一个窗口载入过的所有页面中的。

e. 代理方式

如:www.123.com/index.html需调用www.456.com/server.php,可以写一个接口www.123.com/server.php,由这个接口在后端去调用www.456.com/server.php并拿到返回值,然后再返回index.html。这就是一个代理模式,相当于绕过了浏览器端,自然就不存在跨域问题。

f. php端修改Header方式

在php接口脚本中加入一下两句即可:

header('Access-Control-Allow-Origin', '*'); //允许所有来源访问

header(‘Access-Control-Allow-Method: POST, GET'); //允许访问方式

 

9. 闭包的作用和不足

1)什么是闭包?

   当内部函数被保存到了外部,就会产生闭包。

   本质上,闭包是将函数的内部和外部连接起来的桥梁。

   用处:(1)读取函数内部变量

              (2)这些变量的值始终保存在内存中,不会在外层函数调用后被自动清除。

2)闭包的作用

    (1)实现公有变量。不依赖外部变量可反复执行,如:累加器

    (2)可做缓存(存储结构)。当多个函数同时与一个函数产生闭包时,它们之间的变量可以共用

    (3)可实现封装,属性私有化

    (4)模块化开发,防止污染全局变量

3)缺点

    闭包会导致原有作用域链不释放,造成内存泄漏。所以,在函数退出之前,将不使用的局部变量全部删除(null)

 

10. Promise是什么?

promise是异步比编程的一种解决方案,比传统的回调函数和事件更合理和强大。简单来说就是一个容器,里面保存着某个未来才会结束的事情。从语法上来说,它是一个对象。

 

11. 网络请求编码

1xx:信息响应类,表示接收到请求并且继续处理

2xx:处理成功响应类,表示动作被成功接收、理解和接受

3xx:重定向响应类,为了完成指定的动作,必须接受进一步处理

4xx:客户端错误,客户请求包含语法错误或者是不能正确执行

5xx:服务端错误,服务器不能正确执行一个正确的请求

200:交易成功(服务器成功返回数据)

400:错误请求(如语法错误)

403:请求不允许

404:没有发现文件,URL或查询

500:服务器产生内部错误

502:服务器暂时不可用(过载)

 

12. JavaScript中的隐式转换

1.[]

!![] == true     //true
[] == true       //false
![] == []        //true

原因:[]是javascript中比较特殊的隐式转换

String([]) //""
Number([]) //0
Boolean([]) //true

[] == true // 0 == 1 ->false

// !是逻辑非,将布尔值求反;!!是类型转换,将对应类型转为Boolean型
![] == [] //![] -> false; false == 0 ->true

!![] == true //true == true ->true

不要晕,我们再来一段:

[] == 0 //true
![] == 0 //true (Boolean([])是true)
[] == "" //true
!![] == "" //false
"" == true //false

2. null 与 undefined

null == undefined //true

3. 一个是数字,一个是字符串 。先将字符串转为数字,再比较

4. 其中一个值是true,则将其转为1再比较;false亦然

5. 一个值是对象,另一个是数字或字符串。则将对象转为原始值,再比较。

    对象通过toString()或valueOf()转换为原始值:

    toString(): 返回一个反映这个对象的字符串;

    valueOf(): 一个对象如果存在一个原始值,就将其转换为它的原始值。

    JavaScript语言核心的内置类先尝试使用valueOf(),再尝试使用toString(),除了日期类,日期类只能使用toString转换。

 

13. for ... in 与 for ... of 的区别

(1) for ... of 是ES6新引入的特性,修复的es5 for ... in 的不足

(2)for...in 循环出的是key, for...of 循环出的是value

(3)for...of 不能循环普通的对象,需要和Object.keys()搭配使用

(4)推荐在循环对象的时候,使用for...in ;在遍历数组的时候使用for... of。

14. js的防抖和节流

防抖:在事件被触发n秒后再执行回调,如果在这n秒内又被触发,则重新计时。

  使用场景:

          (1)给按钮加函数防抖防止表单多次提交;

          (2)对于输入框连续输入进行AJAX验证时,用函数防抖能有效减少请求次数。

  思路: 每次触发事件时,都取消之前的延时调用方法

function debounce(fn, wait){
       var timeout = null;
       return function (){
           if(timeout !== null){
               clearTimeout(timeout)
           }
           timeout = setTimeout(fn, wait)
       }
   }
   // 处理函数
   function handle(){
       console.log(Math.random())
   }
   //滚动事件
   window.addEventListener('scroll', debounce(handle, 2000))

节流:指连续触发事件但在n秒中只执行一次函数。节流会稀释函数的执行效率。

        思路:每次触发事件时都判断当前是否有等待执行的延时函数

function throttle(func, delay){
    var prev = Date.now();
    return function (){
        var context = this;
        var args = arguments;
        var now = Date.now();
        if(now - prev >= delay){
            func.apply(context, args);
            prev = Date.now();
        }
    }
}
function handle(){
    console.log(Math.random());
}
window.addEventListener('scroll', throttle(handle, 2000));

区别:函数节流不管事件触发有多频繁,都会保证在规定时间内,一定会执行一次真正的事件处理函数;而函数防抖只是在最后一次事件后才触发一次函数。比如:在页面的无线加载情况下,我们需要用户在滚动页面时,每隔一段时间发一次AJAX请求,而不是在用户停下滚动页面操作时才去请求数据,这种场景就适合节流技术来实现。

 

15.JavaScript全局执行上下文为你做了两件事:全局对象和this关键字

基本执行上下文是全局执行上下文,它是代码中可以随处访问的内容。

16.js进阶整理

for (let i = 1; i < 5; i++) {
  if (i === 3) continue
  console.log(i)
}
//1 2 4
//当某次结果返回true时,continue跳过本次迭代

 

for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 1)
}

for (let i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 1)
}
//3 3 3 和 0 1 2
//js中的setTimeout回调会在遍历结束时执行
//第一个遍历是在var关键字下声明的,就是全局作用下。当i=3时跳出循环,执行三次setTimeout
//第二个遍历是在let关键字下声明,let声明的变量具有块级作用域({}),每次遍历i都有一个新值

 

const shape = {
  radius: 10,
  diameter() {
    return this.radius * 2
  },
  perimeter: () => 2 * Math.PI * this.radius
}

shape.diameter()
shape.perimeter()
//20 NaN
//perimeter是一个箭头函数,它的this指向当前周围作用域,即它指向的不是shape,而是window,而window中没有radius属性,返回undefined.

 

let a = 3
let b = new Number(3)
let c = 3

console.log(a == b)
console.log(a === b)
console.log(b === c)
//true false false
//new Number()是一个内建的函数构造器,得到一个对象。当使用==操作符时,会检测它里面有没有3这个数字,有就返回true
class Chameleon {
  static colorChange(newColor) {
    this.newColor = newColor
    return this.newColor
  }

  constructor({ newColor = 'green' } = {}) {
    this.newColor = newColor
  }
}

const freddie = new Chameleon({ newColor: 'purple' })
freddie.colorChange('orange')
//TypeError
//colorChange是一个静态方法,而静态方法只能被创建它的构造器使用,不能传给实例。本例中freddie是一个实例,因此会报类型错误
let greeting
greetign = {} // 这两个是我故意打错的
console.log(greetign)
//{}
//比如我们不小心把greeting打错了,但它的返回结果是一个空对象。
//因为我们在全局创建了一个空对象。js解释器将其视为window.greetign={}.变量未经声明就赋值则归全局所有。
//为避免这种情况可以使用严格模式"use strict"

 

function bark() {
  console.log('Woof!')
}

bark.animal = 'dog'

//问题:当我们这么做时,会发生什么?会报错吗?
//答:正常运行
//函数是特殊的对象。函数是一个拥有属性的对象,并且属性可以被调用
function Person(firstName, lastName) {
  this.firstName = firstName;
  this.lastName = lastName;
}

const member = new Person("Lydia", "Hallie");
Person.getFullName = function () {
  return `${this.firstName} ${this.lastName}`;
}

console.log(member.getFullName());

//TypeError
//不能像常规对象那样给构造函数添加属性,如果想一次性给所有实例添加特性,可以在原型链上写,如下代码:
Person.prototype.getFullName = function () {
  return `${this.firstName} ${this.lastName}`;
}
//这样member.getFullName才会有效果

function Person(firstName, lastName) {
  this.firstName = firstName
  this.lastName = lastName
}

const lydia = new Person('Lydia', 'Hallie')
const sarah = Person('Sarah', 'Smith')

console.log(lydia)
console.log(sarah)

//Person {firstName: "Lydia", lastName: "Hallie"} 和 undefined
//sarah没有new,那么this引用的是全局对象,而在全局上,sarah是undefined
let number = 0
console.log(number++)
console.log(++number)
console.log(number)

//0 2 2
//num++:返回0, num为1
//++num: 返回2,值自增
function getAge(...args) {
  console.log(typeof args)
}

getAge(21)

//“object"
//数组是对象
function getAge() {
  'use strict'
  age = 21
  console.log(age)
}

getAge()
//ReferenceError(引用错误)

 

var length = 1000
var obj = {
    length: 10,
    fn: function (fn) {
           fn()
           arguments[0]()
     }
}
function fn() {
    console.log(this.length)
}

obj.fn(fn, 1, 2, 3)
//1000 4

 // 第一个:fn(), this指向window
 // arguments: [fn,1,2,3] => arguments[0]->fn()
 // arguments[0]() => 1.fn()() 
                 //   2.this指向arguments => 输出arguments.length

 

// 上一题的加强版
var len = 10
var obj1 = {
    len:6,
    method:function(){
         console.log(this.len)
    }
}
var obj2 = {
    len:5,
    method:function(fn){
         fn();
         arguments[0]()
    }
}

obj2.method(obj1.method,obj2.method) //10 undefined

//fn(),this指向window, this.len => 10
//arguments[0]() => obj1.method() && this指向arguments,
//但是,arguments没有len属性 => undefined

 

function Person() {}
var person = new Person()
console.log(person.__proto__ == Person.prototype) //true
console.log(person.__proto__.__proto__ == Object.prototype) //true
console.log(person.__proto__.__proto__.__proto__) // null
console.log(person.__proto__ == Function.prototype) // false
console.log(Object.__proto__ == Function.prototype) // true

//person.__proto__: {constrctor :f}
//person.__proto__.__proto__: Object
//person.__proto__.__proto__.__proto__: null
//Object.__proto__: f
//Object.__proto__ === Function.__proto__ ==== Function.protorype

 

function fn(arg1, arg2) {
    console.log(arg1, arg2) //html css
    arguments[0]="duyi"
    arg2='web'
    console.log(arg1) // duyi
    console.log(arguments[1]) // web
}
fn("html", "css")
var a = 0,
    b = 0
function A(a) {
     A = function(b) {
            alert(a + b ++)
     }
     alert(a++)
 }
A(1) //A=function(b){alert(a+b++)}  alert(0++) => 1  a=1
A(2) //alert(1+2++) => 4

//答案:1   4

 

17. 块级作用域与for和if

JavaScript没有块级作用域,但是函数有块级作用域,但是,if 和 for 没有块级作用域(这是js留下的坑)

在JavaScript中,for循环创建的 i 在for循环结束后依然存在外部的执行环境中,因此可以访问到变量i 的值。

for(var i=0;i<5;i++){
     var a = 100
}
console.log(a) //100
console.log(i) //5

if(fn){
     var c = 'ccc'
}
console.log(c) //ccc

function fn(){
     var b = 'bbb'
      return b
}
console.log(b) //TypeError: b is not defined

 

18. round(), ceil(), floor()

Math.round(-1.1) //-1
Math.round(-1.5) //-1
Math.round(-1.6) //-2
Math.round(1.5) //2

Math.ceil(-1.1) //-1
Math.ceil(-1.9) //-1
Math.ceil(1.01) //2

Math.floor(1.99) //1
Math.floor(-1.99) //-2

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值