什么是闭包?闭包的用途是什么?闭包的缺点是什么?
1.闭包就是声明一个变量,声明一个函数,在函数内部访问外部的变量,那么这个函数加这个变量叫做闭包。
2.闭包的用途
- 从外部读取内部的变量
- 将创建的变量的值始终保持在内存中
3.闭包的缺点是
- 由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。
- 闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(
object
)使用,把闭包当作它的公用方法(Public Method
),把内部变量当作它的私有属性(private value)
,这时一定要小心,不要随便改变父函数内部变量的值
call、apply、bind 的用法分别是什么?
- call、apply和bind的作用都是改变this的指向,其中call和apply的区别在于它们的传参方式不同
- call可以传多个形参
- apply只能传一个数组形参
- 而bind和call、apply的区别在于bind不会立即调用,而是返回一个函数对象
请说出至少 10 个 HTTP 状态码,并描述各状态码的意义。
常见http状态码:
- 200 请求成功,一般用于GET与POST请求
- 204 表示客户端发送给客户端的请求得到了成功处理,但在返回的响应报文中不含实体的主体部分(没有资源可以返回)
- 206 表示客户端进行了范围请求,并且服务器成功执行了这部分的GET请求
- 301 永久性重定向,表示请求的资源被分配了新的URL,之后应使用更改的URL
- 302 临时性重定向,表示请求的资源被分配了新的URL,希望本次访问使用新的URL
301与302的区别:前者是永久移动,后者是临时移动(之后可能还会更改URL)
- 303 请求的资源被分配了新的URL,应使用GET方法定向获取请求的资源
302与303的区别:后者明确表示客户端应当采用GET方式获取资源
- 304 客户端发送附带条件(是指采用GET方法的请求报文中包含if-Match、If-Modified-Since、If-None-Match、If-Range、If-Unmodified-Since中任一首部)的请求时,服务器端允许访问资源,但是请求为满足条件的情况下返回改状态码
- 307 临时重定向,与303有着相同的含义,307会遵照浏览器标准不会从POST变成GET;(不同浏览器可能会出现不同的情况)
- 400 语义有误,当前请求无法被服务器理解或者请求参数有误
- 401 未经许可,需要通过HTTP认证
- 403 服务器拒绝该次访问(访问权限出现问题)
- 404 服务器无法根据客户端的请求找到资源(网页)
- 500 服务器错误,导致了无法完成对请求的处理
- 503 服务器暂时处于超负载或正在进行停机维护,无法处理请求
著名面试题:
如何实现数组去重?
假设有数组 array = [1,5,2,3,4,2,3,1,3,4]
你要写一个函数 unique,使得
unique(array) 的值为 [1,5,2,3,4]
也就是把重复的值都去掉,只保留不重复的值。
要求写出两个答案:
- 一个答案不使用 Set 实现
- 另一个答案使用 Set
- (附加分)使用了 Map / WeakMap 以支持对象去重的,额外加
- 说出每个方案缺点的,再额外每个方案加
不使用Set
unique = (array) => {
const hash = []
for(let i=0;i<array.length;i++){
hash[array[i]] = true
}
const result = []
for(let k in hash){
result.push(k)
}
return result;
}
缺点:此法只支持数字或者字符串数组,如果数组里面有对象,比如 array = [{number:1}, 2],就会出错。
使用Set
unique = (array)=>{
return [...new Set(array)]
}
缺点:API 太新,旧浏览器不支持
使用Map
unique = (array)=>{
let map = new Map();
let result = [];
for (let i=0;i<array.length;i++){
if(map.has(array[i])){//判断map中是否有这个key
continue
}else{ //如果没有,就加入result中
map.set(array[i],true);
result.push(array[i])
}
}
return result;
}
缺点:API 太新,旧浏览器不支持
DOM 事件相关
- 什么是事件委托
- 事件委托也称之为事件代理。是JavaScript中常用绑定事件的常用技巧。顾名思义,即是把原本需要绑定在子元素的响应事件委托给父元素,让父元素担当事件监听的职务。事件代理的原理是DOM元素的事件冒泡。
- 怎么阻止默认动作
- w3c 的方法是 e.preventDefault(),IE 则是使用 e.returnValue = false
- 在支持 addEventListener() 的浏览器中,也能通过调用时间对象的 preventDefault()方法取消时间的默认操作。不过,在 IE9 之前的 IE 中,可以通过设置事件对象的 returnValue 属性为 false
- 怎么阻止事件冒泡
- w3c 的方法是 e.stopPropagation(),IE 则是使用 e.cancelBubble = true
- 在支持 addEventListener() 的浏览器中,可以调用事件对象的一个 stopPropagation()
方法已阻止事件的继续传播。如果在同一对象上定义了其他处理程序,剩下的处理程序将依旧被调用,但调用 stopPropagation()
方法可以在事件传播期间的任何时间调用,它能工作在捕获阶段、事件目标本身中和冒泡阶段。
IE9 之前的IE不支持 stopPropagation() 方法。相反,IE事件对象有一个 cancleBubble 属性,设置这个属性为
true 能阻止事件进一步传播。( IE8 及之前版本不支持事件传播的捕获阶段,所以冒泡是唯一待取消的事件传播。)当前的 DOM 事件规范草案在 Event 对象上定义了另一个方法,命名为stopImmediatePropagation()。类似
stopPropagation(),这个方法组织了任何其他对象的事件传播,但也阻止了在相同对象上注册的任何其他事件处理程序的调用。
如何理解JS的继承
-
基于原型的继承给
基本原理是:将父类的实例赋值给子类的原型。
// 父类 function Staff() { this.company = 'tianchuang'; this.list = []; } // 父类的原型 Staff.prototype.getComName = function() { return this.company; }; // 子类 function Coder(name, skill) { this.name = name; this.skill = skill; } // 继承 Staff Coder.prototype = new Staff(); // 因为子类原型的指向已经变了,所以需要把子类原型的co ntructor指向子类本身 Coder.prototype.constructor = Coder; // 给子类原型添加属性 Coder.prototype.getInfo = function() { return { name: this.name, skill: this.skill }; }; let coder = new Coder('小明', 'javascript'); coder.getInfo(); // {name: '小明', skill: 'javascript'} coder.getComName(); // 'tianchuang' //这种继承方式的缺点:子类的实例可以访问父类的私有属性,子类的实例还可以更改该属性,这样不安全。
-
基于 class 的继承给
ES6 中有了类的概念,可以通过 class 声明一个类,通过 extends 关键字来实现继承关系。
class 与 ES5 构造函数的主要区别:class 只能通过 new 来调用,而构造函数则可以直接调用;
class 内部所有定义的方法,都是不可枚举的(non-enumerable)
class Parent {
constructor(name) {
this.name = name;
}
static say() {
return 'hello';
}
}
class Child extends Parent {
constructor(name, age) {
super(name); // 调用父类的 constructor(name)
this.age = age;
}
}
let child1 = new Child('kevin', '18');
console.log(child1);
数组排序
给出正整数数组 array = [2,1,5,3,8,4,9,5]
请写出一个函数 sort,使得 sort(array) 得到从小到大排好序的数组 [1,2,3,4,5,5,8,9]
新的数组可以是在 array 自身上改的,也可以是完全新开辟的内存。
不得使用 JS 内置的 sort API
let array = [2,1,5,3,8,4,9,5]
function sort(arr){
let max = arr.length - 1;
for(let j = 0;j<max;j++){
let done = true;
for(let i = 0;i < max - j;i++){
if(arr[i] > arr[i + 1]){
let temp = arr[i];
arr[i] = arr[i+1];
arr[i+1] = temp;
done = false;
}
}
if(done){
break;
}
}
return arr;
}
sort(array)
你对 Promise 的了解?
答题要点:
-
Promise 的用途
- Promise对象是JavaScript的异步操作解决方案,为异步操作提供统一接口,可以让异步操作写起来就像再写同步操作的流程,不必一层层的嵌套回调地狱,让回调地狱变得可控。
-
如何创建一个 new Promise
- new Promise((resolve,reject) => {})
-
如何使用 Promise.prototype.then
const p1 = new Promise( (resolve,reject) => { resolve('success!') } ) p1.then(value => { console.log(value) }, reason => { console.error(reason) })
-
如何使用 Promise.all
let p = Promise.all([1,2,3,Promise.resolve(444)]); console.log(p); // Promise { <state>: "fulfilled", <value>: Array[4] } let p2 = Promise.all([1,2,3,Promise.reject(555)]); console.log(p2); // Promise { <state>: "rejected", <reason>: 555 }
-
如何使用 Promise.race
let p1 = new Promise((resolve, reject) => {
setTimeout(resolve, 500, "one");
})
let p2 = new Promise(() => {
setTimeout(resolve, 200, "two")
})
Promise.all([p1, p2]).then((value) => {
console.log(value);
}, (reason) => {
console.log(reason)
})
//"two" 两个都完成,
// 但是p2更快
let p3 = new Promise((resolve, reject) => {
setTimeout(reject, 100, "three")
})
Promise.all([p3, p2]).then((value) => {
console.log(value);},(reason) => {console.log(reason)
}
)
// three 因为p3更快,
// 所以失败了//使用Promise.race: Promise.race()方法返回一个 promise,一旦迭代器中的某个promise解决或拒绝,返回的 promise就会解决或拒绝。
说说跨域。
要点:
- 什么是同源
- 同源策略(Same origin policy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。
- 非同源受到的限制:cookie不能读取、dom无法获得、ajax请求不能发送
- 什么是跨域
- 跨域是指跨域名的访问,以下情况都属于跨域:
- 域名不同 www.jd.com 与 www.taobao.comhttps://link.zhihu.com/?target=http%3A//www.taobao.com/
- 域名相同,端口不同 www.jd.com:8080 与 www.jd.com:8081
- 二级域名不同 http://item.jd.com 与 http://miaosha.jd.com
- 如果域名和端口都相同,但是请求路径不同,不属于跨域
- JSONP 跨域
- 由于同源策略的限制,XmlHttpRequest只允许请求当前源(域名、协议、端口)的资源。而动态添加一个
- 由于同源策略的限制,XmlHttpRequest只允许请求当前源(域名、协议、端口)的资源。而动态添加一个