JavaScript
数据类型
基本数据类型(按值访问):Undefined 、 Null 、 Boolean 、 Number 和 String
引用数据类型(按引用访问):object、Array、function
两种类型的区别是:存储位置不同;
改变原数组的方法
· pop():删除数组最后一个元素,并返回该元素
· push():在数组尾部添加元素,并返回更新后的数组长度
· shift():删除数组的第一个元素,并返回该元素
· unshift():在数组第一位添加元素,并返回更新后的数组长度
· sort():对数组排序(按字符ASCII进行排序),也可添加回调函数按照想要的规则排序
· reverse():数组反转
· splice(index, howmany, 新数据):返回被删除元素所组成的数组。
不改变原数组的方法
1、concat():用于连接两个或多个数组,仅会返回被连接数组的一个副本,arrayObject.concat(arrayX,arrayX,……,arrayX)
2、join():返回一个字符串。该字符串是通过把 arrayObject 的每个元素转换为字符串,然后把这些字符串连接起来,arrayObject.join(separator)
3、slice()::如果数组是空的arrayObject.slice(start,end)
数组扁平化
1.flat
2.递归处理
3.reduce
4.扩展运算符
es6新特性
1.变量声明const和let
2.模板对象与模板字符串
3.箭头函数
4.class类的支持
5.Symbol :表示独一无 二的值,Symbol最大的用途是用来定义对象的唯一属性名。
6.map和set
7.promise
8.async/await
删除数组里undefined元素
方法一:
循环数组找到undefined的值利用slice删除。
function removeEmptyArrayEle(arr){
for(var i = 0; i < arr.length; i++) {
if(arr[i] == "undefined") {
arr.splice(i,1);
i = i - 1; // i - 1 ,因为空元素在数组下标 2 位置,删除空之后,后面的元素要向前补位,
// 这样才能真正去掉空元素,觉得这句可以删掉的连续为空试试,然后思考其中逻辑
}
}
return arr;
};
方法二:
使用Boolean过滤数组中的所有假值。包括undefined,NaN,0,false
const compact = arr => arr.filter(Boolean)
compact([0, 1, false, 2, '', 3, 'a', 'e' * 23, NaN, 's', 34])
结果是[1, 2, 3, “a”, “s”, 34] 把0也删除了。这样有点删的多了啊。
结果是[1, 2, 3, “a”, “s”, 34] 把0也删除了。这样有点删的多了啊。
我们来改版一样,让他只删除undefined的值。
const compact = arr => arr.filter(res=>res!="undefined")
compact([0, 1, false, 2, '', 3, 'a', 'undefined', 's', 34])
结果是[0, 1, false, 2, “”, 3, “a”,“s”, 34]
简化一下上面的写法
arr=arr.filter(res=>{return res!=“undefined}”)
dom0级和dom2级的区别
DOM 0级事件处理:
优点:通过javascript制定事件处理程序的传统方式。就是将一个函数赋值给一个事件处理属性。第四代web浏览 器出现,至今为所有浏览器所支持。优点,简单且具有跨浏览器的优势。
缺点:一个事件处理程序只能对应一个处理函数。
DOM 2级事件处理:
优点:同时绑定几个事件,不会覆盖。
缺点:不具有跨浏览器优势
let,var,const的区别
1、var定义的变量,没有块的概念,可以跨块访问, 不能跨函数访问。
2、let定义的变量,只能在块作用域里访问,不能跨块访问,也不能跨函数访问。
3、const用来定义常量,使用时必须初始化(即必须赋值),只能在块作用域里访问,而且不能修改。
4、let 的用法类似于 var ,但是 let 只在所在的代码块内有效,所以我们一般使用 let 替代 var 。而 const 用来声明常量。
5、让我们先看一看这张表:
声明方式 | 变量提升 | 暂时性死区 | 重复声明 | 初始值 | 作用域 |
---|---|---|---|---|---|
var | 允许 | 不存在 | 允许 | 不需要 | 除块级 |
let | 不允许 | 存在 | 不允许 | 不需要 | 块级 |
const | 不允许 | 存在 | 不允许 | 需要 | 块级 |
Split和Join方法区别
Split方法是将字符串转成数组形式,Join方法是将数组转化成字符串形式
统计字符串中次数最多字母
function findMaxDuplicateChar(str) {
if(str.length == 1) {
return str;
}
var charObj = {};
for(var i = 0; i < str.length; i++) {
if(!charObj[str.charAt(i)]) {
charObj[str.charAt(i)] = 1;
} else {
charObj[str.charAt(i)] += 1;
}
}
var maxChar = '',
maxValue = 1;
for(var k in charObj) {
if(charObj[k] >= maxValue) {
maxChar = k;
maxValue = charObj[k];
}
}
return maxChar + ':' + maxValue;
}
数组去重
1.indexof
function unique(array){
var result = [];
for(var i = 0;i < array.length; i++){
if(result.indexOf(array[i]) == -1) {
result.push(array[i]);
}
}
return result;
}
2.es6中的set
Array.from(new Set(array))
map和foreach
总结:大体是
forEach()会修改原来的数组。而map()方法会得到一个新的数组并返回。
1、map速度比foreach快
2、map会返回一个新数组,不对原数组产生影响,foreach不会产生新数组,foreach返回undefined
3、map因为返回数组所以可以链式操作,foreach不能
4, map里可以用return ,而foreach里用return不起作用,foreach不能用break,会直接报错
那么接下来,我继续做分析,为什么更推荐用.map()
,而不是.forEach()
?
首先,.map()
要比.forEach()
执行速度更快。虽然我也说过执行速度不是我们需要考虑的主要因素,但是他们都比for()
要更好用,那肯定要选更优化的一个。
第二,.forEach()
的返回值并不是array。如果你想用函数式编程写个链式表达式来装个逼,.map()
将会是你不二的选择。
typeof和instanceof的区别:
instanceof :instanceof 是用来判断变量是否为某个对象的实 例,表达式为:A instanceof B,如果A是B的实例, 则返回true,否则返回false。instanceof 运算符用来 测试一个对象在其原型链中是否存在一个构造函数的 prototype 属性,但它不能检测Null和 undefined
typeof 对数组 [] 和对象 {} 的返回值都是Object,无法区分数组和对象,但是instanceof可以区分。
Object.prototype.toString.call() :是准确常用 的方式
箭头函数和普通函数
-
箭头函数的 this 永远指向其上下文的 this ,任何方法都改变不了其指向,如 call() , bind() , apply()
-
普通函数的this指向调用它的那个对象
箭头函数:
-
不需要用关键字function来定义函数;
-
一般情况下可以省略return;
-
在箭头函数内部,this并不会跟其他函数一样指向调用它的对象,而是继承上下文的this指向的对象。
call,apply和bind的区别
相同点是三者都可以把一个函数应用到其他对象上;都是用来改变函数的this对象的指向的;第一个参数都是this要指向的对象;都可以利用后续参数传参。不同点是call,apply是修改函数的作用域,即修改this指向,并且立即执行,而bind是返回了一个新的函数,不是立即执行;而call和apply的区别是call接受逗号分隔的无限多个参数列表,apply则是接受数组作为参数。
typescript
它是JavaScript的超集,最终会被编译为JavaScript代码,对js的一种扩展,对数据进行一个类型限制
promise原理
promise是什么?
1、主要用于异步计算
2、可以将异步操作队列化,按照期望的顺序执行,返回符合预期的结果
3、可以在对象之间传递和操作promise,帮助我们处理队列
原理:
1、解决回调地狱
比如我们经常可能需要异步请求一个数据之后作为下一个异步操作的入参
2、promise 可以实现在多个请求发送完成后 再得到或者处理某个结果
最简单的实现
基于上面的应用场景发现promise
可以有三种状态,分别是pending
、Fulfilled
、 Rejected
。
Pending Promise
对象实例创建时候的初始状态
Fulfilled
可以理解为成功的状态
Rejected
可以理解为失败的状态
- 构造一个
Promise
实例需要给Promise
构造函数传入一个函数。传入的函数需要有两个形参,两个形参都是function
类型的参数。分别是resolve
和reject
。 Promise
上还有then
方法,then
方法就是用来指定Promise
对象的状态改变时确定执行的操作,resolve
时执行第一个函数(onFulfilled),reject
时执行第二个函数(onRejected)- 当状态变为
resolve
时便不能再变为reject
,反之同理。
promise
Promise 是异步编程的一种解决方案,其实是一个构造函数,自己身上有all、reject、resolve这几个方法,原型上有then、catch等方法。
Promise的构造函数接收一个参数,是函数,并且传入两个参数:resolve,reject,分别表示异步操作执行成功后的回调函数和异步操作执行失败后的回调函数。
Promise 的常用 API 如下:
- Promise.resolve(value) : 类方法,该方法返回一个以 value 值解析后的 Promise 对象
- Promise.reject : 类方法,且与 resolve 唯一的不同是,返回的 promise 对象的状态为 rejected。
- Promise.prototype.then : 实例方法,为 Promise 注册回调函数,函数形式:fn(vlaue){},value 是上一个任务的返回结果,then 中的函数一定要 return 一个结果或者一个新的 Promise 对象,才可以让之后的then 回调接收。
- Promise.prototype.catch : 实例方法,捕获异常,函数形式:fn(err){}, err 是 catch 注册 之前的回调抛出的异常信息。
- Promise.race :类方法,多个 Promise 任务同时执行,返回最先执行结束的 Promise 任务的结果,不管这个 Promise 结果是成功还是失败。
- Promise.all : 类方法,多个 Promise 任务同时执行,如果全部成功执行,则以数组的方式返回所有 Promise 任务的执行结果。 如果有一个 Promise 任务 rejected,则只返回 rejected 任务的结果。
async/await
async用于申明一个function是异步的,而await可以认为是async wait的简写,等待一个异步方法执行完成。
async/await使得异步代码看起来像同步代码,明显节约了不少代码,大大地提高可读性
async 会将其后的函数(函数表达式或 Lambda)的返回值封装成一个 Promise 对象,而 await 会等待这个 Promise 完成,并将其 resolve 的结果返回出来。
es6中map和set的区别
Map 中存储的是 key-value 形式的键值对, 其中的 key 和 value 可以是任何类型的,
即对象也可以作为 key . 这比用对象来模拟的方式就灵活了很多
Map 可用的 方法
set(key, value): 向其中加入一个键值对
get(key): 若不存在 key 则返回 undefined
has(key):返回布尔值
delete(key): 删除成功则返回 true, 若key不存在或者删除失败会返回 false
clear(): 将全部元素清除
set
Set 和 Map 最大的区别是只有键 key 而没有 value,
所以一般用来判断某个元素(key)是否存在于其中.
JS的垃圾回收机制?
Js具有自动垃圾回收机制。垃圾收集器会按照固定的时间间隔周期性的执行。
JS中最常见的垃圾回收方式是***标记清除***。
工作原理:是当变量进入环境时,将这个变量标记为“进入环境”。当变量离开环境时,则将其标记为“离开环境”。标记“离开环境”的就回收内存。
工作流程:
-
垃圾回收器,在运行的时候会给存储在内存中的所有变量都加上标记。
-
去掉环境中的变量以及被环境中的变量引用的变量的标记。
-
再被加上标记的会被视为准备删除的变量。
-
垃圾回收器完成内存清除工作,销毁那些带标记的值并回收他们所占用的内存空间。
引用计数 方式
工作原理:跟踪记录每个值被引用的次数。
工作流程:
-
声明了一个变量并将一个引用类型的值赋值给这个变量,这个引用类型值的引用次数就是1。
-
同一个值又被赋值给另一个变量,这个引用类型值的引用次数加1.
-
当包含这个引用类型值的变量又被赋值成另一个值了,那么这个引用类型值的引用次数减1.
-
当引用次数变成0时,说明没办法访问这个值了。
-
当垃圾收集器下一次运行时,它就会释放引用次数是0的值所占的内存。
但是循环引用的时候就会释放不掉内存。循环引用就是对象A中包含另一个指向对象B的指针,B中也包含一个指向A的引用。
因为IE中的BOM、DOM的实现使用了COM,而COM对象使用的垃圾收集机制是引用计数策略。所以会存在循环引用的问题。
解决:手工断开js对象和DOM之间的链接。赋值为null。IE9把DOM和BOM转换成真正的JS对象了,所以避免了这个问题。
什么情况会引起内存泄漏?
虽然有垃圾回收机制但是我们编写代码操作不当还是会造成内存泄漏。
- 意外的全局变量引起的内存泄漏。
原因:全局变量,不会被回收。
解决:使用严格模式避免。
- 闭包引起的内存泄漏
原因:闭包可以维持函数内局部变量,使其得不到释放。
解决:将事件处理函数定义在外部,解除闭包,或者在定义事件处理函数的外部函数中,删除对dom的引用。
- 没有清理的DOM元素引用
原因:虽然别的地方删除了,但是对象中还存在对dom的引用
解决:手动删除。
- 被遗忘的定时器或者回调
原因:定时器中有dom的引用,即使dom删除了,但是定时器还在,所以内存中还是有这个dom。
解决:手动删除定时器和dom。
- 子元素存在引用引起的内存泄漏
原因:div中的ul li 得到这个div,会间接引用某个得到的li,那么此时因为div间接引用li,即使li被清 空,也还是在内存中,并且只要li不被删除,他的父元素都不会被删除。
解决:手动删除清空。
为什么要用事件委托
在JavaScript中,添加到页面上的事件处理程序数量将直接关系到页面的整体运行性能,因为需要不断的与dom节点进行交互,访问dom的次数越多,引起浏览器重绘与重排的次数也就越多,就会延长整个页面的交互就绪时间,这就是为什么性能优化的主要思想之一就是减少DOM操作的原因;每个函数都是一个对象,是对象就会占用内存,对象越多,内存占用率越大,100个li就要占用100个内存空间。如果要用事件委托,就会将所有的操作放到js程序里面,只对它的父级(如果只有一个父级)这一个对象进行操作,与dom的操作就只需要交互一次,这样就能大大的减少与dom的交互次数,提高性能;
事件委托使用场景:
大量标签绑定同一事件
动态渲染的标签
事件委托的原理
事件委托是利用事件的冒泡原理来实现的,何为事件冒泡呢?就是事件从最深的节点开始,然后逐步向上传播事件,举个例子:页面上有这么一个节点树,div>ul>li>a;比如给最里面的a加一个click点击事件,那么这个事件就会一层一层的往外执行,执行顺序a>li>ul>div,有这样一个机制,那么我们给最外面的div加点击事件,那么里面的ul,li,a做点击事件的时候,都会冒泡到最外层的div上,所以都会触发,这就是事件委托,委托它们父级代为执行事件。
JS中的异步运行机制如下:
(1)所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。
(2)主线程之外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。
(3)一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。
(4)主线程不断重复上面的第三步。
JS的执行机制:
1.首先判断JS是同步还是异步,同步就进入主进程,异步就进入event table
2.异步任务在event table中注册函数,当满足触发条件后,被推入event queue
3.同步任务进入主线程后一直执行,直到主线程空闲时,才会去event queue中查看是否有可执行的异步任务,如果有就推入主进程中
注:JavaScript是一门单线程语言
Event Loop是JavaScript的执行机制
http和https
HTTP 的URL 以http:// 开头,而HTTPS 的URL 以https:// 开头
HTTP 是不安全的,而 HTTPS 是安全的
HTTP 标准端口是80 ,而 HTTPS 的标准端口是443
在OSI 网络模型中,HTTP工作于应用层,而HTTPS 的安全传输机制工作在传输层
HTTP 无法加密,而HTTPS 对传输的数据进行加密
HTTP无需证书,而HTTPS 需要CA机构wosign的颁发的SSL证书
AMD和CMD的区别
AMD 是 RequireJS 在推广过程中对模块定义的规范化产出。
CMD 是 SeaJS 在推广过程中对模块定义的规范化产出。
对于依赖的模块,AMD 是提前执行,CMD 是延迟执行。不过 RequireJS 从 2.0 开始,也改成可以延迟执行(根据写法不同,处理方式不同)。CMD 推崇 as lazy as possible.
CMD 推崇依赖就近,AMD 推崇依赖前置。
AMD 的 API 默认是一个当多个用,CMD 的 API 严格区分,推崇职责单一。比如 AMD 里,require 分全局 require 和局部 require,都叫 require。CMD 里,没有全局 require,而是根据模块系统的完备性,提供 seajs.use 来实现模块系统的加载启动。CMD 里,每个 API 都简单纯粹。
移动端适配方案:
1)viewport(scale=1/dpr)
2)rem
3)flex
4)vm/vh :1.vw:1vw等于视口宽度的1%。2.vh:1vh等于视口高度的1%。
借助两个插件,将px进行转化为rem。
- lib-flexible 用于设置 rem 基准值。由淘宝手机前端开发团队编写的。
- postcss-pxtorem 是一款 postcss 插件,用于将单位转化为 rem。
一、lib-flexible
Install
npm install lib-flexible --save
二、在项目的入口main.js文件中引入lib-flexible
import 'lib-flexible'
第二部分:使用postcss-px2rem-exclude自动将css中的px转换成rem
一、安装postcss-px2rem-exclude
npm install postcss-px2rem-exclude --save
二、配置 postcss-px2rem-exclude
1.在项目的根目录下vue.config.js,在里面添加代码
module.exports = {
css: {
loaderOptions: {
postcss: {
plugins: [
require('postcss-px2rem-exclude')({ // 把px单位换算成rem单位
remUnit: 75,
exclude: /node_modules|folder_name/i, // 忽略node_modules目录下的文件
propList: ['*']
})
]
}
}
}
}
或者在postcss.config.js
module.exports = {
plugins: {
autoprefixer: {},
"postcss-px2rem-exclude": {
remUnit: 75,
exclude: /node_modules|folder_name/i
}
}
};
注意:
1.此方法只能将.vue文件style标签中的px转成rem,不能将script标签和元素style里面定义的px转成rem
2.如果在.vue文件style中的某一行代码不希望被转成rem,只要在后面写上注释 /* no*/就可以了
i++和++i
i++是先赋值,然后再自增;++i是先自增,后赋值。