js
Date
var date = new Date()
// 获取本月第一天
var firstDay = new Date(date.getFullYear(), date.getMonth(), 1);
// 获取本月最后一天
var lastDay = new Date(date.getFullYear(), date.getMonth() + 1, 0);
slice/splice
- slice
从已有的数组中返回选定的元素。
arrayObject.slice(start, end)
| 参数 | 类型 | 描述 | 是否必填 |
|---|---|---|---|
| start | Number | 规定从何处开始选取。如果是负数,那么它规定从数组尾部开始算起的位置。也就是说,-1 指最后一个元素,-2 指倒数第二个元素,以此类推 | 是 |
| end | Number | 规定从何处结束选取。该参数是数组片断结束处的数组下标。如果没有指定该参数,那么切分的数组包含从 start 到数组结束的所有元素。如果这个参数是负数,那么它规定的是从数组尾部开始算起的元素 | 否 |
- splice
从数组中添加/删除项目,然后返回被删除的项目, 会修改原数据
arrayObject.splice(index,howmany,item1,.....,itemX)| 参数 | 类型 | 描述 | 是否必填 | | ------------- | ------------- | ------------- | ------------- | | index | Number | 规定添加/删除项目的位置,使用负数可从数组结尾处规定位置 | 是 | | howmany | Number | 要删除的项目数量。如果设置为 0,则不会删除项目 | 是 | | item1, ..., itemX | < T >[] | 可选。向数组添加的新项目 | 否 |
this作用域
全局模式下,this 默认
window全局对象。
- 一般函数内,严格模式
use strict下 this 指向undefined, 非严格模式下,this 指向window全局对象。
function test(){
this.x = 1 // window
console.log
}
test() // 1
- 作为对象方法使用,this 指向当前对象。
var obj = {
x: 1,
y: this.y, // window
test: function () {
console.log(this.x) // obj
console.log(this.y) // obj
}
}
obj.test()
// 1
// undefined
*注
document.getElementById("div").onclick = function(){
console.log(this) // div DOM
};
- 作为构造函数内使用,this 指向 new 创建的实例对象。
function test(){
this.x = 1
}
var o = new test() // o实例
console.log(o.x) // 1
- 作为js内部函数的回掉函数,this 指向
window对象,如 (setTimeout、setInterval)。
var x = 1 // 提升到window
function () {
var x = 2
setTimeout(function () {
console.log(this.x) // window
}, 0)
}
// 1
- 作为匿名函数,this 通常指向window。
// var x = 1 [3]
function test () {
const x = 1 // [1] // var [2]
return function () {
console.log(this.x) // window
}
}
test()() // undefined[1] // undefined [2] // 1 [3]
- apply、call、bind 改变函数的调用对象(可以改变函数内部的this指向对象)。
const obj = { x: 1 }
function test () {
console.log(this.x)
}
test() // window -> undefined
test().apply(obj) // obj -> 1
setTimeout、Promise、async/await 执行顺序
事件循环中分为宏任务队列和微任务队列
- setTimeout
回调函数放到宏任务队列里,等到执行栈清空以后执行
// Q1
for (var i = 0; i < 3; i++) {
setTimeout(function () {
console.log(i)
}, 0)
}
// for 每次循环 -> 微任务 setTimeout回掉放在for循环结束后,变量i var定义,属于全局,for执行完 i = 3,在执行三次回掉
// 3 3 3
// Q2
for (let i = 0; i < 3; i++) {
setTimeout(function () {
console.log(i)
}, 0)
}
// 0 1 2 // 原理同Q1,但let有局部作用域,每次循环,i都属于不同作用域
// Q3
for (var i = 0; i < 3; i++) {
(function (i) {
setTimeout(function () {
console.log(i)
}, 0)
})(i)
}
// 0 1 2 使用闭包,实现局部作用域
- Promise
.then里的回调函数会放到相应宏任务的微任务队列里,等宏任务里面的同步代码(同属于微任务队列)执行完再执行 三种状态
-
pending 准备状态
-
fulfilled 成功状态(resolve)
-
rejected 失败状态(reject)
- async/await
await会立即执行表达式,然后把表达式后面的代码(同属于微任务队列)放到微任务队列里,让出执行栈让同步代码先执行
- 执行比较
async function test () {
async function a () {
console.log(5)
}
console.log(1)
// setTimeout
setTimeout(function () {
console.log(2)
}, 0)
// Promise
new Promise((resolve) => {
console.log(3)
}).then(() => {
console.log(4)
})
// await
await a()
console.log(6)
}
test() // 1 3 5 6 2
// 宏任务 test() // console.log(2)
// 微任务 console.log(1) console.log(3) console.log(5) console.log(6) console.log(4)
纯函数
函数的输入只取决于函数的输入(与函数内部自定义的变量声明无关),执行过程没有产生副作用
// Q1
const a = 1
const foo = (b) => a + b
foo(2) // => 3
// foo函数的输出依赖于外部变量a,不属于纯函数
// Q2
const a = 1
const b = 2
const foo = (a, b) => a + b
foo(a, b) // => 3
// foo的输出取决于参数a, b,并且没有其他副作用函数,为纯函数
// Q3
const obj = { x: 1 }
const foo = (obj) => {
return obj.x
}
foo(obj)
// foo的输出取决于参数a, b,并且没有其他副作用函数,为纯函数
// Q4
const obj = { x: 1 }
const foo = (obj) => {
obj.x = 2
return obj.x
}
foo(obj)
// foo对外部变量obj产生副作用,改变obj的x值,不是纯函数
// Q5
const obj = { x: 1 }
const foo = (obj) => {
x = localStorage.getItem('x')
return obj.x + x
}
foo(obj)
// foo的输出依赖于缓存变量x,不是纯函数
// Q6
const obj = { x: 1 }
const foo = (obj) => {
localStorage.setItem('x', obj.x)
return obj.x
}
foo(obj)
// foo对缓存产生副作用,不是纯函数
闭包
函数套函数,子函数使用父函数的参数或变量,并且子函数被外界所引用(没有释放)。此时父函数的参数或变量是不会被浏览器垃圾回收机制所回收,这个时候父级就形成了闭包环境。(一个函数返回一个内部函数,且内部函数使用函数内部的变量,使得变量对外部隐藏(对内部私有))
function fn(){
let a =10;
return function(){
a++;
console.log(a);
}
}
let f = fn();
闭包的特点:
- 函数套函数
- 子函数访问父函数的参数/变量
- 子函数被外界所使用着,不会被释放 应用场景:
- 存储父函数的参数或者变量
- 保护私有变量不受外界的干扰 闭包的缺点:
- 相对于普通函数要消耗内存,使用不当容易造成内存泄漏。
null/undefined
含义:
- null: 代表空对象指针。现在没有,将来可能会有
- undefined:空,未定义。现在没有,将来也不会有(Js中独有的数据类型) 区别:
// typeof 类型 不同
console.log(typeof null); //object
console.log(typeof undefined);//undefined
// Number 类型转换值 不同
console.log(Number(null));//0
console.log(Number(undefined));//NaN
js 检查数据类型
- typeof
string、number、function、object、boolean、undefined特殊- null -> object
- undefined -> undefined
- array -> object
- regex -> object
- instanceof 检查对象是否为某个类实例
let arr = [1,2,3];
console.log(arr instanceof Array);//true 检测arr是不是内置类Array的实例
- constructor 对象构造类
function fns (){}
let f = new fns;
console.log(f.constructor === fns);//true
console.log(f.constructor.name);//fns构造函数名称
- Object.prototype 对象原型
Cookie、SessionStorage、LocalStorage
- 使用
- cookie: document.cookie -> String
- sessionStorage -> 全局对象 sessionStorage
- localStorage -> 全局对象 localStorage
- 生命周期
- cookie:可以通过 expires 设置失效时间,不设置默认关闭浏览器即失效
- localStorage:除非手动清除,否则永久保存
- sessionStorage:仅在当前会话时候生效,关闭页面即失效
- 存储大小
- cookie: 4KB左右
- localStorage、sessionStorage: 可以保存5M的信息
- 易用性
- cookie: 需要程序员自己封装,源生的Cookie接口不友好
- localStorage、sessionStorage: 源生接口可以接受,亦可再次封装来对Object和Array有更好的支持
数组去重
- 原声js
- 二重循环
- indexOf 一次循环 (可以filter使用)
- 排序后相邻元素去重
- 对象属性去重 *
- es6
- 使用
Set去重
let es = [1, 2, 2, 3, 4, 1, 3, 5]; function unique6(arr){ return [...new Set(arr)] } console.log(unique6(es)); // [1, 2, 3, 4, 5] - 使用
变量提升
在某一作用域中,声明变量的语句会默认解析,在该作用域的最开始就已经声明了
内存泄漏
- 意外的全局变量引起的内存泄漏
- 原因:全局变量,不会被回收
- 解决:可以使用严格模式避免
- 闭包引起的内存泄漏
- 原因:闭包可以维持函数内局部变量,使其得不到释放
- 解决:将事件处理函数定义在外部,解除闭包,或者在定义事件处理函数的外部函数中,删除对dom的引用
- 没有清理的DOM元素引用
- 原因:虽然别的地方删除了,但是对象中还存在对dom的引用
- 解决:手动删除即可
- 忘记的定时器或者回调
- 原因:定时器中有dom的引用,即使dom删除了,但是定时器还在
- 解决:手动清除定时器和dom
JavaScript垃圾回收机制
Js具有自动垃圾回收机制。垃圾收集器会按照固定的时间间隔周期性的执行 垃圾回收机制主要有两种方法: 1、标记清除法 打开页面时,先把所有的变量打上标记,然后把被使用的变量清除标记。如果下次查询之前标记的没有被引用,此时就回收 2、引用计数法 变量每用一次就记录一次,一开始为0,下次引用+1,每少用一次就-1。当变为0时候 就清空回收
暂时性死区
let 和 const 声明的变量不存在变量提升,其作用域都是块级作用域,凡是在声明变量之前使用变量就会报错,所以,在代码块内,使用let命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”。
防抖和节流
- 防抖:短时间内连续触发的事件,在某个时间期限内事件函数只执行一次
// 防抖
function debounce(fn, wait) {
var timeout = null;
return function() {
if(timeout !== null) clearTimeout(timeout);
timeout = setTimeout(fn, wait);
}
}
// 处理函数
function handle() {
console.log(Math.random());
}
// 滚动事件
window.addEventListener('scroll', debounce(handle, 1000));
- 节流:如果短时间内大量触发同一事件,在函数执行一次之后,在指定的时间期限内不再工作,直至过了这段时间才重新生效
// 定时器
// 节流throttle代码(定时器):
var throttle = function(func, delay) {
var timer = null;
return function() {
var context = this;
var args = arguments;
if (!timer) {
timer = setTimeout(function() {
func.apply(context, args);
timer = null;
}, delay);
}
}
}
function handle() {
console.log(Math.random());
}
window.addEventListener('scroll', throttle(handle, 1000));
// 时间戳
var throttle = function(func, delay) {
var prev = Date.now();
return function() {
var context = this;
var args = arguments;
var now = Date.now();
if (now - prev >= delay) {
func.apply(context, args);
prev = Date.now();
}
}
}
function handle() {
console.log(Math.random());
}
window.addEventListener('scroll', throttle(handle, 1000));
// 时间戳 + 定时器 (第一次触发可以调用处理函数)
// 节流throttle代码(时间戳+定时器):
var throttle = function(func, delay) {
var timer = null;
var startTime = Date.now();
return function() {
var curTime = Date.now();
var remaining = delay - (curTime - startTime);
var context = this;
var args = arguments;
clearTimeout(timer);
if (remaining <= 0) {
func.apply(context, args);
startTime = Date.now();
} else {
timer = setTimeout(func, remaining);
}
}
}
function handle() {
console.log(Math.random());
}
window.addEventListener('scroll', throttle(handle, 1000));
JQuery
插入节点方法
- append:向每个匹配的元素内部末尾追加内容(父子关系)
- prepend:向每个匹配的元素内部开头追加内容(父子关系)
- after:向每个匹配的元素之后追加内容(兄弟关系)
- before:向每个匹配的元素之前追加内容(兄弟)
- insertAfter:将匹配元素插入指定节点之后(转移节点)
- insertBefore:将匹配元素插入指定节点之前(转移节点)
- empty: 清空匹配元素的所有内容,不包括匹配元素本身
- remove: 将匹配元素从DOM树中移除,包括子元素
插入类方法
- addClass():添加某个类
- removeClass():移除某个类
- hasClass():判断是否有某个类
- toggleClass(): 自动移除/添加
attr/prop
- attr操作自定义属性
- *prop更适合操作表单元素
// checkbox
$("#id").prop("checked", true) // JQ对象
$("#id").get(0).checked = true // DOM对象
Web攻击技术
- XSS(Cross-Site Scripting,跨站脚本攻击):指通过存在安全漏洞的Web网站注册用户的浏览器内运行非法的HTML标签或者JS进行的一种攻击。
- SQL注入攻击
- CSRF(跨站点请求伪造):指攻击者通过设置好的陷阱,强制对已完成的认证用户进行非预期的个人信息或设定信息等某些状态更新
- 点击拦截:通过覆盖图片或按钮,导致用户误操作。
JQuery 自定义插件
- 类级别插件
- $.extend({ plugin: function() })
;(function($){ $.extend({ ltrim: function(text){ return (text||"").replace(/^\s+g,""); }, rtrim: function(text){ return (text||"").replace(/\s+$/g,""); } }); })(jQuery); // 使用 JQuery.ltrim(' abcsd ') - $.extend.plugin = function()
;(function($){ $.extend.ltrim = function(text){ return (text||"").replace(/^\s+g,""); } $.extend.rtrim = function(text){ return (text||"").replace(/\s+$/g,""); } })(jQuery); // 使用 JQuery.ltrim(' abcsd ')
- $.extend({ plugin: function() })
- 对象级别插件
-
$.fn.extend({ plugin: function() })
;(function($){ $.fn.extend({ ltrim: function(text){ return (text||"").replace(/^\s+g,""); }, rtrim: function(text){ return (text||"").replace(/\s+$/g,""); } }); })(jQuery); // 使用 $('div').ltrim(' abcsd ') -
$.fn.extend.plugin = function()
;(function($){ $.fn.extend.ltrim = function(text){ return (text||"").replace(/^\s+g,""); } $.fn,extend.rtrim = function(text){ return (text||"").replace(/\s+$/g,""); } })(jQuery); // 使用 $('div').ltrim(' abcsd ')
-
jquery对象和DOM对象是如何转换的
- JQuery对象是一个包含了dom对象的数组 可以 [下标] 获取dom对象 / .get(下标)
- Dom对象放入$("")中转为jQuery对象
ES6
ES6新增的一些特性:
- let声明变量和const声明常量,两个都有块级作用域 ES5中是没有块级作用域的,并且var有变量提升,在let中,使用的变量一定要进行声明
- 箭头函数 ES6中的函数定义不再使用关键字function(),而是利用了()=>来进行定义
- 模板字符串 模板字符串是增强版的字符串,用反引号(`)标识,可以当作普通字符串使用,也可以用来定义多行字符串
- 解构赋值 ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值
- for of循环 for...of循环可以遍历数组、Set和Map结构、某些类似数组的对象、对象,以及字符串
- import、export导入导出 ES6标准中,Js原生支持模块(module)。将JS代码分割成不同功能的小块进行模块化,将不同功能的代码分别写在不同文件中,各模块只需导出公共接口部分,然后通过模块的导入的方式可以在其他地方使用
- set数据结构 Set数据结构,类似数组。所有的数据都是唯一的,没有重复的值。它本身是一个构造函数
- ... 展开运算符 可以将数组或对象里面的值展开;还可以将多个值收集为一个变量
- 修饰器 @ decorator是一个函数,用来修改类甚至于是方法的行为。修饰器本质就是编译时执行的函数
- class 类的继承 ES6中不再像ES5一样使用原型链实现继承,而是引入Class这个概念
- async、await 使用 async/await, 搭配promise,可以通过编写形似同步的代码来处理异步流程, 提高代码的简洁性和可读性 async 用于申明一个 function 是异步的,而 await 用于等待一个异步方法执行完成
- promise Promise是异步编程的一种解决方案,比传统的解决方案(回调函数和事件)更合理、强大
- Symbol Symbol是一种基本类型。Symbol 通过调用symbol函数产生,它接收一个可选的名字参数,该函数返回的symbol是唯一的
- Proxy代理 使用代理(Proxy)监听对象的操作,然后可以做一些相应事情
var、let、const
- var声明变量可以重复声明,而let不可以重复声明
- var是不受限于块级的,而let是受限于块级
- var会与window相映射(会挂一个属性),而let不与window相映射
- var可以在声明的上面访问变量,而let有暂存性死区,在声明的上面访问变量会报错
- const声明之后必须赋值,否则会报错
- const定义不可变的量,改变了就会报错
- const和let一样不会与window相映射、支持块级作用域、在声明的上面访问变量会报错
解构赋值
-
默认值
只有当一个 数组成员 严格等于(===)
undefined,默认值才会生效, 否则取解构值
默认值可以引用解构赋值的其他变量,但该变量必须已经声明。(否则报错)// Q1 let [f = true] = []; console.log(f);//true let [x,y = 'b'] = ['a']; console.log(x,y);//a b let [m, n = 'b'] = ['a', undefined]; // 严格等于undefined 取默认值 console.log(m,n);//a b let [x=1] = [undefined]; // 严格等于undefined 取默认值 console.log(x);//1 let [z=1] = [null]; // null !== undefined 取默认值 console.log(z);//null // Q2 let [x = 1,y = x] = []; let [a = 1,b = a] = [3]; let [m = 5,n = m] = [1, 2]; let [e = i,i = 1] = []; console.log(x,y); // 1 1 console.log(a,b); // 3 3 console.log(m,n); // 1 2 console.log(e,i); // ReferenceError: i is not defined报错 -
数组解构
先 从右往左 赋值,如果右边都为undefined,
此时就看左边的数组变量,数组变量执行的顺序为从左边到右边
如果数组变量的右边使用了数组变量的左边的变量,那么是能够赋值的。
在解构模式中,也可以直接省略元素,只为感兴趣的元素提供变量名
解构不成功变量的值会为undefined// Q1 let colors = [ "red", "green", "blue" ]; let [ firstColor, secondColor ] = colors; console.log(firstColor); // "red" console.log(secondColor); // "green" // Q2 let colors = [ "red", "green", "blue" ]; let [ , , thirdColor ] = colors; console.log(thirdColor); // "blue" // Q3 var [b=5,a] = [,b]; console.log(b); //5 console.log(a); //5 // undefined b使用默认值5 -
对象解构
对象字面量的语法形式是在一个赋值操作符左边放置一个对象字面量
解构的值,必须要为对象的key值,不然会为undefined
等号左边变量的次序,与右边同名属性次序不一致也可以取到值</br 与数组一样,解构也可以用于嵌套结构的对象// Q1 let {name,age,sex} = {name:'小明',age:'20',sex:'男'}; console.log(name,age,sex); //小明 20 男 // Q2 let {b,f} = {f:'我的',b:163}; console.log(f,b);//我的163 // Q3 let obj = { p: [ 'Hello', { y: 'World' } ] }; let { p, p: [x, { y }] } = obj;//这里p也作为了变量赋值 console.log(x) // "Hello" console.log(y) // "World" console.log(p) // ["Hello", {y: "World"}] -
混合解构
可以混合使用对象解构和数组解构来创建更多复杂的表达式
-
字符串解构(可迭代)
const [a, b, c, d, e] = 'hello'; console.log(a);//"h" console.log(b);//"e" console.log(c);//"l" console.log(d);//"l" console.log(e);//"o"
箭头函数
- *用了箭头函数,this就不是指向window,而是父级(指向是可变的)
- 不能够使用
arguments对象 - 不能用作构造函数,这就是说不能够使用new命令,否则会抛出一个错误
- 不可以使用yield命令,因此箭头函数不能用作
Generator函数
forEach、for in、for of三者区别
- forEach更多的用来遍历数组
- for in 一般常用来遍历对象或json
- for of数组对象都可以遍历,遍历对象需要通过和Object.keys() *for in循环出的是 key,for of循环出的是 value
Object/Array
Object.keys()、Object.values()、Object.entries() array.keys()、array.values()、array.entries()
ES2020 链判断运算符/Null判断符
- 链判断符
?.如果读取对象内部的某个属性,往往需要判断一下该对象是否存在
- obj?.prop // 对象属性
- obj?.[expr] // 同上
- function?.(...args) // 函数或对象方法的调用 如果有定义则调用否则返回undefined
const firstName = message?.body?.user?.firstName || 'default'; // 上面代码使用了 ?. 运算符,直接在链式调用的时候判断,左侧的对象是否为*null或undefined*。如果是的,就不再往下运算,而是返回 *undefined*。 - Null判断符
??读取对象属性的时候,如果某个属性的值是null或undefined,有时候需要为它们指定默认值, 与
||相似,只有运算符左侧的值为null或undefined时,才会返回右侧的值。可与链判断运算符?.配合使用,为null或undefined的值设置默认值。const headerText = response.settings.headerText ?? 'Hello, world!'; const animationDuration = response.settings.animationDuration ?? 300; const showSplashScreen = response.settings.showSplashScreen ?? true;
字符串新增
模版字符串
模板字符串(template string)是增强版的字符串,用反引号(`)标识。它可以当作普通字符串使用,也可以用来定义多行字符串,或者在字符串中嵌入变量。 模板字符串中嵌入变量,需要将变量名写在${}之中。
新增方法
- includes 返回布尔值,表示是否找到了参数字符串。
- startsWith 返回布尔值,表示参数字符串是否在原字符串的头部。
- endsWith 返回布尔值,表示参数字符串是否在原字符串的尾部。
- repeat
返回一个新字符串,表示将原字符串重复n次。 (n为0 或 正数)
'hello'.repeat(2) // "hellohello"'na'.repeat(0) // "" - padStart
字符串补全长度,用于头部补全
'x'.padStart(5, 'ab') // 'ababx''x'.padStart(4, 'ab') // 'abax' - padEnd
字符串补全长度,用于尾部补全
'x'.padEnd(5, 'ab') // 'xabab''x'.padEnd(4, 'ab') // 'xaba' - trimStart 消除字符串头部的空格
- trimEnd 消除尾部的空格
Set / Map
Set用于数据重组,Map用于数据储存
Set
类似于数组,但是成员的值都是唯一的,没有重复的值。
// 例一
const set = new Set([1, 2, 3, 4, 4]);
[...set]
// [1, 2, 3, 4]
// 例二
const items = new Set([1, 2, 3, 4, 5, 5, 5, 5]);
items.size // 5
// 去除数组的重复成员
[...new Set(array)]
属性和方法
- size 返回Set实例的成员总数
- constructor 构造函数,默认就是Set函数。
- add(value) 添加某个值,返回 Set 结构本身。
- delete(value) 删除某个值,返回一个布尔值,表示删除是否成功。
- has(value) 返回一个布尔值,表示该值是否为Set的成员。
- clear() 清除所有成员,没有返回值。
遍历操作
- Set.prototype.keys():返回键名的遍历器
- Set.prototype.values():返回键值的遍历器
- Set.prototype.entries():返回键值对的遍历器
- Set.prototype.forEach():使用回调函数遍历每个成员
Map
类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。
const map = new Map([
['name', '张三'],
['title', 'Author']
]);
属性和方法
- size 返回 Map 结构的成员总数。
- constructor 构造函数,默认就是Map函数。
- set(key, value) 设置键名key对应的键值为value,然后返回整个 Map 结构。
- get(key) 读取key对应的键值,如果找不到key,返回
undefined。 - has(key) 返回一个布尔值,表示某个键是否在当前 Map 对象之中。
- delete(key) 删除某个键,返回true。如果删除失败,返回false。
- clear() 清除所有成员,没有返回值。
遍历操作
- Map.prototype.keys():返回键名的遍历器。
- Map.prototype.values():返回键值的遍历器。
- Map.prototype.entries():返回所有成员的遍历器。
- Map.prototype.forEach():遍历 Map 的所有成员。