js前端面试题及答案整理(一)


Posted in Javascript onAugust 26, 2016

Part1 手写代码

现场手写代码是现在面试中很常见的一类面试题,考察基础的数据结构与算法能力。

1 数组去重的实现
基本数组去重

Array.prototype.unique = function(){
 var result = [];
 this.forEach(function(v){
  if(result.indexOf(v) < 0){
   result.push(v);
  }
 });
 return result;
}

•利用hash表去重,这是一种空间换时间的方法

Array.prototype.unique = function(){
 var result = [],hash = {};
 this.forEach(function(v){
  if(!hash[v]){
   hash[v] = true;
   result.push(v);
  }
 });
 return result;
}

上面的方法存在一个bug,对于数组[1,2,'1','2',3],去重结果为[1,2,3],原因在于对象对属性索引时会进行强制类型转换,arr[‘1']和arr[1]得到的都是arr[1]的值,因此需做一些改变:

Array.prototype.unique = function(){
 var result = [],hash = {};
 this.forEach(function(v){
  var type = typeof(v); //获取元素类型
  hash[v] || (hash[v] = new Array());
  if(hash[v].indexOf(type) < 0){
   hash[v].push(type); //存储类型
   result.push(v);
  }
 });
 return result;
}

•先排序后去重

Array.prototype.unique = function(){
 var result = [this[0]];
 this.sort();
 this.forEach(function(v){
  v != result[result.length - 1] && result.push(v); //仅与result最后一个元素比较
 });
}

2 快速排序的实现

方法一(尽可能不用js数组方法):

function quickSort(arr){
 qSort(arr,0,arr.length - 1);
}
function qSort(arr,low,high){
 if(low < high){
  var partKey = partition(arr,low,high);
  qSort(arr,low, partKey - 1);
  qSort(arr,partKey + 1,high);
 }
}
function partition(arr,low,high){
 var key = arr[low]; //使用第一个元素作为分类依据
 while(low < high){
  while(low < high && arr[high] >= arr[key])
   high--;
  arr[low] = arr[high];
  while(low < high && arr[low] <= arr[key])
   low++;
  arr[high] = arr[low];
 }
 arr[low] = key;
 return low;
}

方法二(使用js数组方法):

function quickSort(arr){
 if(arr.length <= 1) return arr;
 var index = Math.floor(arr.length/2);
 var key = arr.splice(index,1)[0];
 var left = [],right = [];
 arr.forEach(function(v){
  v <= key ? left.push(v) : right.push(v);
 });
 return quickSort(left).concat([key],quickSort(right));
}

另外要知道,快速排序的平均时间复杂度O(nlogn),最坏情况是有序的情况,时间复杂度为n的平方,另外快速排序是不稳定的。

Part2 JavaScript相关

1 JavaScript基础数据类型

JavaScript数据类型包括原始类型和引用类型,原始类型有五个:
  Number(数值) String(字符串) Boolean(布尔) Null(空) Undefined(未定义)

引用类型有一个:
 Object(对象)

通过typeof(x)可以返回一个变量x的数据类型“number”、“string”、“boolean”、“undefined”、"object",这里要注意一点:typeof运算符对于null类型返回的是object。

《JavaScript高级程序设计》:
 这实际上是JavaScript最初实现中的一个错误,后来被ECMAScript沿用了。现在null被认为是对象的占位符,从而解释了这一矛盾。但是从技术上来说,它仍然是原始值。

2 谈一谈JavaScript作用域链

当执行一段JavaScript代码(全局代码或函数)时,JavaScript引擎会创建为其创建一个作用域又称为执行上下文(Execution Context),在页面加载后会首先创建一个全局的作用域,然后每执行一个函数,会建立一个对应的作用域,从而形成了一条作用域链。每个作用域都有一条对应的作用域链,链头是全局作用域,链尾是当前函数作用域。
作用域链的作用是用于解析标识符,当函数被创建时(不是执行),会将this、arguments、命名参数和该函数中的所有局部变量添加到该当前作用域中,当JavaScript需要查找变量X的时候(这个过程称为变量解析),它首先会从作用域链中的链尾也就是当前作用域进行查找是否有X属性,如果没有找到就顺着作用域链继续查找,直到查找到链头,也就是全局作用域链,仍未找到该变量的话,就认为这段代码的作用域链上不存在x变量,并抛出一个引用错误(ReferenceError)的异常。

3 如何理解JavaScript原型链

JavaScript中的每个对象都有一个prototype属性,我们称之为原型,而原型的值也是一个对象,因此它也有自己的原型,这样就串联起来了一条原型链,原型链的链头是object,它的prototype比较特殊,值为null。
原型链的作用是用于对象继承,函数A的原型属性(prototype property)是一个对象,当这个函数被用作构造函数来创建实例时,该函数的原型属性将被作为原型赋值给所有对象实例,比如我们新建一个数组,数组的方法便从数组的原型上继承而来。
当访问对象的一个属性时, 首先查找对象本身, 找到则返回; 若未找到, 则继续查找其原型对象的属性(如果还找不到实际上还会沿着原型链向上查找, 直至到根). 只要没有被覆盖的话, 对象原型的属性就能在所有的实例中找到,若整个原型链未找到则返回undefined;

4 JavaScript变量声明提前

《JavaScript权威指南》中是这样解释的:JavaScript变量在声明之前已经可用,JavaScript的这个特性被非正式的称为声明提前(hoisting),即JavaScript函数中声明的所有变量(但不涉及赋值)都被“提前”至函数的顶部。
从一个例子来看:

var scope = "global";
function myFunc(){
 console.log(scope); 
 var scope = "local";
}

控制台打印出来的不是“global”而是“undefined”,这是因为在myFunc这个函数的作用域中,局部变量scope声明被提前至函数顶部,而此时,scope仅声明,未赋值,因此输出undefined。实际上,上面的代码和下面的效果是一样的:

var scope = "global";
function myFunc(){
 var scope;
 console.log(scope);
 scope = "local";
}

5 如何理解和应用JavaScript闭包

关于闭包具体的定义文献中给的概念很抽象,我认为闭包是一种使函数能够都去其它函数的局部变量的语法机制。

举个例子:

function outFunc(){
 var name = "Vicfeel";
 function inFunc(){
  console.log(name);
 }
 return inFunc;
}
inFunc(); //控制台显示"Vicfeel"

这这个例子我们可以看出,在函数inFunc中依然可以访问outFunc的局部变量name。

闭包应用举例,模拟类的私有属性,利用闭包的性质,局部变量只有在sayAge方法中才可以访问,而name在外部也访问,从而实现了类的私有属性。

function User(){
  this.name = "Vicfeel"; //共有属性
  var age = 23; //私有属性
  this.sayAge:function(){
   console.log("my age is " + age); 
  }
 }
 var user = new User();
 console.log(user.name); //"Vicfeel"
 console.log(user.age); //"undefined"
 user.sayAge(); //"my age is 23"

要了解详细的闭包,推荐一下 阮一峰的网络日志-学习Javascript闭包(Closure)。

6 new构建对象的本质

function User(){
this.name = "Vicfeel";
this.age = 23;
}

var user = new User();

通过new操作符,实际上在构造函数User中完成了如下操作:
•创建一个新的对象,这个对象的类型是object;
•设置这个新的对象的内部、可访问性和prototype属性为构造函数(指prototype.construtor所指向的构造函数)中设置的;
•执行构造函数;
•返回新创建的对象。

function User(){
  //this = {}; 
  //this.constructor = User;
  this.name = "Vicfeel";
  this.age = 23;
  //return this;
 }
 
 var user = new User();

如果构造函数默认返回的新创建的this对象,如果手动return 一个变量的话,如果该变量是原始类型则无效,如果是对象,则返回该对象。

7 JavaScript代理

当我们需要对很多元素添加事件的时候,可以通过将事件添加到它们的父节点而将事件委托给父节点来触发处理函数。

比如我们需要向一个ul中动态添加很多个li,需要遍历li逐个添加点击事件

<ul id='list'></ul>
 var count = 100;
 var ulList = document.getElementById("list");
 //动态构建节点
 for(var i = count;i--;){
  var liDom = document.createElement('li');
  ulList.appendChild(liDom);
 }
 //绑定点击事件
 var liNode = ulList.getElementByTagName("li");
 for(var i=0, l = liNodes.length; i < l; i++){
  liNode[i].onClick = function(){
   //li点击事件
  }
 }

众所周知,DOM操作是十分消耗性能的。所以重复的事件绑定简直是性能杀手。而事件代理的核心思想,就是通过尽量少的绑定,去监听尽量多的事件。如何做呢?答案是利用事件冒泡机制,对其父节点ul进行事件绑定(Event Bubble),然后通过event.target来判断是哪个节点触发的事件,从而减少很多EventHandler的绑定。

var count = 100;
 var ulList = document.getElementById("list");
 //动态构建节点
 for(var i = count;i--;){
  var liDom = document.createElement('li');
  ulList.appendChild(liDom);
 }
 //绑定点击事件
 var liNode = ulList.getElementByTagName("li");
 liNode.onClick = function(e){
  if(e.target && e.target.nodeName.toUpperCase == "LI") {
   // li点击事件
  }
 }

发现新内容会持续更新...

Javascript 相关文章推荐
如何确保JavaScript的执行顺序 之jQuery.html深度分析
Mar 03 Javascript
javascript算法学习(直接插入排序)
Apr 12 Javascript
javascript中var的重要性分析
Feb 11 Javascript
使用AngularJS中的SCE来防止XSS攻击的方法
Jun 18 Javascript
js实现接收表单的值并将值拼在表单action后面的方法
Nov 23 Javascript
Vue.js常用指令汇总(v-if、v-for等)
Nov 03 Javascript
JavaScript 动态三角函数实例详解
Jan 08 Javascript
微信小程序用户位置权限的获取方法(拒绝后提醒)
Nov 15 Javascript
关于vue的npm run dev和npm run build的区别介绍
Jan 14 Javascript
JavaScript获取页面元素的常用方法详解
Sep 28 Javascript
vue请求数据的三种方式
Mar 04 Javascript
Openlayers实现地图的基本操作
Sep 28 Javascript
JavaScript中ES6字符串扩展方法
Aug 26 #Javascript
总结十个Angular.js由浅入深的面试问题
Aug 26 #Javascript
Jquery遍历select option和添加移除option的实现方法
Aug 26 #Javascript
响应式表格之固定表头的简单实现
Aug 26 #Javascript
jQuery基于BootStrap样式实现无限极地区联动
Aug 26 #Javascript
BootStrap无限级分类(无限极分类封装版)
Aug 26 #Javascript
jquery 动态合并单元格的实现方法
Aug 26 #Javascript
You might like
PHP+SQL 注入攻击的技术实现以及预防办法
2010/12/29 PHP
php用正则表达式匹配URL的简单方法
2013/11/12 PHP
php中ltrim()、rtrim()与trim()删除字符空格实例
2014/11/25 PHP
php实现遍历目录并删除指定文件中指定内容
2015/01/21 PHP
php准确获取文件MIME类型的方法
2015/06/17 PHP
PHP+jQuery+Ajax实现分页效果 jPaginate插件的应用
2015/10/09 PHP
Zend Framework教程之Application用法实例详解
2016/03/14 PHP
Yii2基于Ajax自动获取表单数据的方法
2016/08/10 PHP
Yii2.0中使用js异步删除示例
2017/03/10 PHP
yii2.0整合阿里云oss上传单个文件的示例
2017/09/19 PHP
Bootstrap+PHP实现多图上传功能实例详解
2018/04/08 PHP
在Z-Blog中运行代码[html][/html](纯JS版)
2007/03/25 Javascript
JavaScript-世界上误解最深的语言分析
2007/08/12 Javascript
javascript 继承实现方法
2009/08/26 Javascript
实用的JS正则表达式(手机号码/IP正则/邮编正则/电话等)
2013/01/11 Javascript
js(jQuery)获取时间的方法及常用时间类搜集
2013/10/23 Javascript
js在数组中删除重复的元素自保留一个(两种实现思路)
2014/08/22 Javascript
jQuery中Form相关知识汇总
2015/01/06 Javascript
jQuery实现的仿select功能代码
2015/08/19 Javascript
JS实现的合并两个有序链表算法示例
2019/02/25 Javascript
jQuery zTree插件使用简单教程
2019/08/16 jQuery
JavaScript ECMA-262-3 深入解析(二):变量对象实例详解
2020/04/25 Javascript
[01:16:50]DOTA2-DPC中国联赛 正赛 Phoenix vs CDEC BO3 第一场 3月7日
2021/03/11 DOTA
Python装饰器用法实例总结
2018/02/07 Python
Python猴子补丁Monkey Patch用法实例解析
2020/03/23 Python
Python多线程实现支付模拟请求过程解析
2020/04/21 Python
Python实现一个论文下载器的过程
2021/01/18 Python
Python tkinter实现日期选择器
2021/02/22 Python
浅析Python模块之间的相互引用问题
2021/02/26 Python
HTML5 预加载让页面得以快速呈现
2013/08/13 HTML / CSS
Nike比利时官网:Nike.com (BE)
2019/02/07 全球购物
电焊工岗位职责
2014/03/06 职场文书
安全生产月演讲稿
2014/05/09 职场文书
感恩小明星事迹材料
2014/05/23 职场文书
公务员处分决定书
2015/06/25 职场文书
2015年秋学期教研工作总结
2015/10/14 职场文书