JavaScript总结(答)
1.var会变量提升,可以重复声明
let不会变量提升,不可以重复声明
const是一个只读的常量,一旦赋值不会改变
2.基本数据类型有:Number,String,Boolean,Null,undefined
es6新增:Symbol
es10新增:BigInt
数据类型的存储方式:
基本数据类型存在栈中
引用数据类型存在栈和堆中,引用的指针存在栈中,引用的值存在堆中
类型判断:typeof或instanceof或Object.prototype.toString.call()
基本数据类型可以用typeof判断:
console.log(typeof(null));//object console.log(typeof(undefined))//undefined console.log(typeof('dfd1'))//string console.log(typeof(1112))//number console.log(typeof([11]))//object console.log(typeof(function(){})) //function console.log(typeof(true))//boolean
object instanceof constructor console.log(6 instanceof Number); // false console.log(true instanceof Boolean); // false console.log('nanjiu' instanceof String); // false console.log([] instanceof Array); // true console.log(function(){} instanceof Function); // true console.log({} instanceof Object); // true
引用数据类型可以用instanceof判断
Object.prototype.toString.call()判断
console.log(Object.prototype.toString.call(new Set([1,2,3,4,3,3,3])));//[object Set] console.log(Object.prototype.toString.call("1111"));//[object String] console.log(Object.prototype.toString.call(1111));//[object Number] console.log(Object.prototype.toString.call(true));//[object Boolean] console.log(Object.prototype.toString.call(null));//[object Null] console.log(Object.prototype.toString.call(undefined));//[object Undefined] console.log(Object.prototype.toString.call([1,2]));//[object Array] console.log(Object.prototype.toString.call({'a':1}));//[object Object]
3.闭包:保存着其他函数内部变量的函数就是闭包
function use() { let id = 111; function idGenerate() { return id } return idGenerate } let useId = use(); console.log(useId());
4.遍历的方法:
for in 遍历自身和继承里所有可枚举的属性 可以中断循环
for of 能遍历带iterator 接口(Array,map,Set,string,arguments)的对象
forEach只能遍历数组,不能中断,没有返回值
map只能遍历数据,不能中断,返回值是修改后的数组
5.promise
promise类似一个容器,里面有3种状态,pedding,fulfilled,rejected,一个then方法是绑定fulfilled回调和rejected回调
优点:使用promise后代码可读性更高
6.字符串转数字:
隐式转换:+string // typeof(+"1") // number
Number(string),praseInt(string,radix),praseFloat(string)
1 Number('1') // 1 2 Number(true) // 1 3 Number('123s') // NaN 4 Number({}) //NaN
1 parseInt('2') //2 2 parseInt('2',10) // 2 3 parseInt('2',2) // NaN 4 parseInt('a123') // NaN 如果第一个字符不是数字或者符号就返回NaN 5 parseInt('123a') // 123
1 parseFloat('123a') 2 //123 3 parseFloat('123a.01') 4 //123 5 parseFloat('123.01') 6 //123.01 7 parseFloat('123.01.1') 8 //123.01
转成字符串:String(),toString()
String都能转换成字符串
toString()除了null和undefined都能转成字符串
转换成布尔:Boolean()
除了,空字符串,undefined,null,NaN,0,为false。其他情况都为true。
7.valueof()是返回对象的原始值,不会改变原对象
toString()是Object上原型链上的方法,是将对象转成字符串,如果是未定义的对象会将对象转成[object type]这里的type就是这个对象的类型
8.作用域是指程序中定义代码的区域,他决定了当前执行代码对变量的访问权限
作用域链是指当执行代码访问内部变量时,会从当前作用域进行寻找,找到时会立即返回,未找到时候会去上级作用域寻找知道在那一层作用域找到时会返回,这种作用域的嵌套机制叫作用域链
9.this的绑定规则有4种,默认绑定,隐式绑定,显示绑定,new绑定
new绑定,当函数使用new关键字的时候,这时this会指向新生成的对象
显示绑定,比较容易看出,一般是通过call,apply,bind进行的绑定,如果这里的参数传的是null或者undefined则会走默认绑定规则
隐式绑定,一般是一个函数在上下文中调用,这时候this指向调用的位置
默认绑定,就是上述情况之外,一般this会总默认绑定规则
箭头函数没有this指向,它的this指向上一层代码的this指向
10.显示原型:每个函数(类)天生自带一个属性prototype,属性值是一个对象,里面存储了当前类供实例
使用的属性和方法
构造函数:在浏览器默认给原型开辟的堆内存中有一个constructor属性:存储的是当前类本身(注意:自己开辟的堆内存中默认没有constructor属性,需要自己手动添加)
隐式原型:每个对象都有一个__proto__,这个属性指向当前实例所属类的原型
(不确定所属类,都指向Object.prototype)
原型链:当你试图获取一个对象的某个属性时,如果这个对象本身没有这个属性,那么它会去它的隐式原型__proto__(也就是它的构造函数的显示原型prototype)中查找。
11.DOM0级事件模型:这种模型,没有事件流,可以在网页上直接添加监听函数或者在js里添加监听函数,所有浏览器都支持。
IE事件模型:这个模型有两个阶段,事件处理阶段和事件冒泡阶段,事件处理阶段会首先执行目标元素绑定的监听事件。然后是事件冒泡阶段,冒泡指的是事件从目标元素冒泡到 document,依次检查经过的节点是否绑定了事件监听函数,如果有则执行。这种模型通过 attachEvent 来添加监听函数,可以添加多个监听函数,会按顺序依次执行。
DOM2级事件模型:在该事件模型中,一次事件共有三个过程,第一个过程是事件捕获阶段。捕获指的是事件从 document 一直向下传播到目标元素,依次检查经过的节点是否绑定了事件监听函数,如果有则执行。后面两个阶段和 IE 事件模型的两个阶段相同。这种事件模型,事件绑定的函数是 addEventListener,其中第三个参数可以指定事件是否在捕获阶段执行。
12.(1)把js放在html页面的最底部(2)script标签的defer属性:脚本会立即下载但延迟到整个页面加载完毕再执行。该属性对于内联脚本无作用 (即没有 「src」 属性的脚本)
(3)Async是在外部JS加载完成后,浏览器空闲时,Load事件触发前执行,标记为async的脚本并不保证按照指定他们的先后顺序执行,该属性对于内联脚本无作用 (即没有 「src」 属性的脚本)
(4)动态创建script标签,监听dom加载完毕再引入js文件
13.大概有4种方案:
commonjs方案:它通过 require 来引入模块,通过 module.exports 定义模块的输出接口。输出是一个值的拷贝,commonjs是运行时加载, CommonJS 模块就是对象;即在输入时是先加载整个模块,生成一个对象,然后再从这个对象上面读取方法
es6方案:它通过import和export来引入和导出模块。输出是一个值的引用,es6是编译时加载,ES6 模块不是对象,而是通过 export
命令显式指定输出的代码,import
时采用静态命令的形式。即在import
时可以指定加载某个输出值,而不是加载整个模块
amd方案:通过异步加载的方式来实现模块话,执行的内容在加载完成后的回调函数里执行,require.js 实现了 AMD 规范
cmd方案:通过异步加载的方式来实现模块话,不同点在于:AMD 推崇依赖前置、提前执行,CMD推崇依赖就近、延迟执行,sea.js实现了cmd方案
13.
组件化开发和模块化开发实际上是两种编程思想,也可以被认为是两种解决方案。组件化开发注重重用,可以用作实现基础架构的技术方案。举个例子:加入现在我需要实现一个几何图形库,包括图形的生成、修改、删除等基本功能。按照组件化的开发思想来说,所有的几何图形都有共同的方法,即新增、编辑和移除。我们这里就可以先定义一个几何图形的基本类型。一般说来,通常从点、线、面三个方面分别对几何图形进行基类的定义处理。至于不同的几何形状甚至形状的几何体则可以基于已经定义好的基类进行实现。这样做最大的好处是可以有效的提高代码的重用性,提高编码效率;另一方面也使得代码容易被了解,逻辑结构和层次关系清晰。组件化开发实际上可以看做是一个不断对实体进行抽象的过程。
模块化开发则是另一种编程思想,从出发点来说,两者还是存在较大的区别。模块化开发从实际的业务模块触发,根据整体业务的模块划分,分别对整个软件或系统的子模块进行一一实现,从而实现模块化开发。既然要对业务功能模块进行分别开发,那就要保证模块之间接口的一致性,使得最终模块继承的时候能够正确进行。现在所流行的微服务架构可以在一定程度上看作是模块化开发的典型代表(个人看法)。本人曾从事过一个微服务相关的项目,主要内容就是对各个不同的业务系统按照一定的粒度进行模块划分,进而再对各个模块进行实现,在实现的过程中要不同模块的技术人员要及时进行沟通,保证模块之间数据接口的一致性。
总体而言,组件化开发和模块化开发实际上有各自的适用领域。组件化开发更多被应用于技术底层的实际实现,而模块化开发则需要结合实际的业务功能。在项目的实际开发过程中,两种开发模式往往是并存的,结合使用的。
17.==或者===对于非基本数据类型,只能检查他们的引用是否相等
==会进行类型转换再比较
===不会进行类型转换,类型不同直接返回flase
Object.is()在===的基础上又加了 +0和-0的判断,NaN还是等于NaN
18.实际上call与apply的功能是相同的,只是两者的传参方式不一样,而bind传参方式与call相同,但它不会立即执行,而是返回这个改变了this指向的函数
19.本地存储:localStorage sessionStorage cookies离线缓存:application cache
前端数据库:indexedDB WebSQL
cookie是与其设定的域名绑定的,设置cookie后,他会与请求一起创建它的域。
cookie会有一些限制,不会超过300个cookie,每个cookie不会超过4096字节(4k)。每个域不会超过20个cookie,每个域不会超过81920字节
获取cookie:document.cookie 内容需要解码需要用decodeURIComponent(),编码需要encodeURIComponent()
设置cookie:
1 name=value; expires=expiration_time; path=domain_path; domain=domain_namel secure
2 //在所有这些参数中,只有cookie的名称和值是必须的
3
4 document.cookie = `${encodeURIComponent('name')}=${encodeURIComponent('haha')};domain=i.cnblogs.com;`
删除cookie:没有删除现有cookie的方法,可以通过修改cookie的过期时间来删除cookie
例子:document.cookie = 'uid=dkfywqkrhkwehf23;expires=' + new Date(0) + ';path=/;secure;
cookie工作原理:
当浏览器发起一个请求时,浏览器会自动检查是否有相应的cookie,如果有则将cookie添加到Request Headers
的Cookie
字段中
当服务器需要cookie时,在http请求的Response Headers字段中添加Set-Cookie字段,浏览器接收到之后会自动解析识别,将cookie种下。
localStorage和sessionStorage
每个域名给localStorage和sessionStorage分配的存储空间时5M
相同的协议,域名,端口
下就能读取或修改同一份localStorage数据,永久存储,除非手动删除。
Storage
是以字符串保存数据的,所以取的时候需要prase反序列化和stringfy()序列化1 setItem('key','value') // 存储数据
2
3 getItem('key') // 读取数据
4
5 remove('key') // 删除数据
6
7 clear() // 清空
每当localStorage和sessionStorage变化时,都会触发storage事件,这个事件有四个属性,例:
1 window.addEventListener('storage', e=>{
2 console.log(e.domain) //存储变化对应的域
3 console.log(e.key) //被设置或被删除的键
4 console.log(e.newValue) //键被设置的新值,若被删除则为null
5 console.log(e.oldValue) //键变化之前的值
6 })
indexedDB:用于替代WebSQL,是浏览器中存储结构话数据的一个方案
具体api:https://developer.mozilla.org/zh-CN/docs/Web/API/IndexedDB_API
向数组删除数据的方法:pop,shift,splice
给数组排序的方法:reserve,sort
数组迭代的方法:every(都true才true),some(有true就true),map(每个元素都执行一个函数,生成新数组),filter(每个元素都执行一个函数满足条件的会返回出去,生成新数组),forEach进行遍历,reduce(对数组中的每个元素执行一个由你提供的reducer函数(升序执行),将其结果汇总为单个返回值)
21..首先会提高性能:在JS代码执行之前,会进行语法检查和预编译,并且这一操作只进行一次。这么做就是为了提高性能,如果没有这一步,那么每次执行代码前都必须重新解析一遍该变量(函数),而这是没有必要的,因为变量(函数)的代码并不会改变,解析一遍就够了。
其次容错性更好:变量提升可以在一定程度上提高JS的容错性
22.首先解释下event loop,它表示的是js的时间循环,因为js是单线程的所以,如果一个函数如果处理时间很久的话,会导致下面的代码没法执行,所以会有两种任务,同步任务和异步任务。同步任务:按照顺序执行,上面没有执行完不会执行下面的程序
异步任务:不会按照顺序执行
还有一种解释是:js代码分成两种任务,宏任务和微任务
js代码主线程运行的是宏任务,Promise和process.nextTick()是微任务,setTimeOut()这种是宏任务,当宏任务运行完成后会检查是否有微任务,有微任务时会执行微任务,没有宏任务的时候会执行下一个宏任务
23.Set:Set类似于数组,区别在于Set里面的值不重复的,是惟一的,可以存值类型和对象,它也有iterable接口,所以可以用for of遍历,可以用于给数组或者字符串去重,它的判断逻辑类似于“===”,两个相同的对象是不等的
它包含属性有constructor构造函数,size元素数量
它包含几个方法,add()添加元素,delete()删除元素,clear()清空元素,has()是否包含元素, keys()返回键名的遍历器,values()返回键值的遍历器,entries()返回键值对的遍历器,forEach()
扩展运算符“...”里用的for of 所有也能在set里使用
Array.form()可以把set转换成数组
WeakSet:WeakSet和Set很像,他只能存对象,不能存值类型的值,WeakSet里面的值都是弱引用,外面的对象回收时里面的内容也会消失
它包含的方法:add()添加元素,delete()删除元素,clear()清空元素,has()是否包含元素,
Map:Map和Object类似,都是以键值对的形式存储数据,区别在于Object的键只能是字符串,Map可以存任何数据类型,字符串,对象,数组等
Map的键跟内存地址绑定,所以相同名字的不同位置的键不会重合,如果这个键是简单数据类型。只要这两个键严格相等,那么就会认为是一个键,例如,0和-0是一个键,true和字符串“true”不是一个键,null和undefined不是一个键,NaN和NaN是一个键
它包含的方法:set(key,value)设置元素,get(key)获取元素,has(key)是否包含元素,delete(key)删除元素,clear()清除集合, keys()返回键名的遍历器,values()返回键值的遍历器,entries()返回键值对的遍历器,forEach()遍历Map所有成员
和其他类型转换:
Map转数组:Map结构转数组的最快的方法就是扩展运算符“...”
数组转Map:将数组传入 Map 构造函数,就可以转为 Map,new Map()
Map转对象:当键是字符串时候可以直接转换,非字符串时会将键转成字符串然后进行转换
对象转Map:对象转为 Map 可以通过Object.entires() new Map()
Map转JSON:
JSON转Map:
WeakMap:WeakMap和Map相似,只不过WeakMap是弱引用,他只接受对象作为键名,null除外,其他简单类型会报错
它包含的方法:set(key,value)设置元素,get(key)获取元素,has(key)是否包含元素,delete(key)删除元素
它包含的属性:size元素的数量
24.iterator是一个迭代器的接口,凡是具有这个接口的数据集合都可以用for..of..遍历
es6中已经给一些集合赋上了Symbol.iterator属性:Array,Map,Set,String,TypedArray,函数的 arguments 对象,NodeList 对象
调用iterator的场合:
//结构赋值时 let set = new Set().add('a').add('b').add('c'); let [x,y] = set; // x='a'; y='b' let [first, ...rest] = set; // first='a'; rest=['b','c'];
//扩展运算符时 var str = 'hello'; [...str] // ['h','e','l','l','o'] let arr = ['b', 'c']; ['a', ...arr, 'd'] // ['a', 'b', 'c', 'd']
//yield*后面跟的是一个可遍历的结构
let generator = function* () { yield 1; yield* [2,3,4]; yield 5; }; var iterator = generator(); iterator.next() // { value: 1, done: false } iterator.next() // { value: 2, done: false } iterator.next() // { value: 3, done: false } iterator.next() // { value: 4, done: false } iterator.next() // { value: 5, done: false } iterator.next() // { value: undefined, done: true }
其他场合:凡是数组遍历都会调用例如:for..of..,Array.from(),Map(),Set(),WeakMap(),WeakSet(),Promise.all(),Promise.race()
25.Generator是es6为异步编程提供的一种解决方案,像是一个状态机,
例子:*号在哪都不报错,建议在function后面直接加,yield表示一个暂停标志
1 function* helloWorldGenerator() { 2 yield 'hello'; 3 yield 'world'; 4 return 'ending'; 5 } 6 7 var hw = helloWorldGenerator();
调用 Generator 函数,返回一个遍历器对象,代表 Generator 函数的内部指针。以后,每次调用遍历器对象的next
方法,就会返回一个有着value
和done
两个属性的对象。value
属性表示当前的内部状态的值,是yield
表达式后面那个表达式的值;done
属性是一个布尔值,表示是否遍历结束。
1 hw.next() 2 // { value: 'hello', done: false } 3 4 hw.next() 5 // { value: 'world', done: false } 6 7 hw.next() 8 // { value: 'ending', done: true } 9 10 hw.next() 11 // { value: undefined, done: true }
yield的使用注意:
yield表达式如果用在另一个表达式之中,必须放在圆括号里面。
1 function* demo() { 2 console.log('Hello' + yield); // SyntaxError 3 console.log('Hello' + yield 123); // SyntaxError 4 5 console.log('Hello' + (yield)); // OK 6 console.log('Hello' + (yield 123)); // OK 7 }
yield表达式用作函数参数或放在赋值表达式的右边,可以不加括号。
1 function* demo() { 2 foo(yield 'a', yield 'b'); // OK 3 let input = yield; // OK 4 }
26.async简单的描述就是Generatior的语法糖,将Generator的*号替换成async,将Generator的yield替换成await
优点:async返回Promise对象
27.
28.
29.正则表达式一般使用在字符串的方法中,search()和replace()
var str = "Visit Runoob!"; var n = str.search(/Runoob/i); // n = 6
正则表达式修饰符:
i:执行对大小写不敏感的匹配。
g:执行全局匹配(查找所有匹配而非在找到第一个匹配后停止)。
m:执行多行匹配
正则表达式元字符:
\d:查找数字
\s:查找空白字符
\b:匹配单词边界
\uxxxx:查找以十六进制数 xxxx 规定的 Unicode 字符。
量词:
n+:匹配任何包含至少一个 n 的字符串。
n*:匹配任何包含零个或多个 n 的字符串。
n?:匹配任何包含零个或一个 n 的字符串。
使用RegExp对象:test()和exec()
test() 方法用于检测一个字符串是否匹配某个模式,如果字符串中含有匹配的文本,则返回 true,否则返回 false。
exec() 方法用于检索字符串中的正则表达式的匹配。该函数返回一个数组,其中存放匹配的结果。如果未找到匹配,则返回值为 null。
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步