Javascript篇
1. 数据类型
代码语言:javascript复制* 基本数据类型(Number,Boolean,String,null,undefined,symbol(ES6新增),BigInt)
* 引用类型 (Object,Array,Function)
2. 基本数据类型和引用数据类型的区别
代码语言:javascript复制 * 存储位置不同:
。基本数据类型存储在栈当中,值与值之间独立存在,修改一个变量不会影响其他变量。
。引用数据类型存储在堆当中,每创建一个新的引用数值,就会在堆内存当中开辟一个新的空间。
* 前者复制后,两个变量是独立的,变量保存的是内存地址。
3. let,var和const的区别
代码语言:javascript复制* let声明的变量只在let命令的块级作用域内有效。
* let命令不存在变量提升,var会发生变量提升
* let声明变量会存在暂时性死区,即变量会绑定某个区域不受外部影响。
* let命令不允许重复定义,但是var可以,重复定义之后后面的值回覆盖前面的值。
* const声明常量,赋值后不可改变,一旦声明必须初始化。
* const也只在声明的块级作用域内有效。变量声明不会提升。
* const实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动
4. Null和undefined区别
代码语言:javascript复制* Null代表空值,代表一个空对象指针;undefined表明变量声明了却未被初始化
* 用typeof判断时,typeof(undefined)能够得到undefined类型,但typeof(null)为object
* 转换时数值不一样,Number(undefined)为NaN,Number(null)为0
* Null一般用于释放内存空间/原型链顶端,当函数没有返回值时,返回undefined
5. 如何判断一个变量是不是数组。
代码语言:javascript复制* typeof() //判断基本数据类型。但是null判断为object
* Array.isArray() //返回true说明是数组。
* xxx instanceof Array //返回true说明是数组,可判断复杂数据类型
* Object.prototype.toString.call() //返回[object Array]字符串,说明是数组
6. 数组方法
代码语言:javascript复制* join() 数组转字符串,默认为逗号分隔符,原数组不变。
* push() 向数组末尾添加一个或多个元素,并返回新的长度。
* pop() 用于删除并返回数组的最后一个元素。
* shift() 用于把数组的第一个元素删除,并返回第一个元素的值。
* unshift() 向数组的开头添加一个或更多元素,并返回新的长度。
* sort() 排序,但是是按照字符编码顺序进行排序的,所以需要实现一个排序函数。
。 升序return a-b;
。 逆序return b-a.
* concat() 用于连接两个或多个数组。该方法不会改变现有的数组,而仅仅会返回被连接数组的一个副本。没有传
参的情况下,复制当前数组并返回。
* slice(start,end) 数组截取 ,返回一个新数组,(包含)start到end(不包括)的元素。
* splice(index,howmany,index1….indexX) 数组更新,用于向数组中添加/删除项目,
然后返回被删除的项目(会改变原始数组)
* indexOf(item,start),从前向后查找,没有返回-1。
* lastIndexOf(item,start)从数组末尾向前查找。
7. Object构造函数的方法
代码语言:javascript复制* object.assign() 复制一个或多个对象来创建新对象。
* Object.create() 使用指定的原型对象和属性创建一个新对象。
* Object.defineProperty(object , propName , descriptor) 给对象添加一个属性并指定该属性的配置。
- object 对象 => 给谁加
- propName 属性名 => 要加的属性的名字 [类型:String]
- descriptor 属性描述 => 加的这个属性有什么样的特性[类型:Object]
* Object.defineProperties() 给对象添加多个属性并分别指定他们的配置。
* Object.entries() 返回给定对象自身的可枚举属性的[key,value]数组。
* Object.freeze() 冻结对象:其它代码不能删除或更改任何属性。
* Object.is() 比较两个值是否相同NaN相等。
* Object.isExtensible() 判断对象是否可扩展。
* Object.isFrozen() 判断对象是否已经被冻结。
* Object.isSealed() 判断对象是否已经密封。
* Object.keys() 返回一个包含所有给定对象自身可枚举属性名称的数组。
* Object.values() 返回给定对象自身可枚举值的数组。
8. js字符串方法
代码语言:javascript复制* Slice(start,end),字符串的截取。
* Substr(start,length),字符串的截取。
* Substring(start,end) ---以两个参数中较小的一个作为起始位置,较大的一个作为结束位置。
* Split(字符串/正则,length) 字符串转数组。
* CharAt(position)返回指定位置上的字符,如果position<0或大于length,返回空字符串。
* Concat(string) 连接两个或多个字符串,可以直接用 。
* IndexOf(searchString,position)。返回某个指定的字符串值在字符串中首次出现的位置。
* lastIndexOf(searchString,position)。可返回一个指定的字符串值最后出现的位置
* localeCompare(target) 比较字符串,返回比较结果数字。大于target返回正数,小于返回负数,相等返回0。
* match(regexp) 让字符串和一个正则进行匹配。
* replace(searchValue,replaceValue) 对字符串进行查找和替换,并返回一个新字符串。
9. for...of,for...in,forEach和map的区别
代码语言:javascript复制// for ... of ... 接收数组,具有(iterable)迭代器
// for(item of str) {
// console.log(item)
// }
代码语言:javascript复制// for ... in 循环数组时 循环的索引, 循环对象时, 循环的是key
// 速度最慢
// for(item in str) {
// console.log(item)
// }
代码语言:javascript复制// forEach 循环遍历数组
// arr.forEach(function(item, index, ar) {
// console.log("item" item);
// console.log("index" index)
// console.log(ar)
// })
代码语言:javascript复制// map 只循环数组,不会对空数组进行检测,不会改变原数组
// var m = arr.map(function(item, index, ar) {
// return item === "a" ? "dddd" : item;
// })
// console.log(m);
10. JS中的原型链的理解
这篇文章讲解的很详细:juejin.cn/post/693449…
代码语言:javascript复制* 原型:js中每个对象都有一个与它关联的对象,叫做原型对象。
* 构造函数:用new来调用,就是为了创建一个自定义类
* 原型链:js查找属性得过程中,在自有的属性中找不到就去原型对象中查找,原型对象中找不到,就去原型对象得原型中查找,
一层一层向上查找的机制,叫做原型链。
代码语言:javascript复制function Person() {
}
var person = new Person();
console.log(person.__proto__ == Person.prototype) // true
console.log(Person.prototype.constructor == Person) // true
console.log(Object.getPrototypeOf(person) === Person.prototype) // true
11. 闭包
代码语言:javascript复制* 内部变量访问外部变量的函数。
代码语言:javascript复制function foo(){
var a = 2;
function bar(){
console.log(a)
}
return bar()
}
var baz = foo()
baz() //2 这就是闭包的效果
代码语言:javascript复制* 优点:可以隔离作用域,不造成全局污染。
* 缺点:由于闭包长期驻留内存,则长期这样会导致内存泄露。
* 如何解决内存泄漏: 将暴露全外部的闭包变量置为null。
* 作用:
。解决循环变量泄漏为全局变量的问题
。ES6模块化之前防止变量冲突,通过闭包定义变量方法
。只能暴露一个接口去访问,私有化属性
。可以做累加器,函数内部return到外部 // 闭包实现一个count函数,每次调用 1
。实现柯里化。
. 将函数内部和函数外部连接起来
. 防止函数内部变量执行完成后被垃圾机制回收,使其一直保存在内存中
12. this的指向
代码语言:javascript复制* 在全局作用域内,this指向window
* 在函数中
。 箭头函数,this指向包裹箭头函数的第一个普通函数
。 普通函数,如果是直接调用则指向window,如果被obj调用则指向obj,如果通过new的方式创建实例,
则指向创建出来的实例化对象
。 使用call、apply、bind,this指向参数中创建的类实例
。 立即执行函数的话,this指向window
13. new的过程
代码语言:javascript复制* 创建一个新的空对象
* 新对象的__proto__指向构造函数的prototype
* 新对象赋值给构造函数内部的this上下文,并执行构造函数
* 如果构造函数没有显示返回对象,默认返回this
14. call、bind、apply、的区别
代码语言:javascript复制* call apply会立即执行,bind不会立即执行,因为她返回的是一个函数。
* 参数不同,apply的参数是数组,call和bind有多个参数。
代码语言:javascript复制//手写call
Function.prototype.myCall = function(context,...args){
if(!context && context === null){
context = window
}
let fn = Symbol()
context[fn] = this //此时this指向foo
console.log(' context[fn]: ', context[fn]);
return context[fn](...args)
}
let obj = {
a:1
}
function foo(){
console.log(this)
}
foo.myCall(obj,[1,2,3,4])
代码语言:javascript复制//手写apply
Function.prototype.myCall = function(context,args){
if(!context && context === null){
context = window
}
let fn = Symbol()
context[fn] = this //此时this指向foo
console.log(' context[fn]: ', context[fn]);
return context[fn](...args)
}
let obj = {
a:1
}
function foo(){
console.log(this)
}
foo.myCall(obj,[1,2,3,4])
15. 为什么0.1 0.2 !== 0.3
链接juejin.cn/post/689794…
代码语言:javascript复制* 这个问题有个博主总结的很全面,链接 https://juejin.cn/post/6897949585558208525
16. 事件循环机制
代码语言:javascript复制a.JS是单线程,防止代码阻塞,我们把代码 (任务) :同步和异步
b.同步代码给is引擎执行,异步代码交给宿主环境(浏览器或者node)
c.同步代码放入执行栈中,异步代码等待时机成熟送入任务队列排队
d.执行栈执行完毕,会去任务队列看是否有异步任务,有就送到执行栈执行,
反复循环查看执行,这个过程是事件循环(eventloop)
* 同步代码执行完,才会执行事件循环,事件循环包括宏任务和微任务。执行宏任务的前提是清空所有微任务
* 三部分:主线程、宏队列(macrotask)、微队列(microtask)
* 主线程:scripts标签里包含的内容
* 宏队列:setTimeout、setInterval、setImmediate、I/O、UI rendering
* 微队列:promise.then(promise本身是同步,promise.then和promise。catch是
异步)、process.nextTick,async/await(await后面的的代码)
* Event loop 顺序
。执行同步代码,这属于宏任务
。执行栈为空,查询是否有微任务需要执行
。执行所有微任务
。必要的话渲染 UI
。然后开始下一轮 Event loop,执行宏任务中的异步代码
17. 事件委托和事件代理
代码语言:javascript复制* 原因:添加到页面的事件处理程序数量将直接关系到页面的整体运行性能,因为需要不断与DOM进行交互,
容易引起重绘重排,事件委托可以减少操作dom的次数。
* 原理:利用冒泡思想。把原本需要绑定在子元素的响应事件委托给父元素,让父元素担当事件监听的职责。
18. promise
(总结不够全面,建议参考es6.ruanyifeng.com/#docs/promi…
代码语言:javascript复制* 异步编程的一种解决方案。
* promise的api
promise.then()成功时的回调
promise.catch()失败时的回调
promise.finally()无论promise处于什么状态都会执行
* 优点
。一旦状态(pending,resolved,rejected)改变,就不会再变。
。可以将异步的操作以同步的流程表达出来,避免回调地域。
* 缺点
。首先,无法取消Promise,一旦新建它就会立即执行,无法中途取消。
。其次,如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。
。最后,当处于pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。
* promise.all 和 promise.race的区别
。promise.all //并发处理多个异步任务,所有任务都执行完成才能得到结果。
。promise.race // 并发处理多个异步任务,只有有一个完成就能得到相应结果。
。promise.allSettled //所有promise的参数数组发生变更(成功或失败),返回的promise对象才会变更。
。promise.any //只要参数实例有一个变成fulfilled 状态,包装实例就会fulfilled 状态,如果所有参数实例都变成 rejected 状态,包装实例就会变成 rejected 状态。
19. 深拷贝和浅拷贝
代码语言:javascript复制* 浅拷贝:假设B复制了A,当A修改时,B也变了。
* 深拷贝:(基本数据类型基本都是深拷贝),深拷贝的对象与原来的对象是完全隔离的,一个对象的修改不会影响另外一个对象。
代码语言:javascript复制* 实现浅拷贝的方式
。 ...结构
。 object.assgin()
代码语言:javascript复制* 实现深拷贝的几种方式:
。json.parse(json.stringfy())
。Object.create() //实现的是深拷贝通过原型链的方式
代码语言:javascript复制* 函数库 lodash,提供 cloneDeep 实现
* 1.下载相关库**
* npm i --save lodash**
* 2.在相关文件中引入**
* import _ from "lodash"**
* 3.调用 _.cloneDeep() 方法实现深拷贝**
<script>
import _ from "lodash"
var objects = [{ 'a': 3 }, { 'b': 4 }];
var deep = _.cloneDeep(objects);
console.log(deep[0] === objects[0]); 输出false
</script>
代码语言:javascript复制function deepClone(source){
const targetObj = source.constructor === Array ? [] : {}
console.log('source.constructor: ', source.constructor);
for(let keys in source){
//判断是否有当前属性 keys = 基本数据类型,{},[]
if(source.hasOwnProperty(keys)){
if(source[keys] && typeof(source[keys] === 'Object') ){
//递归
targetObj[keys] = deepClone(source[keys])
}else {
targetObj[keys] = source[keys]
}
}
}
return targetObj
}
let obj = {a:1}
let newObj = deepClone(obj)
console.log(obj.a === newObj.a) //false
20. cookie、sessionStorage和localStorage的区别
代码语言:javascript复制* cookie的弊端
。数量和长度的限制
。安全性问题,如果cookie被拦截,就可以获取所有session信息
。有些状态不能一直保存在客户端,(为了防止表单重复提交,需在服务端加计数器)
21.js显示类型和隐式类型的转换
代码语言:javascript复制显示:
Number():Number(null):0;Number(undefined):NaN
parseInt(value, radix):
parseFloat(value):
隐式:
算术运算符:加( )、减(-)、乘(*)、除(/)、取模(%);
逻辑运算符:逻辑与(&&)、逻辑或(||)、逻辑非(!);
字符串运算符: 、 =。
'=='隐式转换
22.防抖和节流
解答来源blog.csdn.net/weixin_5973…
函数防抖:
单位时间内,频繁触发一个事件,以最后一次触发为准。
防抖的实现:
1.声明一个全局变量存储定时器ID。
2.每一次触发交互的时候,先清除上一次的定时器,然后开启本次定时器。
//输入框事件
let timeID = null
document.querySelector('input').oninput = function () {
//1先清除之前的定时器
clearTimeout(timeID)
//2.开启本次定时器
timeID = setTimeout(() => {
console.log(`发送ajax,搜索的内容是${this.value}`)
}, 500)
}
函数节流
单位时间内,频繁触发一个事件,只会触发一次。
使用场景:
ajax请求数据;图片懒加载
节流的实现
//声明一个全局变量存储触发时间
let lastTime = null
//页面滚动事件
window.onscroll = function () {
//1.每一次触发 先获取本次时间戳
let currentTime = Date.now()
//2.判断当前时间 与 上次触发时间 是否超过间隔
if (currentTime - lastTime >= 500) {
console.log(document.documentElement.scrollTop)//获取滚动距离
//3.存储本次的触发时间
lastTime = currentTime
}
}
23.async和await
代码语言:javascript复制* Async/await作用是用同步方式,执行异步操作。
* Await只能在async函数中使用,不然会报错
* Async函数返回的是一个状态为fulfilled的promise对象,有无值取决于有无return值。(没有则undefined),如果函数内部抛出异常或是返回reject,都会使函数的promise状态变为失败reject(只要不是异常,都不会走catch,哪怕是return false NaN undefined。内部含有未声明变量/函数,函数方法执行出错另当别论)。
* Await后面只有接promise才能实现排队操作。
24.数组去重的方法
1.new Set()实现数组去重
2.利用数组的indexof方法。新建一个空数组,遍历需要去重的数组,将数组元素存入新数组中,存放前判断数组中是否已经含有当前元素,没有则存入。此方法也无法对NaN
去重。
function removeDuplicate(arr) {
const newArr = []
arr.forEach(item => {
if (newArr.indexOf(item) === -1) {
newArr.push(item)
}
})
return newArr // 返回一个新数组
}
const result = removeDuplicate(arr)
console.log(result) // [ 1, 2, 'abc', true, false, undefined, NaN, NaN ]
3.利用双层循环加数组的splice方法。通过两层循环对数组元素进行逐一比较,然后通过splice方法来删除重复的元素。此方法对NaN是无法进行去重的
代码语言:javascript复制function removeDuplicate(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)
len-- // 减少循环次数提高性能
j-- // 保证j的值自加后不变
}
}
}
return arr
}
const result = removeDuplicate(arr)
console.log(result) // [ 1, 2, 'abc', true, false, undefined, NaN, NaN ]
代码语言:javascript复制水平有限,如有错误,敬请指正。