目录
1.JavaScript和Java是什么关系
2.==和===
3.var let const区别
4.new的执行过程
5.call bind apply的区别
6.Js数据类型及区别
7.如何判断一个对象为数组
8.JavaScript判断变量类型的方法
9.闭包
10.原型链
11.作用域与作用域链
12.为什么要使用模块化?实现模块化的方式?
13.ES Module和CommonJs区别
14.js如何实现继承
15.Promise
17.async和await
18.async/await 和 Promise 的关系
19.深拷贝与浅拷贝
20.防抖和节流
21.同步和异步的区别
22.es6有哪些新特性?
23.es7新特性
24.数组去重的方法
25.什么原因会造成内存泄露
26.浏览器和nodejs的事件循环
26.axios 是什么,其特点和常用语法
27.事件的触发过程是怎么样的?
29.普通函数和箭头函数的区别
30.JS内存泄露如何检测?场景有哪些?
Java和Javascript的关系就像雷锋和雷峰塔的关系,也像老婆和老婆饼的关系。Java和JavaScript是两门不同的编程语言。
相同之处:
不同之处:
JavaScript除了长得和Java比较像之外,语言风格相去甚远。JavaScript在设计时所参考的对象不包括Java,而包括了像Self和Scheme这样的语言。
==:先进行类型转换,再对值进行比较,判断值是否相等,相等则为true 不等就为false
===:全等,不做类型转换,判断类型和值是否都相等 相等则为true 不等为false
(1)var声明的变量会挂载在window上,而let和const声明的变量不会
(2)var声明变量存在变量提升,let和const不存在变量提升
(3)var 不具备块级作用域,会造成变量穿透,let,const具有块级作用域({},如for,if都是),不会造成穿透
(4)同一作用域下,var可以重复声明,而let和const不能声明同名变量
(5)let,const会造成暂时性死区(不能先使用再声明)
(6)const一旦声明必须赋值,不能写成【const a】,但是【const a=null】是合法的,但赋值后其值不能修改。如果声明的是复合类型数据,可以修改其属性
1.开辟内存空间;2.this指向内存空间;3.执行构造函数;4.返回this实例
call()、apply()、bind() 都是用来重定义 this 这个对象的
function.apply(thisArg, [argsArray])//argsArray 是一个可选的数组
function.call(thisArg, arg1, arg2, ...)//arg1、arg2、... 是将传递给函数的参数列表
function.bind(thisArg, arg1, arg2, ...)//arg1、arg2、... 是一些可选的参数,这些参数将在调用新函数时作为参数列表传递给原函数
相同点:第一个参数thisArg 表示函数要绑定的上下文,是用来改变函数this指向的,第二个参数是是用于传参
不同点:
call 和apply可以直接调用,bind不会立即调用函数,而是会返回一个新的函数。
call和bind可以传递多个参数,apply只能传递一个参数 (数组或者伪数组)
function test(a, b) {
console.log(a + b);
}
test.call({ name: "call" }, 1, 2); //3
test.apply({ name: "apply" }, [2, 3]); //5
const test1 = test.bind({ name: "bind" }, 4);
console.log(test1); //[Function: bound test]
test1(2); //6
值类型(基本类型):字符串(String)、数字(Number)、BigInt、布尔(Boolean)、空(Null)、未定义(Undefined)、Symbol。
BigInt:ES11引入的新的基本数据类型。BigInt数据类型的目的是比Number数据类型支持的范围更大的整数值,以任意精度表示整数。使用 BigInt解决了之前Number整数溢出的问题。
Symbol是ES6新出的一种数据类型,这种数据类型的特点就是没有重复的数据,可以作为object的key。
引用数据类型(对象类型):对象(Object)、数组(Array)、函数(Function),还有两个特殊的对象:正则(RegExp)和日期(Date)
区别:
最可靠的两种方式为Array.isArray(arr) ;Object.prototype.toString.call(arr)==''[object Array]'' 。另外可以使用arr instanceof Array 及借助构造函数和原型的方式判断。
注意:typepf操作符可以用来检测给定变量的数据类型,返回的值有以下几种:undefined,boolean,string,number,object,function。
使用typeof检测以下数据类型返回都为object: null,数组,json对象,Date,RegExp,因此不能用来判断是否为数组类型。
typeof对于原始类型来说,除了null都可以显示正确的类型;typeof对于对象来说,除了函数都会显示object,所以说typeof并不能准确判断变量到底是什么类型。
判断一个对象为数组的方法如下
arr=[1,2,3];
1)Array.isArray(arr) //true
2)arr instanceof Array //true 构造函数判断,instanceof操作符可以来表示实例是否属于某个构造函数创建的,内部机制是通过原型链来判断的
3)arr.constructor == Array //true 构造函数
4)Array.prototype.isPrototypeOf(arr)//true原型
5)Object.getPrototypeOf(arr) == Array.prototype //true
6)arr.__proto__ == Array.prototype //true
7)Object.prototype.toString.call(arr)==''[object Array]'' // true 根据对象的class属性来判断,class:每个对象的内部属性,记录创建对象时使用的类型名,一旦创建,无法修改。
以上方法中,最可靠的为第1种和第7种,因为对象的原型指向可以修改
var obj={}; obj.__proto__=Array.prototype;//修改原型指向
JavaScript有4种方法判断变量的类型,分别是typeof、instanceof、Object.prototype.toString.call()(对象原型链判断方法)、 constructor (用于引用数据类型)
typeof:常用于判断基本数据类型(typeof null返回object),对于引用数据类型除了function返回function,其余全部返回object。
instanceof:主要用于区分引用数据类型,检测方法是检测的类型在当前实例的原型链上,用其检测出来的结果都是true,不太适合用于简单数据类型的检测,检测过程繁琐且对于简单数据类型中的undefined, null, symbol检测不出来。
constructor:用于检测引用数据类型,检测方法是获取实例的构造函数判断和某个类是否相同,如果相同就说明该数据是符合那个数据类型的,这种方法不会把原型链上的其他类也加入进来,避免了原型链的干扰。
Object.prototype.toString.call(arr):检测最可靠,返回的是该数据类型的字符串。
简单说就是子函数可以使用父函数的局部变量。
定义:函数A内部有一个函数B,函数B可以访问到函数A中的变量,那么函数B就是闭包
优点:既可以长久的保存变量又不会造成全局污染,闭包使得函数内部的变量在函数执行完后, 仍然存活在内存中(延长了局部变量的生命周期),让函数外部可以操作(读写)到函数内部的数据(变量/函数)。
缺点:闭包的缺点就是常驻内存,会增大内存使用量,使用不当很容易造成内存泄露。
对象拥有__proto__隐式原型属性和constructor属性;
函数拥有prototype显式原型属性和__proto__隐式原型属性、constructor属性(JS中函数也是一种对象)。
__proto__隐式原型属性的作用就是当访问一个对象的属性时,如果该对象内部不存在这个属性,那么就会去它的__proto__属性所指向的那个对象(父对象)里找,一直找,直到__proto__属性的终点null,再往上找就相当于在null上取值,会报错。通过__proto__属性将对象连接起来的这条链路即我们所谓的原型链。
javaScript---原型链 __proto__、constructor、prototype_maidu_xbd的博客-CSDN博客
作用域:规定了如何查找变量,也就是确定当前执行代码对变量的访问权限。换句话说,作用域决定了代码区块中变量和其他资源的可见性。(全局作用域、函数作用域、块级作用域)
作用域链:从当前作用域开始一层层往上找某个变量,如果找到全局作用域还没找到,就放弃寻找 。这种层级关系就是作用域链。
模块化的优点:解决命名冲突;提供复用性;提高代码可维护性
实现模块化的方式:立即执行函数;AMD和CMD;common.js;ES Module
(1)立即执行函数:在早期,使用立即执行函数实现模块化是常见的手段,通过函数作用域解决了命名冲突、污染全局作用域的问题
(function(globalVariable){
globalVariable.test = function() {}
// ... 声明各种变量、函数都不会污染全局作用域
})(globalVariable)
(2)AMD 和 CMD(这两种方式目前很少见到了)
// AMD
define(['./a', './b'], function(a, b) {
// 加载模块完毕可以使用
a.do()
b.do()
})
// CMD
define(function(require, exports, module) {
// 加载模块
// 可以把 require 写在函数体的任意地方实现延迟加载
var a = require('./a')
a.doSomething()
})
(3)CommonJs:CommonJS最早是Node在使用,目前也仍然广泛使用,比如在Webpack中你就能见到它,当然目前在Node中的模块管理已经和CommonJS有一些区别了
// a.js
module.exports = {
a: 1
}
// or
exports.a = 1
// b.js
var module = require('./a.js')
module.a // -> log 1
(4)ES Module:ES Module是原生实现的模块化方案
// 引入模块 API
import XXX from './a.js'
import { XXX } from './a.js'
// 导出模块 API
export function a() {}
export default function() {}
javaScript---js如何实现继承_maidu_xbd的博客-CSDN博客
Promise是一种异步编程的解决方案。Promise是ES6提供的一个构造函数,可以使用Promise构造函数new一个实例,Promise构造函数接收一个函数作为参数,这个函数有两个参数,分别是两个函数 resolve和reject ,有三种状态,pending(进行中)、fulfilled(已完成)、rejected(已失败),只要Promise 的状态发生了变化,之后就再也不会改变,就像整个 Promise 实例凝固了
特性:①代码立即执行 ②状态的不可逆性 ③回调异步性④链式调用
Promise作用:Promise是异步微任务,解决了异步多层嵌套回调的问题,让代码的可读性更高,更容易维护。
promise使用:
(1)当Promise的状态由pending转变为fulfilled,执行resolve,传递异步操作的结果。
(2)当Promise的状态由pending转变为rejected时,执行reject,传递异步操作报出的错误。
(3)实例创建完成后,可以使用then方法分别指定成功或失败的回调函数,也可以使用catch捕获失败,then和catch最终返回的也是一个Promise,所以可以链式调用。
var p1 = new Promise(function (resolve, reject) {
var a = Math.random() * 10;
console.log("a=", a);
if (a >= 5) {
resolve("p1 resolve!:a>=5");
} else {
reject("p1 reject!:a<5");
}
});
p1.then((resolve) => {
console.log("resolve成功的结果:", resolve);
}).catch((err) => {
console.log("reject失败的原因:", err);
});
Promise的静态方法:
// 与Promise.all一样,参数是一组包含Promise实例的数组,返回值是一个新的Promise实例,其实例在调用then方法中的回调函数的参数仍是一个数组。不同之处在于无论参数实例resolve还是reject,Promise.allSettled都会执行then方法的第一个回调函数
// 由于单一 Promise 进入 rejected 状态便会立即让 Promise.all() 的结果进入 rejected 状态,以至于通过 Promise.all() 进入 rejected 状态时,其中的源 Promise 仍然可能处于 pending 状态,以至于无法获得所有 Promise 完成的时机。
// Promise.allSettled() 静态方法会等待所有源 Promise 进入 fulfilled 或者 rejected 状态,从而确保不会造成时序上的冲突
var p1 = new Promise(function (resolve, reject) {
setTimeout(function () {
resolve("p1 resolve!");
}, 2000);
});
var p2 = new Promise(function (resolve, reject) {
setTimeout(() => {
reject("p2 reject!");
}, 3000);
});
var p3 = Promise.resolve("p3 resolve!");
var p4 = Promise.reject("p4 reject!");
Promise.all([p1, p2, p3])
.then((res) => {
console.log("all成功返回:",res);
})
.catch((err) => {
console.log("all失败返回:",err);
});
// Promse.race 就是赛跑的意思,意思就是说,Promise.race([p1, p2, p3])里面哪个结果获得的快,就返回那个结果,不管结果本身是成功状态还是失败状态。
Promise.race([p1, p2, p3]).then((res) => {
console.log("race返回:",res);
});
Promise.any([ p1,p2, p4])
.then((res) => {
console.log("any成功返回:",res);
})
.catch((err) => {
console.log("any失败返回:",err);
});
// 使用async函数可以让代码简洁很多,不需要像Promise一样需要then,不需要写匿名函数处理Promise的resolve的值,也不需要定义多余的data变量,还避免了嵌套代码。
async function f() {
res = await Promise.allSettled([p1, p2, p3, p4]);
console.log("res==",typeof res,res);
const successPromise = res.filter((p) => p.status === "fulfilled");
console.log("成功的promise:", successPromise);
}
f();
17.async和await
async、await从字面上理解,async异步,await等待,async用于申明一个function是异步的,而await用于等待一个异步方法执行完成,await是同步写法,但本质还是异步调用。
async和await的优点
await后面一般接什么?await 命令后面是一个 Promise 对象,它也可以跟其他值,如字符串,布尔值,数值以及普通函数
使用await处理多个异步任务时怎么并行?
(1)先生成promise,然后再调用,就会并行执行await以后的语句;
(2)使用Promise.all来调用
js多个await语句并行调用方法 | 码农家园
async/await 是消灭异步回调的终极武器。但和 Promise 并不互斥,反而,两者相辅相成。执行 async 函数,返回的一定是 Promise 对象。await 相当于 Promise 的 then。try…catch 可捕获异常,代替了 Promise 的 catch。
浅拷贝 :只复制了该复杂数据存储在栈内存中的地址(引用),而不是在内存中重新开辟一个新的存储空间用于存储新的对象,新旧对象还是共用一个内存块.
浅拷贝是创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值。如果属性是引用类型,拷贝的就是内存地址,浅拷贝基本类型之前互不影响,引用类型其中一个对象改变了地址,就会影响另一个对象。相当于只拷贝了一层。
Object.assign(),展开运算符...,concat(),slice()也属于浅拷贝
let obj = { name: "maidu_xbd", action: { say: "hi" } };
let obj1 = Object.assign({}, obj); //把obj复制给{}中
obj1.name = "tom";
obj1.action.say = "你好";
console.log("obj", obj); //obj { name: 'maidu_xbd', action: { say: '你好' } }
console.log("obj1", obj1); //obj1 { name: 'tom', action: { say: '你好' } }
let obj2 = { ...obj };
obj2.name = "buding";
obj2.action.say = "hello";
console.log("obj", obj); //obj { name: 'maidu_xbd', action: { say: 'hello' } }
console.log("obj2", obj2); //obj2 { name: 'buding', action: { say: 'hello' } }
深拷贝:是新建一个一模一样的对象,该对象与原对象不共享内存,修改新对象也不会影响原对象
JSON.parse(JSON.stringify()), jQuery.extend()
let obj = { name: "maidu_xbd", action: { say: "hi" } };
let obj3 = JSON.parse(JSON.stringify(obj));
obj3.name = "buding";
obj3.action.say = "hello";
console.log("obj", obj); //obj { name: 'maidu_xbd', action: { say: 'hi' } }
console.log("obj3", obj3);//obj3 { name: 'buding', action: { say: 'hello' } }
防抖:
像鼠标缩放监听,输入框输入监听等高频发触发操作只需触发一次,需要等用户高频事件完了,再进行事件操作。
防抖实现:事件触发-》开启一个定时器-》如果再次触发,则清除上一次的,重新开一个定时器-》定时到,触发操作
防抖存在的一个问题,事件会一直等到用户完成操作后一段时间再操作,如果一直操作,会一直不触发。正确思路:第一次操作触发事件,后面的操作等这次触发事件完成后再触发下一次。
节流:
某个操作希望上一次的完成后再进行下一次,或者说希望间隔一定时间触发一次。如前端向后端发送一次请求,希望后端响应请求后才能再次发送请求。
节流实现:事件触发-》操作执行-》关闭阀门-》阀门关闭,后续触发无效-》一定时间后,阀门打开-》操作可再次触发
防抖和节流相同点和不同点
Lodash防抖节流的参数
_.debounce(func, [wait=0], [options=])
参数:
testDebounce: _.debounce(function() {
console.log("debounce");
}, 2000, {
leading: true,
trailing: false})
_.throttle(func, [wait=0], [options=])
参数:
同步是阻塞模式,异步是非阻塞模式
同步就是指一个进程在执行某个请求的时候,若该请求需要一段时间才能返回信息,那么这个进程将会一直等待下去,直到收到返回信息才继续执行下去;
异步是指进程不需要一直等下去,而是继续执行下面的操作,不管其他进程的状态。当有消息返回时系统会通知进程进行处理,这样可以提高执行的效率
includes(解决indexOf), **乘方运算,Object.values(obj),Object.entries(obj)
这9种数组去重方法,直到今天,我才彻底弄懂_数据去重方法_唯一的阿金的博客-CSDN博客
let arr = [1, 2, 1, 4, 5, 3, 2];
let newArr = [...new Set(arr)];
console.log(newArr);
(4)filter+indexOf(返回给定值找到的第一个索引位置)
let newArr2 = arr.filter((item, index, array) => {
return array.indexOf(item) === index;
});
console.log(newArr2);
let newArr3 = arr.reduce((total, item, index) => {
total.includes(item) ? total : [...total, item];
}, []);
console.log(newArr3);
浏览器的Event loop是在 HTML5 中定义的规范,而 node 中则由libuv库实现
macro-task(宏任务):包括整体代码script,setTimeout,setInterval,setImmediate,I/O ,UI rendering
micro-task(微任务):Promise,object.observe,process.nextTick,MutationObserver,postMessage
EventLoop过程:①先执行全局script任务,执行完主线程执行栈空;②检查微任务队列里的所有任务,依次执行,全部执行完成;③检查宏任务,读取第一个宏任务执行(执行宏任务时可能产生新的微任务),执行每个宏任务之前都要检查微任务队列是否有任务,如果有,优先执行微任务队列。微任务会全部执行,而宏任务会一个一个来执行。微任务执行完后,再去取一个宏任务来进行。
libuv库负责Node API的执行。负责各种回调函数的执行时间。它将不同的任务分配给不同的线程,形成一个Event Loop(事件循环),以异步的方式将任务的执行结果返回给V8引擎 。
nodejs的EventLoop机制的阶段操作顺序为timers、pending callbacks、idle,prepare、poll、check、close callbacks六个阶段。
Axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中。前端最流行的 ajax 请求库,react/vue 官方都推荐使用 axios 发 ajax 请求。
特点:基于 promise 的异步 ajax 请求库,支持promise所有的API
浏览器端/node 端都可以使用,浏览器中创建XMLHttpRequests
支持请求/响应拦截器,支持请求取消,可以转换请求数据和响应数据,并对响应回来的内容自动转换成 JSON类型的数据,批量发送多个请求,安全性更高,客户端支持防御 XSRF,就是让你的每个请求都带一个从cookie中拿到的key, 根据浏览器同源策略,假冒的网站是拿不到你cookie中得key的,这样,后台就可以轻松辨别出这个请求是否是用户在假冒网站上的误导输入,从而采取正确的策略。
常用语法:
分为三个阶段:
(1)window往事件触发处传播,遇到注册的捕获事件会触发
(2)传播到事件触发处时触发注册的事件
(3)从事件触发处往window传播,遇到注册的冒泡事件会触发
通常我们使用addEventListener注册事件
28.箭头函数
不适用箭头函数的场景
普通函数与箭头函数的区别是什么?_箭头函数和普通函数的区别_落叶--的悲伤的博客-CSDN博客
关于this指向:
普通函数:直接调用时,其指向为全局对象;方法调用时,其指向为调用该方法的对象;new 调用时,其指向为新创建的实例对象;call、apply、bind 调用时,其指向为三种方法的第一个参数。
内存泄漏:当一个对象不再被使用,但是由于某种原因,它的内存没有被释放,这就是内存泄漏。
有两种垃圾回收策略:标记清除、引用计数
V8 的垃圾回收机制也是基于标记清除算法,对其做了一些优化,
拓展:WeakMap、WeakMap的作用
内存泄露的场景(Vue为例)