JavaScript数据类型

文章详细介绍了JavaScript中的let、const与var的区别,包括变量提升和函数提升的概念,以及如何用ES5实现let和const。此外,还讨论了instanceof、this的指向、普通函数与箭头函数的区别。文章深入讲解了闭包的定义、优缺点及应用,并提供了防抖和节流函数的手写实现。最后,提到了函数柯里化和隐式转换的规则。
摘要由CSDN通过智能技术生成

秋招提前批开始了,现在复习应该不会晚吧


let、const、var

三者的区别

区别letconstvar
是否可以重复申明可以不可以可以
块级作用域没有
是否有变量提升有(变量提升为未初始化)有(变量提升为未初始化)有(变量提升为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()方法比较

类型valueOftoString
Number已经是原始值,直接输出原数值转化为string类型
String已经是原始值已经是原始值
Booleantrue‘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
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值