什么是闭包?闭包的用途是什么?闭包的缺点是什么?
什么是闭包
闭包:内部函数总是可以访问其所在的外部函数中声明的参数和变量。声明在一个函数中的函数,叫做闭包函数。
存在不被释放或者互相引用的场景都可以叫做闭包。
代码举例:
function Counter(){
let num = 0
function add(){
num++
console.log(num)
}
return add
}
let addOne = Counter()
addOne()//1
addOne()//2
在Counter函数中创建了一个add函数,且add函数中用到了Counter函数的num变量,这就使得num和add函数构成了一个闭包。这个变量num一直存在于内中,我们很难在外部访问num变量,但是我们可以通过操作内部的函数来控制它。
闭包的用途
- 隐藏变量,只通过制定接口访问数据
const cache = (() => {
const store = {}
return {
get(key) {
return store[key]
},
set(key, val) {
store[key] = val
},
remove(key) {
delete store[key]
}
}
})()
这个箭头函数我们会得到一个对象,我会得到它的get,set和remove方法,我们打印这个cache的时候只会看到get,set和remove三个属性,store就被我们隐藏起来了。但是我们可以通过get,set和remove来操作store。
- 存储临时变量
function sum(a){
return function(b){
return a+b
}
}
const sum1 = sum(1)
const sum2 = sum(2)
console.log(sum1(3))//4
console.log(sum2(3))//5
声明好的sum1和sum2就会隐藏在闭包里,当我们在调用sum1(3)的时候就自动加上了sum1,同理在调用sum2(3)的时候,也自动加上了sum2。sum1和sum2就存储了一个临时的数据给我们用。
- 让对象拥有私有属性
constructor() {
let name
Object.assign(this,{
setName(newName){
name = newName
},
getName(){
return name
}
})
}
sayHi(){
console.log(`Hi,${this.getName()}`)
}
}
let p1 = new People
p1.setName('qyk')
p1.getName()//'qyk'
p1.sayHi()//"Hi,qyk"
sayHi()调用了上层的name,是个闭包。同时也让p1有了私有属性。
- 调用一个函数返回另一个函数
闭包的缺点
- 由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。
- 闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象使用,把闭包当作它的公用方法,把内部变量当作它的私有属性,这时一定要小心,不要随便改变父函数内部变量的值。
call、apply、bind 的用法分别是什么?
call
call 方法第一个参数是要绑定给this的值,后面传入的是一个参数列表。当第一个参数为null、
undefined的时候,默认指向window。
function add(a,b){
return a+b;
}
function.call(add,1,2)//3 其中1和2是连续的参数
apply
apply接受两个参数,第一个参数是要绑定给this的值,第二个参数是一个参数数组。当第一个参数为
null、undefined的时候,默认指向window。
function add(a,b){
return a+b;
}
funciton.apply(add,[1,2]//3 其中1和2数参数数组
当函数需要传递多个变量时, apply 可以接受一个数组作为参数输入, call 则是接受一系列的单独变量。
bind
第一个参数是this的指向,从第二个参数开始是接收的参数列表。bind 方法不会立即执行,而是返回一个改变了上下文 this 后的函数。
fn.bind(x,y,z)
不会执行 fn,而是会返回一个新的函数
新的函数执行时,会调用 fn,调用形式为fn.call(x, y, z)
,其中 x 是 this,y 和z 是其他参数
function add(a, b){
return a+b;
}
var foo1 = add.bind(add, 1,2);
foo1(); //3 只有调用才会执行
在 ES6 的箭头函数下, call 和 apply 将失效。
请说出至少 10 个 HTTP 状态码,并描述各状态码的意义。
- 状态码100表示继续。客户端应继续其请求
- 状态码101表示切换协议。服务器根据客户端的请求切换协议
- 状态码200表示请求成功。一般用于GET与POST请求
- 状态码201表示已创建。成功请求并创建了新的资源
- 状态码202表示已接受。已经接受请求,但未处理完成
- 状态码203表示非授权信息。请求成功。但返回的meta信息不在原始的服务器,而是一个副本
- 状态码300表示多种选择。请求的资源可包括多个位置,相应可返回一个资源特征与地址的列表用于用户终端(例如:浏览器)选择
- 状态码303表示查看其它地址
- 状态码305表示使用代理。所请求的资源必须通过代理访问
- 状态码306表示已经被废弃的HTTP状态码
- 状态码400表示客户端请求的语法错误,服务器无法理解
- 状态码403表示服务器理解请求客户端的请求,但是拒绝执行此请求
- 状态码404表示服务器无法根据客户端的请求找到资源(网页)
如何实现数组去重?
假设有数组 array = [1,5,2,3,4,2,3,1,3,4]
你要写一个函数 unique,使得
unique(array) 的值为 [1,5,2,3,4]
也就是把重复的值都去掉,只保留不重复的值。
使用indexOf
思路:新建一个空的结果数组,for 循环原数组,判断结果数组是否存在当前元素,
如果有相同的值则跳过,不相同则push进数组。
let arr = [1, 5, 2, 3, 4, 2, 3, 1, 3, 4]
function unique(arr) {
let array = [];
for (let i = 0; i < arr.length; i++) { // 首次遍历数组
if (array.indexOf(arr[i]) === -1) { // 判断索引有没有等于
array.push(arr[i])
}
}
return array
}
console.log(unique(arr));
缺点:无法对NaN和对象去重
使用set
let arr = [1, 5, 2, 3, 4, 2, 3, 1, 3, 4]
function unique (arr) {
return Array.from(new Set(arr))
}
console.log(unique(arr))
缺点:无法去重对象,且有兼容性问题。API太新,旧浏览器不支持
使用map
function unique(arr) {
const map = new Map()
const newArr = []
arr.forEach(item => {
if (!map.has(item)) { // has()用于判断map是否包为item的属性值
map.set(item, true) // 使用set()将item设置到map中,并设置其属性值为true
newArr.push(item)
}
})
return newArr
}
console.log(unique(arr))
缺点:API 太新,旧浏览器不支持。
DOM 事件相关
什么是事件委托
由于事件会在冒泡阶段向上传播到父节点,因此可以把子节点的监听函数定义在父节点上,由父节点的监听函数统一处理多个子元素的事件。这种方法叫做事件委托。 通俗点讲就是委托一个元素帮我监听我本该监听的元素。
怎么阻止默认动作
w3c的方法是e.preventDefault()
,IE则是使用e.returnValue = false
<a href="http://www.cnblogs.com/yycode/" id="testA" >caibaojian.com</a>
var a = document.getElementById("testA");
a.onclick =function(e){
if(e.preventDefault){
e.preventDefault();//W3C
}else{
window.event.returnValue = false;//IE
}
}
怎么阻止事件冒泡
w3c使用e.stopPropagation()
,IE使用e.cancelBubble = true
function stopBubble(e) {
if ( e && e.stopPropagation ){
e.stopPropagation(); //W3C
}else{
window.event.cancelBubble = true; //IE
}
}
如何理解 JS 的继承?
基于原型的继承
实例化一个新的函数,子类的原型指向了父类的实例,子类就可以调用其父类原型对象上的共有属性。
function Parent() {
this.parentName = '父类';
}
Parent.prototype.getParentName = function() {
return this.parentName;
};
function Child() {
this.childName = '子类';
}
Child.prototype = new Parent();//继承Parent
Child.prototype.getChildName = function() {
return this.childName
};
let c = new Child();
console.log(c.getParentName()); // '父类'
function Parent(name1){
this.name1 = name1
}
Parent.prototype.pMethod = function(){
console.log(this.name1)
}
function Child(name2, name1){
Parent.call(this, name1) // 得分点
this.name2 = name2
}
Child.prototype.__proto__ = Parent.prototype
Child.prototype.cMethod = function(){
console.log(this.name2)
}
缺点:子类的实例可以访问父类的私有属性,子类的实例还可以更改该属性,不安全
基于class的继承
如需创建类继承,使用 extends 关键字。
class Parent{
constructor(name1){
this.name1 = name1
}
pMethod(){
console.log(this.name1)
}
}
class Child extends Parent{
constructor(name2, name1){
super(name1) // 得分点
this.name2 = name2
}
cMethod(){
console.log(this.name2)
}
}
数组排序
给出正整数数组 array = [2,1,5,3,8,4,9,5]
请写出一个函数 sort,使得 sort(array) 得到从小到大排好序的数组 [1,2,3,4,5,5,8,9]
新的数组可以是在 array 自身上改的,也可以是完全新开辟的内存。
let min = (numbers)=>{
if(numbers.length>2){
return min([numbers[0],min(numbers.slice(1))])
}else {
return Math.min.apply(null,numbers)
}
}//求出最小值的算法
let minIndex = (numbers) =>
numbers.indexOf(min(numbers))//标记最小值
let sort = (numbers)=>{
if(numbers.length>2){
let index = minIndex(numbers)
let min = numbers[index]
numbers.splice(index,1)
return [min].concat(sort(numbers))
}else{
return numbers[0]<numbers[1]? numbers :numbers.reverse()
}
}
let array =[2,1,5,3,8,4,9,5]
sort(array)
对 Promise 的了解?
promise的用途
Promise 用于避免回调地域,让代码看起来更同步
创建一个 new Promise
function fn(){
return new Promise((resolve, reject)=>{
成功时调用 resolve(data)
失败时调用 reject(reason)
})
}
使用 Promise.prototype.then
const promise1 = fn() // 得到 promise1 对象
fn().then(success, fail).then(success2, fail2).catch(fail3)
或者
promise1.then(success, fail).then(success2, fail2).catch(fail3)
均可
使用Promise.all
Promise.all([promise1, promise2]) 并行,等待所有 promise 成功。
如果都成功了,则 all 对应的 promise 也成功;如果有一个失败了,则 all 对应的 promise 失败。
使用 Promise.race
Promise.race([promise1, promise2]),返回一个
promise,一旦数组中的某个promise解决或拒绝,返回的 promise就会解决或拒绝。
说说跨域
什么是同源
源:协议+域名+端口号
Window.origin或者location.origin可以得到当前的源
两个URL的协议、域名和端口号完全一致那么这两个URL就是同源
同源策略就是,浏览器规定:如果JS运行在源A里,那么就只能获取源A的数据,不能获取源B的数据,
即不允许跨域。这是浏览器的功能。浏览器为了主动预防偷数据的问题,设置了严格的同源策略
什么是跨域
跨域,是指浏览器不能执行其他网站的脚本。
它是由浏览器的同源策略造成的,是浏览器对JavaScript实施的安全限制。
当一个请求url的协议、域名、端口三者之间任意一个与当前页面url不同即为跨域。
JSONP跨域
我们在跨域的时候,由于当前浏览器不支持CORS,或者因为某些条件不支持CORS,我们必须使用一种方法来进行跨域。于是我们请求一个JS文件,这个JS文件回执行一个回调,回调里面有我们的数据。回调的名字可以通过随机数生成的,我们把这个随机数以callback的参数传给后台,后台会把函数返回给我们并且执行。
缺点:由于它是一个script标签,所以读不到AJAX那么精确的status值,无法知道状态码是什么,也只能发送GET请求,JSONP不支持POST
CORS跨域
CORS(跨域资源共享)
跨源资源共享 (CORS)是一种基于 HTTP 头的机制,该机制通过允许服务器标示除了它自己以外的其它 origin(域,协议和端口),使得浏览器允许这些 origin 访问加载自己的资源。浏览器默认不同源之间不能互相访问数据,但是我们想让两个网站互相访问。我们就用CORS,如果要共享数据就需要提前声明。例如,源B要访问源A,源A就要在响应头里声明源B可以访问:
response.setHeader("Access-Control-Allow-Origin","http://foo.example")
网友评论