面试问题总结(持续更新中。。。)

郜小超 等级 127 0 0

1.let const var 的区别

在JavaScript中,有三个关键字可用于声明一个变量,并且每个关键字都有其不同之处。分别var,let和const。一个简单粗暴的解释:使用const关键字声明的变量不能被重新赋值,let而且var可以。也可以这么说:const只可以声明一个常量(js中一旦被定义就无法再被修改的变量,称之为常量)。

eg:

const name = '小明'
console.log(name)
name = '小蓝' //can't be reassigned


let name = "小明";
person = "小蓝";
console.log(person); //  reassignment is allowed with let

当然有朋友提出来const定义的引用类型的值,可以进行修改

因为对象是引用类型的,变量中保存的仅是对象的指针,这就意味着,const仅保证指针不发生改变,修改对象的属性不会改变对象的指针,所以是被允许的。也就是说const定义的引用类型只要指针不发生改变,其他的不论如何改变都是允许的。 var声明的变量是函数作用域的,这意味着当在函数中创建变量时,该函数中的所有内容都可以访问该变量。此外,函数中创建的函数作用域变量不能在此函数之外访问。

function myFunction() {
  var name = "小明";
  console.log(myVar); // 可以打印出来
}
console.log(name); // Throws a ReferenceError, name is not accessible outside the function.


function myFunction() {
  var myVar = "Nick";
  if (true) {
    var myVar = "John"; //此处会变量提升
    console.log(myVar); // "John"
    // actually, myVar being function scoped, we just erased the previous myVar value "Nick" for "John"
  }
  console.log(myVar); // "John" - see how the instructions in the if block affected this value
}
console.log(myVar); // Throws a ReferenceError, myVar is not accessible outside the function.

var和let大致相同,但let声明的变量是块范围不存在变量提升不能在同一范围内重新声明

function myFunction() {
  var myVar = "Nick";
  if (true) {
    let myVar2 = "John";
    console.log(myVar); // "Nick"
  }
  console.log(myVar2); //无法访问if块级作用域中的变量myVar2
}
console.log(myVar); //函数外部无法访问函数内部定义的变量

不能重复声明一个let变量

let name = 'Tom';
let name = 'Bob';  //抛出语法错误

暂时性死区 : let不像var那样会发生“变量提升”现象。所以,变量一定要在声明后使用,否则报错。

// var 的情况
console.log(foo); // 输出undefined
var foo = 2;

// let 的情况
console.log(bar); // 报错ReferenceError
let bar = 2;
-------------------------------------------------------------------------
console.log(foo); // 输出undefined
console.log(bar); // 报错ReferenceError

var foo = 2;
let bar = 2;

2.ES6之箭头函数

首先了解一下基本函数的写法

function test(name) {  //声明式写法
    console.log(name)
}
test('Jerry')

let test2 = function(name) {  //赋值式写法
    console.log(name)
}
test2('Tom')

箭头函数的基本用法

let func = value => value;

相当于

let func = function (value) {
    return value;
};

多个参数的箭头函数

let func = (value, num) => value * num;

相当于

let func = (value, num) => {
    return value * num
};

箭头函数的写法:

1.箭头函数只能用赋值式写法,不能用声明式写法

2.如果参数只有一个,可以不加括号,如果没有参数或者参数多于一个就需要加括号

3.如果函数体只有一句话,可以不加花括号

4.如果函数体没有括号,可以不写return,箭头函数会帮你return

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

2.1箭头函数的this的指向问题

箭头函数:出现的作用除了让函数的书写变得很简洁,可读性很好外;最大的优点是解决了this执行环境所造成的一些问题。比如:解决了匿名函数this指向的问题(匿名函数的执行环境具有全局性),包括setTimeout和setInterval中使用this所造成的问题。

2,我们常见的window属性和方法有alter,document,parseInt,setTimeout,setInterval,localtion等等,这些在默认的情况下是省略了window前缀的。(window.alter = alter)。

3,在“use strict”严格模式下,没有直接的挂载者(或称调用者)的函数中this是指向window,这是约定俗成的。在“use strict”严格模式下,没有直接的挂载者的话,this默认为undefined。以下都是在非严格模式下讨论。

普通函数中的this 普通函数,this的概念是:this是JavaScript的一个关键字,他是指函数执行过程中,自动生成的一个内部对象,是指当前的对象,只在当前函数内部使用。(this对象是在运行时基于函数的执行环境绑定的:在全局函数中,this指向的是window;当函数被作为某个对象的方法调用时,this就等于那个对象)。

这种情况下,我们希望在每一次循环后都能得到$("#content")的this,但是因为this是只在当前函数内部使用的,又因为js函数是可以多层嵌套的原因,使得我们无法得到最开始的this,这时我们可以把他先用一个变量存储起来,最终达到我们想要的目的。(一般我们会这样定义:var _this = this var that=this)

打印是结果是the window。原因是,匿名函数的执行环境是全局的,而且this只在函数内部起作用。此时的this.name在匿名函数中找不到,所以就从全局中找,找到后打印出来。

此时可以用一个变量保存当前的this,此时的this就指向的是当前的对象object

es6箭头函数的this

箭头函数的this定义:箭头函数的this是在定义函数时绑定的,不是在执行过程中绑定的。简单的说,函数在定义时,this就继承了定义函数的对象。

所以,这会很好的解决匿名函数和setTimeout和setInterval的this指向问题。我们不用再去给其用that变量存储this。

此时的this就是函数在哪里定义的,this就绑定给谁

3. link与@import的区别

1.@import是CSS提供的语法规则,只有导入样式表的作用;link是HTML提供的标签,不仅可以加载 CSS 文件,还可以定义 RSS,Rel连接属性,设置浏览器资源提示符preload、prefetch等。

2.HTML文档在解析的过程当中,如果遇到link标签,则会立即发起获取CSS文件资源的请求;@import引入的CSS将在页面加载完毕后才会被加载。

3@import是CSS2.1才有的语法,因此需要IE5以上才能识别;link标签作为HTML元素,不存在兼容性问题。

4. js数组中的find、filter、forEach、map四个方法的详解和应用实例

find():返回通过测试的数组的第一个元素的值

在第一次调用 callback 函数时会确定元素的索引范围,因此在 find 方法开始执行之后添加到数组的新元素将不会被 callback 函数访问到。如果数组中一个尚未被callback函数访问到的元素的值被callback函数所改变,那么当callback函数访问到它时,它的值是将是根据它在数组中的索引所访问到的当前值。被删除的元素仍旧会被访问到。

array.find(function(value, index, arr),thisValue)

value:必须,代表当前元素,其他四个参数都是可选,index代表当前索引值,arr代表当前的数组,thisValue代表传递给函数的值,一般用this值,如果这个参数为空,undefined会传递给this值

返回值:返回符合测试条件的第一个数组元素的值,如果没有符合条件的则返回undefined。

var arr = [1,2,3,4,5,6,7];
 var ar = arr.find(function(elem){
     return elem>5;
 });
 console.log(ar);//6
console.log(arr);//[1,2,3,4,5,6,7]

find()方法为数组中的每个元素都调用一次函数执行,当数组中的元素在测试条件时返回true,find()返回符合条件的元素,之后的值不会再执行函数。如果没有符合条件的元素则返回undefined。

filter():创建一个新数组,新数组中的元素是通过检查指定数组中符合条件的所有元素

filter 遍历的元素范围在第一次调用 callback 之前就已经确定了。在调用 filter 之后被添加到数组中的元素不会被 filter 遍历到。如果已经存在的元素被改变了,则他们传入 callback 的值是 filter 遍历到它们那一刻的值。被删除或从来未被赋值的元素不会被遍历到。

array.filter(function(value, index, arr),thisValue)

value:必须,代表当前元素,其他四个参数都是可选,index代表当前索引值,arr代表当前的数组,thisValue代表传递给函数的值,一般用this值,如果这个参数为空,undefined会传递给this值

返回值:返回数组,包含了符合条件的所有元素,如果没有符合条件的则返回空数组

var arr = [1,2,3,4,5,6,7];
 var ar = arr.filter(function(elem){
    return elem>5;
 });
console.log(ar);//[6,7]  返回一个新数组
console.log(arr);//[1,2,3,4,5,6,7]

map():返回一个新数组,数组中的元素为原始数组元素调用函数处理后的值,map()方法按照原始数组元素顺序依次处理元素

map方法会给原数组中的每个元素都按顺序调用一次callback函数,callback每次执行后的返回值(包括undefined)组合起来形成一个新数组。callback函数只会在有值的索引上被调用,那些从来没被赋过值或者使用delete删除的索引则不会被调用。

使用map方法处理数组时,数组元素的范围是在callback方法第一次调用之前就已经确定了。在map方法执行的过程中,原数组中新增加的元素将不会被callback访问到,若已经存在的元素被改变或删除了,则他们传递到callback的值是map方法遍历到他们的那一刻的值,而被删除的元素将不会被访问到。

array.map(function(value, index, arr),thisValue)

value:必须,代表当前元素,其他四个参数都是可选,index代表当前索引值,arr代表当前的数组,thisValue代表传递给函数的值,一般用this值,如果这个参数为空,undefined会传递给this值

返回值:返回一个新数组,数组中的元素为原始数组元素调用函数处理后的值

var arr = [1,2,3,4,5,6,7];
 var ar = arr.map(function(elem){
    return elem*4;
 });
 console.log(ar);//[4, 8, 12, 16, 20, 24, 28]   返回一个新数组
console.log(arr);//[1,2,3,4,5,6,7]

forEach():用于调用数组每个元素,并将元素传递给回调函数(注意没有办法跳出或终止forEach语句,除非抛出异常)

forEach 遍历的范围在第一次调用 callback 前就会确定。调用forEach 后添加到数组中的项不会被 callback 访问到。如果已经存在的值被改变,则传递给 callback 的值是 forEach 遍历到他们那一刻的值。已删除的项不会被遍历到

array.forEach(function(value, index, arr),thisValue)

value:必须,代表当前元素,其他四个参数都是可选,index代表当前索引值,arr代表当前的数组,thisValue代表传递给函数的值,一般用this值,如果这个参数为空,undefined会传递给this值

var arr = [1,2,3,4,5,6,7];
 var sum = 0;
 var ar = arr.forEach(function(elem){
    sum+=elem*4;
 });
 console.log(ar);//undefined
 console.log(arr);//[1,2,3,4,5,6,7]
 console.log(sum);//112

forEach()返回值为undefined,里面即便有return语句,返回值依然是undefined

现在说说各自的意义:

1.find()方法主要用来返回数组中符合条件的第一个元素(没有的话,返回undefined)

2.filter()方法主要用来筛选数组中符合条件的所有元素,并且放在一个新数组中,如果没有,返回一个空数组

3.map()方法主要用来对数组中的元素调用函数进行处理,并且把处理结果放在一个新数组中返回(如果没有返回值,新数组中的每一个元素都为undefined)

4.forEach()方法也是用于对数组中的每一个元素执行一次回调函数,但它没有返回值(或者说它的返回值为undefined,即便我们在回调函数中写了return语句,返回值依然为undefined)

5.JS 中深拷贝的几种实现方法

1.使用递归的方式实现深拷贝

//使用递归的方式实现数组、对象的深拷贝
function deepClone1(obj) {
  //判断拷贝的要进行深拷贝的是数组还是对象,是数组的话进行数组拷贝,对象的话进行对象拷贝
  var objClone = Array.isArray(obj) ? [] : {};
  //进行深拷贝的不能为空,并且是对象或者是
  if (obj && typeof obj === "object") {
    for (key in obj) {
      if (obj.hasOwnProperty(key)) {
        if (obj[key] && typeof obj[key] === "object") {
          objClone[key] = deepClone1(obj[key]);
        } else {
          objClone[key] = obj[key];
        }
      }
    }
  }
  return objClone;
}

2.通过 JSON 对象实现深拷贝

//通过js的内置对象JSON来进行数组对象的深拷贝
function deepClone2(obj) {
  var _obj = JSON.stringify(obj),
    objClone = JSON.parse(_obj);
  return objClone;
}

3.通过jQuery的extend方法实现深拷贝

var array = [1,2,3,4];
var newArray = $.extend(true,[],array);

4.lodash函数库实现深拷贝

lodash很热门的函数库,提供了 lodash.cloneDeep()实现深拷贝
https://www.lodashjs.com/
预览图
收藏
评论区