问答题javascript
2. Typeof返回哪些类型?html
3. 列举强制类型转换和隐式类型转换html5
5. 数组的pop, push, unshift, shift分别是什么css3
6. 数组API中有哪些是纯函数?正则表达式
8. Ajax请求get和post区别canvas
9. [10, 20, 30].map(parseInt)跨域
15. new Object()和Object.create()区别
20. 介绍一下RAF requestAnimationFrame
Var是ES5语法;let const是ES6语法;var有变量提高(变量能够在使用后声明,也就是变量能够先使用再声明);
Var和let是变量能够修改;const是常量,不可修改;
Let const有块级做用域,var没有
值类型:undefined、string、number、Boolean、symbol
引用类型:object(注意 typeof null === 'object')
function
强制:parseInt 、parseFloat、 toString等
隐式:if 、逻辑运算、== 、+拼接字符串
互相反转,拆分vs拼接
'1-2-3'.split('-') //[1, 2, 3] [1,2,3].join('-') // '1-2-3'
(功能是什么、返回值是什么、是否会对原数组形成影响)
(1)pop函数 用于弹出数组最后一个元素,返回值是弹出的元素值,
(2)push函数 用于向数组最后添加一个元素,返回值为数组的长度
(3)shift函数 用于弹出数组第一个元素,返回值为弹出数值
(4)unshift函数 用于向数组首位以前插入一个元素,返回值为数组长度
这几个函数都改变了原来的数组,可能会有反作用
const arr = [10, 20, 30, 40] // pop const popRes = arr.pop() console.log(popRes, arr) // 40 [10,20,30] // shift const shiftRes = arr.shift() console.log(shiftRes, arr) // 10 [20,30,40] // push const pushRes = arr.push(50) // 返回 length console.log(pushRes, arr) // 5 [10,20,30,40,50] // unshift const unshiftRes = arr.unshift(5) // 返回 length console.log(unshiftRes, arr) // 5 [5,10,20,30,40]
(1)纯函数: 不改变原数组(没有反作用),且返回一个数组
concat函数(向数组后面追加一个数组)、map函数、filter函数(过滤)、slice函数(相似于深拷贝)
// 纯函数:1. 不改变源数组(没有反作用);2. 返回一个数组 const arr = [10, 20, 30, 40] // concat const arr1 = arr.concat([50, 60, 70]) // map const arr2 = arr.map(num => num * 10) // filter const arr3 = arr.filter(num => num > 25) // slice const arr4 = arr.slice()
(2)非纯函数:push pop shift unshift、forEach、some 、every、 reduce
(1)功能区别:slice切片、splice剪接
(2)参数和返回值:返回值都为数组
(3)是否为纯函数:slice纯函数、splice非纯函数
const arr = [10, 20, 30, 40, 50] // slice 纯函数 const arr1 = arr.slice() // 10,20,30,40,50 const arr2 = arr.slice(1, 4) // 20,30,40 const arr3 = arr.slice(2) // 30,40,50 const arr4 = arr.slice(-2) //40,50 // splice 非纯函数 const spliceRes = arr.splice(1, 2, 'a', 'b', 'c') // 把1-2的位置移除再放入a,b,c // const spliceRes1 = arr.splice(1, 2) // 把1-2位置移除 // const spliceRes2 = arr.splice(1, 0, 'a', 'b', 'c') console.log(spliceRes, arr)
get通常用于查询操做,post通常用于用户提交操做
get参数拼接在url上,post放在请求体内(数据体积能够更大),所以当进行上传提交操做时若是较大就是用post
安全性:post易于防止CSRF
map() 方法返回一个新数组,数组中的元素为原始数组元素调用函数处理后的值。
parseInt() 函数可解析一个字符串,并返回一个整数 parseInt(string, radix),将string以radix进制转化为10进制
const res = [10, 20, 30].map(parseInt) console.log(res) //[10, NAN, NAN] // 拆解 [10, 20, 30].map((num, index) => { return parseInt(num, index) })
闭包就是:有权访问另外一个函数做用域变量的函数都是闭包
应用场景:函数做为参数被传递(函数在一个地方定义好以后,到另外一个地方去执行)
函数做为返回值被返回(函数定义好以后会被返回到另外一个地方执行)
机制:当咱们调用一个闭包函数,在函数执行时,其上下文有个Scope属性,该属性做为一个做用域链包含有该函数被定义时全部外层的变量对象的引用,因此定义了闭包的函数虽然销毁了,可是其变量对象依然被绑定在函数inner上,保留在内存中。
注意:全部的自由变量的查找,是在函数定义的地方,向上级做用域查找,不是在执行的地方
影响:变量会常驻内存,得不到释放。所以闭包不要乱用,可能会影响性能(通常状况一个函数(函数做用域)执行完毕,里面声明的变量会所有释放,被垃圾回收器回收。但闭包让做用域里面的变量,在函数执行完以后依旧保存没有被垃圾回收处掉。)
event.stopPropagation()
event.preventDefault()
DOM操做很是‘昂贵’(占用CPU,可能会形成浏览器重排,耗时),所以尽可能避免频繁的DOM操做;
(1)缓存DOM查询结果
(2)屡次DOM操做合并到一块儿
函数声明 function fn() { ... }
函数表达式 const fn = function() { ... }
函数声明会在代码执行前预加载(相似变量提高),而函数表达式不会
// 函数声明 const res = sum(10, 20) console.log(res) function sum(x, y) { return x + y } // 函数表达式 var sum = function (x, y) { return x + y } var res = sum(10, 20) console.log(res)
{} 等同于new Object(), 原型 Object.prototype
ob1 = Object.create(null) 没有原型 obj2 = new Object()有原型 Object.create({ ... })能够指定原型
const obj1 = { a: 10, b: 20, sum() { return this.a + this.b } } const obj2 = new Object({ a: 10, b: 20, sum() { return this.a + this.b } }) const obj21 = new Object(obj1) // obj1 === obj21 const obj3 = Object.create(null) // {}没有属性没有原型 const obj4 = new Object() // {}有原型 // 意思建立一个空对象,把原型挂载到create内容上 const obj5 = Object.create({ a: 10, b: 20, sum() { return this.a + this.b } }) // 经过ob1建立obj6,那么obj6的原型指向obj1 obj6._proto_ === obj1 const obj6 = Object.create(obj1) // obj6的原型指向obj1
(1)用户名 字符串 字母开头 后面字母数字下划线,长度为6-30
const reg = / ^[a-zA-Z] \w {5,29} $ /
^开始 [ ] 选择 \w 字母数字下划线 {} 长度范围 $结尾 \d数字 +一次或屡次 .匹配任意字符
(2)邮政编码 / \d{6} /
(3)小写英文字母 /^[a-z]+$ /
(4)英文字母 /^[a-zA-Z]+$/
(5)日期 /^\d{4}-\d{1,2}-\d{1,2}$/
(6)简单IP地址 /\d+\.\d+.\d+.\d+/ \. 表示.
经常使用正则表达式教程 https://www.runoob.com/regexp/regexp-syntax.html
(1)手动捕获异常try-catch
(2)自动捕获异常 window.onerror
// 自动捕获 window.onerror = function (message, source, lineNum, colNum, error) { // 第一,对于跨域的js,如CDN的,不会有详细的报错信息 // 第二,对于压缩的js,还要配合sourceMap反查到未压缩代码行列 }
json是一种数据格式,本质是一段字符串
json格式和JS对象结构一致,对JS语言更友好
window.JSON是全局对象(key都须要双引号),JSON.stringify JSON.parse
(1)传统方法,查找Location.search
(2)URLSearchParams
// 传统方式 function query(name) { const search = location.search.substr(1) // 相似 array.slice(1)除去第一个 // search: 'a=10&b=20&c=30' const reg = new RegExp(`(^|&)${name}=([^&]*)(&|$)`, 'i') // i表示大小写不区分 const res = search.match(reg) if (res === null) { return null } return res[2] // 固定写法 输出b对应的值20 } query('b') // URLSearchParams function query(name) { const search = location.search const p = new URLSearchParams(search) return p.get(name) } console.log( query('b') )
在Web应用中,实现动画效果的方法比较多,Javascript 中能够经过定时器 setTimeout或者setInterval 来实现,css3 可使用 transition 和 animation 来实现,html5 中的 canvas 也能够实现。除此以外,html5 还提供一个专门用于请求动画的API,那就是 requestAnimationFrame,顾名思义就是请求动画帧。
要想动画流畅,更新频率要60帧/秒,即16.67ms更新一次视图
setTimeout须要手动控制频率,而RAF浏览器会自动控制
后台标签或者隐藏iframe中(最小化),RAF会暂停,而setTimeout依然执行
// 3s 把宽度从 100px 变为 640px ,即增长 540px // 60帧/s ,3s 180 帧 ,每次变化 3px const $div1 = $('#div1') let curWidth = 100 const maxWidth = 640 // RAF function animate() { curWidth = curWidth + 3 $div1.css('width', curWidth) if (curWidth < maxWidth) { window.requestAnimationFrame(animate) // 时间不用本身控制 } } animate()
// 判断是不是对象或数组(不考虑函数) function isObject(obj) { return typeof obj === 'object' && obj !== null } // 全相等(深度) function isEqual(obj1, obj2) { // 首先判断是不是对象 if (!isObject(obj1) || !isObject(obj2)) { // 值类型(注意,参与 equal 的通常不会是函数) return obj1 === obj2 } if (obj1 === obj2) { return true } // 两个都是对象或数组,并且不相等 // 1. 先取出 obj1 和 obj2 的 keys ,比较个数 const obj1Keys = Object.keys(obj1) const obj2Keys = Object.keys(obj2) if (obj1Keys.length !== obj2Keys.length) { return false } // 2. 以 obj1 为基准,和 obj2 依次递归比较 for (let key in obj1) { // 比较当前 key 的 val —— 递归!!! const res = isEqual(obj1[key], obj2[key]) if (!res) { return false } } // 3. 全相等 return true } // 测试 const obj1 = { a: 100, b: { x: 100, y: 200 } } const obj2 = { a: 100, b: { x: 100, y: 200 } } // console.log( obj1 === obj2 ) console.log( isEqual(obj1, obj2) ) const arr1 = [1, 2, 3] const arr2 = [1, 2, 3, 4]
trim 掐头去尾去空格
String.prototype.trim = unction () { return this.replace(/^\s+/,'').replace(/\s+$/,'') // \s字符 }
// 传统方法,分析search function queryToObj() { const res = {} const search = location.search.substr(1) // 去掉? search.split('&').forEach(paramStr => { const arr = paramStr.split('=') const key = arr[0] const key = arr[1] res[key] = val }) return res } // 使用URLSearchParams function queryToObj() { const res = {} const pList = new URLSearchParams(location.search) pList.forEach((val, key) => { res[key] = val }) return res }
将[1, 2, [3, 4, [10, 20, [100, 200]]], 5]变为[1, 2, 3, 4, 10, 20, 100, 200, 5]
function flat(arr) { // 验证 arr 中,还有没有深层数组 [1, 2, [3, 4]] const isDeep = arr.some(item => item instanceof Array) if (!isDeep) { return arr // 已是 flatern [1, 2, 3, 4] } // oncat只能解决单层[] const res = Array.prototype.concat.apply([], arr) return flat(res) // 递归 } const res = flat( [1, 2, [3, 4, [10, 20, [100, 200]]], 5] ) console.log(res)
// 传统方式 function unique(arr) { const res = [] arr.forEach(item => { if (res.indexOf(item) < 0) { // 没有当前元素 res.push(item) } }) return res } // 使用 Set (无序,不能重复) function unique(arr) { const set = new Set(arr) return [...set] // 解构 } const res = unique([30, 10, 20, 30, 40, 10]) console.log(res)
function deepClone(obj = {}) { if (typeof obj !== 'object' || obj == null) { // 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 }
class jQuery { constructor(selector) { const result = document.querySelectorAll(selector) const 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++) { const elem = this[i] fn(elem) } } on(type, fn) { return this.each(elem => { elem.addEventListener(type, fn, false) }) } // 扩展不少 DOM API } // 插件 jQuery.prototype.dialog = function (info) { alert(info) } // 扩展 “造轮子” class myJQuery extends jQuery { constructor(selector) { super(selector) } // 扩展本身的方法 addClass(className) { } style(data) { } }
// 模拟 bind Function.prototype.bind1 = function () { // 将参数拆解为数组 const args = Array.prototype.slice.call(arguments) // 获取 this(数组第一项) const t = args.shift() // 拿走数组第一项 // fn1.bind(...) 中的 fn1 const self = this // bind返回一个函数 return function () { return self.apply(t, args) } } function fn1(a, b, c) { console.log('this', this) console.log(a, b, c) return 'this is fn1' } const fn2 = fn1.bind1({x: 100}, 10, 20, 30) const res = fn2() console.log(res)
function bindEvent(elem, type, selector, fn) { // selector是个css选择器 if (fn == null) { // 判断只传入三个参数 fn = selector selector = null } elem.addEventListener(type, event => { const target = event.target if (selector) { // 有selector时是代理绑定 if (target.matches(selector)) { // 判断DOM元素是否符合css选择器 fn.call(target, event) } } else { // selector为空,只有三个参数,是普通绑定 fn.call(target, event) } }) } // 普通绑定 const btn1 = document.getElementById('btn1') bindEvent(btn1, 'click', function (event) { // 注意不能使用箭头函数,不然获取的是上级window // console.log(event.target) // 获取触发的元素 event.preventDefault() alert(this.innerHTML) // this }) // 代理绑定 const div3 = document.getElementById('div3') bindEvent(div3, 'click', 'a', function (event) { event.preventDefault() // 阻止默认行为(页面页面调转) alert(this.innerHTML) // 若是使用箭头函数的写法 event.target.innerHTML })
// 防抖封装 function debounce(fn, delay = 500) { // timer 是闭包中的 let timer = null // 返回一个函数 return function () { if (timer) { clearTimeout(timer) } timer = setTimeout(() => { fn.apply(this, arguments) timer = null }, delay) } } input1.addEventListener('keyup', debounce(function (e) { console.log(e.target) console.log(input1.value) }, 600))
// 节流函数 function throttle(fn, delay = 100) { let timer = null return function () { if (timer) { return } timer = setTimeout(() => { fn.apply(this, arguments) timer = null }, delay) } } div1.addEventListener('drag', throttle(function (e) { console.log(e.offsetX, e.offsetY) }, 200))