js面试题小结(1)

本文总结了JavaScript性能优化的策略,包括加载资源和渲染优化,如静态资源压缩、缓存、CSS前放JS后放、DOM操作优化等。同时,讨论了XSS和CSRF等Web安全问题,以及如何预防。还涵盖了常见的前端面试知识点,如事件节流、this的使用场景、异步处理和Ajax实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.性能优化

1.1优化原则和方向

原则
- 多使用内存、缓存或者其他方法
- 减少 CPU 计算、较少网络

方向
- **加载页面和静态资源**
- **页面渲染**

1.2方法:
## 加载资源优化
- 静态资源的压缩合并(JS代码压缩合并、CSS代码压缩合并、雪碧图)
- 静态资源缓存(资源名称加 MD5 戳)
- 使用 CND 让资源加载更快
- 使用 SSR 后端渲染,数据直接突出到 HTML 中

## 渲染优化
- CSS 放前面 JS 放后面
- 懒加载(图片懒加载、下拉加载更多)
- 减少DOM 查询,对 DOM 查询做缓存
- 减少DOM 操作,多个操作尽量合并在一起执行(`DocumentFragment`)
- 事件节流
- 尽早执行操作(`DOMContentLoaded`)

1.3 事件节流
例如要在文字改变时触发一个 change 事件,通过 keyup 来监听。使用节流。

var textarea = document.getElementById('text')
var timeoutId
textarea.addEventListener('keyup', function () {
    if (timeoutId) {
        clearTimeout(timeoutId)
    }
    timeoutId = setTimeout(function () {
        // 触发 change 事件
    }, 100)
})

1.4 尽早执行操作

window.addEventListener('load', function () {
    // 页面的全部资源加载完才会执行,包括图片、视频等
})
document.addEventListener('DOMContentLoaded', function () {
    // DOM 渲染完即可执行,此时图片、视频还可能没有加载完
})

2.常见的 web 攻击方式有哪些,简述原理?如何预防?
2.1前端端最常见的攻击就是 XSS(Cross Site Scripting,跨站脚本攻击):很多大型网站(例如 FaceBook 都被 XSS 攻击过)。举一个例子,我在一个博客网站正常发表一篇文章,输入汉字、英文和图片,完全没有问题。但是如果我写的是恶意的 js 脚本,例如获取到`document.cookie`然后传输到自己的服务器上,那我这篇博客的每一次浏览,都会执行这个脚本,都会把自己的 cookie 中的信息偷偷传递到我的服务器上来。

预防 XSS 攻击就得对输入的内容进行过滤,过滤掉一切可以执行的脚本和脚本链接。大家可以参考[xss.js](https://github.com/leizongmin/js-xss)这个开源工具。

简单总结一下,XSS 其实就是攻击者事先在一个页面埋下攻击代码,让登录用户去访问这个页面,然后偷偷执行代码,拿到当前用户的信息。

2.2还有一个比较常见的攻击就是 CSRF/XSRF(Cross-site request forgery,跨站请求伪造):它是借用了当前操作者的权限来偷偷的完成某个操作,而不是拿到用户的信息。例如,一个购物网站,购物付费的接口是`http://buy.com/pay?id=100`,而这个接口在使用时没有任何密码或者 token 的验证,只要打开访问就付费购买了。一个用户已经登录了`http://buy.com`在选择商品时,突然收到一封邮件,而这封邮件正文有这么一行代码`<img src="http://buy.com/pay?id=100"/>`,他访问了邮件之后,其实就已经完成了购买。

预防 CSRF 就是加入各个层级的权限验证,例如现在的购物网站,只要涉及到现金交易,肯定输入密码或者指纹才行。

3.JS中使用`typeof`能得到的哪些类型

typeof undefined // undefined
typeof 'abc' // string
typeof 123 // number
typeof true // boolean
typeof {}  // object
typeof [] // object
typeof null // object
typeof console.log // function

4.说明 this 几种不同的使用场景
- 作为构造函数执行
- 作为对象属性执行
- 作为普通函数执行
- call apply bind

5.前端使用异步的场景有哪些
setTimeout setInterval
网络请求

6.构造函数

function DomElement(selector) {
    var result = document.querySelectorAll(selector)
    var length = result.length
    var i
    for (i = 0; i < length; i++) {
        this[i] = selectorResult[i]
    }
    this.length = length
}
// 修改原型
DomElement.prototype = {
    constructor: DomElement,
    get: function (index) {
        return this[index]
    },
    forEach: function (fn) {
        var i
        for (i = 0; i < this.length; i++) {
            const elem = this[i]
            const result = fn.call(elem, elem, i)
            if (result === false) {
                break
            }
        }
        return this
    },
    on: function (type, fn) {
        return this.forEach(elem => {
            elem.addEventListener(type, fn, false)
        })
    }
}

// 使用
var $div = new DomElement('div')
$div.on('click', function() {
    console.log('click')
})

7.用于`replace`的示例

function trim(str) {
    return str.replace(/(^\s+)|(\s+$)/g, '')
}

8.获取`2017-06-10`格式的日期

function formatDate(dt) {
    if (!dt) {  dt = new Date()  }
    var year = dt.getFullYear()
    var month = dt.getMonth() + 1
    var date = dt.getDate()
    if (month < 10) {
        // 强制类型转换
        month = '0' + month
    }
    if (date < 10) {
        // 强制类型转换
        date = '0' + date
    }
    // 强制类型转换
    return year + '-' + month + '-' + date
}
var dt = new Date()
var formatDate = formatDate(dt)
console.log(formatDate)

9.获取随机数,要求是长度一直的字符串格式

var random = Math.random()
var random = random + '0000000000'  // 后面加上 10 个零
var random = random.slice(0, 10)
console.log(random)

10.写一个能遍历对象和数组的`forEach`函数

function forEach(obj, fn) {
    var key
    if (obj instanceof Array) {
        // 准确判断是不是数组
        obj.forEach(function (item, index) {
            fn(index, item)
        })
    } else {
        // 不是数组就是对象
        for (key in obj) {
            fn(key, obj[key])
        }
    }
}
var arr = [1,2,3]
// 注意,这里参数的顺序换了,为了和对象的遍历格式一致
forEach(arr, function (index, item) {console.log(index, item)})
var obj = {x: 100, y: 200}
forEach(obj, function (key, value) {console.log(key, value)})

11.如何检测浏览器的类型

var ua = navigator.userAgent
var isChrome = ua.indexOf('Chrome')
console.log(isChrome)

12.拆解url的各部分

console.log(location.href)
console.log(location.protocol) // 'http:' 'https:'
console.log(location.pathname) // '/learn/199'
console.log(location.search)
console.log(location.hash)

13.编写一个通用的事件监听函数

function bindEvent(elem, type, selector, fn) {
    if (fn == null) {
        fn = selector
        selector = null
    }
    elem.addEventListener(type, function (e) {
        var target
        if (selector) {
            target = e.target
            if (target.matches(selector)) {
                fn.call(target, e)
            }
        } else {
            fn(e)
        }
    })
}

14.手动编写一个 ajax,不依赖第三方库

var xhr = new XMLHttpRequest()
xhr.open("GET", "/api", false)
xhr.onreadystatechange = function () {
    // 这里的函数异步执行,可参考之前 JS 基础中的异步模块
    if (xhr.readyState == 4) {
        if (xhr.status == 200) {  alert(xhr.responseText)  }
    }
}
xhr.send(null)

xhr.readyState 的状态吗说明
- 0 - (未初始化)还没有调用send()方法 
- 1 -(载入)已调用send()方法,正在发送请求 
- 2 -(载入完成)send()方法执行完成,已经接收到全部响应内容
- 3 -(交互)正在解析响应内容 
- 4 -(完成)响应内容解析完成,可以在客户端调用了 

### status
http 状态吗有 `2xx` `3xx` `4xx` `5xx` 这几种,比较常用的有以下几种
 - 200 正常
 - 404 找不到资源
 - 5xx 服务器端出错了

15.跨域的几种实现方式

①JSONP;②服务器端设置 http header

16.请描述一下 cookie,sessionStorage 和 localStorage 的区别?

## cookie 有它致命的缺点:

- 存储量太小,只有 4KB
- 所有 http 请求都带着,会影响获取资源的效率
- API 简单,需要封装才能用

## locationStorage 和 sessionStorage
后来,HTML5标准就带来了`sessionStorage`和`localStorage`,先拿`localStorage`来说,它是专门为了浏览器端缓存而设计的。其优点有:

- 存储量增大到 5M
- 不会带到 http 请求中
- API 适用于数据存储 `localStorage.setItem(key, value)` `localStorage.getItem(key)`

17.写出一些常用的 git 命令

- git add .
- git checkout xxx
- git commit -m "xxx"
- git push origin master
- git pull origin master
- git stash / git stash pop

18.简述多人使用 git 协作开发的基本流程

- git branch
- git checkout -b xxx / git checkout xxx
- git merge xxx

19.上线和回归

### 上线原理
- 将测试完成的代码提交到git版本库的master分支
- 将当前服务器的代码全部打包并记录版本号,备份
- 将master分支的代码提交覆盖到线上服务器,生成新版本号

### 回滚原理
- 将当前服务器的代码打包并记录版本号,备份
- 将备份的上一个版本号解压,覆盖到线上服务器,并生成新的版本号

20.从输入url到得到html的详细过程

- 浏览器根据 DNS 服务器得到域名的 IP 地址
- 向这个 IP 的机器发送 http 请求
- 服务器收到、处理并返回 http 请求
- 浏览器得到返回内容

21.浏览器渲染页面的过程

- 根据 HTML 结构生成 DOM Tree
- 根据 CSS 生成 CSS Rule
- 将 DOM 和 CSSOM 整合形成 RenderTree
- 根据 RenderTree 开始渲染和展示
- 遇到`<script>`时,会执行并阻塞渲染

22.window.onload 和 DOMContentLoaded 的区别

- 页面的全部资源加载完才会执行,包括图片、视频等
- DOM 渲染完即可执行,此时图片、视频还没有加载完

23.ES6

- 搭建 ES6 编译环境(使用 babel 编译,使用 webpack 做模块化处理)
- modules:关键字`import` `export`
- 琐碎的功能:let/const,多行字符串/模板变量,解构赋值,块级作用域,函数默认参数,箭头函数
- class:用法、和原型的关系、继承
- Promise:用法

// 加载一张图片
function loadImg(src) {
    const promise = new Promise(function (resolve, reject) {
        var img = document.createElement('img')
        img.onload = function () {
            resolve(img)
        }
        img.onerror = function () {
            reject()
        }
        img.src = src
    })
    return promise   
}
var src = 'http://www.imooc.com/static/img/index/logo_new.png'
var result = loadImg(src)
result.then(function (img) {
    console.log(img.width)
}, function () {    
    console.log('failed')
}).then(function (img) {
    console.log(img.height)
})

24.event-loop

将 js 中要执行的每个任务都做一个划分,同步执行的放在“执行栈”,而异步执行的**将**放在(不是马上就放进去)“异步队列”。然后,将“执行栈”放在主线程中执行,挨个任务排队执行,执行到最后就立马去看“异步队列”是否有数据,有数据就拿到主线程中执行,执行完再去看“异步队列”是否有数据。

25.异步的变同步的解决方案(异步本身没问题,但callback hell是有问题的,所以需要处理)

25.1  Promise (then的方式,参考23)

25.2  asyc-await(注意,async/await 并不是取代了 promise ,而是利用 promise 之后的另外一种写法,取代了 then 函数,写起来更加像同步代码,看着从上到下书写,也从上到下执行)

function loadImg(src) {
    const promise = new Promise(function (resolve, reject) {
        var img = document.createElement('img')
        img.onload = function () {  resolve(img)  }
        img.onerror = function () {  reject()  }
        img.src = src
    })
    return promise
}
const src1 = 'http://www.imooc.com/static/img/index/logo_new.png'
const src2 = 'https://avatars3.githubusercontent.com/u/9583120'
const load = async function () {
    const result1 = await loadImg(src1)
    console.log(result1)
    const result2 = await loadImg(src2)
    console.log(result2)
}
load()

26.数组对象深拷贝(重点在递归调用,例如在对象层级好几层(特别深的时候))

function deepClone(obj = {}) {
	//如果不是对象直接返回
	if(typeof obj !== 'object' || obj == null) {  return obj  }
	let result;
	if(obj instanceof Array) {
		result = []
	} else {
		result = {}
	}
	for(let key in obj) {
		// 保证key不是原型属性
		if(obj.hasOwnProperty(key)) {
			result[key] = deepClone(obj[key])
		}
	}
	return result
}

27.手写jQuery

class Jquery{
	constructor(selector){
		const result = document.querySelectorAll(selector)
		const length = result.length;
		for(let i=0;i<result.length;i++){
			this[i]=result[i]
		}
		this.length = result.length
		this.selector = selector
	}
	get(index){
		return this[index]
	}
	each(fn){
		for (let i=0;i<this.length;i++){
			const elem = this[i]
			fn(elem)
		}
	}
	on(type,fn){
		return this.each(elem=>{
			elem.addEventListener(type,fn,false)
		})
	}
}

28.手写bind函数

Function.prototype.bind1 = function(){
	// 将参数拆解为数组
	const args = Array.prototype.slice.call(arguments)
	
	// 获取this(数组第一项)
	const t = args.shift()
	
	// fn1.bind(...) 中的fn1
	const self = this
	
	return function(){
		return self.apply(t,args)
	}
}

29.防抖(输入框输入keyword,做搜索)

function debounce(fn,delay=500){
	let timer = null
	return function(){
		if(timer){
			clearTimeout(timer)
		}
		
		timer = setTimeout(()=>{
			fn.apply(this,arguments)
			timer = null
		},delay)
	}
}

30.节流(拖拽根据位置做一些逻辑操作)

function throttle(fn, delay = 100) {
	let timer = null
	return function() {
		if(timer) {
			return
		}

		timer = setTimeout(() => {
			fn.apply(this, arguments)
			timer = null
		}, delay)
	}
}

31.事件绑定(普通事件和事件委托)

function bindEvent(elem,type,selector,fn){
	if(fn==null){
		fn=selector
		selector = null 
	}
	
	elem.addEventListener(type,event=>{
		let target = event.target
		
		if( selector ){
			if(target.matches(selector)){
				fn.call(target,event)
			}
		}else{
			fn.call(target,event)
		}
	})
}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值