对象类型和原始类型的不同之处?函数参数是对象会发生什么问题?
在 JS 中,除了原始类型那么其他的都是对象类型了。
对象类型和原始类型不同的是:
原始类型存储的是值,对象类型存储的是地址(指针)。
当你创建了一个对象类型的时候,计算机会在内存中帮我们开辟一个空间来存放值,
但是我们需要找到这个空间,这个空间会拥有一个地址(指针)。
一、字符串方法
1 |
|
js 数据类型
基本数据类型:
Number,String,Boolean,null,undefined,symbol,bigint
引用数据类型:
object function
基本数据类型是直接存储在栈中的简单数据段,占据空间小、大小固定,属于被频繁使用的数据。栈是存储基 本类型值和执行代码的空间。
引用数据类型是存储在堆内存中,占据空间大、大小不固定。引用数据类型在栈中存储了指针,该指针指向堆 中该实体的起始地址,当解释器寻找引用值时,会检索其在栈中的地址,取得地址后从堆中获得实体。
基本数据类型和引用数据类型的区别:
- 堆比栈空间大,栈比堆运行速度快。
- 堆内存是无序存储,可以根据引用直接获取。
- 基础数据类型比较稳定,而且相对来说占用的内存小。
- 引用数据类型大小是动态的,而且是无限的。
Object.assign():合并对象 Object.assign(target, …sources)
- Object.assign 会将 source 里面的可枚举属性复制到 target,如果和 target 的已有属性重名,则会覆盖。
- 后续的 source 会覆盖前面的 source 的同名属性。
- Object.assign 复制的是属性值,如果属性值是一个引用类型,那么复制的其实是引用地址,就会存在引用共享的问题。
Constructor:
创建的每个函数都有一个 prototype(原型)对象,这个属性是一个指针,指向一个对象。在默认情况下,所有原型对象都会自动获得一个 constructor(构造函数)属性,这个属性是一个指向 prototype 属性所在函数的指针。当调用构造函数创建一个新实例后,该实例的内部将包含一个指针(继承自构造函数的 prototype),指向构造函数的原型对象。注意当将构造函数的 prototype 设置为等于一个以对象字面量形式创建的新对象时,constructor 属性不再指向该构造函数。
map 和 forEach 区别
相同点:
- 都是循环遍历数组中的每一项
- 每次执行匿名函数都支持三个参数,参数分别为 item(当前每一项),index(索引值),arr(原数组)
- 匿名函数中的 this 都是指向 window
- 只能遍历数组
不同点:
- map()会分配内存空间存储新数组并返回,forEach()不会返回数据。
- forEach()允许 callback 更改原始数组的元素。map()返回新的数组。
for…of:
es6 新增的一个遍历方法,但只限于迭代器(iterator), 所以普通的对象用 for..of 遍历
是会报错的。包括 Array, Map, Set, String, TypedArray, arguments 对象
indexOf str.indexOf(searchValue [, fromIndex]) searchValue:要被查找的字符串值。
查找的字符串 searchValue 的第一次出现的索引,如果没有找到,则返回-1。
若被查找的字符串 searchValue 是一个空字符串,则返回 fromIndex。如果 fromIndex 值为空,或者 fromIndex 值小于被查找的字符串的长度,返回值和以下的 fromIndex 值一样。
如果 fromIndex 值大于等于字符串的长度,将会直接返回字符串的长度(str.length)
严格区分大小写
在使用 indexOf 检索数组时,用‘===’去匹配,意味着会检查数据类型
Iframe 的优缺点?
优点:
- iframe 能够原封不动的把嵌入的网页展现出来。
- 如果有多个网页引用 iframe,那么你只需要修改 iframe 的内容,就可以实现调用的每一个页面内容的更改,方便快捷。
- 网页如果为了统一风格,头部和版本都是一样的,就可以写成一个页面,用 iframe 来嵌套,可以增加代码的可重用。
- 如果遇到加载缓慢的第三方内容如图标和广告,这些问题可以由 iframe 来解决。
缺点:
- iframe 会阻塞主页面的 onload 事件;
- iframe 和主页面共享连接池,而浏览器对相同域的连接有限制,所以会影响页面的并行加载。会产生很多页面,不容易管理。
- iframe 框架结构有时会让人感到迷惑,如果框架个数多的话,可能会出现上下、左右滚动条,会分散访问者的注意力,用户体验度差。
- 代码复杂,无法被一些搜索引擎索引到,这一点很关键,现在的搜索引擎爬虫还不能很好的处理 iframe 中的内容,所以使用 iframe 会不利于搜索引擎优化(SEO)。
- 很多的移动设备无法完全显示框架,设备兼容性差。
- iframe 框架页面会增加服务器的 http 请求,对于大型网站是不可取的。
作用域:
作用域就是一个独立的地盘,让变量不会外泄、暴露出去。也就是说作用域最大的用处就是隔离变量,不同作用域下同名变量不会有冲突。
ES6 之前 JavaScript 没有块级作用域,只有全局作用域和函数作用域。ES6 的到来,为我们提供了‘块级作用域’,可通过新增命令 let 和 const 来体现。
js 的 arguments 相关问题
在 js 中,我们在调用有参数的函数时,当往这个调用的有参函数传参时,js 会把所传的参数全部存到一个叫 arguments 的对象里面。它是一个类数组数据由来
Javascrip 中每个函数都会有一个 Arguments 对象实例 arguments,引用着函数的实参。它是寄生在 js 函数当中的,不能显式创建,arguments 对象只有函数开始时才可用
instanceof 原理:判断实例属于什么类型
1 |
|
数组去重:
1、es6 Set 方法
1 |
|
2、es5 利用 for 循环嵌套,然后 splice 去重
1 |
|
3、indexOf
1 |
|
4、sort
1 |
|
5、includes
1 |
|
6、hasOwnProperty:利用 hasOwnProperty 判断是否存在对象属性
1 |
|
7、filter
1 |
|
8、递归去重
1 |
|
9、Map 数据结构去重
创建一个空 Map 数据结构,遍历需要去重的数组,把数组的每一个元素作为 key 存到 Map 中。由于 Map 中不会出现相同的 key 值,所以最终得到的就是去重后的结果。
1 |
|
10、利用 reduce+includes
1 |
|
null 和 undefined 的区别
undefined 表示一个变量自然的、最原始的状态值,而 null 则表示一个变量被人为的设置为空对象,而不是原始状态。所以,在实际使用过程中,为了保证变量所代表的语义,不要对一个变量显式的赋值 undefined,当需要释放一个对象时,直接赋值为 null 即可。
undefined:
- 声明了一个变量,但没有赋值
- 访问对象上不存在的属性
- 函数定义了形参,但没有传递实参
- 使用 void 对表达式求值
null:空值
表示 一个对象被人为的重置为空对象,而非一个变量最原始的状态 。 在内存里的表示就是,栈中的变量没有指向堆中的内存对象
typeof null 为什么是 object?
null 有属于自己的类型 Null,而不属于 Object 类型,typeof 之所以会判定为 Object 类型,是因为 JavaScript 数据类型在底层都是以二进制的形式表示的,二进制的前三位为 0 会被 typeof 判断为对象类型,而 null 的二进制位恰好都是 0 ,因此,null 被误判断为 Object 类型。
类数组转换为数组的方法:
- 使用 Array.from()
- 使用 Array.prototype.slice.call()
- 使用 Array.prototype.forEach()进行属性遍历并组成新的数组
// 转换后的数组长度由 length 属性决定。索引不连续时转换结果是连续的,会自动补位。
数组转树
1 |
|
Set、Map、WeakSet 和 WeakMap 的区别
Set
- 成员不能重复;
- 只有键值,没有键名,有点类似数组;
- 可以遍历,方法有 add、delete、has
WeakSet
- 成员都是对象(引用);
- 成员都是弱引用,随时可以消失(不计入垃圾回收机制)。可以用来保存 DOM 节点,不容易造成内存泄露;
- 不能遍历,方法有 add、delete、has;
Map
- 本质上是键值对的集合,类似集合;
- 可以遍历,方法很多,可以跟各种数据格式转换;
WeakMap
- 只接收对象为键名(null 除外),不接受其他类型的值作为键名;
- 键名指向的对象,不计入垃圾回收机制;
- 不能遍历,方法同 get、set、has、delete;
造成内存泄漏的情况
- 意外的全局变量;
- 闭包;
- 未被清空的定时器;
- 未被销毁的事件监听;
- DOM 引用;
数据类型的判断方法:
- 使用 typeof 检测当需要判断变量是否是 number, string, boolean, function, undefined 等类型时,可以使用 typeof 进行判断。
- 使用 instanceof 检测 instanceof 运算符与 typeof 运算符相似,用于识别正在处理的对象的类型。与 typeof 方法不同的是,instanceof 方法要求开发者明确地确认对象为某特定类型。
- 使用 constructor 检测 constructor 本来是原型对象上的属性,指向构造函数。但是根据实例对象寻找属性的顺序,若实例对象上没有实例属性或方法时,就去原型链上寻找,因此,实例对象也是能使用 constructor 属性的。
什么是 promise 和 async await 以及它们的区别
Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大,简单地说,Promise 好比容器,里面存放着一些未来才会执行完毕(异步)的事件的结果,而这些结果一旦生成是无法改变的
async await也是异步编程的一种解决方案,他遵循的是 Generator 函数的语法糖,他拥有内置执行器,不需要额外的调用直接会自动执行并输出结果,它返回的是一个 Promise 对象。
区别:
- Promise 的出现解决了传统 callback 函数导致的“地域回调”问题,但它的语法导致了它向纵向发展行成了一个回调链,遇到复杂的业务场景,这样的语法显然也是不美观的。而 async await 代码看起来会简洁些,使得异步代码看起来像同步代码,await 的本质是可以提供等同于”同步效果“的等待异步返回能力的语法糖,只有这一句代码执行完,才会执行下一句。
- async await 与 Promise 一样,是非阻塞的。
- async await 是基于 Promise 实现的,可以说是改良版的 Promise,它不能用于普通的回调函数。
js 实现 sleep
1 |
|
优点:这种方式实际上是用了 setTimeout,没有形成进程阻塞,不会造成性能和负载问题。
缺点:虽然不像 callback 套那么多层,但仍不怎么美观,而且当我们需要在某过程中需要停止执行(或者在中途返回了错误的值),还必须得层层判断后跳出,非常麻烦,而且这种异步并不是那么彻底,还是看起来别扭
Event Loop执行顺序
- 所有任务都在主线程上执行,形成一个执行栈(Execution Context Stack)
- 在主线程之外还存在一个任务队列(Task Queen),系统把异步任务放到任务队列中,然后主线程继续执行后续的任务
- 一旦执行栈中所有的任务执行完毕,系统就会读取任务队列。如果这时异步任务已结束等待状态,就会从任务队列进入执行栈,恢复执行
- 主线程不断重复上面的第三步
宏任务 Macrotask 宏任务是指 Event Loop 在每个阶段执行的任务
微任务 Microtask 微任务是指 Event Loop 在每个阶段之间执行的任务
宏任务队列包含任务: A1, A2 , A3
微任务队列包含任务: B1, B2 , B3
执行顺序为,首先执行宏任务队列开头的任务,也就是 A1 任务,执行完毕后,在执行微任务队列里的所有任务,也就是依次执行 B1, B2 , B3,执行完后清空微任务队中的任务,接着执行宏任务中的第二个任务 A2,依次循环。
宏任务 Macrotask 队列真实包含任务:
1script(主程序代码),setTimeout, setInterval, setImmediate, I/O, UI rendering
微任务 Microtask 队列真实包含任务:
1process.nextTick, Promises, Object.observe, MutationObserver
执行顺序:
1script(主程序代码)—>process.nextTick—>Promises...——>setTimeout——>setInterval——>setImmediate——> I/O——>UI rendering
示例:
1 |
|
结果:3——>4——>6——>8——>7——>5——>start: XXXXms——>1——>2