闭包
什么是闭包?
闭包是一种函数的写法,这种写法可以记住函数内所用到的外部变量的值以及绑定了这些变量的环境,
闭包的用途是什么?
答:闭包的用途是保护变量和保存变量
保护变量的例子
- 我在函数内定义的内部变量,外部即使可以访问,但无法篡改,因此是保护变量 保护变量的最经典例子,事件回调 点赞写法
function demo() {
let a = 1;
return function () {
return a;
}
}
var a = demo();
console.log(a());
保存变量的例子
- 异步函数setTimeout在执行栈执行完同步代码后依旧可以读取到当时的数值i,这就是因为暂存了变量i
for(let i=0;i<=5;i++){
setTimeout(()=>{
console.log(i)
},0)
}
闭包的缺点是什么?
答:缺点是闭包常驻内存,内存消耗很大,由于保留了当时的词法作用域,使得变量无法被释放,所以不能大量使用。
call、apply、bind的用法是什么?
var xw = {
name: "小王",
say: function (school,grade) {
console.log(`${this.name}在上${school}${grade}年级`)
},
};
var xh = {
name: "小红"
};
xw.say('小学',5)
xw.say.call(xh,'初中',3)
xw.say.apply(xh,['初中',3])
let xhSay=xw.say.bind(xh,'初中'); // 我把其中一个参数绑死,另外一个参数灵活传参
xhSay(6);
- call与apply第一个参数是this所指的对象,后面的参数根据原函数而定,call是一个个传,而apply是将参数打包成一个数组传,并且两者都是立即执行。
- bind第一个参数是this所指的对象,bind原意就是将原函数所传的参数绑死并生成一个新的参数,因此除了第一个参数可以绑死以外,后面的参数都可以绑死。
说出至少 10 个 HTTP 状态码,并描述各状态码的意义
- 100 服务器已经收到请求头,请求者应当继续提出请求。服务器返回此代码表示已收到请求的第一部分,正在等待其余部分
- 200 已成功处理了请求。出现此状态码是表示正常状态
- 202 服务器已接受请求,但尚未处理
- 400 服务器不理解请求的语法
- 403 服务器拒绝请求。
- 404 服务器找不到请求的资源。 例如,对于服务器上不存在的资源经常会返回此代码。
- 405 请求中的方法不被允许
- 410 如果请求的资源已永久删除,服务器就会返回此响应
- 500 服务器遇到错误,无法完成请求
- 503 服务器目前无法使用(由于超载或停机维护)。 通常,这只是暂时状态。
如何实现数组去重?
indexOf
let arr = [1,5,2,3,4,2,3,1,3,4];
function unique(arr) {
let arr1 = [];
for (var i = 0; i < arr.length; i++) {
if (arr1.indexOf(arr[i]) === -1) {
arr1.push(arr[i]);
}
}
return arr1;
}
let arr1=unique(arr)
console.log(arr1)
对象属性名唯一性
let arr = [1,5,2,3,4,2,3,1,3,4];
function onlyArr(arr){
let obj={},arr1=[];
for(var i=0;i<arr.length;i++){
if(!obj[arr[i]]){
arr1.push(arr[i])
obj[arr[i]]=1;
}
}
return arr1
}
reduce+includes
- 箩筐思想
- 有的话就不把东西放进箩筐,把箩筐传递下去
- 没的话就把东西放进箩筐,把箩筐传递下去
// reduce+includes
let arr = [1, 5, 2, 3, 4, 2, 3, 1, 3, 4];
let arr1=arr.reduce((prev,cur)=>prev.includes(cur)?prev:[...prev,cur],[])
console.log(arr1)
ES6数据结构 Set
- Set特点是元素唯一,因此我们可以数组转集合再集合转数组
- 缺点是没办法去重数组中的空对象
let arr = [1, 5, 2, 3, 4, 2, 3, 1, 3, 4];
function unique(arr) {
let arr1;
// new Set(arr); 数组转Set集合
arr1 = Array.from(new Set(arr)); //Set集合再转数组
// 或者你写[...new Set(arr)]
// 局限性是没办法去重数组中的空对象
return arr1;
}
let arr1=unique(arr)
console.log(arr1);
ES6数据结构 Map
let arr = [1, 5, 2, 3, 4, 2, 3, 1, 3, 4];
function unique(arr) {
let map = new Map();
let arr1 = new Array();
for (let i = 0; i < arr.length; i++) {
if (!map.get(arr[i])) {
map.set(arr[i], 1);
arr1.push(arr[i]);
}
}
return arr1;
}
let arr1 = unique(arr);
console.log(arr1);
DOM 事件相关
什么是事件委托?
答:事件委托就是把本该绑定在目标元素身上的事件却绑定给它的父级元素身上,虽然不再绑定于目标元素自身,但是利用事件流的冒泡机制特性,用户依旧能够通过目标元素去触发该事件。
好处有如下两点:
1.即使目标元素尚未被创建,由于已经提前绑定给父级,在目标元素创建后,即可触发该事件
2.节省内存资源与代码量:当一个父级元素内存在大量相同的需要监听的元素,通过事件委托的形式我们便不再需要逐一对目标元素进行事件监听
怎么阻止默认动作?
e.preventDefault()
怎么阻止事件冒泡?
e.stopPropagation()
你如何理解 JS 的继承?
继承主要分为两大类,基于prototype的继承与es6官方提供的class+extends写法
在es6之前是基于prototype的继承,主要有六种,原型链继承,借用构造函数继承,组合继承,原型式继承,寄生式继承,寄生组合式继承;这些都是民间写法,其中比较完整的写法是寄生组合式继承。
幸运的是es6给我们提供了庆幸 ES6 中为我们提供了两个关键字可以很轻松的实现上述的继承方式:class 和 extends。
class 用来声明类,也就是js中充当类的自定义函数,extends 表示用哪个作为父类来继承。还有一个关键词super 表示父类的构造器函数,可以向内部传参。
寄生组合式继承
function Parent(name) { // 父构造函数
this.name = name
this.likeFood = ["水果", "鸡", "烤肉"]
}
function Child(name, age) { //子构造函数
Parent.call(this, name)
this.age = age
}
Parent.prototype.getName = function() {
return this.name
}
inheritPrototype(Child, Parent)
// inheritPrototype作用是让parentFunc.prototype作为每个子对象的第二层原型
function inheritPrototype(childFunc, parentFunc) {
var prototype = realizeInheritance(parentFunc.prototype)
prototype.constructor = childFunc
childFunc.prototype = prototype
}
function realizeInheritance(parent) {
function tempFunc() {}
tempFunc.prototype = parent
return new tempFunc()
}
Child.prototype.getAge = function() {
return this.age
}
var chongqingChild = new Child('重庆孩子', 18)
var guangdongChild = new Child('广东孩子', 19)
chongqingChild.likeFood.push('花椒')
console.log(chongqingChild.likeFood) // ["水果", "鸡", "烤肉", "花椒"]
console.log(guangdongChild.likeFood) // ["水果", "鸡", "烤肉"]
console.log(chongqingChild.name) // "重庆孩子"
console.log(chongqingChild.getName()) // "重庆孩子"
console.log(chongqingChild.getAge()) // 18
解释:调用realizeInheritance(parentFunc)函数时可以产生一个空对象,这个空对象.prototype->parentFunc;
realizeInheritance这个函数内部利用临时函数来处理,寄生组合式继承的这一部分是沿袭了原型式继承的特点;
inheritPrototype这个函数作用是让parentFunc.prototype成为每个经有childFunc创建的子对象的第二层.__proto__属性的指向所在
寄生组合式继承的优点是:创建的子对象的实例属性是独立的,并不会造成引用类型数据共享的情况,继承了自身构造函数的原型属性与原型方法,还继承了父构造函数的原型属性和原型方法。
es6class的继承
class Person {
constructor(name, age) {
console.log('父类构造函数执行。')
this.name = name
this.age = age
this.commonLikeFood = ["水果", "鸡", "烤肉"]
}
showInfo() {
console.log(`我是${this.name},我今年${this.age}岁了`)
}
showLikeFood() {
console.log(`我是${this.name},我喜欢吃${this.commonLikeFood}`)
}
}
class Child extends Person{
constructor(name, age) {
super(name, age)
console.log('子类构造函数执行。')
}
}
let child1 = new Child('小明', 27)
let child2 = new Child('小红', 28)
child1.commonLikeFood.push('火锅')
child1.showInfo() // 我是小明, 我今年27岁了
child1.showLikeFood() // 我是小明, 我喜欢吃水果,鸡,烤肉,火锅
child2.showInfo() // 我是小红, 我今年28岁了
child2.showLikeFood() // 我是小红, 我喜欢吃水果,鸡,烤肉
你对 Promise 的了解?
Promise 的用途
Promise是异步编程的一种解决方案,获取异步操作的消息.
如何创建一个 new Promise
var promise = new Promise(function(resolve, reject) {
// 异步处理
// 处理结束后会自动调用resolve 或 reject
});
如何使用 Promise.prototype.then
Promise.prototype.then 方法返回的是一个新的 Promise 对象,因此可以采用链式写法一直执行,
.then有两个回调函数作参数,第一个参数是让Promise对象执行异步任务成功时执行的回调,第二个则是失败时执行的。
如何使用 Promise.all(可查 MDN)
Promise.all(iterable)是一个静态方法用于处理多个异步任务,其参数是一个可迭代对象,通常是一个数组,且数组每一项都是promise对象。Promise.all会返回一个 Promise 实例以便后续.then(),- 当参数内所有的 promise对象都呈现
fullfilled状态或参数中不包含promise对象时,那么执行.then的第一个函数会接收到所有的成功的结果,结果也是一个数组,比如:
Promise.all(
[
Promise.resolve(1),
Promise.resolve(2),
Promise.resolve(3)
])
.then(res => console.log(res),err=>console.log(err)) // [1,2,3]
- 如果参数中的promise对象有一个失败(rejected)的状态,那么执行Promise.all().then的第二个函数,且仅仅输出这个失败 promise对象的结果
Promise.all(
[
Promise.resolve(1),
Promise.reject(2),
Promise.resolve(3)
])
.then(res => console.log(res),err=>console.log(err)) // 2 而不是 [2]
如何使用 Promise.race(可查 MDN)
Promise.race(iterable) 方法返回一个 promise,可迭代对象中的哪个promise率先呈现fullfilled或rejected,比如有一个promise对象率先fullfilled,则调用.then中的成功回调
跨域
什么是同源
两个网站的域名,协议,端口三者必须一致才叫同源
什么是跨域
一个域A的页面去请求另一个域B的资源,这就存在要突破浏览器的限制了 只要两个网站之间的主域名不同,子域名不同,端口不同,协议不同,其中有一项满足,则算跨域
JSONP 跨域
JSONP是利用JS语言不受浏览器跨域限制的特性,让JS成为数据的载体,由域B的后端将JSON数据反序列化生成JS对象放在JS文件中进行传输,但所传的数据并不局限于JSON数据。
域A的网站原本想要访问域B的json文件,但是由于跨域了,因此域A需要用js动态创建script标签以访问域B的js文件,域B后端接收到请求之后需要准备一个js文件,将原本要返回的json数据解析成js对象然后重新渲染到js文件中,再把这个js文件返回给网站A。
需要注意的是,由于JSONP缺乏对访客的控制,任何一个网站只要通过script标签的方式去加载js都可以无限制地从你这里获取数据了,因此需要检查对方网站的referer,确认是白名单中的网站才让它跨域.
JSONP的限制是: 只能发get请求,不能发post请求
CORS 跨域
CORS = 网站A的前端发起ajax + 网站B的后端对网站A的域名设置响应头Access-Control-Allow-Origin特许网站A跨域请求
快速排序
function quickSort(arr){
if(arr.length<=1){
return arr
}
let pivot=Math.floor(arr.length/2);
let arrPivot=arr.splice(pivot,1)[0];
let left=[];
let right=[];
for(let i=0;i<arr.length;i++){
if(arr[i]<arrPivot){
left.push(arr[i])
}else{
right.push(arr[i])
}
}
return quickSort(left).concat([arrPivot],quickSort(right))
}
let arr = [2,1,5,3,8,4,9,5]
let arr1 = quickSort(arr)
console.log(arr1)
JS相关题目
关于作用域链的题目
数组.forEach相关题目
解析: 按理说我们都知道forEach是不会修改原数组,不会外传任何值,只会遍历执行自定义操作,
但是这里代码中不是v++,而是写了arr[i]++,因此这样的方式显然是可以修改原数组的