【网易】前端岗-秋招开发工程师三面回顾(网易杭州研究院)

一面:(主要考基础,时间大概40分钟)
1.简单自我介绍;

2.简单问我简历上写的项目,其中一个项目用sessionStorage来存储登录态,问我为什么没有用cookie,我说当时没有用cookie也没有意识到sessionStorage的一个缺点(将项目页面在一个新窗口打开或者关闭浏览器重新打开就会消失);

3.H5中关于语义化的标签有哪些,语义化标签有什么作用?当时就说了几个比较熟悉的:header标签、section标签、footer标签等,至于语义化标签有什么作用我也不是特别清楚就说能够让浏览器更好地解析。。。

这里总结一下答案:

a.语义化标签:header标签、section标签、footer标签、hgroup标签、nav标签、article标签、aside标签、address标签、mark标签、time标签、figure与figcaption标签、datalist标签、details和summary标签、dialog与dt和dd标签、progress标签、keygen标签等。。。

b.语义化标签有什么用,为什么要使用语义化标签:

在HTML5出现之前,我们一般采用div+css布局我们的页面。但是这样的布局方式不仅使我们的文档结构不够清晰,而且不利于搜索引擎爬虫对我们页面的爬取。为了解决上述缺点,HTML5新增了很多新的语义化标签。

引入语义化标签的好处主要有下列三点:

  • 比div标签有更加丰富的含义,方便开发与维护;
  • 搜索引擎能更方便的识别页面的每个部分;
  • 方便其他设备解析(如移动设备、盲人阅读器等);

4.关于doctype怎么理解,一般的用法是什么:当时回答的是规定页面html文本的标准、规则,一般用法为。

现在查看w3cschool补充一下:通过doctype指示页面使用哪个HTML版本进行编写,其通过声明引用DTD(Document Type Definition 文档类型定义),规定了标记语言的规则,这样浏览器才能正确的呈现内容。

5.块级标签和行内标签有哪些:

这个简单,随便说一些就好,块级标签有div标签、p标签、header标签、section标签、footer标签。。。行内标签有span、a、b、i,值得注意的是像input、button等元素是inline-block,即行内块状元素。

6.给出一段代码,让我说出其中的函数执行后输出什么:

    function f() {
        console.log(this.x);
    }
    f();
    var x = 2;
    var a = {x: 1};
    a.f = f;
    var b = a.f;
    a.f();
    b();

上面函数执行分别输出undefined、1、2,而我当时第一个说错了,我说输出了2,因为经常在写js代码的时候有出现函数先调用后声明的现象,所以当时脑袋一懵以为变量也是这样就说2了。。。

关于函数先声明后调用的原理,我这里补充一下知识点:

函数的创建有三种方式:

  • 函数声明:比如function f() {console.log(1)}
  • 函数表达式:var f = function() {console.log(1)}
  • 构造函数:其实和上面那种差不多,只不过函数的初始是通过构造函数,比如:var f = new Function(‘console.log(1)’);

现在我们主要讨论第一种:这种方式创建的函数可以先调用,原因:页面加载过程中,浏览器会对页面上载入的每个js代码块(或文件)进行扫描,如果遇到定义式函数,则进行预处理,处理完成之后再开始由上至下执行。

比如:

f();

function f() {

console.log(111);

}

结果是输出111

7.js中bind和call的区别:bind和call的作用都是将某个函数的this指向绑定到另外一个作用域中,他们的参数调用都是相同的,第一个参数为绑定的作用域对象是什么,接下来就是可以添加不限的参数,而区别就是call在绑定的同时调用函数,bind是返回一个改变this指向的函数。

8.js中的基础数据类型有哪些:undefined、boolean、string、number、null(这里纠正一下,之前是把function包含在内了),还有一种复杂数据类型object(其细分了Array、Date等),ES6中新增了Symbol的数据类型。

那么如何获取某个变量的数据类型:对于基本数据类型都可以用typeof来获取,但是像Array这样的可以通过instanceof来获取,当时其实就仅仅说了typeof和instanceof,印象中还问了其他的,但是记不清了,反正被问到了。

总结一下获取数据类型的方法:(详见:https://www.jb51.net/article/70824.htm

  • typeof: 除了上面五种简单数据类型可以准确获取之外(null返回的是object),以及function返回的是function,其它的都返回的是object;
  • instanceof: 复杂数据类型如Array等都准确返回相应的boolean值,对于基本数据类型则是返回false(只要是通过new运算符来进行初始化,然后通过instanceof来判断是否是某个类的实例,才会返回true,比如”var k = new Number(11); console.log(k instanceof Number)”,其返回true);
  • 使用constructor检测:对于上面的instanceof对于直接声明如:“var a = 1;”不能进行判断,我们可以通过使用constructor来实现判断,如“a.constructorNumber”(其返回true,不过对于“1.constructorNumber”这类直接用数值来访问constructor的情况是会报错的),但对于null则仍然无法判断 ,而且会报错,比如“var k = null; console.log(k.constructorObject)”;

不过有一个问题,就是对象的prototype属性是可以修改的,所以也不准确;

  • 使用Object.prototype.toString.call使用:这个方法可以获取所有的类型(包括null型),不过其返回的是类似“object Array”的形式的字符串。
  • 通过jquery$.type来获取数据类型,这里就不具体说明了。

针对Symbol型的数据,运用上述的方法,其结果如下:

let test = Symbol('yaodebian')
console.log(typeof test) // symbol
console.log(test instanceof Symbol) //false
console.log(test.constructor === Symbol) //true
console.log(Object.prototype.toString.call(test)) // [object Symbol]
console.log($.type(test)) // symbol

9.给一个数组,现场写出数组去重方法:(数组由数字组成)

  function f(arr) {
    var temp = []
    temp.push(arr[0])
    for (var i = 1; i < arr.length; i++) {
      if (arr[i] != temp[temp.length - 1]) {
        temp.push(arr[i])
      }
    }
    return temp
  }

通过filter函数进行简化:

 function f(arr) {
  let temp = arr.filter(function(item, pos, arr) {
    // 只要下标为0,或者与前一项元素不同
    return !pos || item != arr[pos - 1]
  })
  return temp
}
console.log(f([1, 1, 4, 2, 5, 3])) // [ 1, 2, 3, 4, 5 ]

上面的方法运行的前提是,参数arr是经过排序的数组,这个我们可以通过数组的sort方法进行排序,或者用八大排序中的一种来实现。

当时面试官说有没有其他的方法,我就说可以不排序,然后上面的函数改一下,就是每次判断当前的项是否存在与temp数组中(用indexOf),不过时间复杂度就是O(n^2),问我有没有其他方法,我说想不到了。

=2018.9.17更新

补充:(多谢评论中“李元秋”前辈的提醒)

方法二——通过将数组中的元素作为对象的属性,然后再通过Object.keys()返回数组:

let arr = [1, 1, 4, 2, 5, 3];
let obj = {}
for(let i in arr) {
  obj[arr[i]] = i;
}

console.log(Object.keys(obj));//['1', '2', '3', '4', '5']

不过这种方法有个缺点:如果数组是数字字符串还好,但如果是数字的话,返回之后数组中的元素都是字符串。另外,Object.keys()文档提示其数组中属性的顺序和for…in…的顺序是一样的,如果可枚举属性均为数字字符串,则调用顺序和数字的顺序是一样的。

关于数组中元素为数字,我们可以这样改:

let arr = [1, 1, 4, 2, 5, 3];
let obj = {}
for(let i in arr) {
  obj[arr[i]] = arr[i];
}
let temp = [];
for(let i in obj) {
  temp.push(obj[i]);
}
console.log(temp);//[1, 2, 3, 4, 5]

这样的话时间复杂度就大大缩小了

上面还可以进行少许的优化:

let arr = [1, 1, 4, 2, 5, 3];
let obj = {}
let temp = []
for(let i in arr) {
  if (undefined === obj[arr[i]]) {
    obj[arr[i]]= ''
    temp.push(arr[i])
  }
}
console.log(temp);// [ 1, 4, 2, 5, 3 ]

也可以通过filter进行简化:

let arr = [1, 1, 4, 2, 5, 3];
let obj = {}
let temp = arr.filter(function (item, pos, arr) {
  if (undefined === obj[arr[pos]]) {
    obj[arr[pos]]= ''
    return true
  }
  return false
})
console.log(temp);// [ 1, 4, 2, 5, 3 ]

另外,其实上面的Object.keys可以换成Object.values,这样就直接解决key为字符串的问题,而直接适用于任何数据类型,谢谢

@anarkhz

的提醒,修改之后是这样的:

let arr = [1, 1, 4, 2, 5, 3];
let obj = {}
for(let i in arr) {
  obj[arr[i]] = arr[i];
}

console.log(Object.values(obj));//['1', '2', '3', '4', '5']

方法三:另外可以通过数组的下标来做

let arr = [1, 1, 4, 2, 5, 3]
let temp = []
for (let i = 0; i < arr.length; i++) {
  if (i === arr.indexOf(arr[i])) {
    temp.push(arr[i])
  }
}
console.log(temp)

同样的时间复杂度为O(n^2)

该方法可以通过filter进行简化:

let arr = [1, 1, 4, 2, 5, 3]
let temp = arr.filter(function(item, index, array) {
  return array.indexOf(item) === index;
});
console.log(temp)

方法四:获取数组中重复项的最后一项

function uniq(array){
  var temp = [];
  var index = [];
  var l = array.length;
  for(var i = 0; i < l; i++) {
      for(var j = i + 1; j < l; j++){
          if (array[i] === array[j]){
              i++;
              j = i;
          }
      }
      temp.push(array[i]);
      index.push(i);
  }
  console.log(index);
  return temp;
}

console.log(uniq([1, 4, 1, 2, 3]))

方法五:通过ES6的Set结构

let set = new Set([1, 1, 4, 2, 5, 3])
console.log(...set) // [ 1, 4, 2, 5, 3 ]

10.伪元素和伪类有哪些:伪类有如::hover、:focus、:active、:visited、:link,以及:first-child、:last-child、:nth-child(),而伪元素主要有::first-letter、::first-line、::before、::after。简单理解就是伪类可以理解为是一种状态,它和class是类似的,而伪元素则代表着一些实实在在的元素,但是它们都是抽象刻画的,游离于标准文档之外。

11.假设给你一个元素element,它有三个class:a、b、c,现场写一个方法将指定的class删除:

  function removeCla(element, claName) {
    var temp = element.className.split(' ');
    var i = temp.indexOf(claName);
    if (i === -1) {
      return;
    }
    temp.splice(i, 1);
    element.className = temp.join(' ');
  }

方法二:通过classList的remove接口函数进行class删除,但要考虑兼容性。之前有前辈说想要看到对classList做一个兼容性处理,由此写了一篇文章:

yaodebian:兼容getElementsByClassName与classList(ie8及以上)1 赞同 · 0 评论文章

12.position有哪些属性,每个属性的使用方式;

13.box-sizing的使用,其有哪些属性,属性值的作用是什么,这个还是比较简单的,当时自己还顺便说了一下盒子模型——标准盒子模型和IE盒子模型的区别;

14.原生ajax的使用,主要问了监听状态值分别为1、2、3、4时分别表示什么,ajax的调用过程是怎样的。(这里就不做详述了,大家可以百度下)

15.事件的调用过程:是先冒泡后捕获还是先捕获后冒泡,当时脑袋瓜也不是特别清楚就直接说先冒泡后捕获,现在知道是错误的了,应该是先捕获后冒泡,可以看下这篇文章,讲的很细:http://www.cnblogs.com/alvinwei1024/p/4739344.html

16.面试官中间问我事件代理或是事件委托听过吗,我说是不是像使用call这样的,他说不是,尴尬,这个也没答出来。

对于这个,现在发现是很基础的东西,大家可以看看这篇文章:https://www.cnblogs.com/liugang-vip/p/5616484.html

17.最后考察了我float的理解与使用,这个应该算很基础的东西了,就不多说了;

一面的内容基本上是这些吧,可能还有遗漏的,记不太清了。。。

二面:(主要问的是项目,时间大概是40分钟左右)

1.问一些自己做的一些项目或者demo,你是怎样实现,实现了什么样的功能,其中你项目中某些东西是怎样实现的;

2.项目的优化的理解,这个自己扯的;

3.给你一个组件,提供某些需求,你如何去实现这个组件,如果说我这个组件可以是多样化的,简单说就和自定义差不多,你怎样实现;

大概就是这样吧,其实就是你要有项目经验,哪怕是demo,然后扯吧。。。

三面:(hr面,时间30分钟)

这个嘛,就是和hr小姐姐聊天啦。。。

发表评论

后才能评论