说说你对闭包的理解
是一个函数加上到创建函数的作用域的连接,并且关闭了函数的自由变量,一般就是一个函数A,return其内部的函数B,被return出去的B函数能够在外部访问A函数内部的变量,这时候就形成了一个B函数的变量背包,A函数执行结束后这个变量背包也不会被销毁,并且这个变量背包在A函数外部只能通过B函数访问
- 闭包形成的原理:作用域链,当前作用域可以访问上级作用域中的变量
- 闭包解决的问题: 能够让函数作用域中的变量在函数执行结束之后不会被销毁,同时也能在函数外部可以访问函数内部的局部变量
- 闭包带来的问题: 由于垃圾回收器不会将闭包中的变量销毁,于是就会造成内存泄露,内存泄漏积累多了就容易导致内存溢出,解决的方法是将闭包函数设置为null
- 闭包的优点:内部函数可以访问到外部函数的局部变量
- 闭包使用的场景: 回调函数 防抖节流 定时器传参
- 闭包的应用:能够模仿 块级作用域,能够实现柯里化,在构造函数中定义特权方法,vue中数据响应式observe中就使用了闭包
说说JS变量提升
- 变量提升是指js的变量和函数声明会在代码编译期,提升到代码的最前面
- 变量提升成立的前提是使用了var关键字进行了声明,并且变量提升的时候只有声明被提升,赋值并不会被提升,同时函数的声明提升会比变量的提升优先
- 变量提升的结果:可以在变量初始化之前访问该变量,返回的是undefined,在函数声明前可以调用该函数
call、apply、bind区别
- 共同点:改变函数体内的this指向
- 区别:call和apply都是立即执行 ,bind不会立即执行,因为它返回的是一个函数。
参数不同:apply参数是数组,call和bind参数一个一个挨着写
延迟加载JS的方式有哪些
- asnyc:asnyc和html解析是同步的,不是顺序执行的
- defer:html解析完再执行js,顺序执行
null和undefined的区别
null会被隐式的转换成0,作者觉得最好不要是对象,所以又设计了undefined,所以是先有了null再有的undefined
- 区别:null表示“无”的对象(空对象指针),转换成数值为0
undefined是一个表示"无"的原始值,转换成数值为NaN
JS数据类型有哪些
- 基本类型:String number boolean undefined null symbol
- 引用类型:Object
==和 ===有什么不同
- ==:比较的是值 通过valueof隐式转换的
- ===:不仅比较值还比较类型
JS微任务和宏任务
- JS是单线程语言,JS代码执行的流程是:同步执行完==>事件循环
- 事件循环(请求,定时器,事件)就包含了微任务和宏任务
- 一般微任务有:promise then后面
- 宏任务:setTimeout、setInterval注册的回调,click事件的回调
- 要执行完宏任务的前提是清空了所有的微任务
JS作用域考题
- 除了函数以外,没有块级作用域
- 作用域链:内部可以访问外部变量,反之不行,注意:如果内部有优先找内部的,没有的再找外部的,
- 优先级:声明变量>声明普通函数>参数>变量提升
JS中的类型判断
- typeof:对于原始类型来说,除了null都可以正确显示,对于对象来说,除了函数都会显示Object,所以说typeof并不能准确判断变量到底是什么类型,所以想判断一个对象的正确类型,这个时候可以考虑用instanceof
- instanceof:可以正确的判断对象的类型,因为内部机制是通过判断对象的原型链中是不是能找到类型的prototype
- constructor
- Object.prototype.toString.call()
undefined和undeclared区别
- 已在作用域中声明但还没有赋值的变量,是undefined。相反,还没有在作用域中声明过的变量,是undeclared
- 对于undeclared变量的引用,浏览器会报引用错误,如ReferenceError:b is not defined,但是我们可以使用typeof的安全防范机制来避免报错,因为对于undeclared或者notdefined变量,typeof会返回"undefined"
介绍js有哪些内置对象
- js中内置对象主要指的是在程序执行前存在全局作用域的有js定义的一些全局值属性,函数和用来实例化其他对象的构造函数对象
- 一般我们经常用到的如全局变量值NaN,undefined,全局函数如parseInt(),parseFloat()
- 用来实列化对象的构造函数如Date,object等
- 提供数学计算的单体内置对象如Math等
js继承的几种方式
- 原型链继承
缺点:在包含有引用类型的数据时,会被所有实例化对象所共享,容易造成修改的混乱,还有就是在创建子类型的时候不能向超类型传递参数 - 借用构造函数继承
通过在子类型的函数中调用超类型的构造函数来实现的,这一种方式解决了不能向超类型传递参数的缺点,但是存在问题是无法实现函数方法的复用,并且超类型原型定于的方法子类型也没有办法访问到 - 组合继承(组合原型链继承和借用构造函数继承)->常用
结合了两种模式的优点,传参和复用
特点:1、可以继承父类原型上的属性,可以传参,可复用。
2、每个新实例引入的构造函数属性是私有的
缺点:调用了两次父类构造函数(耗内存),子类的构造函数会代替原型上的那个父类构造函数。 - 原型式继承
重点:用一个函数包装一个对象,然后返回这个函数的调用,这个函数就变成了个可以随意增添属性的实例或对象。object.create()就是这个原理
特点:类似于复制一个对象,用函数来包装
缺点:1、所有实例都会继承原型上的属性
2、无法实现复用 - 寄生式继承
思路是创建一个用于封装继承过程的函数,通过传入一个对象,然后复制一个对象的副本,然后对对象进行扩展,最后返回这个对象,这个扩展的过程可以理解是一种继承,这种继承的优点就是对一个简单对象实现继承,如果这个对象不是我们自定义类型时,缺少是没有办法实现函数复用 - 寄生式组合继承(常用)
缺点是:使用超类型的实例做为子类型的原型,导致添加了不必要的原型属性,寄生式组合继承的方法是使用超类型的原型副本作为子类型的原理,这样就避免了创建不必要的属性
讲一下JS字符串截取、切割的方法
-
slice(s,e):从s位开始截取,e位结束,返回新字符串,不改变源字符串
slice(-2,-1) 表示抽取了原数组中的倒数第二个元素到最后一个元素(不包含最后一个元素,也就是只有倒数第二个元素)。 -
substring(s,e):从s位开始截取,e结束,不包含e位
-
substr(s,l):从s位开始截取,截取l个,不改变源字符串
-
split(s,l):把一个字符串分割成字符串数组,2个参数都是可选的,s可以是正则或者字符串,l指定返回长度,此方法常用场景是获取url链接参数,或者根据某个字符切割字符串,返回符合条件的数组,不会改变源字符串
-
join():把数组转成字符串
-
charAt(index):返回指定位置的字符
-
concat():将两个数组连接在一起;