让我们来看看有哪些常见的面试题,比如:
1、typeof能判断哪些类型?
2、何时使用===何时使用==?
3、window.onload和DomCententLoaded有什么区别?
4、JS创建5个P标签,点击的时候弹出对应的序号?
5、手写节流 throttle、防抖debounce?
6、Promise解决了什么问题?
7、值类型和引用类型的区别?
8、手写深拷贝?
9、手写一个简易的jQuery,考虑插件和扩展性?
10、this的不同应用场景,如何取值?
11、手写bind函数、手写call函数
12、闭包在实际开发的应用场景
13、同步和异步的区别
14、手写用promis加载一张图片
15、前端使用异步的场景有哪些?
16、编写一个通用的事件监听函数
17、描述事件冒泡的流程
18、无限下拉的图片列表,如何监听每个图片的点击
19、手写一个简易的ajax
20、跨域常用的实现方式
21、前端常见的攻击方式
22、前端性能优化
解答:
1、typeof可以判断所有的值类型,可以判断函数类型,判断不了引用类型,如图:
2、a==null情况可以用两等,因为null == undefined 为 true,其他情况下全部用三等
3、window.load页面的全部资源加载完才执行,包括图片,视频
DomCententLoaded页面渲染完就执行,图片和视频可能还没加载完,一般用这个方法,不需要等图片和视频加载完
4、
for(let i = 0; i < 5; i++) {
let p = document.createElement('p');
p.innerHTML = i + '<br/>';
p.addEventListener('click', function(e) {
alert(i);
})
document.body.appendChild(p)
}
5、防抖debounce,如果输入框正在输入的时候不会触发函数,输入停止1s的时候,才触发函数
//监听输入框变化onchange
//简单版
let time;
document.getElementById('input').addEventListener('keyup', () => {
if (time) {
clearTimeout(time)
}
time = setTimeout(() => {
time == null
}, 1000)
})
//封装版
function debounce(fn, second = 500) {
let time;
return function () {
if (time) {
clearTimeout(time)
}
time = setTimeout(() => {
fn.apply(this, arguments)
time == null
}, second)
}
}
document.getElementById('input').addEventListener('keyup', debounce(() => {
console.log(document.getElementById('input').value)
}, 1000))
节流throttle,不管拖拽的多快,函数触发时间都一样
//监听div拖拽事件drag
let time;
document.getElementById('div').addEventListener('drag', () => {
if (time) {
return
}
time = setTimeout(() => {
time == null
}, 1000)
})
function throttle(fn, second = 500) {
let time;
return function () {
if (time) {
return;
}
time = setTimeout(() => {
fn.apply(this, arguments)
time == null
}, second)
}
}
document.getElementById('div').addEventListener('drag', throttle((e) => {
console.log(e.offsetX)
}, 600))
6、promise解决了callback hell的问题
//用callback的时候如果逻辑复杂,然后一层一层去加载代码看起来不优雅,可读性不好,而且也显得的复杂,调试也不好调试
$.get('./geo.json', function (data){
console.log(data)
$.get('./geo1.json', function (data1){
console.log(data1)
$.get('./geo2.json', function (data2){
console.log(data2)
$.get('./geo3.json', function (data3){
console.log(data3)
})
})
})
})
7、值类型赋值的时候修改不会改变原始值,引用类型赋值的时候修改会改变原始值
//值类型
let a = 100;
let b = a;
a = 200;
console.log(b) //打印100
//引用类型
let a = {number: 10};
let b = a;
a.number = 20;
console.log(b.number) //打印20
8、
var a = {
a: 1,
b: '2',
c: {
m: 'hahah'
},
d: [1,2,3]
}
function deep(obj = {}) {
//如果不是对象和数组直接返回,typeof只能判断值类型
if(typeof obj !== 'object' || obj == null) {
return obj;
}
let result
//判断是数组还是对象
if(obj instanceof Array) {
result = []
} else {
result = {}
}
for (let key in obj) {
//判断是否为自身属性
if (obj.hasOwnProperty(key)) {
result[key] = deep(obj[key])
}
}
return result
}
var b = deep(a);
a.a = 3;
console.log(b.a) //打印1而不是3
9、
class Jquery {
constructor(selector){
let result = document.querySelectorAll(selector);
let length = result.length;
for(let i = 0; i < length; i++) {
this[i] = result[i];
}
this.length = length;
this.selector = selector;
}
get(index) {
return this[index];
}
each(fn) {
for(let i = 0; i < this.length; i++) {
let elem = this[i];
fn(elem)
}
}
on(type, fn) {
return this.each(elem => {
elem.addEventListener(type, fn, false);
})
}
}
//插件
Jquery.prototype.dialog = function(val) {
alert(val)
}
//扩展
class my extends Jquery {
constructor(selector) {
super(selector)
}
addClass(name) {
}
style(name) {
}
}
//使用获取p元素
const $ = new Jquery('p')
$.get(1)
$.each(elem => console.log(elem.nodeName))
$.on('click', () => {alert(1)})
10、查看基础知识(作用域和闭包)
11、
Function.prototype.bind1 = function () {
let args = Array.prototype.slice.call(arguments)
//获取this
let t = args.shift();
let self = this
return function () {
return self.apply(t, args)
}
}
function fn1(a, b) {
console.log(this)
console.log(a,b)
}
let fn2 = fn1.bind1({a: 10}, 1, 2)
fn2() //打印{a: 10} 1 2
var value = 'v in window';
function func() {
arguments = [].slice.call(arguments, 0);
console.log(arguments);
console.log(this.value);
}
var obj = {
value: 'v in obj'
};
Function.prototype.call2 = function (obj) {
var obj = obj || window;
var args = [];
//从第1项开始循环,过滤掉第一个参数
for(var i = 1; i < arguments.length; i++) {
args.push(arguments[i]);
}
obj.fn = this;
obj.fn(...args);
delete obj.fn; //要删除obj.fn,否则obj就会多个fn
};
func(1,2,3); //打印 [1,2,3] v in window
func.call2(obj,1,2); //打印[1,2] v in obj
12、封装变量,闭包是一种保护私有变量的机制,在函数执行时形成私有的作用域,保护里面的私有变量不受外界干扰。
var add = (function () {
var counter = 0;
return function () {return counter += 1;}
})();
add();
add();
add();
// 计数器为 3
13、js是单线程的语言,js和DOM渲染共用一个线程,因为js可以修改DOM,同步会阻塞代码执行,页面会卡住,异步不会阻塞代码执行
14、
function creatImg(url) {
return new Promise((resolve, reject) => {
var img = document.createElement('img');
img.onload = function (){
resolve(img)
}
img.onerror = function (data){
reject(data)
}
img.src = url
})
}
creatImg('./1.png').then((img) => {
console.log(img.width)
return img
}).then((img) => {
console.log(img.height)
return creatImg('./2.png')
}).then((img2) => {
//img2是creatImg('./2.png')函数返回的数据
console.log(img2.height)
return img2
}).catch(() => {
console.log('error')
})
15、网络请求(ajax、图片的加载等)、定时任务(setTimeout等)
//ajax
console.log(1)
$.get('./geo.json', function (data){
console.log(data)
})
console.log(2)
//加载图片
console.log(1)
var img = document.createElement('img');
img.onload = function (data){
console.log(data)
}
img.src = './1.png'
console.log(2)
//定时器
console.log(1)
setTimeOut(function (){
console.log(3)
}, 1000)
console.log(2)
16、
//html
<body>
<div id='div1'>1</div>
<div id='div2'>2</div>
</body>
//js
function bindEvent(ele, type, selector, fn) {
if (fn == null) {
fn = selector;
selector = null
ele.addEventListener(type, fn)
}
ele.addEventListener(type, function(e){
let target = e.target;
if (selector) {
if(target.matches(selector)){
fn.call(target, event)
}
} else {
fn.call(target, event)
}
})
}
var div1 = document.getElementById('div1')
bindEvent(div1, 'click', function (e) {
console.log(this.innerHTML)
console.log(e.target)
console.log(e.preventDefault()) //阻止事件默认行为
console.log(e.stopPropagation()) //阻止事件冒泡
})
var body = document.body
bindEvent(body, 'click', 'div', function (e) {
e.preventDefault()
console.log(this.innerHTML)
})
17、事件冒泡:点击div,body的点击事件也会执行
//html
<body>
<div id='div1'>1</div>
<div id='div2'>2</div>
</body>
//js
function bindEvent(ele, type, fn) {
ele.addEventListener(type, fn)
}
var div1 = document.getElementById('div1')
bindEvent(div1, 'click', function (e) {
console.log(e.target)
console.log(e.preventDefault()) //阻止事件默认行为
console.log(e.stopPropagation()) //阻止事件冒泡
})
var body = document.body
bindEvent(body, 'click', function (e) {
console.log(e.target)
})
18、
//html
<body>
<div id='div1'><img src='./1.png'/></div>
<div id='div2'><img src='./2.png'/></div>
</body>
//js
function bindEvent(ele, type, fn) {
ele.addEventListener(type, fn)
}
var body = document.body
bindEvent(body, 'click', function (e) {
e.preventDefault()
let target = e.target
if(target.nodeName == 'div') {
console.log(target.innerHTML)
}
})
19、
//get请求
let http = new XMLHttpRequest()
http.open('get', '/web?fname=Henry', true)
http.onreadystatechange = function () {
if (http.readyState == 4) {
if (http.status == 200) {
console.log(http.responseText)
}
}
}
http.send()
//post请求
let xmlhttp = new XMLHttpRequest()
xmlhttp.open("POST","/try/ajax/demo_post2.php",true);
xmlhttp.setRequestHeader("Content-type","application/x-www-form-urlencoded");
xmlhttp.send("fname=Henry&lname=Ford");
//模拟ajax
function ajax(url) {
let p = new Promise((resolve, reject) =>{
let http = new XMLHttpRequest()
http.open('get', url, true)
http.onreadystatechange = function () {
if (http.readyState == 4) {
if (http.status == 200) {
resolve(http.responseText)
}
}
else{
reject('错误')
}
}
http.send()
})
return p
}
ajax('./a.js').then((data) => {
console.log(data)
}).catch((data) => {
console.log(data)
})
20、主要的有jsonp、cors(cors为后端设置响应请求头)
//jsonp
window.callback = function (data) {
console.log(data) //打印{name: hello}
}
//注意这个script标签也可以用document.createElement('script')创建
<script src='http://a.com/a.js'></script> //js返回callback({name: hello})
21、
XSS跨站请求攻击(例如写一篇文章,里面嵌入script代码获取cookie,别人访问你的文章是发送别人的cookie到自己的服务器)
XSS预防:替换特殊字符,显示html实体
CSRF跨站请求伪造(例如正在浏览购物网站,突然收到一封邮件,邮件中有一个<img src='http://aa.com/pay/id=300'>,如果你打开邮件,就直接付款了)
CSRF预防:使用post接口,用img访问是不通的,增加验证(密码,短信,指纹)
22、
让加载更快(压缩代码,减少资源体积;合并代码,减少访问次数;SSR服务器端渲染,缓存;)
让渲染更快(CSS放在head,JS放在body最后面;尽早开始执行JS,用DomCententLoaded触发;懒加载;对DOM查询进行缓存;频繁操作DOM,合并后再一起插入DOM结构;节流,防抖)