文章来源于公众号:猴哥说前端 作者:monkeysoft
本文给大家分享一些 JavaScript 面试经验,在这金九银十的招聘季,希望大家都能找到满意的工作。
JavaScript的数据类型都有什么?
基本数据类型:String,Boolean,Number,Undefined,Null
引用数据类型:Object(Array,Date,RegExp,Function)
javascript中==
和===
的区别是什么?
==
会自动进行类型转换,===
不会
例举3种强制类型转换和2种隐式类型转换?
强制(parseInt,parseFloat,Number())
隐式(==
) 1==”1”//true
null==undefined//true
原生 JS 中 call()、apply()、bind() 方法有什么区别?
三个方法都可以改变函数运行时的 this 指向。
三个方法第一个参数都是函数调用执行时this 指向的对象。
call() 方法第二个参数是个可变参数,是函数调用执行时本身所需要的参数。
apply() 方法第二个参数是数组或arguments。call()与apply()都是立即调用函数执行,在运行时修改this指向。
bind()是返回一个新的函数,新函数的函数主体与原函数的函数主体一致,当新函数被调用时,函数体中 this 指向的是 bind() 方法第一个参数传递的对象,而bind() 方法不会影响原函数本身的 this 指向。
什么是闭包?特点是?
闭包,官方对闭包的解释是:一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分。
闭包的特点:
(1)作为一个函数变量的一个引用,当函数返回时,其处于激活状态。
(2) 一个闭包就是当一个函数返回时,一个没有释放资源的栈区。
简单的说,JavaScript 允许使用内部函数---即函数定义和函数表达式位于另一个函数的函数体内。而且,这些内部函数可以访问它们所在的外部函数中声明的所有局部变量、参数和声明的其他内部函数。当其中一个这样的内部函数在包含它们的外部函数之外被调用时,就会形成闭包。
事件委托是什么?
符合W3C标准的事件绑定 addEventLisntener /attachEvent
让利用事件冒泡的原理,让自己的所触发的事件,让他的父元素代替执行!
如何阻止事件冒泡和默认事件
e. stopPropagation();//标准浏览器
event.canceBubble=true;//ie9之前
阻止默认事件:
为了不让a点击之后跳转,我们就要给他的点击事件进行阻止
return false
e.preventDefault();
document load 和document ready的区别?
Document.onload 是在结构和样式加载完才执行js
window.onload:不仅仅要在结构和样式加载完,还要执行完所有的样式、图片这些资源文件,全部加载完才会触发window.onload事件
Document.ready原生种没有这个方法,jquery中有 $().ready(function)
为了保证页面输出安全,我们经常需要对一些特殊的字符进行转义,请写一个函数escapeHtml,将, &, “进行转义
return str.replace(/[”&]/g, function(match) {
switch (match) {
case “”:
return “>”;
case “&”:
return “&”;
case “\””:
return “"”;
}
});
}
简述创建函数的几种方式
第一种(函数声明):function sum1(num1,num2){ return num1+num2; }
第二种(函数表达式):var sum2 = function(num1,num2){ return num1+num2; }
第三种(函数对象方式):var sum3 = new Function("num1","num2","return num1+num2");
把 Script 标签 放在页面的最底部的body封闭之前 和封闭之后有什么区别?浏览器会如何解析它们?
如果说放在body的封闭之前,将会阻塞其他资源的加载
如果放在body封闭之后,不会影响body内元素的加载
iframe的优缺点?
优点:
- 解决加载缓慢的第三方内容如图标和广告等的加载问题
- Security sandbox
- 并行加载脚本
缺点:
- iframe会阻塞主页面的Onload事件
- 即时内容为空,加载也需要时间
- 没有语意
Javascript如何实现继承?
原型链继承,借用构造函数继承,组合继承,寄生式继承,寄生组合继承
请你谈谈Cookie的弊端?
缺点:
1.Cookie 数量和长度的限制。部分浏览器每个 domain 最多只能有50条 cookie,基本所有浏览器中每个 cookie 长度不能超过4KB,否则会被截掉。
2.安全性问题。如果 cookie 被人拦截了,那人就可以取得所有的 session 信息。即使加密也与事无补,因为拦截者并不需要知道 cookie 的意义,他只要原样转发 cookie 就可以达到目的了。
3.有些状态不可能保存在客户端。例如,为了防止重复提交表单,我们需要在服务器端保存一个计数器。如果我们把这个计数器保存在客户端,那么它起不到任何作用。
4.占用网络上传带宽。每次请求服务器资源时,都会携带 cookie 信息向服务器传递。
DOM操作——怎样添加、移除、移动、复制、创建和查找节点?
创建新节点
- createDocumentFragment() // 创建一个DOM片段
- createElement() // 创建一个具体的元素
- createTextNode() // 创建一个文本节点
添加、移除、替换、插入
- appendChild()
- removeChild()
- replaceChild()
- insertBefore() // 在已有的子节点前插入一个新的子节点
查找
- getElementsByTagName() // 通过标签名称
- getElementsByName() // 通过元素的Name属性的值(IE容错能力较强,会得到一个数组,其中包括id等于name值的)
- getElementById() // 通过元素Id,唯一性
js延迟加载的方式有哪些?
- defer和async
- 动态创建DOM方式(创建script,插入到DOM中,加载完毕后callBack)
- 按需异步载入js
documen.write和 innerHTML 的区别?
document.write 只能重绘整个页面
innerHTML 可以重绘页面的一部分
哪些操作会造成内存泄漏?
内存泄漏指任何对象在您不再拥有或需要它之后仍然存在。垃圾回收器定期扫描对象,并计算引用了每个对象的其他对象的数量。如果一个对象的引用数量为 0(没有其他对象引用过该对象),或对该对象的惟一引用是循环的,那么该对象的内存即可回收。
- setTimeout 的第一个参数使用字符串而非函数的话,会引发内存泄漏。
- 闭包
- 控制台日志
- 循环(在两个对象彼此引用且彼此保留时,就会产生一个循环)
判断一个字符串中出现次数最多的字符,统计这个次数?
var str = 'asdfssaaasasasasaa';
var json = {};
for (var i = 0; i < str.length; i++) {
if(!json[str.charAt(i)]){
json[str.charAt(i)] = 1;
}else{
json[str.charAt(i)]++;
}
};
var iMax = 0;
var iIndex = '';
for(var i in json){
if(json[i]>iMax){
iMax = json[i];
iIndex = i;
}
}
alert('出现次数最多的是:'+iIndex+'出现'+iMax+'次');
数组扁平化
数组扁平化是指将一个多维数组变为一个一维数组
const arr = [1, [2, [3, [4, 5]]], 6];
// => [1, 2, 3, 4, 5, 6]
方法一:使用flat()
const res1 = arr.flat(Infinity);
方法二:利用正则
const res2 = JSON.stringify(arr).replace(/\[|\]/g, '').split(',');
但数据类型都会变为字符串
方法三:正则改良版本
const res3 = JSON.parse('[' + JSON.stringify(arr).replace(/\[|\]/g, '') + ']');
方法四:使用reduce
const flatten = arr => {
return arr.reduce((pre, cur) => {
return pre.concat(Array.isArray(cur) ? flatten(cur) : cur);
}, [])
}
const res4 = flatten(arr);
方法五:函数递归
const res5 = [];
const fn = arr => {
for (let i = 0; i < arr.length; i++) {
if (Array.isArray(arr[i])) {
fn(arr[i]);
} else {
res5.push(arr[i]);
}
}
}
fn(arr);
数组去重
const arr = [1, 1, '1', 17, true, true, false, false, 'true', 'a', {}, {}];
// => [1, '1', 17, true, false, 'true', 'a', {}, {}]
方法一:利用Set
const res1 = Array.from(new Set(arr));
方法二:两层for循环+splice
const unique1 = arr => {
let len = arr.length;
for (let i = 0; i < len; i++) {
for (let j = i + 1; j < len; j++) {
if (arr[i] === arr[j]) {
arr.splice(j, 1);
// 每删除一个树,j--保证j的值经过自加后不变。同时,len--,减少循环次数提升性能
len--;
j--;
}
}
}
return arr;
}
方法三:利用indexOf
const unique2 = arr => {
const res = [];
for (let i = 0; i < arr.length; i++) {
if (res.indexOf(arr[i]) === -1) res.push(arr[i]);
}
return res;
}
方法四:利用include
const unique3 = arr => {
const res = [];
for (let i = 0; i < arr.length; i++) {
if (!res.includes(arr[i])) res.push(arr[i]);
}
return res;
}
方法五:利用filter
const unique4 = arr => {
return arr.filter((item, index) => {
return arr.indexOf(item) === index;
});
}
方法六:利用Map
const unique5 = arr => {
const map = new Map();
const res = [];
for (let i = 0; i < arr.length; i++) {
if (!map.has(arr[i])) {
map.set(arr[i], true)
res.push(arr[i]);
}
}
return res;
}
类数组转化为数组
类数组是具有length属性,但不具有数组原型上的方法。常见的类数组有arguments、DOM操作方法返回的结果。
方法一:Array.from
Array.from(document.querySelectorAll('div'))
方法二:Array.prototype.slice.call()
Array.prototype.slice.call(document.querySelectorAll('div'))
方法三:扩展运算符
[...document.querySelectorAll('div')]
方法四:利用concat
Array.prototype.concat.apply([], document.querySelectorAll('div'));
debounce(防抖)
触发高频时间后n秒内函数只会执行一次,如果n秒内高频时间再次触发,则重新计算时间。
const debounce = (fn, time) => {
let timeout = null;
return function() {
clearTimeout(timeout)
timeout = setTimeout(() => {
fn.apply(this, arguments);
}, time);
}
};
防抖常应用于用户进行搜索输入节约请求资源,window触发resize事件时进行防抖只触发一次。
throttle(节流)
高频时间触发,但n秒内只会执行一次,所以节流会稀释函数的执行频率。
const throttle = (fn, time) => {
let flag = true;
return function() {
if (!flag) return;
flag = false;
setTimeout(() => {
fn.apply(this, arguments);
flag = true;
}, time);
}
}
节流常应用于鼠标不断点击触发、监听滚动事件。
函数珂里化
指的是将一个接受多个参数的函数 变为 接受一个参数返回一个函数的固定形式,这样便于再次调用,例如f(1)(2)
经典面试题:实现add(1)(2)(3)(4)=10; 、 add(1)(1,2,3)(2)=9;
function add() {
const _args = [...arguments];
function fn() {
_args.push(...arguments);
return fn;
}
fn.toString = function() {
return _args.reduce((sum, cur) => sum + cur);
}
return fn;
}
深拷贝
递归的完整版本(考虑到了Symbol属性):
const cloneDeep1 = (target, hash = new WeakMap()) => {
// 对于传入参数处理
if (typeof target !== 'object' || target === null) {
return target;
}
// 哈希表中存在直接返回
if (hash.has(target)) return hash.get(target);
const cloneTarget = Array.isArray(target) ? [] : {};
hash.set(target, cloneTarget);
// 针对Symbol属性
const symKeys = Object.getOwnPropertySymbols(target);
if (symKeys.length) {
symKeys.forEach(symKey => {
if (typeof target[symKey] === 'object' && target[symKey] !== null) {
cloneTarget[symKey] = cloneDeep1(target[symKey]);
} else {
cloneTarget[symKey] = target[symKey];
}
})
}
for (const i in target) {
if (Object.prototype.hasOwnProperty.call(target, i)) {
cloneTarget[i] =
typeof target[i] === 'object' && target[i] !== null
? cloneDeep1(target[i], hash)
: target[i];
}
}
return cloneTarget;
}
以上就是W3Cschool编程狮
关于JavaScript面试经,offer拿到手软的相关介绍了,希望对大家有所帮助。