数据类型
基本数据类型:
Number、String、Boolean、Undefined、Null、Symbol、BigInt
复杂数据类型:
Function、Array、Object、RegExp、Date
判断数据类型的方式:
- typeof
- instanceof
- object.prototype.toString.call()
区别:
typeof :可以快速区分 基本数据类型 + function,不能区分object、array、null,都返回object
instanceof :可以区分 array、objcet、function,适合用于判断复杂类型,number,boolean,string
基本数据类型不能判断。
objcet.prototype.toString.call() :可以用于判断基本数据类型 和复杂数据类型,但是其中 自定义对
象判断有误。
js 中数组的操作方法有哪些:
- push 从数组后面添加一个元素
- pop 从数组最后面删除一个元素
- shift 从数组头部删除一个元素
- unshift 从数组头部添加一个元素
- slice 从已有数组中返回选定的元素,返回一个新数组
- splice 删除或者替换一个元素,会修改原来的数组
- concat 合并两个或多个数组,返回一个新数组
- join 将数组内部的元素 通过一个特定的符号拼接成字符串,默认是英文逗号
- sort 将一个数组排序,会改变原来的数组
- reverse 颠倒一个数组,会改变原来的数组
- forEach 循环一个数组,对每个元素执行一次特定的函数
- map 循环一个数组,会创建一个新的数组,结果是每个元素执行完函数的返回值
- filter 过滤一个数组,会创建一个新的数组,返回数组中符合条件的元素
- every 检查数组中的每一个元素,是否满足特定的函数测试
- some 检查数组中是否至少有一个元素满足特定的函数测试
- find 返回数组中满足特定条件的元素
- includes 检查数组中是否包含某一个元素
关于this 指向:
this 的指向不是固定不变的,它的指向是取决于 上下文环境,一般认为 this指向使用它时所在的对象。
箭头函数的this,默认指向定义它时,所处上下文的对象的this指向。
原型和原型链:
protoype
每一个javascript 对象(除了null)创建的时候,就会与之关联另外一个对象,这个对象就是我们所说的原型,
每个对象都会从原型中 ”继承“ 属性。
proto
这是每个对象(除null外)都会有的属性,叫做__proto__,这个属性会指向该对象的原型。
person.prototype === person.__proto__ // true
constructor
每个原型都有一个constructor属性,指向该关联的构造函数。
person=== person.prototype.constructor // true
当获取 person.constructor 时,其实 person中并没有 constructor 属性,当不能读取到constructor 属性时,
会从 person的原型也就是 person.prototype 中读取,正好原型中有该属性,所以:
person.constructor === person.prototype.constructor
当读取实例的属性时,如果找不到,就会查找与对象关联的原型中的属性,如果还查不到,就去找原型的原型,
一直找到最顶层为止。
原型链:
每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针。那么假如我们让原型对象等于另一个类型的实例,结果会怎样?显然,此时的原型对象将包含一个指向另一个原型的指针,相应地,另一个原型中也包含着一个指向另一个构造函数的指针。假如另一个原型又是另一个类型的实例,那么上述关系依然成立。如此层层递进,就构成了实例与原型的链条。
闭包
闭包就是能够读取其它函数内部变量的函数。例如在javascript中,只有函数内部的子函数才能读取局部变量
所以闭包可以理解成 **定义在一个函数内部的函数** 在本质上,闭包是讲函数内部和函数外部链接起来的桥梁
闭包的优缺点
优点:可以读取其他函数内部的变量,并将其一直保存在内存中
缺点:可能会造成内存泄漏或溢出
深拷贝和浅拷贝
浅拷贝
方法一:Object.assign
方法二:const obj = {...obj1}
深拷贝
方法一: Json.parse(Json.stringify())
缺点:
-obj里面有new Date(),深拷贝后,时间会变成字符串的形式。而不是时间对象
-obj里有RegExp、Error对象,则序列化的结果会变成空对象{};
-obj里有function,undefined,则序列化的结果会把function或 undefined丢失
-obj里有NaN、Infinity和-Infinity,则序列化的结果会变成null;
-如果obj里面有new出来的构造函数,深拷贝之后会丢失
方法二: 用for in 递归遍历
//实现深拷贝函数
function deepClone(data) {
const type = this.judgeType(data);
let obj = null;
if (type == 'array') {
obj = [];
for (let i = 0; i < data.length; i++) {
obj.push(this.deepClone(data[i]));
}
} else if (type == 'object') {
obj = {}
for (let key in data) {
if (data.hasOwnProperty(key)) {
obj[key] = this.deepClone(data[key]);
}
}
} else {
return data;
}
return obj;
}
function judgeType(obj) {
// tostring会返回对应不同的标签的构造函数
const toString = Object.prototype.toString;
const map = {
'[object Boolean]': 'boolean',
'[object Number]': 'number',
'[object String]': 'string',
'[object Function]': 'function',
'[object Array]': 'array',
'[object Date]': 'date',
'[object RegExp]': 'regExp',
'[object Undefined]': 'undefined',
'[object Null]': 'null',
'[object Object]': 'object',
};
if (obj instanceof Element) {
return 'element';
}
return map[toString.call(obj)];
}
防抖、节流
防抖
简单的理解方式就是:用户多次触发事件,在用户一直触发事件中,事件不会执行.
只有在用户停止触发事件一段时间之后再执行这个事件一次。
function debounce(fn, ms) {
let timeout = null
return function() {
clearTimeout(timeout)
timeout = setTimeout(() => {
fn.apply(this, arguments)
}, ms)
}
}
节流
简单的理解方式就是:用户多次触发事件,在用户一直触发事件过程中事件会每间隔一段时间执行一次会执行多次。
function throttle(fn, ms){
let flag = true
return function(){
if(!flag) return
flag = false
setTimeout(()=>{
fn.apply(this, arguments)
flag = true
}, ms)
}
}
同步和异步
最早JavaScript语言就是运行在浏览器端的语言,目的是为了实现页面上的动态交互。实现页面交互的核心就是DOM操作,这就决定了它必须使用单线程模型,否则就会出现很复杂的线程同步问题。
同步模式
指的是代码的任务依次执行,后一个任务必须等待前一个任务结束才能开始执行。程序的执行顺序和代码的编写顺序是完全一致的。
异步模式
该模式不会去等待这个任务的结束才开始下一个任务,都是开启过后就立即往后执行下一个任务,此时异步线程会单独执行异步任务,耗时函数的后续逻辑会通过回调函数的方式定义,执行过后会将回调放到消息队列中,js主线程执行完任务过后会依次执行消息队列中的任务。这里要强调,js是单线程的,浏览器不是单线程的,有一些API是有单独的线程去做的。
跨域
什么是跨域,不符合同源策略就是跨域,协议+域名+端口。
解决跨域的方法:
- jsonp 原理:script标签引入js文件不受跨域影响。不仅如此,带src属性的标签都不受同源策略的影响。正是基于这个特性,我们通过script标签的src属性加载资源,数据放在src属性指向的服务器上,使用json格式。
- document.domain + iframe跨域 这种情况只适用于主域相同,子域不同的应用场景。实现原理:两个页面都通过js设置document.domain为基础主域,就实现了同域,就可以互相操作资源了。
- 跨域资源共享(CORS) 只要在服务端设置Access-Control-Allow-Origin就可以实现跨域请求,若是cookie请求,前后端都需要设置。由于同源策略的限制,所读取的cookie为跨域请求接口所在域的cookie,并非当前页的cookie。
- nginx 反向代理
- WebSocket协议跨域 WebSocket协议是HTML5的新协议。能够实现浏览器与服务器全双工通信,同时允许跨域,是服务端推送技术的一种很好的实现。
常用的ES6新特性
ES6学习传送门:阮一峰入门教程
实际开发常用的ES6新特性:
- 变量声明:
const声明常量 在声明时必须被赋值,声明了值就不能被修改了, 但是如果声明的是对象,只要引用地址没变就行。
let声明变量 关键词声明的变量不具备变量提升特性。
- 模板字符串
基本的字符串格式化。将表达式嵌入字符串中进行拼接。用${}来界定;
ES6反引号(``)直接搞定;
- 箭头函数
ES6 中,箭头函数就是函数的一种简写形式()=>{}
不需要 function 关键字来创建函数
省略 return 关键字
继承当前上下文的 this 关键字
- 展开操作符 (…) 用法比较多,常用于解构。
- for…of 和 for…in
- Proxy 代理,vue3 的核心
- Promise 构造函数 用于解决 回调地狱
- async / await
call,apply,bind的区别
这三个方法都是为了改变this的指向,区别在于call和apply是在函数执行的时候改变this 的指向且apply的第二个参数是数组,而bind是在函数定义的时候就已经改变了this的指向。
setTimeOut和setTimeinterval 的区别
setTimeOut:函数在指定的毫秒数之后执行一次。
setTimeInterval:指定毫秒周期一直调用函数。
js 继承的方式
- 原型链继承
- 构造函数继承
- 组合继承
- class继承
ES5和ES6继承的区别
- ES5:先创建子类的实例对象,在通过父类的方法(apply)添加到this上。
- ES6:先创建父类的实例对象,通过子类的构造函数去修改this。
事件捕获和事件冒泡
- 事件捕获是指:从外到内寻找目标节点的过程。
- 事件冒泡是指:从目标节点向外进行事件发散。
事件委托
事件委托的原理:是通过子元素事件冒泡,在父元素中能通过自定义事件,捕获到子元素的事件发散,从而触发自定义方法,这样做的好处是,可以减少内存的消耗。
回流和重绘
- 回流:是指节点的几何信息发生修改,如宽高,位子。
- 重绘:是指节点的颜色,文字信息,阴影发生修改。
解析浏览器渲染机制
- 解析html,生成dom树。解析css,生成cssdom树。
- 合并dom树和cssdom树,生成渲染树
- 根据渲染树,回流的各个节点的几何信息。
- 根据渲染树,重绘各个节点的几何信息,得到绝对像素
- 将像素发给GPU展示在页面上。
10万条数据渲染
如果后端不提供分页接口的话,前端可以写一个虚拟列表。关键的api 是叫:requestAnimationFrame
数组去重
数组排序
js隐式转换
如果出错欢迎指出,该文章用于本人个人总结。 在这里祝大家新年快乐,都有一份好工作!