2019前端面试总结

HTML面试题

  1. 你是如何理解HTML语义化的?
  • 比较简单的回答:我理解的语义化就是 标签用在合适的位置,比如段落要用p标签,标题要用h1-h6标签.
  • 更细点的回答:我理解的HTML语义化是正确的标签做正确的事情,能够便于开发者阅读和写出更优雅的代码的同时,让网络爬虫更好的解析。
  1. 为什么要做到语义化?
  • 有利于SEO,有利于搜索引擎爬虫更好的解析我们的页面,从而获取更多的有效信息,提升网页的权重。
  • 在没有CSS的时候,能够清晰看出网页的结构,增强可读性。
  • 便于团队合作开发和维护,提高开发效率
  1. 文档声明,它不是HTML标签,是一个指示web浏览关于页面使用哪个HTML版本编写的指令。 声明必须位于文档的第一行,位于标签之前。

  2. lang属性设定文档语言。

    作用:SEO搜索引擎优化;有助于阅读障碍人士,通过读屏器阅读页面

    还可以是

5.meta标签的几种用法。

  • meta指定文档编码
//这行代码的意思是,文档用UTF-8编码的,浏览器解析的时候用UTF-8编码解析。


  • 适配移动页面



  • 添加页面描述


6.你用过哪些 HTML5标签。

内容性的标签:

网页的头部

功能性的标签

  通过脚本绘制图像
  1. 什么是H5?
    H5是中国人制造的一个专有名词
    实际上是指移动端页面,从某种意义上来说它是 HTML5,微信网页,移动PPT的母级。

CSS面试题

  1. 两种盒模型。
    盒模型一共有两种模式:
  • 标准盒模型
    标准盒模型下 盒子的总宽度/高度=width/height+padding+border+margin,标准盒模型下 width是指content的宽度
  • 怪异盒模型
    怪异盒模型下 盒子的总宽度/高度=width/height+margin,怪异盒模型下 width 指的是内容content+padding+border的宽度------IE6,7,8 不设置 情况下,是怪异盒模型,如果加了就是标准盒模型
  • 具体是使用哪一种盒模型,用box-sizing来设置
  • 兼容性: box-sizing现代浏览器都支持,但IE家族只有IE8版本以上才支持
box-sizing:content-box;
box-sizing:border-box;

目前使用此属性,需要加前缀,Mozilla需要加上-moz-,Webkit内核需要加上-webkit-,Presto内核-o-,IE8-ms-
-webkit-box-sizing:content-box;
-moz-box-sizing:birder-box;

标准盒模型的缺点是,盒子的宽度和高度计算复杂(两子元素并排例子)
怪异盒模型的优点,当确定了width/height之后,可以随意修改padding和border的厚度值,而不用担心父容器被撑爆。

  1. 如何实现水平和垂直居中?
  • 元素水平居中
//行内元素
text-align:center

//块级元素
margin-left: auto;
margin-right:auto;

  • 元素垂直居中
//方案1     position-----元素固定宽高的情况下
out
in
image
//方案2     元素宽高不固定的情况下,把上边的 margin-left:(宽度/2);
  margin-top:(高度/2) 换成 transform:translate(-50%,-50%);
out
测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测
image
//方案3  flex
image
//方案4 table-cell
给父元素设置 display:table; 父元素变成块级表格元素(相当于table);
给子元素设置 display:table-cell;使子元素变成表格单元格(相当于td),然后设置 vertical-align:center; text-align:center;
哈哈哈哈哈哈哈哈哈哈
image
  1. flex怎么用?常用的属性有哪些?
    flex弹性布局,为盒模型提供最大的灵活性,任何一个容器,都可以指定为flex布局;
.box{
  display:flex;
}

//行内元素也可以设置为flex
.box{
    display:inline-flex;
}

注意设为flex布局后,子元素的float,clear,vertical-align属性将失效。

采用flex布局的元素,成为flex容器。它的所有子元素自动成为容器成员,称为flex项目。
常用的,设置到容器上的属性有:


1.flex-direction:属性决定主轴的方向(即项目的排列方向)。
.box{
  flex-direction:row | row-reverse | column | column-reverse
}

--------------------------------------------------------------------------------

2\. flex-wrap:默认情况下,项目都排在一条线上。这个属性定义,如果一条轴线拍不下,如何换行。
.box{
  flex-wrap:nowrap | wrap | wrap-reverse
}

--------------------------------------------------------------------------------

3\. justify-content:属性定义了项目在主轴上的对齐方式。
.box{
   justify-content:flex-start | flex-end | center | space-between | space-around
}

--------------------------------------------------------------------------------

4.align-items:属性定义项目在交叉轴上如何对齐
.box{
   align-items:flex-start | flex-end | center | baseline | stretch
}
baseline 项目的第一行文字的基线对齐
stretch(默认值)  如果项目未设置高度或设为auto,将占满整个容器的高度。

--------------------------------------------------------------------------------

5\. align-content:属性定义了多根轴线的对齐方式。如果项目只有一根轴线,该属性不起作用。
.box {
  align-content: flex-start | flex-end | center | space-between | space-around | stretch;
}

--------------------------------------------------------------------------------
6\. flex-flow  是flex-direction和 flex-wrap的简写,默认是 row nowrap

设置到项目上的属性:

1.order 属性定义项目的排列顺序。数值越小,排列越靠前。默认为0
.item {
  order: 1;
}

2.flex-grow属性定义项目的放大比例,默认为0.即如果有剩余空间,也不放大。
.item {
  flex-grow: ; /* default 0 */
}

3.flex-shrink 属性定义了项目的缩小比例。默认为1,如果空间不足,项目将缩小。
.item {
  flex-shrink: ; /* default 1 */
}
如果所有项目的flex-shrink属性都为1,当空间不足时,都将等比例缩小。如果一个项目的flex-shrink属性为0,其他项目都为1,则空间不足时,前者不缩小。

负值对该属性无效。

4\. flex-basis 属性定义了在分配多余空间之前,项目占据的主轴空间。默认为auto.即项目本来的大小。它可以设为跟width或height属性一样的值(比如350px),则项目将占据固定空间。
.item {
  flex-basis:  | auto; /* default auto */
}

5\. flex 属性是flex-grow,flex-shrink,flex-basis,默认值为0 1 auto
该属性有两个快捷值:auto (1 1 auto) 和 none (0 0 auto)。
建议优先使用这个属性,而不是单独写三个分离的属性,因为浏览器会推算相关值。

6.align-self 属性 允许单个项目有与其他项目不一样的对齐方式,可覆盖align-items属性,默认值为auto,表示继承父元素的align-items属性,如果没有父元素,则等同于stretch。
.item {
  align-self: auto | flex-start | flex-end | center | baseline | stretch;
}

  1. BFC是什么?
    (Block Formatting Context)块级格式化上下文。BFC就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素,反之也如此.并且在一个BFC中,块盒与行盒(行盒由一行中所有的内联元素所组成)都会垂直的沿着其父元素的边框排列。
    这个可以具体回答,比如给一个div设置 overflow:hidden 它里边的浮动元素就会被包裹起来。
    如何触发BFC?
1. 根元素 即HTML元素
2. float的值,不为none
3. overflow的值,不为visible
4. display的值,为inline-block,table-cell ,table-caption
5. position 的值,为absolute 或fixed

  1. 选择器的优先级?
    CSS2的时候 (如果面试官更倾向于这个就这样回答)
!important 优先级最高  权值无穷大
style 写在元素行的内联样式其次   权值1000
id   权值100 
class/伪类/属性     权值10 
标签选择器       权值1
通配符        权值0 

更准确的说法
1.越具体,优先级越高
2.写在后面的,会覆盖写在前面的
3.important优先级最高,但是要少用

6.清除浮动的方法:

方法
1:给父元素设置高度
2:给父元素设置overflow:hidden
3:  最佳实践  
.clearfix::after{
  content:'';
  display:block;
  clear:both;
}
.clearfix加到容器上,里边的子元素浮动就被清除了


原生JS面试题

1. ES6语法知道哪些?分别怎么用

A-------新增声明命令 let 和 const, let表示变量 const表示常量

特点:
1. 都是块级作用域,以{}代码块作为作用域范围,只能在代码块里面使用。
2. 不存在变量提升,只能先声明再使用,否则会报错。在代码块内,在声明变量之前,该变量都是不可用的,这在语法上成为'暂时性死区'
3. 在一个代码块内,不允许重复声明
4. const声明的是一个只读常量,在声明的时候就要赋值,否则会摆错,(如果const声明的是一个对象,对象所包含的值是可以修改的,抽象一点说,对象所指的地址是不能够改变的,变量成员是可以修改的)

B--------模板字符串
用一对反引号(``)标识,它可以当做普通字符串使用,也可以用来定义多行字符串。也可以在字符串中嵌入变量,JS表达式,或函数。需要写在${}中

var str = `abc
def
gh`;
console.log(str);
let name = '明';
function a(){
  return 'ming'
}
console.log(`我的名字叫做${name},年龄${17+5}岁,我的性别是${'男'},游戏ID:${a()})

C------函数的扩展

  • 函数的默认参数
    ES6为参数提供了默认值,在定义函数的时候,便初始化了这个参数,以便在参数没有被传递进去的时候使用。
function A(a,b=1){
  console.log(a+b)  
  console.log(a);
  console.log(b);
}
A(1);   // 2, 1,1
A(2,3);   //5,2,3

  • 箭头函数
    ES6中提供了一种简洁的函数的写法,箭头函数。
//只有一个参数时候,可以省略参数的括号。
//如果没有参数,或者参数在2个及两个以上,必须带括号
//当代码只有一行,并且有立即返回值的时候,可以省略花括号{}
var f = a => a+1

var sayHi = ()=>{
  console.log('hi')
}

var sum = (num1,num2)=>{
    console.log(num1+mun2)
}

箭头函数的特点
箭头函数的this是在定义函数的时候绑定,而不是在执行函数的时候绑定。
所谓在定义函数的时候绑定的意思是,箭头函数的this是继承自父执行上下文。箭头函数没有自己的this.

D---------对象的扩展

  • 属性的简写
    ES6允许在对象之中,直接写变量,这时属性名为变量名,属性的值为变量的值。
var foo = 'bar';
var baz = {foo};   //相当于 var bar={foo:foo}

  • 方法的简写
    省略冒号和function关键字
var o = {
  sayHi:function(){
      console.log('hi')
}
}

相当于
var o = {
  sayHi(){
  console.log('hi')
}
}

  • Object.keys()方法,获取对象的所有属性名和方法名。不包括原型的内容,返回一个数组
var person = {name:"john",age:20,study(){alert('study')}};
console.log(Object.keys(person))    //  ["name", "age", "study"]

console.log(Object.keys(['aa','bb','cc']);      //["0", "1", "2"]
console.log(Object.keys('abcdef'));      //["0", "1", "2", "3", "4", "5"]

  • Object.assign()方法
    这个方法将多个原对象的属性和方法,合并到了目标对象上面。
    可以接收多个参数,第一个参数是目标对象,后边都是源对象
var target ={}
var obj1 = {
  name:'petter',
  age:20,
  sex:'女'
}
var obj2 = {
  sex:'男',
  score:100
}

Object.assign(target,obj1,obj2);
console.log(target);  
//{age: 20,name: "petter", score: 100, sex: "男"}

E---------- for...of循环
是遍历所有数据结构的统一方法。for...of循环可以使用的范围包括数组,Set,Map结构,某写类数组对象(arguments, DOM NodeList对象)以及字符串。

var arr = ["水星","金星","地球","火星"];
for(var s of arr){
  console.log(s);   //"水星"   "金星"   "地球"   "火星"
}

F--------import 和 export
ES6标准中,JavaScript 原生支持模块(module)了,这种将JS代码分割成不同功能的小块进行模块化,将不同功能的代码分别写在不同的文件中,各模块只需要导出公共接口部分,然后通过模块的导入方式可以在其他地方使用。

export 用于对外输出本模块(一个文件可以看做是一个模块)的变量接口
import 用于在一个模块中加载另外一个含有export接口的模块
import和export命令只能在模块的顶部,不能在代码块之中。

G-----Promise对象
Promise是异步编程的一种解决方案,将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。

它有三种状态,分别是 pending-进行中,resolved-已完成,rejected-已失败

Promise构造函数接收一个参数,这个参数是函数,并且这个函数传入两个参数,分别是 resolve 和 reject,分别是异步操作执行成功后的回调函数,和异步操作执行失败后的回调函数。(按照标准来讲,resolve是将Promise的状态置为fullfiled,reject是将Promise的状态置为rejected。)
所以我们用Promise的时候一般是包在一个函数中,在需要的时候去运行这个函数,如:

function runAsync(){
  var p = new Promise((resolve,reject)=>{
      setTimeout(function(){
        console.log('执行完成');
        resolve('随便什么数据')
},2000)
})  
return p;  
}

runAsync();

在我们包装好的函数最后,会return出Promise对象,也就是说,执行这个函数我们得到了一个Promise对象。
Promise对象上有then 和 catch方法。

runAsync().then(function(data){
  console.log(data)    //'随便什么数据'
  //后面可以用传过来的数据做些其他操作
  //......
})

在runAsync()的返回上直接调用then方法,then接收一个参数,是函数,并且会拿到我们在runAsync中调用resolve时传的的参数。

这时候你应该有所领悟了,原来then里面的函数就跟我们平时的回调函数一个意思,能够在runAsync这个异步任务执行完成之后被执行。这就是Promise的作用了,简单来讲,就是能把原来的回调写法分离出来,在异步操作执行完后,用链式调用的方式执行回调函数。

而Promise的优势在于,可以在then方法中继续写Promise对象并返回,然后继续调用then来进行回调操作。

链式操作的用法

从表面上看,Promise只是能够简化层层回调的写法,而实质上,Promise的精髓是“状态”,用维护状态、传递状态的方式来使得回调函数能够及时调用,它比传递callback函数要简单、灵活的多。所以使用Promise的正确场景是这样的:


function runAsync1(){
    var p = new Promise(function(resolve, reject){
        //做一些异步操作
        setTimeout(function(){
            console.log('异步执行1完成');
            resolve('随便什么数据1');
        }, 2000);
    });
    return p;            
}
function runAsync2(){
    var p = new Promise(function(resolve, reject){
        //做一些异步操作
        setTimeout(function(){
            console.log('异步执行2完成');
            resolve('随便什么数据2');
        }, 2000);
    });
    return p;            
}
function runAsync3(){
    var p = new Promise(function(resolve, reject){
        //做一些异步操作
        setTimeout(function(){
            console.log('异步执行3完成');
            resolve('随便什么数据3');
        }, 2000);
    });
    return p;            
}

runAsync1()
.then(function(data){
  console.log(data);
  return runAsync2()
})
.then(function(data){
  console.log(data);
  return runAsync3()
})
.then(function(data){
  console.log(data)
})

//2秒后打印
"异步执行1完成"
"随便什么数据1"

//又2秒后打印
"异步执行2完成"
"随便什么数据2"

//又2秒后打印
"异步执行3完成"
"随便什么数据3"

在then方法中,可以直接return 数据,而不是Promise对象,在后边的then中就可以接受到数据了。

runAsync1()
.then(function(data){
  console.log(data);
  return runAsync2()
})
.then(function(data){
  console.log(data);
  return "直接返回数据"
})
.then(function(data){
  console.log(data)
})

//2秒后打印
"异步执行1完成"
"随便什么数据1"

//又2秒后打印
"异步执行2完成"
"随便什么数据2"
"直接返回数据"

reject的用法

我们前面的例子都是只有“执行成功”的回调,还没有“失败”的情况
reject就是把Promise的状态设置为 rejected,这样我们在then中就能捕捉到,然后执行失败情况的回调。

function getNumber(){
  return new Promise((resolve,reject)=>{
    setTimeout(function(){
      var num = Math.ceil(Math.random()*10) //生成0-10之间的整数
      if(num<=5){
        resolve(num)
      }else{
        reject('数字太大了')
      }
    },2000)
  })
}

getNumber()
.then(
  function(data){
    console.log('resolved')
    console.log(data);
  },
  function(reason,data){
    console.log('rejected')
    console.log(reason)
  }
)

getNumber函数用来异步获取一个数字,2秒后执行完成,如果数字小于等于5,我们认为是“成功”了,调用resolve修改Promise的状态。否则我们认为是“失败”了,调用reject并传递一个参数,作为失败的原因。 运行getNumber并且在then中传了两个参数,then方法可以接受两个参数,第一个对应resolve的回调,第二个对应reject的回调。所以我们能够分别拿到他们传过来的数据

catch 的用法

我们知道 Promise除了有一个then方法,还有一个catch方法,它是干什么的呢?
其实它跟then方法的第二个参数的作用是一样的。用来指定reject的回调。
它的用法:

getNumber()
.then(
  function(data){
    console.log('resolved')
    console.log(data);
  }
)
.catch(
  function(reason){
    console.log('rejected')
    console.log(reason)
  }
)

效果和写在then的第二个参数里面一样。不过它还有另外一个作用,在执行resolve的回调(也就是then方法的第一个参数)时,如果抛出异常(代码出错了),那么并不会报错卡死JS,而是会进入到catch方法中


getNumber()
.then(function(data){
    console.log('resolved');
    console.log(data);
    console.log(somedata); //此处的somedata未定义
})
.catch(function(reason){
    console.log('rejected');
    console.log(reason);

在 resolve的会调用,我们console.log(somedata),而somedata是未定义的,如果不用Promise,代码运行到这里,就直接在控制台报错了,不往下运行了。但是在这里会得到这样的结果:

image

也就是说进到catch方法里面去了,而且把错误原因传到了reason参数中。即便是有错误的代码也不会报错了,这与我们的try/catch语句有相同的功能。

all方法

Promise.all()提供了并行执行异步操作的能力。并且在所有异步操作执行完成以后,才执行回调。

Promise
.all([runAsync1(), runAsync2(), runAsync3()])
.then(function(results){
    console.log(results);
});

Promise.all(),接收一个数组作为参数,数组里的元素最终都返回一个Promise对象。这样,三个异步的操作就并行执行了,等到他们都执行完以后,才会进入到then里边,那么三个异步操作执行以后返回的数据哪里去了呢? all会把所有异步操作的结果放进一个数组,并且把数组传递给then,就是上边的results.
所以上边代码输出的结果是:

image

有了all方法,你就可以并行执行多个异步操作,并且在一个回调中处理所有的返回数据,有一个场景很适合用这个方法。
比如一些游戏类的素材比较多的应用,打开网页时预先加载,需要用到各种资源,比如图片,flash,以及各种静态文件。所有都加载完以后,再进行页面的初始化。

race的用法

Promise.all方法,实际上是谁跑的慢,以谁为准执行回调。那么相对的就有另外一个方法,以谁跑的块,以谁为准执行回调。
就是race方法,这个词本来就是赛跑的意思。race的用法与all一样。我们修改下上边的计时器的时间:

function runAsync1(){
    var p = new Promise(function(resolve, reject){
        //做一些异步操作
        setTimeout(function(){
            console.log('异步执行1完成');
            resolve('随便什么数据1');
        }, 1000);
    });
    return p;            
}
function runAsync2(){
    var p = new Promise(function(resolve, reject){
        //做一些异步操作
        setTimeout(function(){
            console.log('异步执行2完成');
            resolve('随便什么数据2');
        }, 3000);
    });
    return p;            
}
function runAsync3(){
    var p = new Promise(function(resolve, reject){
        //做一些异步操作
        setTimeout(function(){
            console.log('异步执行3完成');
            resolve('随便什么数据3');
        }, 2000);
    });
    return p;            
}
Promise
.race(runAsync1(),runAsync2(),runAsync3())
.then(function(results){
  console.log(results);
})

//1秒后打印
"异步执行1完成"
//再过1秒后打印
"异步执行3完成"
//再过2秒后打印
"异步执行2完成"

这个race有什么用呢?使用场景还是很多的,比如我们可以用race给某个异步请求设置超时时间,并且在超时后执行相应的操作,代码如下:

//请求某个图片资源
function requestImg(){
    var p = new Promise(function(resolve, reject){
        var img = new Image();
        img.onload = function(){
            resolve(img);
        }
        img.src = 'xxxxxx';
    });
    return p;
}

//延时函数,用于给请求计时
function timeout(){
    var p = new Promise(function(resolve, reject){
        setTimeout(function(){
            reject('图片请求超时');
        }, 5000);
    });
    return p;
}

Promise
.race([requestImg(), timeout()])
.then(function(results){
    console.log(results);
})
.catch(function(reason){
    console.log(reason);
});

requestImg函数会异步请求一张图片,我把地址写为"xxxxxx",所以肯定是无法成功请求到的。timeout函数是一个延时5秒的异步操作。我们把这两个返回Promise对象的函数放进race,于是他俩就会赛跑,如果5秒之内图片请求成功了,那么遍进入then方法,执行正常的流程。如果5秒钟图片还未成功返回,那么timeout就跑赢了,则进入catch,报出“图片请求超时”的信息。运行结果如下:

image

H-------解构赋值
ES6允许按照一定的模式,从数组和对象中提取值,对变量进行赋值。这杯成为解构赋值。

  • 数组的解构赋值;
    数组中的值会被自动解析到对应接收该值的变量中,数组的解构赋值要一一对应,如果有对应不上的,就是undefined
let [a,b,c,d] = [1,2,3];
console.log(a);  1
console.log(b);  2
console.log(c);  3
console.log(d);  undefined

-对象的结构赋值
对象的结构赋值与数组有一个重要的不同,数组的元素是按照次序排列的,变量的取值由它的位置决定。而对象的属性是没有次序的,变量必须与属性同名,才能取到正确的值。

var person = {name:'小明',age:20,sex:'男'};
var {name,age,sex} = person;
console.log(name);
console.log(age);
console.log(sex);

//如果想要变量名和属性名不同,可以这样写

let {name:foo,age:baz} = {name:'小明',age:20,sex:'男'}
console.log(foo);  '小明'
console.log(baz);  20

I--------set数据结构
Set的数据结构,类似数组,所有的数据都是唯一的,没有重复的值,它本身是一个构造函数;

var arr = [1,2,2,3,4,4];
var s = new Set(arr);
console.log(s);   //{1,2,3,4}

属性和方法:

size 数据的长度
add() 添加某个值,返回 Set 结构本身。
delete() 删除某个值,返回一个布尔值,表示删除是否成功。
has() 查找某条数据,返回一个布尔值。
clear() 清除所有成员,没有返回值。

console.log(s.size);  //4
console.log(s.add(5));  //{1,2,3,4,5}
console.log(s.delete(1));  //true
console.log(s.has(2));   //true
s.clear();


2.函数节流和函数防抖

前提条件:一个函数在短时间内被多次执行。
但是这种短时间内多次重复执行,通常情况下是没有必要的。我们需要优化这种高频执行的JS。

优化的方法就是 函数节流 和 函数防抖
函数节流:
让函数在特定的时间之内只执行一次。特定时间内如果多次触发函数,只有一次生效。
应用场景 :下拉加载。

函数防抖:
频繁触发某一事件,其中两次触发间隔时间大于等于特定时间,才执行代码一次。如果在特定时间内,又触发了一次事件,那么重新开始计算函数执行时间。简单的说,一个动作连续触发,只执行最后一次。
应用场景:搜索框搜索输入,比如用户输入字母间隔超过2秒再发送请求。

函数节流的例子
//函数防抖例子
//公交关门
function close(){
  console.log('关门');
}

var button = document.querySelector('#btn');

var timer = null ;  //定时器一开始是空的
button.onclick = function(){
  //如果点击了按钮,发现上一个计时器还存在,那么就把它清除掉。
  if(timer){
    window.clearTimeout(timer);
  }
  //如果5秒种之内没有再点,就设置一个定时器,并执行关门函数,并且把定时器清除掉。
    timer = setTimeout(function(){
    close();
    timer= null;
  },5000)                   
}

3.手写AJAX

var xhr = new XMLHttpRequest();
xhr.open('GET','/xxx',true);

xhr.onreadystatechange = function(){
  if(xhr.readystate === 4){
    console.log('请求完成')
    if(xhr.status>=200&&xhr.status<300||xhr.status === 304){
          console.log('请求成功')
    }else{
          console.log('请求失败')
    }
  }
}

xhr.send();

4.这段代码里的this是什么?

主要是看函数怎么调用的!

fn()   // this指向 window/global
obj.fn()  //  this 指向 obj
fn.call(xx)  //this 指向 xx
fn.bind(xx)  //this指向 xx
new Fn()   //this指向 新的对象
fn  = ()=>{}   //this 指向外边的this

判断的通用方法
function fn(a,b){
    console.log(this)
}

fn(1,2);//这句等价于下边
fn.call(undefined,1,2);
fn.apply(undefined,[1,2]);
注意:
在严格模式下 'use strict'  ,此时 fn里的this,就是call和apply的第一个参数,也就是 undefined
在非严格模式下,也就是不用 'use strict' ,call和apply里的第一个参数如果是undefined或者是null,那么this会默认替换为 window

在看一个例子:
var  obj = {
    fn:function(a,b){
        console.log(this)
    },
    child:{
        fn2:function(){
            console.log(this)
        }
    }
}
obj.fn(1,2); 等价于  
obj.fn.call(obj,1,2);
obj.fn.apply(obj,[1,2]);  //所以this是obj

obj.child.fn2();等价于
obj.child.fn2.call(obj.child);
obj.child.fn2.apply(obj.child);   //所以this是 obj.child

5. 闭包和立即执行函数是什么?

  • 闭包:
    就是能读取其他函数内部变量的函数,在JS中,只有函数内部的子函数,能够读取函数内部的局部变量。所以闭包可以理解为,定义在一个函数内部的函数。

  • 闭包的缺点:
    让变量始终保持在内容中,内存消耗会很大。

  • 什么是词法作用域?
    词法作用域就是函数创建的时候所在的作用域。
    所以,函数在执行的时候,使用的变量,会先从自己内部去找,如果自己内部没有定义,就到自己的词法作用域(scopes)去找。

function car(){
  var speed = 0;
  function fn(){
      speed++;
      console.log(speed);
  }
  return fn;
}

var speedUp = car();
speedUp();  //1
speedUp();  //2

// 如果在car函数里,再声明一个函数fn,并返回fn,fn()内部又使用了car声明的变量,然后调用car函数并赋值给 一个全局变量speedUp
// 因为speedUp 属于全局作用域,而全局作用域在页面没有关闭的时候,是不会销毁的。所以也就导致了fn函数不会被销毁
// 执行speedUp就相当于 执行fn(),而fn()函数内部有用到局部变量speed,按照作用域链来寻找,fn()函数内没有声明
// 继续往上一级找,fn()函数是声明在car()内的,而speed是在car内声明的
// 所以第一次执行 speedUp() 的时候,结果是1;执行之后,speed是没有被销毁的
// 再次执行就是2

//再看个例子
var fnArr = [];
for(var i=0;i<10;i++){
  fnArr[i] = function(){
      return i;
  }
}

console.log(fnArr[1]);   //  这个时候数组里存的都是函数 function(){ return i}
console.log(fnArr[2]())   //10
console.log(fnArr[5]())   //10
console.log(fnArr[7]())   //10

//为什么会输出10呢?  分析一下
fnArr[2]本身是一个函数,加括号,表示执行它指向的那个函数。
函数内部 return i,用到了i这个变量,所以会从自己内部去找这个变量,发现没有声明i,然后在去函数声明的作用域去找,函数是在for里声明的,但是for本身并不是函数,所以function函数声明的作用域是全局作用域。而全局作用域里,for循环完以后,i已经变为10了,所以fnArr[i]()会输出10.

那么如果我们想输出 0,1,2,3,4,5,....怎么办呢?
用立即执行函数
var fnArr = [];
for(var i=0;i<10;i++){
  fnArr[i] =( function(j){
      return j;
  })(i)
}

console.log(fnArr[0])   //0
console.log(fnArr[5])   //5
console.log(fnArr[7])   //7

再看个例子
for(var i=0;i<5;i++){
  setTimeout(function(){
    console.log(i)
  },2000)
}
//这里2秒后会输出5个5

for(var i=0;i<5;i++){
  (function(j){
    setTimeout(function(){
    console.log(j)
  },2000)
  })(i)
}
//这里2秒后会输出 0,1,2,3,4

再熟悉一下作用域链

var x = 10
function foo(){
  console.log(x);
}
foo();   //10  

function bar(){
  var x = 20;
  foo();
}
bar();  //10
执行bar就是执行foo,foo输出x,先从自己内部去找
发现没有声明x,然后到foo声明的作用域去找
foo是声明在全局作用域的,而全做作用域下,有声明变量x,所以输出10

//下边之所以输出30 是因为foo是在bar内部声明的。
 var x = 10;
 bar();  //30

 function bar(){
 var x = 30;
  function foo(){
        console.log(x); 
   }
    foo();
}

6.什么是JSONP,什么是CORS,什么是跨域?

什么是跨域?
--------是指不同源的网站之间的访问。

同源策略
---------提到跨域,就不得不说一下同源策略,同源策略是NetScape提出的浏览器的一种安全策略,也就是指a网站,不能随便读取b网站的内容。

所谓同源:
--------指的是,协议、域名、端口、相同

最常见的应用是,当我们调用ajax接口时,如果不设置跨域,浏览器会报错。这证明使用XMLHttpRequest对象不能发送跨域请求。

有疑惑的小伙伴肯定会问,那我用a标签请求其他网站,是不是就是跨域了呢?
这里要明白跨域是指在当前域下调用其他域下的东西,而链接则是直接跳转到对方的域下了,跟你之前的域名毫无关系。

如果想从你的网站跨域去另一个网站拿到一些资源,有一个前提是另外一个网站的服务器支持跨域,这个需要在服务器端设置,不同服务器的设置方法不一样,我们在这里不多说,就看客户端跨域如何解决?

解决跨域最常见的方式是 jsonp。

先弄清楚 json和jsonp的区别
json (JavaScript Object Notation)是一种轻量级的数据交换格式,用来在浏览器和服务器之间交换数据。
jsonp (JSON With Padding) 就是打包在函数中调动的json,或者包裹的json
json 是一种数据格式;jsonp是一种数据调用方式

//json
{
  "name":"小明"
}

//jsonp
callback({
 "name":"小明"
})

jsonp是jquery给我们封装的一个方法,使用方法如下:

$.ajax({
  ulr:'xxx',
  type:'GET',
  dataType:'jsonp',
  success:function(data){
    console.log(data)
  }
})

上边的代码是当我们调用外部的一个接口时,通过设置jquery里的ajax方法里的datatype属性的值为jsonp,就可以成功调用这个接口了。

首先我们需要明白,在页面上直接发起一个跨域的ajax请求是不可以的,但是,在页面上引入不同域上的js脚本却是可以的,就像你可以在自己的页面上使用 标签来随意显示某个域上的图片一样。
看下怎么利用

你可能感兴趣的:(2019前端面试总结)