数据的类型有哪些
-
数据类型
- 基本类型:undefined、NULL 、Number、Boolean、String、Symbol(es6 新增)
- 复杂类型:Object
-
检测数据类型的方法
-
typeof
console.log(typeof 123); //number console.log(typeof 'abc'); //string console.log(typeof true); //boolean console.log(typeof undefined); //undefined console.log(typeof null); //object console.log(typeof [1,2,3]); //object console.log(typeof {a:1,b:2,c:3}); //object console.log(typeof function(){}); //function console.log(typeof Symbol()); //symbol console.log(typeof BigInt('1'));//bigint(BigInt是ES2020新增内置对象)
-
instanceof
function A(){} function B(){} var c = new A() console.log(c instaceof A) //true Object.getProtypeOf(c) === A.prototype console.log(c instaceof B) //false B.prototype 不在c 的原型链上
-
constructor
let num = 123; console.log(num.constructor); //ƒ Number() { [native code] } console.log(''.constructor); //ƒ String() { [native code] } console.log(true.constructor); //ƒ Boolean() { [native code] } console.log([].constructor); //ƒ Array() { [native code] } console.log({}.constructor); //ƒ Object() { [native code] } console.log(function(){}.constructor); //ƒ Function() { [native code] } console.log(new Date().constructor); //ƒ Date() { [native code] } console.log(new RegExp().constructor); //ƒ RegExp() { [native code] } console.log(Symbol().constructor); //ƒ Symbol() { [native code] }
-
Object.prototype.toString.call().slice(8,-1)
// 原理: const checkType = (val) => { let type = Object.prototype.toString.call(val).slice(8, -1); console.log(type) } checkType('')//String checkType(null)//Null checkType([])//Array checkType({})//Object checkType(function () { })//Function checkType(new RegExp())//RegExp checkType(new Date())//Date checkType(undefined)//Undefined checkType(1)//Number checkType(Symbol())//Symbol checkType(true)//Boolean
-
堆栈的理解
栈存放的是基本类型数据,简单数据类型,堆存放的是引用类型的数据,复杂数据类型,当let 或者 const 时,会遍历栈,没有会添加,否则报错。当然,const定义的常量,也就是指针,指向栈中的地址,改变这个指针是不被允许的,但改变对象中的值并不会改变它的指针,是可以的
object.is()
、"==
" 、 " ===
"的区别
- "
==
"先进行类型转换,再判断是否相等 - “
===
”,类型相等才相等 object.is()
与"===
"基本一致,不过有两点不同:(NAN和NAN相等);+0和-0不相等
对象的方法
Object.assign()
将一个或多个源对象复制到目标对象中,且源对象会随之改变
当复制的对象的属性值为简单数据类型时,为深拷贝,当复制的属性值为复杂数据类型时,为浅拷贝
-
Object.keys
const obj = { name: 'remons', age: 10 }; console.log(Object.keys(obj)) // ['name', 'age']
-
Object.values
const obj = { name: 'remons', age: 10 } console.log(Object.values(obj)) // ['remons', 10]
-
Object.entries
const obj = { name: 'remons', age: 10 }; console.log(Object.entries(obj)) // [ // ['name', 'age'], // ['remons', 10] // ]
函数提升和变量提升
es6之前没有块级作用域
-
变量提升
console.log(v1); //undefined var v1 = 100; function foo() { console.log(v1); //undefined var v1 = 200; console.log(v1); //200 } foo(); console.log(v1); //100 //其实类似于下面的 var v1; console.log(v1); v1 = 100; function foo() { var v1; console.log(v1); v1 = 200; console.log(v1); } foo(); console.log(v1);
-
函数提升
//函数声明式 function bar () {} // 会提升到该作用域的最顶端,而且高于变量提升。 //函数字面量式 var foo = function () {} //没有函数提升
ES6新特性
let 和 const
区别:let 和 var都是声明变量,let 有块级作用域,同一作用域下不允许重复声明,var 可以;const声明常量,不可改变
let 和const 不存在变量提升,var声明变量存在变量提升;
扩展运算符
...
将一个数组转为用逗号分隔的参数序列,可以用来深拷贝数组,合并数组等操作,也可以将字符串转成数组- 和
rest
正好相反
模板字符串
Symbol 类型
symbol(表示独一无二的值)
- 首字母大写
- symbol函数前不能使用new,否则会报错,原因在于symbol 是一个原始类型的值,不是对象。
Set和Map数据结构
Set
- 成员值唯一,不会重复
- 使用
new Set()
创建一个Set数据结构 - 不会发生数据类型转换
WeakSet
WeakSet
结构与Set
类似,也是不重复的值的集合
Map
- 解决JS对象只能用字符串作为键的限制
- get 和set 方法
WeakMap
WeakMap
结构与Map
结构类似,也是用于生成键值对的集合
WeakSet
与Set
的区别WeakSet
的成员只能是对象,而不能是其他类型的值WeakSet
中的对象都是弱引用
WeakMap
与Map
的区别WeakMap
只接受对象作为键名(null
除外),不接受其他类型的值作为键名WeakMap
的键名所指向的对象,不计入垃圾回收机制
箭头函数
- 值得注意,箭头函数的this来自父级
- 不能使用arguments对象,该对象在函数体内不存在。
- 不能作为构造函数,不可以使用new命令,否则会抛出一个错误
- 不可使用 yield
- 箭头函数一定是匿名函数
解构赋值
从数组和对象中提取值,对变量进行赋值
函数默认参数
柯里化函数
// 柯里化就是把一个需要传入多个参数的函数变成多个嵌套的只要传入一个参数的函数
// 举个简单的例子
function add(a) {
return function(b){
return a + b
}
}
add(1)(2) // 3
Class
类的数据类型就是函数,类本身就指向构造函数。
class和构造函数使用的区别:
必须使用new
命令,才能使用类
static
关键字不会被实例继承
constructor
方法是类的默认方法,默认返回实例对象(即this
),通过new
命令生成对象实例时,自动调用该方法。一个类必须有constructor
方法,如果没有显式定义,一个空的constructor
方法会被默认添加。
类里面共有的属性和方法必须使用this访问
class 和 构造函数?
super 关键字的理解
继承
function Father(name) {
//属性
this.name = name || 'Annie'
//实例方法
this.sleep = () => {
console.log('名字:'+this.name)
}
}
//原型方法
Father.prototype.eat = function (food) {
console.log(this.name + '正在吃:' + food);
}
-
原型链继承
let Son = function () {}; Son.prototype = new Father() // 创建实例时父类构造函数中传参数无效 Son.prototype.name = 'Remons' let child = new Son() child.sleep() // 名字:Remons
-
构造函数继承
function Son(name) { Father.call(this); this.name = name } let Child = new Son('Remons') Child.sleep() // 名字:Remons // 无法继承父级原型上的方法和属性 Child.eat is not a function
-
原型继承
function Son(name) { let this_ = new Father() this_.name = name; return this_ } let Child = new Son('Remons') Child.sleep() // 名字:Remons
-
Class继承
class Father{ constructor(x,y){ this.x=x; this.y=y } sum(){ return this.x + this.y } } class Son extends Father{ constructor(x,y){ super(x,y) // 必须先使用super,才能访问this,用来调用父类的属性和方法 } } let sum=new Son(5,6) console.log(sum.sum()) // 11
this的指向
-
如果一个函数中有this,这个函数被上一级的对象所调用,this指向上一级的对象
-
如果一个函数中有this,这个函数中包含多个对象,尽管这个函数是被最外层的对象所调用,this指向上一级的对象
-
this永远指向的是最后调用它的对象
-
箭头函数的this指向父级
-
如何改变this指向
-
apply
var a ={ name : "Cherry", fn : function (a,b) { console.log( a + b) } } var b = a.fn; b.apply(a,[1,2]) // 3
-
call
var a ={ name : "Cherry", fn : function (a,b) { console.log( a + b) } } var b = a.fn; b.call(a,1,2) // 3
-
bind
var a ={ name : "Cherry", fn : function (a,b) { console.log( a + b) } } var b = a.fn; b.bind(a,1,2)()
-
call 、apply、bind 区别:call和bind挨个传值,apply传一个数组,call和apply会直接执行这个函数,bind 返回绑定this之后的函数,调用时执行
-
Event-loop、事件队列、微任务和宏任务
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6QZ0fnnc-1661481662890)(https://remons.gitee.io/feq/summarize/assets/img/异步任务队列.png)]
Even-loop:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JpxOI9t9-1661481662896)(https://remons.gitee.io/feq/summarize/assets/img/事件循环.png)]
由于JS是单线程,同时也会有请求接口等耗时较长的业务,这种业务如果阻塞下面代码的执行,会造成很大的浪费,所以JS会将其归置到异步任务里面,遇到异步代码,会将异步任务交给指定模块处理,当这些异步任务达到触发条件时,例如:定时器结束、接口返回,会将这些回调压入到一个队列中,这个队列也叫做任务队列。当同步代码执行完后,会检查执行栈,当执行栈为空时,从任务队列中取出一个任务执行,此时继续执行上述过程,如此循环,称为事件循环,也叫Event loop
微任务 => 宏任务(微任务 => 宏任务)
浏览器常见的宏任务:定时器
浏览器常见的微任务:Promise.then catch finally
JS中的异步
-
promise
-
then()
Promise.resolve().then(function success (res) { throw new Error('error') }, function fail1 (e) { console.error('fail1: ', e) }) .catch(function fail2 (e) { console.error('fail2: ', e) }) //then 可以接收两个参数,第一个是处理成功的函数,第二个是处理错误的函数。.catch 是 .then 第二个参数的简便写法,但是.then 的第二个处理错误的函数捕获不了第一个处理成功的函数抛出的错误,而后续的 .catch 可以捕获之前的错误。
-
catch()
-
finally()
无论状态如何,都会执行,且不接受任何参数 -
all()
let p1 = new Promise(resolve => { resolve('a') }) let p2 = new Promise(resolve => { setTimeout(() => { resolve('b') }, 3000); }) let p3 = new Promise((resolve, reject) => { reject('error') }) function promiseFn(arr) { Promise.all(arr).then(res => { console.log(res); }).catch(err => { console.log(err) }) } promiseFn([p1, p2, p3]) //error promiseFn([p1, p2]) //3s后:['a','b'] //可以看出Promise.all接受一个由promise对象组成的数组,当所有状态都变为resolve时触发then(),参数为每个promise对象返回值组成的数组;有一个为reject的时候,都会触发catch回调 // 问题:all 返回的顺序问题?
-
allSettled()
let p1 = new Promise((resolve, reject) => { setTimeout(() => { resolve('aaa') }, 1000); }) let p2 = new Promise((resolve, reject) => { setTimeout(() => { reject('aaa') }, 2000); }) Promise.all([p1, p2]).then(res => { console.log(res , 'then all'); }).catch(res => { console.log(res, 'error all'); // error all }) Promise.allSettled([p1, p2]).then(res => { console.log(res,'then allSettled'); // 数组 }).catch(res => { console.log(res, 'error allSettled'); }) // 由此看出 allSettled 和 all 的区别: // allSettled : 无论每一个Promise状态如何,都会返回一个携带状态数组 // all 都成功返回成功,否则 catch
-
race()
let p1 = new Promise(resolve => { setTimeout(() => { resolve('a') }, 10); }) let p2 = new Promise(resolve => { resolve('b') }) let p3 = new Promise((resolve, reject) => { reject('err') }) function promiseFn(arr) { Promise.race(arr) .then(res => { console.log(res) } .catch(err => { console.log(err) }) } promiseFn([p1, p3, p2]) //err promiseFn([p1, p2, p3]) //b //race接受一个promise对象组成的数组,里面谁的状态先改变就先触发回调(无论状态如何)
-
-
async await
async function async1() { console.log('async1 start') await async2() console.log('async1 end') } async function async2() { console.log('async2') } console.log('script start') setTimeout(()=>{ console.log('setTimeout') }, 0) async1(); new Promise(resolve=> { console.log('promise1') resolve(); }).then(()=> { console.log('promise2') }) console.log('script end') //script start async1 start async2 promise1 script end async1 end promise2 setTimeout
本质上是Generator和Promise的语法糖,将异步的变为同步的写法,更加优雅;await 关键字只在async函数内有效,且阻塞代码执行
-
Generator/ yield
function* gen(x){ let y = yield x + 2; return y; } // yield 会阻断代码执行 // 直接调用 函数并不会返回计算结果,会返回一个指针 // gen(1).next() // 3
跨域
-
跨域的产生
浏览器的安全机制,阻止两个不同域进行交互,也就是同源策略,端口不同,域名不同,协议不同,三者都可造成跨域
-
如何解决跨域
- JSONP
- CORS
- 代理(原理)
- 后端配置
fetch、axios、ajax
-
ajax(四部曲)
//1)创建ajax对象 xhr = new XMLHttpRequest //2)规定请求地址 xhr.open(method,url,async) //3)等待服务器相应 xhr.onload //4)向服务器发送请求 xhr.send()
事件冒泡、事件捕获、事件委托
事件流分为三个阶段:事件捕获=>目标阶段=>事件冒泡
事件冒泡:从最具体的元素到最不具体的元素
事件捕获:从最不具体的元素到最具体的元素
当然,默认是事件冒泡
事件委托:将子元素的事件委托给父元素执行(当子元素过多时,委托为父元素执行,也可以提高性能,减少事件注册,节约内存)
深浅拷贝
-
问题由来:深浅拷贝针对于引用类型来说的,基本类型的值是存放在栈内存中,当复制一个基本类型的值是,会额外开辟一个地址,赋予相同的值;而引用类型将地址存放在栈中,值存储在堆内存中,两者相互关联,当复制时,复制的是栈内存中的地址,它们指向同一堆内存中的值,所以当改变值时,原来的值也会改变。
-
常用的深拷贝:
JSON.parse(JSON.stringify())
缺点:属性值的类型为undifind,正则或者函数时,无法正确拷贝
const deepCopy = (data) => {
function checkType(val) {
return Object.prototype.toString.call(val).slice(8, -1);
}
let BaseType = [
'Null',
'String',
'Boolean',
"Number",
'Undefined',
'Function'
]
if (BaseType.includes(checkType(data))) {
return data
}
if (checkType(data) === 'RegExp') return new RegExp(data);
if (checkType(data) === 'Date') return new Date(data);
let newData = checkType(data) === 'Array' ? [] : {}
for (let key in data) {
newData[key] = deepCopy(data[key])
}
return newData
}
- 浅拷贝
concat()
slice()
Object.assign()
闭包
闭包让你可以在一个内层函数中访问到其外层函数的作用域
函数嵌套函数
可以避免全局变量的污染
但它的变量常驻内存,不会被垃圾回收机制回收,滥用闭包有可能造成内存泄露
防抖节流
-
防抖(例:滚动条)
const debonce = (fn, delay) => { let time = null return () => { if (time) { clearTimeout(time) } time = setTimeout(fn, delay); } } const scroll = () => { let scrollTop = document.body.scrollTop || document.documentElement.scrollTop; console.log(scrollTop) } window.onscroll = debonce(scroll, 1000) // 每隔 1s 输出
-
节流:一段时期内重复触发事件只会执行一次
Object.defineProperty和Proxy的区别
let obj = { name: [] };
// proxy
obj = new Proxy(obj, {
get(target, prop) {
return target[prop]
},
set(target, prop, val) {
target[prop] = val
}
})
// Object.defineProperty
let newObj = JSON.parse(JSON.stringify(obj))
Object.defineProperty(obj, 'name', {
get() {
return newObj.name
},
set(val) {
newObj.name !== val && (obj.name = val)
}
})
可以看出,Vue3.0对性能是有很大提升的,在Object.defineProperty方法中,需要对每个属性进行递归监听,不但浪费性能,而且如果初始值中没有定义相关属性,就无法进行监听,这也就是Vue2.0中新增属性不会在视图发生变化,从而必须使用$set进行新增属性的原因
事件兼容
-
取消事件冒泡:
- IE下取消冒泡: ev.cancelBubble=true;
- 标准取消冒泡: ev.stopPropagation();
-
阻止浏览器默认行为:
- ev.preventDefault(); 标准浏览器阻止默认事件,DOM事件使用此方法取消默认事件。
- ev.returnValue = false; 非标准浏览器(IE8)阻止默认事件
- return false; 退出执行, 所有触发事件和动作都不会被执行. 可以用来替代 preventDefault
-
事件监听器:(可以绑定多个函数在一个对象上)
- target.addEventListener(“事件类型”, 函数, 是否捕获(布尔值))–标准浏览器事件监听
- target.removeEventListener()–标准浏览器取消监听
- target.attachEvent(“事件类型”,函数) --IE浏览器事件监听
- target.detachEvent() --IE浏览器取消监听
注意:移除事件监听的参数和添加事件监听的参数是一致的。
- 滚动条距离
- document.documentElement.scrollTop
- document.body.scrollTop
设计模式
高阶函数
接受或返回另一个函数称为高阶函数,常见的map,filter…
数组扁平化
flat()
DOM
-
文档对象模型:用来描绘一个层次化的节点树,允许开发人员获取、添加、移除、修改页面的某一部分元素
- 获取节点
getElementById('id') //:获取特定ID元素的节点 getElementsByTagName('p') //:获取相同元素的节点列表,返回类数组,使用[0]来获取 getElementsByClassName('class') //:获取相同类名的节点列表(IE8以下不支持),返回类数组 querySelecter('.class') //:通过选择器获取元素 querySelecterAll('.class') //:通过选择器获取元素,可获取多个元素 firstChild() llastChild() childNodes() previousSibling() nextSibling() document.documentElement //: 获取html标签元素 document.body //:获取html标签元素
-
节点操作
// 创建节点 createElement createAttribute createTextNode //插入节点 appendChild insertBefore //替换节点 repalceChild //删除节点 removeChild //复制节点 cloneNode
-
属性操作
//获取属性 getAttribute //设置属性 setAttribute //删除属性 removeAttribute
-
文本操作
insertData(offset,String) appendData(string) deleteData(offset,count) replaceData(offset,count,string) splitData(offset) substring(offset,count)
BOM
-
浏览器对象模型:用于描述与浏览器进行交互的方法和接口
document //对象 location //对象 href//属性:控制浏览器地址栏的内容 reload(true)//方法:刷新页面,如果参数为true,通过缓存刷新 navigator// 对象 userAgent//:用户代理信息,该属性可获取浏览器及操作系统信息 screen// 对象 window //对象(核心,既是通过js访问浏览器窗口的一个接口,又是ECMAScript规定的全局对象) //内置对象和方法: //常用事件: onload//:页面内容加载完成(DOM结构,图片) onscroll//: 拖动浏览器的滚动条触发此事件 onresize//: 浏览器窗口缩放所触发的事件 //可视区的宽高: document.documentElement.clientWidth document.documentElement.clientHeight