js基础
new源码
-
将新创建出来的函数的Prototype 改成传入进来的函数 function createObject(o){ function Fn(){} Fn.prototype = o return new Fn() }
-
改变创建出来对象的this,使用apply或者call方法
数组方法
影响原数组:
1. 尾减加 pop 和 push
2. 首减加 shift 和 unshift
3. 插入splice
4. 排序sort
5. 反转数组reverse
不影响原数组:
1 .连接 concat
2 .截取 slice
3 .索引查找 indexOf lastIndexOf
4 .迭代方法 every some filter map forEach
- reduce 和 reduceRight
- 数组转字符 => toString() join()
解释DOM和BOM
DOM是文档对象 , 这个对象定义了处理网页内容的方法和接口
BOM是浏览器对象, 这个对象定义了与浏览器交互的接口, 核心是
window, window是js访问浏览器的接口也是全局对象,
网页中定义的方法和属性都属于全局对象 , dom的核心对象document也是window的子对象
类数组
一个拥有 length 属性和索引属性的对象可以被叫做类数组对象,和数组的区别就是不能调用数组方法, 常见的有arugments属性
转换成数组的方法有
(原理就是在arrayLiike方法中调用Array.prototype.slice方法)
Array.prototype.slice.call(arrayLike);
Array.prototype.splice.call(arrayLike, 0);
Array.prototype.concat.apply([], arrayLike);
Array.from(arrayLike);
AJAX
JavaScript 的 异步通信, 从服务器获取数据, 更新部分网页不刷新整个网页
Ajax源码:
1. 创建一个 XMLHttpRequest 对象
2. 使用 open 方法创建一个 HTTP 请求
3. 通过responseType 设置返回类型
4. 通过setRequestHeader 设置请求头信息
5. xhr.send(null); 发送请求
const SERVER_URL = "/server";
let xhr = new XMLHttpRequest();
// 创建 Http 请求
xhr.open("GET", url, true);
// 设置状态监听函数
xhr.onreadystatechange = function() {
if (this.readyState !== 4) return;
// 当请求成功时
if (this.status === 200) {
handle(this.response);
} else {
console.error(this.statusText);
}
};
// 设置请求失败时的监听函数
xhr.onerror = function() {
console.error(this.statusText);
};
// 设置请求头信息
xhr.responseType = "json";
xhr.setRequestHeader("Accept", "application/json");
// 发送 Http 请求
xhr.send(null);
js变量提升
变量提升的表现是,在函数声明前调用函数不保存(codeway的图)
解析阶段,和执行阶段有助于性能提升
常见DOM操作
DOM 节点的获取
getElementById 按照 id 查询
getElementsByTagName 按照标签名查询
getElementsByClassName 按照类名查询
querySelectorAll 按照 css 选择器查询
DOM 节点的创建
// 首先获取父节点
var container = document.getElementById('container')
// 创建新节点
var targetSpan = document.createElement('span')
// 设置 span 节点的内容
targetSpan.innerHTML = 'hello world'
// 把新创建的元素塞进父节点里去
container.appendChild(targetSpan)
DOM 节点的删除
// 获取目标元素的父元素
var container = document.getElementById('container')
// 获取目标元素
var targetNode = document.getElementById('title')
// 删除目标元素
container.removeChild(targetNode)
修改DOM
// 获取父元素
var container = document.getElementById('container')
// 获取两个需要被交换的元素
var title = document.getElementById('title')
var content = document.getElementById('content')
// 交换两个元素,把 content 置于 title 前面
container.insertBefore(content, title)
JavaScript脚本延迟加载的方式有哪些?
- defer 属性:给 js 脚本添加 defer 属性,这个属性会让脚本的加载与文档的解析同步解析,然后在文档解析完成后再执行这个脚本文件,这样的话就能使页面的渲染不被阻塞。多个设置了 defer 属性的脚本按规范来说最后是顺序执行的,但是在一些浏览器中可能不是这样。
- async 属性:给 js 脚本添加 async 属性,这个属性会使脚本异步加载,不会阻塞页面的解析过程,但是当脚本加载完成后立即执行 js 脚本,这个时候如果文档没有解析完成的话同样会阻塞。多个 async 属性的脚本的执行顺序是不可预测的,一般不会按照代码的顺序依次执行。
- 使用 setTimeout 延迟方法:设置一个定时器来延迟加载js脚本文件
- 让 JS 最后加载:将 js 脚本放在文档的底部,来使 js 脚本尽可能的在最后来加载执行。
- 动态创建 DOM 方式:动态创建 DOM 标签的方式,可以对文档的加载事件进行监听,当文档加载完成后再动态的创建 script 标签来引入 js 脚本。
JavaScript 类数组对象的定义?
一个拥有 length 属性和若干索引属性的对象就可以被称为类数组对象
Array.prototype.slice.call(arrayLike);
Array.prototype.splice.call(arrayLike, 0);
Array.prototype.concat.apply([], arrayLike);
Array.from(arrayLike);
如何判断一个对象是否属于某个类?
- 第一种方式,使用 instanceof 运算符来判断构造函数的 prototype 属性是否出现在对象的原型链中的任何位置。
- 第二种方式,通过对象的 constructor 属性来判断,对象的 constructor 属性指向该对象的构造函数,但是这种方式不是很安全,因为 constructor 属性可以被改写。
- 第三种方式,如果需要判断的是某个内置的引用类型的话,可以使用 Object.prototype.toString() 方法来打印对象的[[Class]] 属性来进行判断。
for...in和for...of的区别
for…of 是ES6新增的遍历方式,允许遍历一个含有iterator接口的数据结构(数组、对象等)并且返回各项的值,和ES3中的for…in的区别如下
-
for… in 会遍历对象的整个原型链,性能非常差不推荐使用,
-
for…of 只遍历当前对象不会遍历原型链
总结:for...in 循环主要是为了遍历对象而生
for...of 循环可以用来遍历数组、类数组对象,符串、Set、Map 、Generator 对象。
数组的遍历方法有哪些
方法 | 是否改变原数组 | 特点 |
---|---|---|
forEach() | 否 | 数组方法,不改变原数组,没有返回值 |
map() | 否 | 数组方法,不改变原数组,有返回值,可链式调用 |
filter() | 否 | 数组方法,过滤数组,返回包含符合条件的元素的数组,可链式调用 |
for...of | 否 | for...of遍历具有Iterator迭代器的对象的属性,返回的是数组的元素、对象的属性值,不能遍历普通的obj对象,将异步循环变成同步循环 |
every() 和 some() | 否 | 数组方法,some()只要有一个是true,便返回true;而every()只要有一个是false,便返回false. |
find() 和 findIndex() | 否 | 数组方法,find()返回的是第一个符合条件的值;findIndex()返回的是第一个返回条件的值的索引值 |
reduce() 和 reduceRight() | 否 | 数组方法,reduce()对数组正序操作;reduceRight()对数组逆序操作 |
数据类型
js数据类型:
八大数据类型: Undefinded Null Boolean Number String Object、Symbol、BigInt
数据可以分为原始数据类型和引用数据类型
原始数据类型(栈) : Undefinded Null Boolean Number String
引用数据类型(堆): Object 对象、数组和函数
存储位置不同
栈空间(原始数据类型): 空间小 ,大小固定 , 频繁使用
堆空间(指针数据类型): 空间大 , 大小不固定, 引用数据类型在栈中存储了指针,
解释器会查栈中的地址,取到地址后得到数据
数据类型检测方式
typeof ,instanceof, construector, Object.prototype.toString.call()
判断数组的方式
Object.prototype.toString.call()
原型链: obj.proto === Array.prototype;
ES6: Array.isArray()做判断
instanceof
Array.prototype.isPrototypeOf
null和undefined
1. 它们都是基本数据类型, null表示空对象, undefined是未定义
2. typeof null=onject
3. null ==object ---true 历史遗留问题
000 开头代表是对象,然而 null 表示为全零,所以将它错误的判断为 object 。
4. null ====object ---false
instanceof源码
instanceof 运算符用于判断**构造函数的 prototype 属性**是否出现在对象的原型链中的任何位置。
1.proto=通过Object.getPrototype 获取对象原型
2. prototype=获取构造函数的prototyper对象
3.判断构造函数是否出现在指定对象的函数上
4.没有就继续调用Object.getPrototype 方法
function myInstanceof(left, right) {
// 获取对象的原型
let proto = Object.getPrototypeOf(left)
// 获取构造函数的 prototype 对象
let prototype = right.prototype;
// 判断构造函数的 prototype 对象是否在对象的原型链上
while (true) {
if (!proto) return false;
if (proto === prototype) return true;
// 如果没有找到,就继续从其原型上找,Object.getPrototypeOf方法用来获取指定对象的原型
proto = Object.getPrototypeOf(proto);
}
}
//使用
console.log(auto instanceof Car);
0.1+0.2 ! == 0.3
toFixed(num)
方法 例:(n1 + n2).toFixed(2)
计算机是通过二进制的方式存储数据的,所以计算机计算0.1+0.2的时候,实际上是计算的两个数的二进制的和。
== 操作符转换规则
1.判断类型是否相同
2.相同就比较大小,不同就转换数据类型
3.null==undefined 结果为 true
4.string和number 转number
5.boolean 和 number 转number 1 0
- object 和 基本数据类型 obj转原始数据类型
Object.js() === ==
==类型不一致会转换数据类型
===类型不一致直接返回fasle
Object.is(a,b) 和 ===一样, 但是-0和+0不等, 两个NaN相等 (''aaaaa'' - 1 )
typeof null 的结果是什么,为什么?
typeof null 的结果是Object。
isNaN 和 Number.isNaN 函数的区别?
- 函数 isNaN 接收参数后,会尝试将这个参数转换为数值,任何不能被转换为数值的的值都会返回 true,因此非数字值传入也会返回 true ,会影响 NaN 的判断。
- 函数 Number.isNaN 会首先判断传入参数是否为数字,如果是数字再继续判断是否为 NaN ,不会进行数据类型的转换,这种方法对于 NaN 的判断更为准确。
其他值到字符串的转换规则?
- Null 和 Undefined 类型 ,null 转换为 "null",undefined 转换为 "undefined",
- Boolean 类型,true 转换为 "true",false 转换为 "false"。
- Number 类型的值直接转换,不过那些极小和极大的数字会使用指数形式。
- Symbol 类型的值直接转换,但是只允许显式强制类型转换,使用隐式强制类型转换会产生错误。
- 对普通对象来说,除非自行定义 toString() 方法,否则会调用 toString()(Object.prototype.toString())来返回内部属性 [[Class]] 的值,如"[object Object]"。如果对象有自己的 toString() 方法,字符串化时就会调用该方法并使用其返回值。
其他值到数值的转换规则?
- Undefined 类型的值转换为 NaN。
- Null 类型的值转换为 0。
- Boolean 类型的值,true 转换为 1,false 转换为 0。
- String 类型的值转换如同使用 Number() 函数进行转换,如果包含非数字值则转换为 NaN,空字符串为 0。
- Symbol 类型的值不能转换为数字,会报错。
- 对象(包括数组)会首先被转换为相应的基本类型值,如果返回的是非数字的基本类型值,则再遵循以上规则将其强制转换为数字。
其他值到布尔类型的值的转换规则?
以下这些是假值:
• undefined
• null
• false
• +0、-0 和 NaN
• ""
|| 和 && 操作符的返回值?
|| 和 && 首先会对第一个操作数执行条件判断,如果其不是布尔值就先强制转换为布尔类型,然后再执行条件判断。
- 对于 || 来说,如果条件判断结果为 true 就返回第一个操作数的值,如果为 false 就返回第二个操作数的值。
- && 则相反,如果条件判断结果为 true 就返回第二个操作数的值,如果为 false 就返回第一个操作数的值。
|| 和 && 返回它们其中一个操作数的值,而非条件判断的结果
JavaScript 中如何进行隐式类型转换?
首先要介绍ToPrimitive
方法,这是 JavaScript 中每个值隐含的自带的方法,用来将值 (无论是基本类型值还是对象)转换为基本类型值。如果值为基本类型,则直接返回值本身;如果值为对象,其看起来大概是这样:
/**
* @obj 需要转换的对象
* @type 期望的结果类型
*/
ToPrimitive(obj,type)
console.log('1'+1);
console.log('1'-1);
String() a.toString() JSON.stringify()
String()是js的全局函数可以把null、undefined转换为字符,但是没办法转换为进制字符串,其他的都和toString()一样
toString()是object原型的一个方法,它与String的区别就是不能转换null、undefined会报错
JSON.stringify() 对象序列化转换为字符串的时候也调用了toString的方法,这里值得注意的是并非严格意义上的强制类型转换用法与toString基本一致.
Date()
Number() a. parseInt() parseFloat()
ES6
let const var
块级作用域: let const 有 var没有
变量提升: var 有 let const没有
给全局添加属性: var 有 let const没有
重复声明 : var有并且后面的会覆盖前面的, const 和 let 不能重复声明
暂时性死区 : 函数外面使用var定义a ,函数内在let const 定义 a 在定义前使用,显示变量 不可用,语法上称作暂时性死区
初始值 : var 和let不用给初试值, cosnt要给
指针指向 : let可以重新赋值 const不行 因为const指针不可变
箭头函数和普通函数的区别
更加简洁: 没有参数一个空括号 ()=>{},
一个参数不用括号 a=>{}
函数体只有一句话 a=>log( a )
函数体不需要返回值,viod调用方法 () => viod xxx()
箭头函数没有自己的this
箭头函数不会创建自己的this, 会用自己上层作用域的this。
箭头函数中this的指向在定义时已经确定了,之后不会改变。
显示调用, 隐式调用(call apply bind) , new 不能改变
不能当做构造函数:
因为new 关键字要改变创建出来新对象的this
没有arguments
调用的是外层函数的arguments
箭头函数没有prototype
箭头函数不能用作Generator函数,不能使用yeild关键字
const对象的属性可以修改吗
常量不可以修改,指针不可变
对象内的值可以改变.
如果new一个箭头函数的会怎么样
箭头函数是ES6中的提出来的,它没有prototype,也没有自己的this指向,更不可以使用arguments参数,所以不能New一个箭头函数。
new操作符的实现步骤如下:
- 创建一个对象
- 将构造函数的作用域赋给新对象(也就是将对象的__proto__属性指向构造函数的prototype属性)
- 指向构造函数中的代码,构造函数中的this指向该对象(也就是为这个对象添加属性和方法)
- 返回新的对象
所以,上面的第二、三步,箭头函数都是没有办法执行的。
箭头函数的this指向哪⾥?
箭头函数不同于传统JavaScript中的函数,箭头函数并没有属于⾃⼰的this,它所谓的this是捕获其所在上下⽂的 this 值,作为⾃⼰的 this 值,并且由于没有属于⾃⼰的this,所以是不会被new调⽤的,这个所谓的this也不会被改变。
对 rest 参数的理解( ... )
它还可以把一个分离的参数序列整合成一个数组
ES6中模板语法与字符串处理``${}
html``
边量${}
存在性判定:includes, startsWith, endsWith
原型
原型和原型链
js使用构造函数创建对象, 当中有一个prototype属性指向这个对象的原型,
浏览器实现__proto__属性也能访问到这个函数,一般不推荐使用,
ES5实现了Object.getPrototypeOf() 方法可以访问
当访问一个对象属性的时候,内部不存在这个属性就去原型上找, 原型上没有就去, 原型上的原型查, 最终找到源头Object.prototype
js对象是通过引用传递,每个新对象没有自己的原型对象 (new 和 var 创建对象),修改原型, 相关对象也会继承这个改变 function定义才有副本
原型修改、重写
修改__proto__指向 , 修改constructor指向,
原型链的指向
异步编程
Promise理解
Promise是异步编程的一种解决方案, 为了解决回调地狱的问题.
Promise的三个状态 Pending Resolved Rejected
当把一件事情交给promise时,它的状态就是Pending,任务完成了状态就变成了Resolved、没有完成失败了就变成了Rejected。
实现方法
promise调用reslove()法会跳转到对应的then方法中,
原因是在Promise内部定义的reslove()方法会 调用onFulfilled或者onRejected方法,
这个方法就是then中的方法, promise内部定义的 reslove或reject 方法会将onRejected
添加到微任务队列, 先实现then的方法, 在将then方法赋值给primise对象
**Promise的用法 **
new Promise()来创建promise对象
const promise = new HYpromise((resolve,reject) =>{
// resolve(1111)
reject(2222)
})
Promise.resolve(11).then 会直接进入resolve
状态
catch()指向reject函数
async/await理解
async /await 像生成器和Promise结合的效果 ,为了解决Promise.then的链式调用
function* 定义一个生成器, yield关键字是一种 for of 的语法糖.
写一个递归函数处理.then的函数
通过function.next()可以调用,并且可以给yield返回值. 返回的值会赋给上次调用的方法
async await有类似的效果,所以await也可以接受返回值,await会return一个promise
await后面跟的值就是then中的代码
async/await对Primise的优势
代码更容易读,解决链式调用的问题
更好进行调试
错误处理更友好
实现方式
回调函数, Promise, async
setTimeout、Promise、Async/Await 的区别
(1)setTimeout
(2)Promise
Promise本身是同步的立即执行函数, 当在executor中执行resolve或者reject的时候, 此时是异步操作, 会先执行then/catch等,当主栈完成后,才会去调用resolve/reject中存放的方法执行,打印p的时候,是打印的返回结果,一个Promise实例。
(3)async/await
async 函数返回一个 Promise 对象,当函数执行的时候,一旦遇到 await 就会先返回,等到触发的异步操作完成,再执行函数体内后面的语句。可以理解为,是让出了线程,跳出了 async 函数体。
Promise解决了什么问题
在工作中经常会碰到这样一个需求,比如我使用ajax发一个A请求后,成功后拿到数据,需要把数据传给B请求;那么需要如下编写代码
Promise.all和Promise.race的区别的使用场景
Promise.all
Promise.all
可以将多个Promise
实例包装成一个新的Promise实例,
成功的时候返回的是一个结果数组,而失败的时候则返回最先被reject失败状态的值。
Promise.race
顾名思义,Promse.race就是赛跑的意思,意思就是说,Promise.race([p1, p2, p3])里面哪个结果获得的快,就返回那个结果,不管结果本身是成功状态还是失败状态。当要做一件事,超过多长时间就不做了,可以用这个方法来
await 到底在等啥
await 在等待什么呢?一般来说,都认为 await 是在等待一个 async 函数完成。不过按语法说明,await 等待的是一个表达式,这个表达式的计算结果是 Promise 对象或者其它值(换句话说,就是没有特殊限定)。
因为 async 函数返回一个 Promise 对象,所以 await 可以用于等待一个 async 函数的返回值——这也可以说是 await 在等 async 函数,但要清楚,它等的实际是一个返回值。注意到 await 不仅仅用于等 Promise 对象,它可以等任意表达式的结果,所以,await 后面实际是可以接普通函数调用或者直接量的。所以下面这个示例完全可以正确运行:
function getSomething() {
return "something";
}
async function testAsync() {
return Promise.resolve("hello async");
}
async function test() {
const v1 = await getSomething();
const v2 = await testAsync();
console.log(v1, v2);
}
test();
async/await的优势
单一的 Promise 链并不能发现 async/await 的优势,但是,如果需要处理由多个 Promise 组成的 then 链的时候,优势就能体现出来了(很有意思,Promise 通过 then 链来解决多层回调的问题,现在又用 async/await
执行上下文/作用域链/闭包
闭包
当前作用域访问上层作用域的变量,导致内存无法回收这个块区域的内存,闭包会有内存泄露
,在内存中的表现这个变量被别的作用域指向,导致内存无法被回收
解决循环中var定义变量的问题
1. 使用闭包
setTimeout是异步函数,for循环是同步的, 当 i 结束后才调用 setTimeout函数
使用立即执行函数包裹setTimeout , setTimeout中的timer就会形成闭包
i
传入函数内部,这个时候值就被固定在了参数 j
上面不会改变
2. 使用let关键字
作用域和作用域链
全局作用域和函数作用域
(1)全局作用域
所有window对象的属性拥有全局作用域, 全局作用域变量会污染全局命名空间
(2)函数作用域
函数作用域声明在函数内部的变量,一般只有固定的代码片段可以访问到
块级作用域
ES6中新增的let和const指令可以声明块级作用域
作用域链:
在当前作用域中查找所需变量,但是该作用域没有这个变量,那这个变量就是自由变量。如果在自己作用域找不到该变量就去父级作用域查找,直到访问到window对象就被终止,
执行上下文(图)
js引擎内部有一个执行上下文栈(ECS) 函数执行上下文(FEC)
当开始执行代码的时候会在ECS创建全局执行上下文(GEC),
GEC有一个VO对应GO +父级作用域, GO存在堆空间, GO会进行预解析,也就是作用域提升,
解析变量为undefined,解析函数会在堆空间开辟空间并将指针值传递过去 , 其中包括代码块 和父级作用域
然后在GEC中开始执行代码,执行变量赋值, 执行函数就会创建一个函数执行上下文(FEC)
FEC有一个VO对应AO和父级作用域链 ,AO在堆空间,AO会进行预解析,变量赋值为undeined
函数就会接着创建下一个函数执行上下文,使用完FEC AO都会被移除
this/call/apply/bind
this 的理解
this 有四种调用模式
默认调用
显示调用
隐式调用
构造调用
call/apply/bind源码
给Function原型上添加上一个方法
判断传入的对象是不是空的,
通过上下文对象掉用这个函数
Function.prototype.myCall = function(context) {
// 判断调用对象
if (typeof this !== "function") {
console.error("type error");
}
// 获取参数
let args = [...arguments].slice(1),
result = null;
// 判断 context 是否传入,如果未传入则设置为 window
context = context || window;
// 将调用函数设为对象的方法
context.fn = this;
// 调用函数
result = context.fn(...args);
// 将属性删除
delete context.fn;
return result;
};
面相对象
对象的定义
**1.直接定义**
var student = new Object();
student.name="Lucy";
2.初始化定义
var student = {
name:"Tim",
age:12 }
3.构造函数式
function Student(name){
this.name=name;
this.eatting=function(){ console.log(this.name+"正在吃东西"); }; }
4.原型式
function Student(){ }
Student.prototype.name="Kitty";
Student.prototype.eatting=function(){ console.log(this.name+"正在吃东西"); };
5.混合式
function Student(name){
this.name=name; }
Student.prototype.eatting=function(){ console.log(this.name+"正在吃东西"); };
对象创建方式
字面量定义: var a={}
new
new Object ( Object.name=aaa )
对象继承方式
(1) 原型链的方式来实现继承
弊端: 1. 在包含有引用类型的数据时,会被所有的实例对象所共享
2. 引用数据类型会去prototype添加属性, 基本数据类型会在当前实例上添加属性
3. 无法传递参数给父类,new Student 只能传递给自己的构造函数
var stu1=new Student()
var stu2=new Student()
stu1.friends.push("kobe")
console.log(stu1.friends);
console.log(stu2.friends);
结果是stu1 和 stu2都会打印出数据
(2)借用构造函数的方式
弊端: 1. Person会被调用两次
2.原型对象增加不需要的属性
function Person(name,sno,friends){
//给stu加上属性
this.name=name
this.sno=sno
this.friends =friends
}
function Student(name,sno,friends){
person.call(this,name,sno,friends)
this.sno=111
}
(4)第四种方式是原型式继承
弊端:缺点和原型链相同
function createObject(o){
function Fn(){}
Fn.prototype = o
// newObj.__proto__=o 不要通过这种方式给隐式原型父子
var newObj = new Fn()
return newObj
}
function createObject(o){
var newObj = {}
//给newObject设置原型
Object.setPrototypeOf(newObj,o)
return newObj
}
var info2=Object.create(obj)
(5)第五种方式是寄生式继承
function createStudent(name){
var stu = Object.create(PersonObj)
stu.name=name
stu.studying = function(){
console.log("studying~");
}
}
(6)第六种方式是寄生式组合继承
function createObject(o){
function Fn(){}
Fn.prototype = o
return new Fn()
}
function inheritPrototype(SubType,SuperType){
SubType.prototype=createObject(SuperType.prototype)
Object.defineProperty(SubType.prototype,"constructor",{
enumerable: false,
configurable: true,
writable: true,
value: SubType
})
}
垃圾回收和内存泄漏
回收机制:
JS具有自动垃圾回收机制,会定期对那些不再使用的变量、对象进行释放,
原理就是找到不再使用的变量,然后释放掉其占用的内存。
局部变量会在堆或栈中存储它们的值当函数执行结束后,这些局部变量不再被使用,它们所占有的空间就会被 释放。当局部变量被外部函数使用时,其中一种情况就是闭包,在函数执行结束后,函数外部的变量依然指向 函数内部的局部变量,此时局部变量依然在被使用,所以不会回收。
垃圾回收的方式:
标记清除
当变量进入执行环境时,就标记这个变量“进入环境”
当变量离开环境时,就会被标记为“离开环境”
被标记为“离开环境”的变量会被内存释放。
引用计数
引用计数就是跟踪记录每个值被引用的次数。
当这个引用次数变为0时,会被回收
这种方法会引起循环引用的问题:例如: obj1
和obj2
通过属性进行相互引用,
内存泄露
闭包不合理的使用闭包,从而导致某些变量一直被留在内存当中。
面试题
js
本地存储方式有哪些
对比项目 | cookie | localstorage | sessionStorage |
---|---|---|---|
数据存储时间 | 可设置失效时间 | 永久 | 仅当前会话 |
容量 | <=4kb | <=5mb | <=5mb |
css
响应式布局有哪些
响应式布局方法一:媒体查询
响应式布局方法二:百分比%
响应式布局方法三:vw/vh
响应式布局方法四:rem
-
节流: n 秒内只运行一次,若在 n 秒内重复触发,只有一次生效
-
防抖: n 秒后在执行该事件,若在 n 秒内被重复触发,则重新计时
es模块化 导入导出