闭包是什么
一句话:闭包是指有权访问另一个函数作用域中变量的函数
使一个函数能访问另一个函数作用域中的变量
闭包优缺点
-
使用闭包的优点是可以避免全局变 量污染,延长变量生命周期。
-
缺点是由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在低版本IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。
防抖和节流,以及应用场景
简述:
(1)防抖:就是将一段时间内连续的多次只会执行最后一次触发。
(2)节流:减少一段时间内触发的频率
场景:
防抖:earch搜索联想,用户在不断输入值时或者登录时点击按钮
节流:鼠标拖拽
柯里化
柯里化是一个函数,它一次接受一个参数,并返回一个新函数,该函数期待下一个参数,利用闭包的原理。它是一种函数转换,将 f(a,b,c) 转换为可以被以 f(a)(b)(c) 的形式进行调用。
优点:
-
可以把函数式编程变得简洁,没有冗余。
-
可以将函数作为返回值输出,提前返回。
纯函数
1.返回值只和函数参数有关,与外部无关。无论外部发生什么样的变化,函数的返回值都不会改变
2.函数内部创建的变量。进行修改则不会产生副作用
总结:简单的说就是一个函数的返回结果只依赖其参数,并且执行过程中没有副作用。
作用域
所谓作用域就是变量可作用的范围
作用域链
在函数嵌套下,由内向外的链式规则就叫作用域链
事件轮询(Event Loop)
-
JS是单线程的,即同一时间只能执行一个任务
-
在JS中,任务分为同步任务和异步任务
-
同步任务:在主线程上执行的,形成一个执行栈,前一个任务执行完后执行后一个任务
-
比如for循环,事件绑定,Dom...
-
-
异步任务:通过回调函数实现在做任务的同时还能做其他任务
-
比如定时器的回调,ajax的回调,事件函数
-
-
-
异步任务又分为宏任务和微任务
-
宏任务:setTimeout,setInterval,ajax,dom事件监听...
-
微任务:promise,.then,async/await...
执行过程
-
优先执行同步任务,遇到异步任务推入任务队列中,等同步任务执行完再执行任务队列中的异步任务,异步任务中又分宏任务和微任务,先执行微任务,再执行宏任务
-
-
Ajax原理是什么?实现步骤?
通过 XmlHttpRequest 对象来向服务器发异步请求,从服务器获得数据,然后⽤ JavaScript 来操作 DOM ⽽更新⻚⾯。
步骤:
-
创建XMLHttpRequest对象,即创建一个异步调用对象.
-
创建一个新的HTTP请求,并指定该HTTP请求的方法、URL及验证信息.
-
设置响应HTTP请求状态变化的函数.
-
发送HTTP请求.
-
获取异步调用返回的数据.
什么是原型?
原型是function对象的一个属性,定义了构造函数创造出的对象的公共祖先。通过该构造函数产生的对象,可以继承该原型的属性和方法。
什么是prototype?
显示原型,是函数(不包含箭头函数)本身存在的一个属性,他指向的是一个对象,即为原型对象。
什么是 __ proto __ ?
可以称为隐式原型,或者叫连接点。是对象的一个属性,它里面存储的是该构造函数的原型对象,即prototype.
什么是构造函数?
构造函数其实是一种特殊的函数,主要用来初始化对象,也就是为对象成员变量赋初始值,它总与new关键字一起使用
什么是原型链?
当我们访问一个实例(例如person)的属性或方法时,会先在当前实例
上查找,若查找不到,会到原型
上查找,若原型上查找不到,就到原型的原型
上查找,若还是查找不到就指向null
。
原型的优点
如果方法不放在原型上继承的缺点:
-
如果方法不放在原型上面的话,构造函数创建对象的时候 ,当构造函数的方法很多的时候,其结构很冗余复杂。
-
每次实例一个对象,都需要开辟一个空间,造成内存严重浪费
-
每一个对象的属性和方法都是独立的,不会互相影响,这就无法做到数据共享
原型思想的继承的优点:
-
可以让构造函数结构变得简单
-
可以节省内存的空间
-
每一个实例可以共享原型上的方法和属性,达成数据共享
原型思想的继承的缺点:
***因为数据存在共享,所以可以被修改或者覆盖
hasOwnProperty() 和 in()操作符的区别
1.hasOwnProperty 只能检测对象实例属性,无法检测原型中的属性
2.in 只要能访问到的属性都可以检测到
this指向问题
第一种:
默认绑定 指向window
1: 直接在全局中打印this 直接指向 全局环境
2: 独立调用函数 指向window
第二种
隐式绑定 : 谁调用 指向谁
第三种 显示绑定
1:call
2:apply
3:bind
第四种:new绑定绑定在实例上
第五种:
箭头函数的this
箭头函数的this其实是根据他的上级决定,也就是他的this指向等于他的上级上下文。
优先级
箭头函数 > new > call,apply bind >obj. >默认绑定
call,apply,bind的区别
call: 第一个参数: 要更改的this指向(上下文) 第二个参数 传值的参数 单个形式传递呢
apply: 第一个参数: 要更改的this指向(上下文) 第二个参数 传值的参数 以数组的形式进行打包传递
bind: 有返回值,是一个新的函数。多次绑定的时候,只认第一次绑定的上下文
new都干了些什么事?
1: 在内存中创建一个空对象 {};
2: 将空对象的原型指向构造函数的原型;
3: 将空对象作为构造函数的上下文(即改变this指向);
4: 对有返回值的构造函数做判断。
继承
第一种:盗用构造函数
缺点: 1:无法继承原型上的属性和方法
2:构造函数的代码不简洁
第二种:原型链继承(原型继承)【 子类的原型继承父类的对象】
缺点:由于原型链是所有实例的公共祖先,所以容易引发 当原型更改,实例全部更改的结果。
第三种:组合继承(构造函数继承 + 原型继承)
父类被调用两次,浪费资源
第四种:原型式继承
第五种:寄生式继承
第六种:寄生式组合继承
1:利用Object.create()创建一个对象,将父类的原型赋值给新对象,并将此对象给子类原型 2:将子类的构造函数更改回自己。
1:什么是深拷贝
另外创建一个一摸一样的对象,新对象跟原对象不共享内存,修改新对象不会更改原对象
2:什么是浅拷贝
只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共存一块内存
递归的思想:
在深拷贝之前,会判断是否为数组或对象,如果是数组或对象,我会用for循环遍历每一项,赋值给事先准备好的空数组或空对象,如果是一个多维数组或对象 会选择使用递归去复制
深拷贝使用深度递归注意什么?
无论是否为深拷贝使用递归,平时我们使用递归的时候,也需要考虑是否存在时候循环引用的情况 在深拷贝中,我们可以将每一次的递归记录存在weakmat
中,每一次递归前先去查找是否存在相同记录,若存在,返回该记录