typeof运算符
1.识别所有值类型
2.识别函数
3.判断是否是引用类型(不可再细分)
instanceof 类型判断
手写js深拷贝
function deepClone(
if(typeof obj !== 'object' || obj === null){
return obj
}
let result;
result = obj instanceof Array?[]:{}
for(const key in obj){
//保证key不是原型的属性
if(obj.hasOwnProperty(key)){
//递归调用
result[key] = deepClone(obj[key])
}
}
return result
}
原型和原型链
js的class实际上是函数,语法糖
隐式原型和显式原型
bob.__proto__ ==== Student.prototype
每个class都有显式原型prototype
每个实例都有隐式原型__proto__
实例的__proto__指向对应的class的prototype
基于原型的执行规则
先在自身属性和方法寻找
如果找不到则自动去__proto__去寻找
instanceof 是基于原型链实现的
手写简易jQuery考虑插件和扩展性
class JQuery {
constructor(selector) {
const res = document.querySelectorAll(selector)
const length = res.length
for (let i = 0; i < length; i++) {
this[i] = res[i]
}
this.length = 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(ele => {
ele.addEventListener(type, fn, false)
})
}
...
}
JQuery.prototype.dialog = function(info){
alert(info)
}
class newJQuery extends JQuery{
construct(selector){
super(selector)
}
addClass(className){
....
}
style(data){
...
}
}
作用域和闭包
作用域
1.全局作用域
2.函数作用域
3.块级作用域
自由变量
1.一个变量在当前作用域没有定义,但被使用了
2.向上级作用域,一层一层依次寻找,直至找到为止
3.如果到全局作用域都没找到,则报错xx is not defined
闭包
自由变量的查找,是在函数定义的地方,向上级作用域查找 (注意:不是执行的地方)
function create() {
const a = 100
return function() {
console.log(a)
}
}
const a = 200
const fn = create()
fn() 输出:100
function print(fn) {
const a = 200
fn()
}
const a = 100
function fn() {
console.log(a)
}
print(fn) 输出:100
this的场景 和指向
场景
1.作为普通函数
2.使用call apply bind 可以改变this指向
bind会返回一个新的函数
3.作为对象方法被调用
4.在class方法中调用
5.箭头函数
指向
this的指向是在函数执行的时候确认的,不是定义的时候
手写bind函数
function fn1(a, b, c) {
console.log('this', this)
console.log(a, b, c)
return 'this is fn1'
}
console.log('----------')
fn1(10, 20, 30)
console.log('----------')
console.log('----------')
const fn2 = fn1.bind({
x: 100
}, 10, 20, 30)
const res = fn2()
console.log(res)
console.log('----------')
Function.prototype.bind1 = function(){
const args = Array.prototype.slice.call(arguments)
const t = args.shift()
const self = this
return function(){
return self.apply(t,args)
}
}
console.log('----------')
const fn3 = fn1.bind1({
x: 100
}, 10, 20, 30)
const result = fn3()
console.log(result)
console.log('----------')
实际开发中闭包的应用
1.隐藏数据
function createCache() {
const data = {}
return {
set: (key, value) => {
data[key] = value
},
get: (key) => {
return data[key]
}
}
}
const c = createCache()
c.set('a', 100)
console.log(c.get('a'))
2.创建10个a标签,点击弹出序号
for (let i = 0; i < 10; i++) {
let a = document.createElement('a')
a.innerHTML = i
a.style.display = 'block'
a.addEventListener('click',function(){
alert(i)
}, false)
document.body.appendChild(a)
}
异步和单线程
JS是单线程语言,只能同时做一件事
浏览器和nodejs已支持JS启动进程,如Web Worker
JS和DOM渲染共用同一个线程,因为JS可修改DOM结构
遇到等待(网络请求,定时任务)不能卡住
需要异步
回调函数callback
同步和异步
基于JS是单线程语言
异步不会阻塞代码执行
同步会阻塞代码执行
回调地狱 Promise解决回调地狱
手写用Promise加载一张图片
function loadImg(src){
return new Promise((resolve,reject) => {
const img = document.createElement('img')
img.onload = () => {
resolve(img)
}
img.onerror = () => {
const error = `加载失败 - ${src}`
reject(error)
}
img.src = src
})
}
-----------------------------------------
const src = 'https://dss2.bdstatic.com/lfoZeXSm1A5BphGlnYG/skin/493.jpg?2%22);'
loadImg(src).then(res => {
const img = res
document.body.appendChild(img)
}).catch(err => {
console.log(err)
})
前端使用异步的场景有哪些
网络请求 如ajax 图片加载
定时任务 如setTimeout
DOM 树结构
获取DOM节点
document.getElementById
document.getElementByTagName
document.getElementsByClassName
document.querySelector
document.querySelectorAll
property和attribute
property:修改对象属性,不会体现到html结构中
attribute:修改html属性,会改变html结构
两者都有可能引起DOM重新渲染
DOM结构操作
新增/插入节点
获取子元素列表,获取父元素
box.childNodes box.parentNode
删除节点
removeChild
DOM性能
避免频繁的DOM操作
对DOM查询做缓存
将频繁操作改为一次性操作

BOM
navigator history screen location
事件绑定 事件冒泡 事件代理
event.preventDefault()
事件冒泡的流程:
基于DOM树型结构
事件会顺着触发元素往上冒泡
应用场景:代理
事件代理
代码简洁 减少浏览器内存占用
无限下拉图片列表,如何监听每个图片的点击
事件代理
用e.target获取触发元素
用matches来判断是否是触发元素
跨域
什么是跨域(同源策略)
ajax请求时,浏览器要求当前网页和server必须同源(安全)
同源:协议、域名、端口
加载图片、css、js可无视同源协议 (比如cdn jsonp)
<img />可用于统计打点,可使用第三方统计服务
所有的跨域,都必须经过server端允许和配合
未经server端允许就实现跨域,说明浏览器有漏洞,危险信号
JSONP
<script>可绕过跨域限制
服务器可以任意动态拼接数据返回
所以,<script>就可以获得跨域的数据只要服务端愿意返回


CORS(服务端支持)

手写一个简易的ajax
function ajax(url){
return new Promise((resolve,reject) => {
const xhr = new XMLHttpRequest()
xhr.open('GET',url,true)
xhr.onreadystatechange = function(){
if(xhr.readyState === 4){
if(xhr.status === 200){
resolve(
JSON.parse(xhr.responseText)
)
}else if(xhr.status === 404){
reject(new Error('404 Not Found'))
}
}
}
xhr.send(null)
})
}
ajax常用工具
JQuery Fetch Axios
Cookie SessionStorage LocalStorage
Cookie
存储大小 最大4kb
http请求时需要发送到服务端,增加请求数据量
document.cookie修改 用于浏览器和server通讯
SessionStorage LocalStorage
H5专门为存储而设计,最大可存5M
API简单易用 setItem getItem
不会随着http请求被发送出去
LocalStrorage:数据会永久存储,除非手动删除
SessionStorage:数据只存在于当前会话,浏览器关闭则清空
手写防抖和节流
防抖
监听一个输入框,文字变化后触发change事件
直接用keyup事件,则会频发触发change事件
防抖:用户输入结束或暂停时,才触发change事件
const inputEl = document.getElementById('input');
inputEl.addEventListener('keyup', debouce(function(){
console.log(input.value)
},300), false)
function debounce(fn,delay = 500){
let timer
return function(){
if(timer){
clearTimeout(timer)
}
timer = setTimeout(()=>{
fn.apply(this,arguments)
},delay)
}
}
节流
拖拽一个元素时,要随时拿到该元素被拖拽的位置
直接用drag事件,则会频发触发,很容易导致卡顿
节流:无论拖拽速度多快,都会每隔100ms触发一次
function throttle(fn, delay = 100) {
let timer
return function() {
if (timer) {
return
}
timer = setTimeout(function() {
fn.apply(this, arguments)
timer = null
}, delay)
}
}
const divEl = document.getElementById('div')
divEl.addEventListener('drag',throttle(function(e){
console.log(e.offsetX,e.offsetY)
},200),false)