【JS】面试题 2

1. sort 排序原理

冒泡排序法解析:冒泡排序法的原理:
比较相邻的元素。如果第一个比第二个大,就交换他们两个。
对每一对相邻元素做同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。
针对所有的元素重复以上的步骤,除了最后一个。
持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
示例:

 var arr = [1, 5, 4, 2];
// sort()方法的比较逻辑为:
// 第一轮:1和5比,1和4比,1和2比
// 第二轮:5和4比,5和2比
// 第三轮:4和2比// 一.sort排序规则 return大于0则交换数组相邻2个元素的位置
// 二.arr.sort(function (a,b) {})中
//         a --›代表每一次执行匿名函时候,找到的数组中的当前项;
//         b --›代表当前项的后一项;

1.升序
  var apple = [45, 42, 10, 147, 7, 65, -74];
// ①默认法,缺点:只根据首位排序
console.log(apple.sort());
// ②指定排序规则法,return可返回任何值
console.log(
  apple.sort(function(a, b) {
    return a - b; //若return返回值大于0(即a>b),则a,b交换位置
  })
);

2.降序
 var arr = [45, 42, 10, 111, 7, 65, -74];
  console.log(
      apple.sort(function(a, b) {
  return b - a; //若return返回值大于零(即b>a),则a,b交换位置
})
);参考 1、参考 2

2.JavaScript 的数据类型

JS 数据类型共有六种,分别是 String、Number、Boolean、Null、Undefined 和 Object 等, 另外,ES6 新增了 Symbol 类型。其中,Object 是引用类型,其他的都是基本类型(Primitive Type)。

3.call() 和 apply() 的含义和区别?

首先说明两个方法的含义:
call:调用一个对象的一个方法,用另一个对象替换当前对象。例如:B.call(A, args1,args2);即 A 对象调用 B 对象的方法。
apply:调用一个对象的一个方法,用另一个对象替换当前对象。例如:B.apply(A, arguments);即 A 对象应用 B 对象的方法。
call 与 apply 的相同点:
方法的含义是一样的,即方法功能是一样的;
第一个参数的作用是一样的;
call 与 apply 的不同点:两者传入的列表形式不一样
call 可以传入多个参数;
apply 只能传入两个参数,所以其第二个参数往往是作为数组形式传入

4.Zepto 的点透问题如何解决?

方案一:
来得很直接 github 上有个 fastclick 可以完美解决https://github.com/ftlabs/fastclick引入 fastclick.js,因为 fastclick 源码不依赖其他库所以你可以在原生的 js 前直接加上

window.addEventListener(
  "load",
function() {
  FastClick.attach(document.body);
},
false
);

或者有 zepto 或者 jqm 的 js 里面加上

$(function() {
FastClick.attach(document.body);
});

当然 require 的话就这样:

var FastClick = require("fastclick");
FastClick.attach(document.body, options);

方案二
用 touchend 代替 tap 事件并阻止掉 touchend 的默认行为 preventDefault()$("#cbFinish").on("touchend", function(event) {

//很多处理比如隐藏什么的
event.preventDefault();
});

方案三:
延迟一定的时间(300ms+)来处理事件

  $("#cbFinish").on("tap", function(event) {
    setTimeout(function() {
  //很多处理比如隐藏什么的
    }, 320);
  });

这种方法其实很好,可以和 fadeInIn/fadeOut 等动画结合使用

5. 如何判断当前脚本运行在浏览器还是 node 环境中?

通过判断 Global 对象是否为 window,如果不为 window,当前脚本没有运行在浏览器中

6. 移动端最小触控区域是多大?

苹果推荐是 44pt x 44pt解析:参考

7.解释 JavaScript 中的作用域与变量声明提升?

对作用域的理解是只会对某个范围产生作用,而不会对外产生影响的封闭空间。在这样的一些空间里,外部不能访问内部变量,但内部可以访问外部变量。
所有申明都会被提升到作用域的最顶上
同一个变量申明只进行一次,并且因此其他申明都会被忽略
函数声明的优先级优于变量申明,且函数声明会连带定义一起被提升

8.Node.js 的适用场景?

比如:RESTFUL API、实时聊天、客户端逻辑强大的单页 APP,具体的例子比如说:本地化的在线音乐应用,本地化的在线搜索应用,本地化的在线 APP 等。解析:参考

9.如何判断一个对象是否属于某个类?

instanceof解析:if (a instanceof Person) {
alert("yes");
}

10.bind、call、apply 的区别

call 和 apply 其实是一样的,区别就在于传参时参数是一个一个传或者是以一个数组的方式来传。
call 和 apply 都是在调用时生效,改变调用者的 this 指向。

let name = 'Jack'
const obj = {name: 'Tom'}
function sayHi() {console.log('Hi! ' + this.name)}

sayHi() // Hi! Jack
sayHi.call(obj) // Hi! Tom

bind 也是改变 this 指向,不过不是在调用时生效,而是返回一个新函数。

const newFunc = sayHi.bind(obj)
newFunc() // Hi! Tom

11.移动端的点击事件的有延迟,时间是多久,为什么会有? 怎么解决这个延时?

300 毫秒
因为浏览器捕获第一次单击后,会先等待一段时间,如果在这段时间区间里用户未进行下一次点击,则浏览器会做单击事件的处理。如果这段时间里用户进行了第二次单击操作,则浏览器会做双击事件处理。
推荐 fastclick.js

12.如何实现文件断点续传

断点续传最核心的内容就是把文件“切片”然后再一片一片的传给服务器,但是这看似简单的上传过程却有着无数的坑。首先是文件的识别,一个文件被分成了若干份之后如何告诉服务器你切了多少块,以及最终服务器应该如何把你上传上去的文件进行合并,这都是要考虑的。因此在文件开始上传之前,我们和服务器要有一个“握手”的过程,告诉服务器文件信息,然后和服务器约定切片的大小,当和服务器达成共识之后就可以开始后续的文件传输了。前台要把每一块的文件传给后台,成功之后前端和后端都要标识一下,以便后续的断点。当文件传输中断之后用户再次选择文件就可以通过标识来判断文件是否已经上传了一部分,如果是的话,那么我们可以接着上次的进度继续传文件,以达到续传的功能。
有了 HTML5 的 File api 之后切割文件比想想的要简单的多的多。只要用 slice 方法就可以了var packet = file.slice(start, end);
参数 start 是开始切片的位置,end 是切片结束的位置 单位都是字节。通过控制 start 和 end 就可以是实现文件的分块如file.slice(0,1000);
file.slice(1000,2000);
file.slice(2000,3000);
// ......
在把文件切成片之后,接下来要做的事情就是把这些碎片传到服务器上。
如果中间掉线了,下次再传的时候就得先从服务器获取上一次上传文件的位置,然后以这个位置开始上传接下来的文件内容。

13.使用构造函数的注意点

一般情况下构造函数的首字母需要大写,因为我们在看到一个函数首字母大写的情况,就认定这是一个构造函数,需要跟new关键字进行搭配使用,创建一个新的实例(对象)
构造函数在被调用的时候需要跟new关键字搭配使用。
在构造函数内部通过this+属性名的形式为实例添加一些属性和方法。
构造函数一般不需要返回值,如果有返回值

  1. 如果返回值是一个基本数据类型,那么调用构造函数,返回值仍旧是那么创建出来的对象。

  2. 如果返回值是一个复杂数据类型,那么调用构造函数的时候,返回值就是这个return之后的那个复杂数据类型。

14.数组的常用方法

Array.map()

此方法是将数组中的每个元素调用一个提供的函数,结果作为一个新的数组返回,并没有改变原来的数组

let arr = [1, 2, 3, 4, 5];
let newArr = arr.map(x =› x * 2);
//arr= [1, 2, 3, 4, 5]   原数组保持不变
//newArr = [2, 4, 6, 8, 10] 返回新数组

Array.forEach()

此方法是将数组中的每个元素执行传进提供的函数,没有返回值,直接改变原数组,注意和 map 方法区分

let arr = [1, 2, 3, 4, 5];
num.forEach(x =› x * 2);
// arr = [2, 4, 6, 8, 10]  数组改变,注意和map区分

Array.filter()

此方法是将所有元素进行判断,将满足条件的元素作为一个新的数组返回

let arr = [1, 2, 3, 4, 5]
const isBigEnough =› value =› value ›= 3
let newArr = arr.filter(isBigEnough )
//newNum = [3, 4, 5] 满足条件的元素返回为一个新的数组

Array.every()

此方法是将所有元素进行判断返回一个布尔值,如果所有元素都满足判断条件,则返回 true,否则为 false:

let arr = [1, 2, 3, 4, 5]
const isLessThan4 =› value =› value ‹ 4
const isLessThan6 =› value =› value ‹ 6
arr.every(isLessThan4 ) //false
arr.every(isLessThan6 ) //true

Array.some()

此方法是将所有元素进行判断返回一个布尔值,如果存在元素都满足判断条件,则返回 true,若所有元素都不满足判断条件,则返回 false:

let arr= [1, 2, 3, 4, 5]
const isLessThan4 =› value =› value ‹ 4
const isLessThan6 =› value =› value › 6
arr.some(isLessThan4 ) //true
arr.some(isLessThan6 ) //false

Array.reduce()

此方法是所有元素调用返回函数,返回值为最后结果,传入的值必须是函数类型:

let arr = [1, 2, 3, 4, 5];
const add = (a, b) =› a + b;
let sum = arr.reduce(add);
  //sum = 15  

相当于累加的效果与之相对应的还有一个 Array.reduceRight() 方法,区别是这个是从右向左操作的

Array.push()

此方法是在数组的后面添加新加元素,此方法改变了数组的长度:

Array.pop()

此方法在数组后面删除最后一个元素,并返回数组,此方法改变了数组的长度:

let arr = [1, 2, 3, 4, 5];
 arr.pop();
console.log(arr); //[1, 2, 3, 4]
console.log(arr.length); //4

Array.shift()

此方法在数组后面删除第一个元素,并返回数组,此方法改变了数组的长度:

let arr = [1, 2, 3, 4, 5];
  arr.shift();
console.log(arr); //[2, 3, 4, 5]
 console.log(arr.length); //4

Array.unshift()

此方法是将一个或多个元素添加到数组的开头,并返回新数组的长度:

let arr = [1, 2, 3, 4, 5];
arr.unshift(6, 7);
console.log(arr); //[6, 7, 2, 3, 4, 5]
console.log(arr.length); //7

Array.isArray()

判断一个对象是不是数组,返回的是布尔值

Array.concat()

此方法是一个可以将多个数组拼接成一个数组:

  let arr1 = [1, 2, 3]
  arr2 = [4, 5]
  let arr = arr1.concat(arr2)
console.log(arr)//[1, 2, 3, 4, 5]

. Array.toString()

此方法将数组转化为字符串:

let arr = [1, 2, 3, 4, 5];
let str = arr.toString()
console.log(str)// 1,2,3,4,5

### Array.join()

此方法也是将数组转化为字符串

 let arr = [1, 2, 3, 4, 5];
  let str1 = arr.toString()
  let str2 = arr.toString(',')
  let str3 = arr.toString('##')
  console.log(str1)// 12345
  console.log(str2)// 1,2,3,4,5
  console.log(str3)// 1##2##3##4##5

通过例子可以看出和 toString 的区别,可以设置元素之间的间隔

Array.splice(开始位置, 删除的个数,元素)

万能方法,可以实现增删改:

let arr = [1, 2, 3, 4, 5];
 let arr1 = arr.splice(2, 0 'haha')
 let arr2 = arr.splice(2, 3)
 let arr1 = arr.splice(2, 1 'haha')
 console.log(arr1) //[1, 2, 'haha', 3, 4, 5]新增一个元素
 console.log(arr2) //[1, 2] 删除三个元素
 console.log(arr3) //[1, 2, 'haha', 4, 5] 替换一个元素

15..如何获取浏览器版本信息

window.navigator.userAgent

16..字符串常用操作

charAt(index):返回指定索引处的字符串
charCodeAt(index):返回指定索引处的字符的 Unicode 的值
concat(str1,str2,...):连接多个字符串,返回连接后的字符串的副本
fromCharCode():将 Unicode 值转换成实际的字符串
indexOf(str):返回 str 在父串中第一次出现的位置,若没有则返回-1
lastIndexOf(str):返回 str 在父串中最后一次出现的位置,若没有则返回-1
match(regex):搜索字符串,并返回正则表达式的所有匹配
replace(str1,str2):str1 也可以为正则表达式,用 str2 替换 str1
search(regex):基于正则表达式搜索字符串,并返回第一个匹配的位置
slice(start,end):返回字符索引在 start 和 end(不含)之间的子串
split(sep,limit):将字符串分割为字符数组,limit 为从头开始执行分割的最大数量
substr(start,length):从字符索引 start 的位置开始,返回长度为 length 的子串
substring(from,to):返回字符索引在 from 和 to(不含)之间的子串
toLowerCase():将字符串转换为小写
toUpperCase():将字符串转换为大写
valueOf():返回原始字符串值

17.作用域的概念及作用

作用域 : 起作用的一块区域
作用域的概念: 对变量起保护作用的一块区域
作用: 作用域外部无法获取到作用域内部声明的变量,作用域内部能够获取到作用域外界声明的变量。

18.call 与 apply 区别

第二个参数的类型不同解析:call 和 apply 的作用,完全一样,唯一的区别就是在参数上面。call 接收的参数不固定,第一个参数是函数体内 this 的指向,第二个参数以下是依次传入的参数。apply 接收两个参数,第一个参数也是函数体内 this 的指向。第二个参数是一个集合对象(数组或者类数组)

19.正则表达式构造函数 var reg = new RegExp('xxx')与正则表达字面量 var reg = // 有什么不同?

使用正则表达字面量的效率更高解析:下面的示例代码演示了两种可用于创建正则表达式以匹配反斜杠的方法:

//正则表达字面量
var re = /\\/gm;

//正则构造函数
var reg = new RegExp("\\\\", "gm");

var foo = "abc\\123"; // foo的值为"abc\123"
console.log(re.test(foo)); //true
console.log(reg.test(foo)); //true如上面的代码中可以看到,使用正则表达式字面量表示法时式子显得更加简短,而且不用按照类似类(class-like)的构造函数方式思考。其次,在当使用构造函数的时候,在这里要使用四个反斜杠才能匹配单个反斜杠。这使得正则表达式模式显得更长,更加难以阅读和修改。正确来说,当使用 RegExp()构造函数的时候,不仅需要转义引号(即"表示"),并且通常还需要双反斜杠(即\表示一个\)。使用 new RegExp()的原因之一在于,某些场景中无法事先确定模式,而只能在运行时以字符串方式创建。参考

20.js 中 callee 与 caller 的作用

caller 返回一个调用当前函数的引用 如果是由顶层调用的话 则返回 null
(举个栗子哈 caller 给你打电话的人 谁给你打电话了 谁调用了你 很显然是下面 a 函数的执行 只有在打电话的时候你才能知道打电话的人是谁 所以对于函数来说 只有 caller 在函数执行的时候才存在)

var callerTest = function() {
  console.log(callerTest.caller);
};
function a() {
  callerTest();
}
a(); //输出function a() {callerTest();}
callerTest(); //输出null

callee 返回一个正在被执行函数的引用 (这里常用来递归匿名函数本身 但是在严格模式下不可行)
callee 是 arguments 对象的一个成员 表示对函数对象本身的引用 它有个 length 属性(代表形参的长度)

var c = function(x, y) {
console.log(arguments.length, arguments.callee.length,   arguments.callee);
};
c  (1, 2, 3); //输出3 2 function(x,y)     {console.log(arguments.length,arguments.callee.length,a

1. 数组filter()参数详解,巧用filter()数组去重

数组方法挺多,但是用来用去可能也就foreach,splice以及slice接触较多,filter()说实话之前也没过多了解。其实filter()为数组提供过滤功能,它会遍历数组所有元素,并返回满足条件的元素组成的新数组filter()不会修改原数组如下:

第一位形参

var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; 
var arr2 = arr.filter(function(x) { 
    return x >= 8;
}); 
console.log(arr2); //[8, 9, 10]

上述代码所做的事情,就是将arr中每个元素一次传入函数与8进行比较,得出8,9,10。第一个形参X就代表了数组中的元素。

第二位形参

让我们再来看看如下代码:

var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; 
var arr2 = arr.filter(function(x, index) { 
return index % 3 === 0 || x >= 8;
}); 
console.log(arr2); //[1, 4, 7, 8, 9, 10]

index代表数组索引,它的循环过程是这样的,首先传入元素1,它的索引为0,而0%3===0,满足了条件。

第二遍传入了2,索引为1,但1%3!==0,且1比8小,所以被排除,依次循环,得出我们arr2的输出结果。

第三位形参

我们再来看一段代码,结合filter进行数组去重

var arr = [1, 2, 2, 3, 4, 5, 5, 6, 7, 7];
var arr2 = arr.filter(function(x, index,self) { 
return self.indexOf(x)===index;
}); 
console.log(arr2); //[1, 2, 3, 4, 5, 6 ,7]

这是怎么实现的呢,filter的第三参数self代表数组本身,而indexOf始终返回第一次找到匹配该元素的索引,我们来走走遍历过程。

第一次循环,传入元素1,index(1)的索引为0,而此时1的索引本来就是1,OK,满足。

第二次循环,传入元素2,index(2)的索引为1,而此时2的索引也是1,OK,也满足。

第三次循环,传入元素2,index(2)的索引为1,而此时2的索引为2,OK,不满足,被PASS,这里就是巧妙的借用了indexOf始终查找到第一次出现的位置。

总结

filter(x,index,self)可以为数组提供过滤功能,其中x代表元素,index是与X一同传入元素的索引,而self代表数组本身。

2. JavaScript的typeof返回哪些数据类型?
3. 列举3种强制类型转换和2种隐式类型转换?
4. 数组方法
5. ajax请求的时候get和post方式的区别?
6. call和apply区别?
7. ajax请求时,如何解析json数据?
8. 事件委托是什么?
9. 闭包是?特性?对页面影响?你理解的闭包?举例?
10. 用类的方式写一个b继承a的方法
11. 如何阻止事件冒泡,和默认事件
12. 谈谈this对象
13.ES6语法知道哪些?怎么用?
14. promise、promise.all、promise.race分别怎么用?
15.手写AJAX
手写函数防抖和函数节流
什么是jsonp?什么是cors?什么是跨域?
数组去重
深拷贝
promise async await
箭头函数和一般函数有什么区别?
jQuery事件绑定中on和bind区别?
前端常用的性能优化方法有哪些?
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 概要 64学时 3.5学分 章节安排 电子商务网站概况 HTML5+CSS3 JavaScript Node 电子...
    阿啊阿吖丁阅读 13,104评论 0 3
  • 第5章 引用类型(返回首页) 本章内容 使用对象 创建并操作数组 理解基本的JavaScript类型 使用基本类型...
    大学一百阅读 8,455评论 0 4
  • 总结了一些开发中常用的函数: usleep() //函数延迟代码执行若干微秒。 unpack() //函数从二进制...
    ADL2022阅读 3,294评论 0 3
  • PHP常用函数大全 usleep() 函数延迟代码执行若干微秒。 unpack() 函数从二进制字符串对数据进行解...
    上街买菜丶迷倒老太阅读 5,186评论 0 20
  • PNG 有PNG8和truecolor PNG PNG8类似GIF颜色上限为256,文件小,支持alpha透明度,...
    hudaren阅读 5,578评论 0 0