秋招提前批开始了,现在复习应该不会晚吧
JavaScript基础知识复习
let、const、var
三者的区别
| 区别 | let | const | var |
|---|---|---|---|
| 是否可以重复申明 | 可以 | 不可以 | 可以 |
| 块级作用域 | 有 | 有 | 没有 |
| 是否有变量提升 | 有(变量提升为未初始化) | 有(变量提升为未初始化) | 有(变量提升为undefined) |
| 是否污染全局变量 | 不会 | 不会 | 会 |
说明 :
1、let和const存在变量提升,和var不同的是,变量提升为尚未定义,而var的变量提升为undefined。
2、const定义常量变量,一旦定义不可以修改,但是如果为对象类型的变量时,变量中保存的是地址指针,只要保证这个指针指向的地址不变,即可以对这个地址中的内容进行修改。
3、由var声明的全局变量 --> 全局对象的属性(即window),由let和const声明的全局变量 -->不是全局对象的属性
变量提升和函数提升
变量提升
1、只在当前作用域做变量提升,例如全局作用域和函数作用域。
2、由var定义的变量会变量提升,但是不带var声明的变量相当于在window上设置属性。
函数提升
1、通过function声明的函数,在函数定义之前就可以调用。由于代码在编译阶段对函数进行了提升。
2、函数的作用域在函数声明阶段就已经确定,与执行时的阶段无关。即在确定函数的父级作用域时应该看函数在声明阶段的父级作用域。
3、变量提升优先函数提升。在 var 和 function 同名的变量提升的条件下,函数会先执行。换一句话说,var 和 function 的变量同名 var 会先进行变量提升,但是在变量提升阶段,函数声明的变量会覆盖 var 的变量提升,所以直接结果总是函数先执行优先。所以输出的结果都是一样的。
变量提升练习题
a = 2 //window.a = 12
function foo(){
var a =12;
b = 'bbb' //window.b = 'bbb'
console.log('b' in window) //
console.log(a, b) //12,bbb
}
foo()
console.log(b) //输出window.b
console.log(a) //输出window.a
注意:对于在作用域链上从来没声明和定义过的变量,输出时会报错
foo()
console.log(a) //Uncaught ReferenceError: a is not defined
//在作用域链上没找到就会抛出错误
function foo(){
var a =12;
b = 'bbb' //window.b = 'bbb'
console.log(a, b) //12,bbb
}
fn()
function fn(){
console.log(a) // Uncaught ReferenceError: a is not defined
}
用ES5实现let和const
let:用立即执行函数来模拟块级作用域
//let模拟
(function(){
var a = 1;
console.log(a) //
})()
const:1、用Object.defineProperty()实现对属性的拦截,修改属性描述符(实现不可修改)2、再用立即执行函数实现块级作用域
//const模拟
function __const(data,value){
// 把要定义的data挂载到某个对象,并赋值value
this.data = value
Object.defineProperty(this,data,{
enumerable:false,
configurable:false,
get:function(){
return value
},
set:function(data){
if(data!=value){
// 要对当前属性进行修改时,报错
throw new TypeError('Assignment to constant variable.')
}else{
return value
}
}
})
}
// 立即执行函数实现块级作用域
(function(){
var obj = {}
_const.call(obj,'a',10)
})()
实现instanceof
this的指向
普通函数和箭头函数的区别
箭头函数是普通函数的语法糖,箭头函数使函数的书写更加的简洁,箭头函数的作用是使函数内部的this指向和函数外部this是一样的。
| 区别 | 普通函数 | 箭头函数 |
|---|---|---|
| this指向 | 谁调用指向谁 | 定义时确定,没有自己的this,会沿着作用域链找父级的this |
| 改变this指向 | cal、apply、bind可以改变 | 不可以改变 |
| arguments | 有 | 没有,可以用rest参数代替 |
| 作为构造函数 | 可以 | 不可以,没有prototype属性 |
| 匿名函数 | 可以匿名或不匿名 | 匿名函数 |
闭包
闭包定义、优缺点、应用
闭包就是函数中可以读取其他函数内部的变量;
本质就是在函数的执行完成后会回收当前的执行上下文,但是由于函数的内部变量被执行上下文外部引用,因此不会释放当前的执行栈,形成了不被销毁的执行上下文。
function foo(){
let a = 1; //被引用
return function(){
console.log(a++); //引用外部的变量
}
}
var fn = foo();
fn() //1,执行完后a变量未被释放,因此a的值为2
fn() //2
闭包的优点
1、可以让函数外部读取函数内部的变量;
2、可以延长函数内局部变量的生命周期;
闭包的缺点
1、变量占用内存的时间会变长;
2、容易造成内存泄漏(变量有被引用时,不会被系统回收);
解决
手动将变量设置为null,系统垃圾回收时会回收值为null的变量。
应用
防抖、节流、函数柯里化
手写防抖函数和节流函数
防抖:顾名思义,实质上是在每次函数执行之前延时一段时间,避免在执行后仍要执行(每次都执行最后一次);间隔时间内触发第二次,则重新计时;
// 立即执行版本
function debounce(fn,delay){
let timer = null;
return function(...args){
if(timer) clearTimeout(timer);
let immediate = !timer;
timer = setTimeout(()=>{
timer = null;
},delay)
if(immediate) fn.apply(this,args)
}
}
// 先延时,再执行的版本
function debounce(fn,delay){
let timer = null;
return function(...args){
if(timer) clearTimeout(timer);
timer = setTimeout(()=>{
fn.apply(this,args)
timer = null;
},delay)
}
}
节流:在某段时间内只执行一次函数,在这段时间内如果需要函数执行,则忽略;只有间隔时间大于设定的延时时间,才会执行第二次;
// 定时器版本
function throttle(fn,delay){
let timer = null;
return function(...args){
if(timer)return;
timer = setTimeout(()=>{
fn.apply(this,args);
timer = null
},delay)
}
}
// 时间戳版本
function throttle(fn,delay){
let date = new Date();
return function(...agrs){
let now = new Date();
if(now-date >= delay){
fn.apply(this,args);
date = now;
}
}
}
作用都是控制函数的触发频率,优化性能。
手写函数柯里化
const currying = function(fn, ...args) {
// fn需要的参数个数
const len = fn.length
// 返回一个函数接收剩余参数
return function (...params) {
// 拼接已经接收和新接收的参数列表
let _args = [...args, ...params]
// 如果已经接收的参数个数还不够,继续返回一个新函数接收剩余参数
if (_args.length < len) {
return currying.call(this, fn, ..._args)
}
// 参数全部接收完调用原函数
return fn.apply(this, _args)
}
}
柯里化的优势
1、柯里化突出一种重要思想:降低适用范围,提高适用性
2、柯里化的三个作用和特点:参数复用、提前返回、延迟执行
3、柯里化是闭包的一个典型应用,利用闭包形成了一个保存在内存中的作用域,把接收到的部分参数保存在这个作用域中,等待后续使用。并且返回一个新函数接收剩余参数。
隐式转换的规则
1、如果是两个对象类型进行运算(必须先转换为原始类型即基础类型),没有指定转换成哪种类型时,首先调用valueOf()方法,如果valueOf()返回值不是原始值,继续执行toString()方法,肯定可以得到原始值。
toString()和valueOf()方法比较
| 类型 | valueOf | toString |
|---|---|---|
| Number | 已经是原始值,直接输出原数值 | 转化为string类型 |
| String | 已经是原始值 | 已经是原始值 |
| Boolean | true | ‘true’ |
| undefined | 报错 | 报错 |
| null | 报错 | 报错 |
| object | 输出原对象的值 | ‘[object Object]’ |
| Array | 输出原数组的值 | 直接转化为字符串‘1,2,3’ |
| function | [Function: 函数名] | 直接输出函数体‘function(){}’ |
| Date | 输出时间戳 | 输出‘Fri Jul 21 2023 17:20:07 GMT+0800 (中国标准时间)’ |
==号的隐式转换规则
1、类型相同直接比较,注意NaN不与任何值相等,NaN不等于NaN。引用类型比较时,只有当引用指向地址相同时才相等。
2、类型不同时
- 两边为null或undefined其中一种时,为true
- 两边为number和String类型时,则转换为Number进行比较。
- 一个为Boolean,将布尔类型转化为数字类型进行比较
- 一个为Object,一个是String或number类型,将Object进行原始转换,再按照上面流程进行原始值比较。
参考博客https://juejin.cn/post/6844903557968166926#heading-8
注意:if判断的时候默认是转化为Boolean类型 ToBoolean(参数) 指其他类型转换为布尔类型的操作 js中的假值只有false、null、undefined、空字符、0和NaN,其它值转为布尔型都为true。
set、weakset、map、weakmap
map/weakmap
weakmap的key值只能是对象(除了null),键名所指的对象不计入垃圾回收机制,一般当对象的引用消失后,系统会回收该对象。weakmap里的键名所对应的键值对会自动消失,不用手动删除引用。(这样有助于防止内存泄漏)
| 类型 | key值 | 方法 |
|---|---|---|
| map | 任意值 | 多了遍历、size、clear方法 |
| weakmap | 对象 | 只有get、set、has、delete |
文章详细介绍了JavaScript中的let、const与var的区别,包括变量提升和函数提升的概念,以及如何用ES5实现let和const。此外,还讨论了instanceof、this的指向、普通函数与箭头函数的区别。文章深入讲解了闭包的定义、优缺点及应用,并提供了防抖和节流函数的手写实现。最后,提到了函数柯里化和隐式转换的规则。
979

被折叠的 条评论
为什么被折叠?



