前端知识点
JavaScript
ES6新特性
声明变量方法
-
let 关键字
- let 声明的变量只在块级作用域有效
- 块级作用域:两个大括号之间
- 原来的作用域是一个函数,用 let 的话作用域变成了
{}
之间 - let 可以防止循环变量变成全局变量
- let 没有变量提升 (也就是不能先使用后定义)
- 暂时性死区:外部同名变量无法穿透大括号作用域
javascriptfor(let i = 0;i < 2;i++) { console.log(i) //正常输出 } console.log(i); // 报错,i 变量未定义
javascript// 暂时性死区 var num = 10; if(true) { let num = 20; console.log(num); } // 输出结果 20
- let 声明的变量只在块级作用域有效
-
const 关键字
- const:声明常量,也就是内存地址不能改变的量
- const 声明的常量有块级作用域 (只能在两个大括号之间使用)
- const 定义的常量时必须赋初始值
const PI = 3.14;
PI = 100;
// 报错:TypeError: Assignment to constant variable.
const arr = [100,200];
arr[0] = 'a';
arr[1] = 'b';
console.log(arr);
// 输出:['a','b']
arr = ['a','b'];
// 报错
const实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址不得改动。对于简单类型的数据(数值、字符串、布尔值),值就保存在变量指向的那个内存地址,因此等同于常量。
但对于复合类型的数据(主要是对象和数组),变量指向的内存地址,保存的只是一个指针,const只能保证这个指针是固定的,至于它指向的数据结构是不是可变的,就完全不能控制了。因此,将一个对象声明为常量必须非常小心。
-
解构赋值
- 解构赋值可以把对象或者数组里面的 属性/值 从里面取出,赋给其他的变量
- 里面不止可以使用 let 来定义,还可以使用 var
- 假如变量名比数组里面的内容多,则未分配到内容的变量值为 undefined
let arr = [1, 2, 3];
let [a, b, c, d] = arr;
console.log(a);// 输出 1
console.log(d);//undefined
- 对象结构同理:
let person = { name: '菜鸟十三', age: 10 }
let { name, age } = person;
console.log(name);
console.log(age);
// 输出:菜鸟十三 10
// 可以使用键值对的形式来获取变量
let {name: myname,age: myage} = person;
// 冒号左侧仅用于匹配,冒号右侧的变量用来赋值
console.log(myname);
console.log(myage);
// 输出:菜鸟十三 10
变量名必须和属性名相同才能取到这个变量的值
-
箭头函数
- 箭头函数:
() => {}
小括号放形参,大括号放函数体 - 通常把箭头函数赋值给一个变量来调用
const fn = () => { console.log('菜鸟十三'); }
//等价于
const fn = function(){
console.log('菜鸟十三');
}
- 如果函数中只有一句代码,且执行结果就是返回值,可以省略大括号
- 如果形参只有一个,可以省略小括号
function sum(num1,num2) {
return num1 + num2;
}
// 上下两个函数等价
const sum = (num1,num2) => num1 + num2;
-
剩余参数
- 当形参大于实参个数时,可以将剩下的参数表示为一个数组
- 和 arguments 很像,但是像箭头函数中没有 arguments,所以用箭头函数举例
- 使用时参数前面加三个点
...args
代表接收所有参数
const sum = (...args) => {
let total = 0;
args.forEach(item => total += item);
}
sum(10, 20, 30);
- 剩余参数还可以用在数组中接受多余的参数 (例如返回的是一个数组的函数可以使用这个特性)
let arr = [1, 2, 3];
let [num, ...num2] = arr;
console.log(num); // 1
console.log(num2); // [2, 3]
-
扩展运算符
- 扩展运算符
...arr
把数组或者对象转为参数序列
let arr = [1, 2, 3];
console.log(...ary);
// 等价于 console.log(1,2,3);
// 输出 1 2 3
- 扩展运算符合并数组:
let arr1 = [1, 2];
let arr2 = [3, 4];
let arr3 = [...arr1, ...arr2];
// 也可以使用 push 方法合并数组
arr1.push(...arr2);
- 这个方法和
apply(this, [arr])
方法有点像,apply也可以把数组作为参数去调用,只不过他多了一个改变 this 指向的方法
-
数组,字符串的扩展方法
- find 方法,查找数组中第一个满足条件的值,没有找到返回 undefined
- 之前的 forEach 方法
let arr = [{
id: 1,
name: '菜鸟十三'
}];
let target = arr.find((item, index) => item.id == 1);
console.log(target)
// 输出 arr里的对象:{id: 1, name: '菜鸟十三'}
- findIndex 方法 找出第一个符合条件的数组索引,没有找到返回 -1
let arr = [1, 5, 10, 15];
let index = arr.findIndex((value, index) => value > 9);
console.log(index); // 2
- includes 方法,数组是否包含给定的值,返回布尔值
let arr = [1, 2, 3];
let status = arr.includes(2);
console.log(status) // true
status = arr.includes(5);
console.log(status) // false
- 模板字符串用反引号定义:
let name = `菜鸟十三`;
- 模板字符串可以解析变量 (就像 innerHTML 可以解析标签一样)
let name = '菜鸟十三';
let sayHello = `hello,${name}`;
// hello,菜鸟十三
- 模板字符串内部可以换行
let name = `菜
鸟
十
三`;
console.log(name);
// 菜
// 鸟
// 十
// 三
- 在模板字符串里可以调用函数,显示函数的返回值
const name = () => {
return '菜鸟十三';
};
let sayHello = `${name()} hello`;
// 菜鸟十三 hello
var 、let、const之间的区别
var声明变量可以重复声明,而let不可以重复声明
var是不受限于块级的,而let是受限于块级
var会与window相映射(会挂一个属性),而let不与window相映射
var可以在声明的上面访问变量,而let有暂存死区,在声明的上面访问变量会报错
const声明之后必须赋值,否则会报错
const定义不可变的量,改变了就会报错
const和let一样不会与window相映射、支持块级作用域、在声明的上面访问变量会报错
var | let | const |
---|---|---|
函数级作用域 | 块级作用域 | 块级作用域 |
存在变量提升 | 不存在变量提升 | 不存在变量提升 |
值可更改 | 值可更改 | 值不可更改 |
const 关键字不需要实时监听变量的变化,所以效率要比 var,let 高
使用箭头函数应注意什么?
- 用了箭头函数,this就不是指向window,而是父级(指向是可变的)
- 不能够使用arguments对象
- 不能用作构造函数,这就是说不能够使用new命令,否则会抛出一个错误
- 不可以使用yield命令,因此箭头函数不能用作 Generator 函数
ES6 新增方法
- 判断字符串是否以某字符串开头或结尾,返回布尔值
let str = 'noobming';
str.startsWith('noob');
// true
str.endsWith('ming');
// true
- 将原字符串重复 n 次,返回重复后的字符串
'noobming'.repeat(2);
// "noobmingnoobming"
set 数据结构
- set 结构类似于数组,但是成员的值都是唯一的,没有重复的值
- set 是使用构造函数生成 set 数据结构的
- 创建的时候可以传递数组进去,会自动转换为 set 数据结构**(不能像数组一样直接在构造函数中写值,需要把他包裹成一个数组传进去)**
- set 数据结构里面有 size 属性,表示里面有多少种类的值
const s = new Set(['1', '2', '3']);
console.log(s.size); // 3
// 数组作为参数
const s1 = new Set([1, 2, 2]);
console.log(s1.size);
// 输出 2
- set 和 array 的互化 (可以利用 set 为数组去重)
const s = new Set([1, 2, 2]);
// 转化为数组
const arr = [...s];
console.log(arr) //[1, 2]
- set 常用方法:
- add(value):添加个值,返回 Set 结构本身
- delete(value):删除某个值,返回一个布尔值,表示删除是否成功
- has(value):返回一个布尔值,表示该值是否为 Set 的成员
- clear():清除所有成员,没有返回值
const s = new Set([1, 2, 2]);
s.add(3)
console.log(s) //{1, 2, 3}
s.delete(1)
console.log(s) // {2, 3}
let status = s.has(2)
console.log(status) // true
status = s.has(5)
console.log(status) //false
s.clear()
console.log(s) // {size: 0}
- set 遍历:同样也用
forEach()
方法
s.forEach(value => console.log(value));
ES6 Class 类
- 在ES6中,class (类)作为对象的模板被引入,可以通过 class 关键字定义类。
- class 的本质是 function。
- 它可以看作一个语法糖,让对象原型的写法更加清晰、更像面向对象编程的语法。
- 类定义不会被提升,这意味着,必须在访问前对类进行定义,否则就会报错。
- 类中方法不需要 function 关键字。
- 方法间不能加分号。
- 类声明:
//定义一个MyClass类
class MyClass {
//new 时就开始调用,相当于python 类中得__init__()方法
constructor() {
console.log("初始化?")
}
// 类成员方法
first(){
console.log("完成")
}
//静态方法
static sec(){
console.log("静态方法")
}
}
let CLas = new MyClass() // 输出初始化?
CLas.first() //输出:完成
// CLas.sec() //报错
MyClass.sec() //静态方法
- 类表达式可以为匿名或命名。
// 匿名类
let Example = class {
constructor(a) {
this.a = a;
}
}
// 命名类
let Example = class Example {
constructor(a) {
this.a = a;
}
}
- 通过 extends 实现类的继承。
- 子类 constructor 方法中必须有 super ,且必须出现在 this 之前。
//定义一个MyClass类
class father {
//new 时就开始调用,相当于python 类中得__init__()方法
constructor(a,b) {
this.a = a;
this.b = b;
}
first(){
console.log("十三")
}
}
//继承father,包括父类的属性和方法
class child extends father{
constructor(c,d) {
//super必须在this之前
super();
this.c = c;
this.d = d;
this.a = c;
console.log(this.c);//1
console.log(this.d);//2
console.log(this.a)//1
this.first()//十三
}
}
//通过new实例化
let chid_ = new child(1,2)
setTimeout、Promise、Async/Await的区别
setTimeout是异步执行函数 , 当js主线程运行到此函数时,不会等待settimeout中的回调函数 ,会直接进行settimeout下面的语句(尽管setTimeout的延迟时间为0时) 当执行完当前事循环的时候,settimeout中的回调会在下次(或者某一个)事件循环中被执行。
console.log('setTimeout start');
setTimeout(function(){
console.log('setTimeout execute');
})
console.log('setTimeout end ');
//setTimeout start => setTimeout end => setTimeout execute
Promise 本身是同步的立即执行函数,当在执行体中执行resolve()或者reject的时候,此时是异步操作
会先执行then/catch(异步执行)等,等主栈完成后,才会去执行resolve()/reject中的方法,
console.log('script start');
var promise1 = new Promise(function (resolve) {
console.log('promise1');
resolve();
console.log('promise1 end');
}).then(function () {
console.log('promise2');
})
setTimeout(function () {
console.log('setimeout');
})
console.log('script end');
//script start => promise1 => promise1 end =>script end =>promise2 => settimeout
根据以上解释: 主栈开始时从上到下执行,会先打印出 script start 遇到了promise函数,由于promise函数是同步立即执行函数,因此会立即执行方法体内的程序,打印出promise1,遇到了resolve()函数,跳过(会在then/catch之后执行) 打印 promise1 end.此时Promise()函数的执行 ,已经执行完毕, 主栈继续下面语句,遇到settimeout时异步执行, 然后会执行 script end,结束 ,此时会执行下一个事件循环, 然后会打印出promise2,然后在打印出settimeout
async函数返回一个promise对象,当函数执行的时候,一旦遇到await就会先返回,等到触发的异步操作完成(await的函数),在执行函数体后面的语句,可以理解为,async让出了线程,跳出了async函数体,因此await函数后的语句相当于在then回调中执行.
await的含义为等待,也就是 async 函数需要等待await后的函数执行完成并且有了返回结果(Promise对象)之后,才能继续执行下面的代码。await通过返回一个Promise对象来实现同步的效果。
async function async1(){
console.log('async1 start');//NO.2
await async2();
//等待 async2()返回之后 再执行下面的语句 ,
// 相当于将 console.log('async1 end')异步化了 相当于 console.log('async1 end')在then之后执行了
console.log('async1 end')//NO.5
}
async function async2(){
console.log('async2')//NO.3
}
//
console.log('script start');//NO.1
async1();
console.log('script end')//NO.4
//script start->async1 start->async2->script end->async1 end
promise
promise有几种状态,什么时候会进入catch?
-
三个状态:pending、fulfilled、reject
-
两个过程:padding -> fulfilled、padding -> rejected当pending为rejectd时,会进入catch
Promise 中rejec和catch处理上有什么区别
-
reject 是用来抛出异常,catch 是用来处理异常
-
reject 是 Promise 的方法,而 catch 是 Promise 实例的方法
-
reject后的东西,一定会进入then中的第二个回调,如果then中没有写第二个回调,则进入
catch
-
网络异常(比如断网),会直接进入catch而不会进入then的第二个回调
闭包
由于在javascript中,只有函数内部的子函数才能读取局部变量,所以说,闭包可以简单理解成“定义在一个函数内部的函数“。
所以,在本质上,闭包是将函数内部和函数外部连接起来的桥梁。
function a(){
var aa = 0;
function b(){
aa ++;
console.log(aa);
}
return b;//将b函数返回
}
var ab = a();
ab(); //1
ab(); //2
使用闭包的注意点
由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。
防抖与节流
什么是函数防抖
函数防抖(debounce),就是指触发事件后,在 n 秒内函数只能执行一次,如果触发事件后在 n 秒内又触发了事件,则会重新计算函数延执行时间。
//方法一:
//步骤:
// 1-创建需要防抖函数
// 2.创建处理防抖函数,里面相当于一个闭包,返回一个匿名函数
// 3.创建定时器/清空定时,重新计时
//定义一个防抖函数
var debounce = function(func,await=50) {
var timer = 0
//相当于一个闭包
return function () {
//每次调用,都会清空计时器,重新计时
if (timer) clearTimeout(timer);
//等待await秒后执行
timer = setTimeout(func,await)
}
}
//方法二:
// //定义一个防抖函数
// var debounce = function(func,await=50) {
// var timer = 0
// //相当于一个闭包
// return function (...args) {
// //每次调用,都会清空计时器,重新计时
// if (timer) clearTimeout(timer);
// //等待await秒后执行
// timer = setTimeout(()=>{
// func.apply(args)
// },await)
// }
// }
//需要防抖的函数
function abc(){
console.log(Math.random())
}
//监听窗口变化
window.addEventListener('resize',debounce(abc,2000))
函数节流
当持续触发事件时,保证在一定时间内只调用一次事件处理函数,意思就是说,假设一个用户一直触发这个函数,且每次触发小于既定值,函数节流会每隔这个时间调用一次
//方法一:
function throttle(func,await) {
var RunFlag = true;//通过闭包保存一个标志
//返回匿名函数
return function(...args) {
if(!RunFlag) return ;//直接返回
//不为false,立即置false
RunFlag = false;
//计时器计时
setTimeout(()=>{
func.apply(args);
RunFlag = true;
},await)
}
}
//方法二:
// function throttle(func,await) {
// var RunFlag = true;//通过闭包保存一个标志
// //返回匿名函数
// return function() {
// if(!RunFlag) return ;//直接返回
// //不为false,立即置false
// RunFlag = false;
// //计时器计时
// setTimeout(()=>{
// func.apply(this,arguments);
// RunFlag = true;
// },await)
//
// }
// }
//方法三:
// var throttle = function (func,await) {
// // 定时器
// var timer = null;
// // 设置开始时间
// var startTime = Date.now();
// return function() {
// // 当前时间
// var curTime = Date.now();
// // 剩余时间
// var remaining = await-(curTime-startTime);
// var context = this;
// var args = arguments;
// if (timer)clearTimeout(timer);
// if(remaining<=0){
// func.apply(context,args);
// //重新计时
// startTime = Date.now();
// }else {
// timer = setTimeout(func,remaining);
// }
//
// }
//
// }
function handle() {
console.log(Math.random())
}
window.addEventListener('resize',throttle(handle,2000))
防抖和节流的区别
用一句话总结防抖和节流的区别:防抖是将多次执行变为最后一次执行,节流是将多次执行变为每隔一段时间执行
应用场景:
- 用一句话总结防抖和节流的区别:防抖是将多次执行变为最后一次执行,节流是将多次执行变为每隔一段时间执行
- 表单的按钮提交事件,例如登录,发短信,避免用户点击太快,以至于发送了多次请求
- search搜索框输入,只需用户最后一次输入完再发送请求
- 文本编辑器实时保存,当无任何更改操作1s后进行保存
节流
- 鼠标不断点击触发,mousedown(单位时间内只触发一次) mousemove事件
- 商品预览图的放大镜效果
- 谷歌搜索框(支持实时搜索),搜索联想功能
- scroll事件,每隔1s计算一次位置信息
js中的垃圾回收机制
由于字符串、对象和数组没有固定大小,所以当他们的大小已知时,才能对他们进行动态的存储分配,只有这样动态分配了内存,最终解解释器都要释放这些内存以便他们能够被再用,否则,JavaScript的解释器将会消耗完系统中所有的可用内存,造成系统崩溃。
垃圾回收方法
- 标志清除:当变量进入环境时,就标志这个变量已经进入环境,当离开环境时,就标志为离开环境。
- 引用计数法:当声明了一个变量时,用一个引用类型的值赋值给这里变量,则这个值的引用次数就+1;相反的,包含对这个值引用的变量又取得另外一个值,则原来的值引用次数就-1,当这个值引用次数为0的时候,说明已经没有办法再访问这个值,因此把所占用的内存给释放出来。
注意:用引用计数法会存在内存泄露
function problem() {
var objA = new Object();
var objB = new Object();
objA.someOtherObject = objB;
objB.anotherObject = objA;
}
在这个例子里面,objA和objB通过各自的属性相互引用,这样的话,两个对象的引用次数都为2,在采用引用计数的策略中,由于函数执行之后,这两个对象都离开了作用域,函数执行完成之后,因为计数不为0,这样的相互引用如果大量存在就会导致内存泄露。
手写call、apply、bind
-
call
实现步骤:
6部曲 1.创建对象赋值,存在则用,不存在则window 2.创建fn对象保存传进来的this对象 3.截取不定参数可以使用ES6,...arguments 4.创建变量保存这个传参数给fn的结果 5.删除新增的fn对象属性 6.返回传参后的fn结果
基础版
javascript// 手写call /* 6部曲 1.创建对象赋值,存在则用,不存在则window 2.创建fn对象保存传进来的this对象 3.截取不定参数可以使用ES6,...arguments 4.创建变量保存这个传参数给fn的结果 5.删除新增的fn对象属性 6.返回传参后的fn结果 */ Function.prototype.myCall = function (context){ /* ...args接收多个参数 */ // 改变this,如果context不存在默认指向window let self = context||window; // 创建函数fn self.fn = this //获取传进来的对象 const args = [...arguments].slice(1)//从第二位开始截取参数 const result = self.fn(...args) //展开参数 delete self.fn;//删除新增的对象 return result;//返回 } function MyTest(a,b,c) { console.log(this.name)//11 console.log(a)//1 console.log(b)//2 } var obj = { name:11 } // MyTest()默认是这样 // 改变this指向,则这样 MyTest.myCall(obj,1,2,3)
进阶版
javascript/** * 手写call * @param {*} ctx * @param {...any} args * @returns */ Function.prototype.MyCall = function(ctx, ...args) { // 处理ctx为null、undefined情况, globalThis-指向全局,Windows/node环境 ctx = ctx ? Object(ctx) : globalThis ; // 使用ES6 symbol解决避免函数重名情况 let key = Symbol('fn'); // Object.defineProperty(ctx, key,{ enumerable: false, value: this}) ctx[key] = this; // 处理函数有返回值的情况 // 处理函数有参数的情况 let result = ctx[key](...args) // 删除自定义 delete ctx[key] return result } function test(a,b){ console.log(this,a,b) } test.MyCall({}, 1,2) // { [Symbol(fn)]: [Function: test] } 1 2 // 优化 /** * 手写call * @param {*} ctx * @param {...any} args * @returns */ Function.prototype.MyCall = function(ctx, ...args) { // 处理ctx为null、undefined情况, globalThis-指向全局,Windows/node环境 ctx = ctx ? Object(ctx) : globalThis ; // 使用ES6 symbol解决避免函数重名情况 let key = Symbol('fn'); Object.defineProperty(ctx, key,{ enumerable: false, value: this}) // ctx[key] = this; // 处理函数有返回值的情况 // 处理函数有参数的情况 let result = ctx[key](...args) // 删除自定义 // delete ctx[key] return result } function test(a,b){ console.log(this,a,b) } test.MyCall({}, 1,2) // {} 1 2 test.MyCall(12, 1,2) // [Number: 12] 1 2
-
apply
// 手写apply /* call与apply基本一致,只是在参数上有所区别, apply主要接收的是数组参数,所以在处理参数上面有区别而已 6部曲 1.创建对象赋值,存在则用,不存在则window 2.创建fn对象保存传进来的this对象 3.截取不定参数可以使用ES6,...arguments 4.创建变量保存这个传参数给fn的结果 5.删除新增的fn对象属性 6.返回传参后的fn结果
Function.prototype.myApply = function(context) {
//创建保存第一个参数对象
let self = context||window;
//创建对象fn
self.fn = this;
let result;
// 解析参数,传参保存
let args = arguments[1];
//判断第一个参数是否有数参
if(args){
result = self.fn(...args);
}else {
result = self.fn()
}
// 删除fn
delete self.fn;
// 返回
return result;
}
function MyTest(a,b,c) {
console.log(this.name)//11
console.log(a)//1
console.log(b)//2
}
var obj = {
name:11
}
// MyTest()默认是这样
// 改变this指向,则这样,传入参数必须是数组
MyTest.myApply(obj,[1,2,3])
-
bind
// 手写bind /* call与bind基本一致,只是bind返回的是一个函数,因此需要调用,并且bind已经没有创建fn的步骤,而是直接赋值this */
javascriptFunction.prototype.myBind =function(context) { /* 注意:在bind中。已经没有创建对象fn,而是直接将this赋值 */ // 创建fn对象,赋值this let self= this; // 解析参数,从第一位开始 let args = [...arguments].slice(1); // 返回函数 return function F(){ // 判断是否继承 if(this instanceof F){ //调用apply方法 return self.apply(this,args.concat([...arguments])) } else { return self.apply(context,args.concat([...arguments])) } } } function MyTest(a,b,c) { console.log(this.name) console.log(a) console.log(b) } var obj = { name:11 } // MyTest()默认是这样 // 改变this指向,则这样,传入参数必须是数组 let bind = MyTest.myBind(obj,1)(3)
如何判断一个数组
可以用typeof来判断数据的类型,但是会发现,{}与[]都是object,对象是对象,数组也是对象,js中万物皆对象,很显然,通过简单的typeof运算符是不能够达到目的
alert(typeof 1); // 返回字符串"number"
alert(typeof "1"); // 返回字符串"string"
alert(typeof true); // 返回字符串"boolean"
alert(typeof {}); // 返回字符串"object"
alert(typeof []); // 返回字符串"object "
alert(typeof function(){}); // 返回字符串"function"
alert(typeof null); // 返回字符串"object"
alert(typeof undefined); // 返回字符串"undefined"
-
从原型入手,Array.prototype.isPrototypeOf(obj);
javascriptconsole.log(Array.prototype.isPrototypeOf([])) //true console.log(Object.prototype.isPrototypeOf([])) //true console.log(Array.prototype.isPrototypeOf({})) //false
-
也可以从构造函数入手 obj instanceof Array
instanceof 与typeof的区别:使用typeof会返回数据类型,而instanceof返回布尔值
console.log([] instanceof Array) //true
console.log({} instanceof Array) //false
- 根据对象的class属性(类属性),跨原型链调用toString()方法
js中提供了,调用对象原型中的toString方法, Object.prototype.toString.call(obj)
因为很多对象继承的toString()方法被重写了,为了能够调用正确的toString()版本,也就是最原始的版本。可以使用Function.call()的方法,其中call可以这么理解,相当于obj去借用这个 Object.prototype.toString();
console.log(Object.prototype.toString.call([])) //[object Array]
console.log(Object.prototype.toString.call({})) //[object Object]
实现深拷贝
// 实现深拷贝
function deepCopy(obj) {
// 判断是否是对象,不是对象直接返回
if (typeof obj !=='object') return ;
// 判断是数组还是object
var newObj = obj instanceof Array?[]:{}
// 循环遍历
for (var key in obj) {
//当前元素类型是否还是object类型,是就继续递归下去
newObj[key] = typeof obj[key] ==='object'?deepCopy(obj[key]):obj[key];
}
return newObj;
}
console.log([1,2,3,5])
JS中继承实现的几种方式
-
原型链继承:父类实例作为子类原型,可以继承父类的原型属性和方法
优点:父类新增的属性和方法,子类都可以访问到 缺点:如果需要新增原型的属性和方法,需要在new 后面 无法实现多继承
javascript/* 实现继承的方式: 1.原型继承 2.构造函数继承 3.实例继承 4.组合继承 5.寄生组合继承 6.拷贝继承 */ // 原型继承:父类实例作为子类原型,可以继承父类的原型属性和方法 /* 优点:父类新增的属性和方法,子类都可以访问到 缺点:如果需要新增原型的属性和方法,需要在new 后面 无法实现多继承,无法向父类构造函数传参 */ // 创建父类 function Parent(name) { console.log('我是父类') //父类属性 this.name=name||'不知道咯'; // 父类方法 this.func = function(name) { console.log(name) return name; } } // 创建子类 function Child() { console.log('我是子类') } // 父类的实例作为子类的原型 Child.prototype = new Parent() //实例化子类 let child = new Child() console.log(child.name) // 不知道咯 console.log(child.func('十三')) // 十三 // 给原型父类新增属性 Parent.prototype.age = '18' // 再次获取属性 console.log(child.age)//18
-
构造函数继承:
优点:
解决了子类构造函数向父类构造函数中传递参数
可以实现多继承(call或者apply多个父类)
缺点:
方法都在构造函数中定义,无法复用
不能继承原型属性/方法,只能继承父类的实例属性和方法
/*
实现继承的方式:
1.原型继承
2.构造函数继承
3.实例继承
4.组合继承
5.寄生组合继承
6.拷贝继承
*/
// 构造函数继承:父类实例作为子类原型,可以继承父类的属性和方法
/*
优点:解决子类构造函数向父类构造函数传递参数问题,通过call获取apply多个父类可以实现多继承
缺点:
不能继承原型的属性和方法,只能继承父类的属性和方法,方法在构造函数中定义,无法复用
*/
// 创建父类
function Parent(name) {
console.log('我是父类')
//父类属性
this.name=name||'不知道咯';
// 父类方法
this.func = function(name) {
console.log(name)
return name;
}
}
// 创建子类
function Child() {
console.log('我是子类')
// 通过call方法改变this指向,实现继承
Parent.call(this)
}
// 实例化子类
let child = new Child()
//继承父类属性
console.log(child.name) //不知道咯
// 继承父类方法
console.log(child.func('十三')) //十三
-
实例继承:(原型式继承)
javascript/* 实现继承的方式: 1.原型继承 2.构造函数继承 3.实例继承 4.组合继承 5.寄生组合继承 6.拷贝继承 */ // 实例继承:(原型式继承) /* 优点:简单,易实现 缺点: 不能多次继承 */ // 创建父类 function Parent(name) { console.log('我是父类') //父类属性 this.name=name||'不知道咯'; // 父类方法 this.func = function(name) { console.log(name) return name; } } // 创建子类 function Child() { // 在子类内部实例化父类 let instance = new Parent() return instance } // 实例化子类 let child =new Child() console.log(child.name)//不知道咯 child.func('十三')//十三
-
组合继承
javascript/* 实现继承的方式: 1.原型继承 2.构造函数继承 3.实例继承 4.组合继承 5.寄生组合继承 6.拷贝继承 */ // 组合继承:调用父类构造函数,继承父类的属性,通过将父类实例作为子类原型,实现函数复用 /* 优点:可以继承属性和方法,并且可以继承原型的属性和方法,函数可以复用 缺点: 由于调用了两次父类,产生两次实例 */ // 创建父类 function Parent(name) { console.log('我是父类') //父类属性 this.name=name||'不知道咯'; // 父类方法 this.func = function(name) { console.log(name) return name; } } // 创建子类 function Child() { Parent.call(this) } // 实例化子类 Child.prototype = new Parent() let child =new Child() console.log(child.name)//不知道咯 child.func('十三')//十三
-
拷贝继承
-
寄生组合继承
javascript/* 实现继承的方式: 1.原型继承 2.构造函数继承 3.实例继承 4.组合继承 5.寄生组合继承 6.拷贝继承 */ // 组合继承:通过寄生的方式来修复组合式继承的不足,完美的实现继承 // 创建父类 function Parent(name) { console.log('我是父类') //父类属性 this.name=name||'不知道咯'; // 父类方法 this.func = function(name) { console.log(name) return name; } } // 创建子类 function Child() { Parent.call(this) } // 通过父类的原型创建一个对象 // 子类继承该原型对象 Child.prototype = Object.create(Parent.prototype) // 实例化子类 let child = new Child() console.log(child.name) //不知道咯 console.log(Child.prototype.__proto__==Parent.prototype) //true
手写reduce
Array.prototype.myReduce = function(callback,initValue) {
// 判断是不是函数
if(typeof callback !== "function"){
throw Error('callback must is function!');
}
// 将this指向数组的数据赋值
var arr = this;
/*
是否传入第二个参数,如果有第二个参数,
则pre的值应该是该参数,如果没有,则是数组的第一个值
*/
var pre = initValue||arr[0];
/*
处理函数里面的第二个参数,也就是curIndex,如果有第二个参数传入,
则该值应该为数组的第一个索引,否则是第二个数组的第二个索引
*/
var curIndex = initValue?0:1;
// 遍历
for(let i=curIndex;i<arr.length;i++){
pre = callback(pre,arr[i],i,arr)
}
return pre;
}
let a = [1,2,3,4,5,6]
let res = a.myReduce((pre,cur)=>{
return pre+cur;
})
console.log(res)//21
Array.prototype.myReduce = function(callback,initValue) {
// 判断是不是函数
if(typeof callback !== "function"){
throw Error('callback must is function!');
}
// 将this指向数组的数据赋值
var arr = this;
/*
是否传入第二个参数,如果有第二个参数,
则pre的值应该是该参数,如果没有,则是数组的第一个值
*/
var pre = initValue||arr[0];
/*
处理函数里面的第二个参数,也就是curIndex,如果有第二个参数传入,
则该值应该为数组的第一个索引,否则是第二个数组的第二个索引
*/
var curIndex = initValue?0:1;
// 遍历
for(let i=curIndex;i<arr.length;i++){
pre = callback(pre,arr[i],i,arr)
}
return pre;
}
let a = [1,3,3,4,4,6]
let res = a.myReduce((pre,cur)=>{
/*
pre,不传入第二个参数时,默认时数组的第一个值,并作为回调后的结果,1+2+3--->3+3
cur,不传入第二个参数时,默认时数组的第二个值,否则第二个
*/
pre[cur] = pre[cur]?pre[cur]+1 : 1;
return pre
},{})
//实现重复元素计数
console.log(res)//{ '1': 1, '3': 2, '4': 2, '6': 1 }
手写map
Array.prototype.myMap = function(callback,args) {
// 判断是否为函数
if(typeof callback !== 'function'){
throw Error(`${callback} not is function`)
}
/*
由于map方法会返回一个新的数组,并且不会改变原来的数组
因此,定义一个空数组
*/
let newArr = [];
let arr = this;//当前this指向的数调用该方法的数组
let redirectThis = args||Object.create(null);
// 遍历
for (let i = 0; i < arr.length; i++) {
//通过call()方法修改this指向
newArr.push(callback.call(redirectThis,arr[i],i,arr))
}
return newArr;
}
//测试
let a=[1,5,4,2,3,5,6,8]
let res = a.myMap(function (item,index,a) {
return item*2
})
console.log(res)
/*
[
2, 10, 8, 4,
6, 10, 12, 16
]
*/
vue
轻量级框架:只关注视图层,是一个构建数据的视图集合,大小只有几十 kb;
简单易学:国人开发,中文文档,不存在语言障碍 ,易于理解和学习;
双向数据绑定:保留了 angular 的特点,在数据操作方面更为简单;
组件化:保留了 react 的优点,实现了 html 的封装和重用,在构建单页面应用方面有着独特
的优势;
视图,数据,结构分离:使数据的更改更为简单,不需要进行逻辑代码的修改,只需要操作数
据就能完成相关操作;
虚拟 DOM:dom 操作是非常耗费性能的, 不再使用原生的 dom 操作节点,极大解放 dom
操作,但具体操作的还是 dom 不过是换了另一种方式;
运行速度更快: 相比较与 react 而言,同样是操作虚拟 dom,就性能而言,vue 存在很大的优
势。
如何理解MVVM开发模式?
-
MVVM分为Model、View、ViewModel三者。
-
Model:代表数据模型,数据和业务逻辑都在Model层中定义;
-
View:代表UI视图,负责数据的展示;
-
ViewModel:负责监听Model中数据的改变并且控制视图的更新,处理用户交互操作;
Model和View并无直接关联,而是通过ViewModel来进行联系的,Model和ViewModel之间有着双向数据绑定的联系。因此当Model中的数据改变时会触发View层的刷新,View中由于用户交互操作而改变的数据也会在Model中同步。
这种模式实现了Model和View的数据自动同步,因此开发者只需要专注对数据的维护操作即可,而不需要自己操作dom。
MVVM和MVC区别?和其他框架(jquery)区别?那些场景适用?
- MVVM和MVC都是一种设计思想,主要就是MVC中的Controller演变成ViewModel,,MVVM主要通过数据来显示视图层而不是操作节点,解决了MVC中大量的DOM操作使页面渲染性能降低,加载速度慢,影响用户体验问题。主要用于数据操作比较多的场景。
- 场景:数据操作比较多的场景,更加便捷
vue父组件向子组件传递数据?
- 通过props
子组件像父组件传递事件?
- $emit方法
v-show 和 v-if 指令的共同点和不同点?
-
共同点:都能控制元素的显示和隐藏;
-
不同点:实现本质方法不同,v-show 本质就是通过控制 css 中的 display 设置为 none,控
制隐藏,只会编译一次;v-if 是动态的向 DOM 树内添加或者删除 DOM 元素,若初始值为
false,就不会编译了。而且 v-if 不停的销毁和创建比较消耗性能,当只需要一次显示或隐藏时,使用v-if更加合理。
如何让 CSS只在当前组件中起作用?
- 在组件中的 style 前面加上 scoped
Vue的双向数据绑定原理是什么?
- vue.js是采用数据劫持结合发布者 - 订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调。
如何获取 dom?
- ref="domName" 用法:this.$refs.domName
说出几种
vue 当中的指令和它的用法?
-
v-model 双向数据绑定;
-
v-for 循环
-
v-if v-show 显示与隐藏
-
v-on 事件
-
v-once: 只绑定一次
-
v-bind:动态参数绑定
vue-loader 是什么?使用它的用途有哪些?
-
vue 文件的一个加载器,将 template/js/style 转换成 js 模块。
-
用途:js 可以写 es6、style 样式可以 scss 或 less、template 可以加 jade 等
为什么使用 key?
- 需要使用 key 来给每个节点做一个唯一标识,Diff 算法就可以正确的识别此节点。
- 作用主要是为了高效的更新虚拟 DOM。
computed计算属性与watch监听属性区别
-
功能上:computed是计算属性,watch是监听一个值的变化,然后执行对应的回调。
-
是否调用缓存:computed中的函数所依赖的属性没有发生变化,那么调用当前的函数的时候会从缓存中读取,而watch在每次监听的值发生变化的时候都会执行回调。
-
是否调用return:computed中的函数必须要用return返回,watch中的函数不是必须要用return。
-
computed默认第一次加载的时候就开始监听;watch默认第一次加载不做监听,如果需要第一次加载做监听,添加immediate属性,设置为true(immediate:true)。
-
使用场景:computed----当一个属性受多个属性影响的时候,使用computed-------购物车商品结算。watch----当一条数据影响多条数据的时候,使用watch-------搜索框。
$nextTick 的使用
- 当你修改了 data 的值然后马上获取这个 dom 元素的值,是不能获取到更新后的值,你需要使用 $nextTick 这个回调,让修改后的 data 值渲染更新到 dom 元素之后在获取,才能成功。
vue 组件中 data 为什么必须是一个函数?
- data 写成一个函数,数据以函数返回值的形式定义,这样每次复用组件的时候,都会返回一份新的 data,相当于每个组件实例都有自己私有的数据空间,它们只负责各自维护的数据,不会造成混乱。而单纯的写成对象形式,就是所有的组件实例共用了一个 data,这样改一个全都改了。
v-if和v-for一起使用的弊端及解决办法
- 由于v-for的优先级比v-if高,所以导致每循环一次就会去v-if一次,而v-if是通过创建和销毁dom元素来控制元素的显示与隐藏,所以就会不停的去创建和销毁元素,造成页面卡顿,性能下降。
- 解决办法:
- 在v-for的外层或内层包裹一个元素来使用v-if
- 用computed处理
assets 和 static 的区别
-
相同点:assets 和 static 两个都是存放静态资源文件。项目中所需要的资源文件图片,字
体图标,样式文件等都可以放在这两个文件下。
-
不相同点:assets 中存放的静态资源文件在项目打包时,也就是运行 npm run build 时会将assets 中放置的静态资源文件进行打包上传,所谓打包简单点可以理解为压缩体积,代码格式化。而压缩后的静态资源文件最终也都会放置在 static 文件中跟着 index.html 一同上传至服务器。static 中放置的静态资源文件就不会要走打包压缩格式化等流程,而是直接进入打包好的目录,直接上传至服务器。因为避免了压缩直接进行上传,在打包时会提高一定的效率,但是 static 中的资源文件由于没有进行压缩等操作,所以文件的体积也就相对于 assets 中打包后的文件提交较大点。在服务器中就会占据更大的空间。
vue 的两个核心点
- 数据驱动:ViewModel,保证数据和视图的一致性
- 组件系统:应用类 UI 可以看作全部是由组件树构成的。
vue 和 jQuery 的区别
- jQuery依赖 DOM 元素的值
- vue将数据与视图分离开来,对数据进行操作不再需要引用相应的DOM 对象
Vue-router 跳转和 location.href 有什么区别
- 使用 location.href='/url'来跳转,简单方便,但是刷新了页面
- 使用 history.pushState('/url'),无刷新页面,静态跳转
- 后使用 router.push('/url') 来跳转,使用了 diff 算法,实现了按需加载,减少了 dom 的消耗
其实使用 router 跳转和使用 history.pushState() 没什么差别的,因为 vue-router 就是用了
history.pushState(),尤其是在 history 模式下。
route和router的区别
- route是“路由信息对象”,包括path,params,hash,query,fullPath,matched,name等路由信息参数。而router是“路由实例”对象包括了路由的跳转方法,钩子函数等
vue slot
-
简单来说,假如父组件需要在子组件内放一些 DOM,那么这些 DOM 是显示、不显示、
在哪个地方显示、如何显示,就是 slot 分发负责的活
Vue 里面 router-link 在电脑上有用,在安卓上没反应怎么解决?
- Vue 路由在 Android 机上有问题,babel 问题,安装 babel polypill 插件解决。
Vue2 中注册在 router-link 上事件无效解决方法
- 使用 @click.native。原因:router-link 会阻止 click 事件,.native 指直接监听一个原
生事件。
vue-router 路由钩子函数是什么?执行顺序是什么?
路由钩子的执行流程,钩子函数种类有:全局守卫、路由守卫、组件守卫。
完整的导航解析流程:
1、导航被触发。
2、在失活的组件里调用 beforeRouterLeave 守卫。
3、调用全局的 beforeEach 守卫。
4、在重用的组件调用 beforeRouterUpdate 守卫(2.2+)。
5、在路由配置里面 beforeEnter。
6、解析异步路由组件。
7、在被激活的组件里调用 beforeRouterEnter。
8、调用全局的 beforeResolve 守卫(2.5+)。
9、导航被确认。
10、调用全局的 afterEach 钩子。
11、触发 DOM 更新。
12、调用 beforeRouterEnter 守卫中传给next的回调函数,创建好的组件实例会作为回调函数的参数传入。
使用过 Vue SSR 吗?说说 SSR
SSR 也就是服务端渲染,也就是将 Vue 在客户端把标签渲染成 HTML 的工作放在服务端完成,然后再把 html 直接返回给客户端。
优点:
SSR 有着更好的 SEO、并且首屏加载速度更快。
缺点:
-
开发条件会受限制,服务器端渲染只支持 beforeCreate 和 created 两个钩子,当我们需要一些外部扩展库时需要特殊处理,服务端渲染应用程序也需要处于 Node.js 的运行环境。
-
服务器会有更大的负载需求。
你都做过哪些 Vue 的性能优化?
这里只列举针对 Vue 的性能优化,整个项目的性能优化是一个大工程。
- 对象层级不要过深,否则性能就会差。
- 不需要响应式的数据不要放在 data 中(可以使用 Object.freeze() 冻结数据)
- v-if 和 v-show 区分使用场景
- computed 和 watch 区分场景使用
- v-for 遍历必须加 key,key最好是id值,且避免同时使用 v-if
- 大数据列表和表格性能优化 - 虚拟列表 / 虚拟表格
- 防止内部泄露,组件销毁后把全局变量和时间销毁
- 图片懒加载
- 路由懒加载
- 异步路由
- 第三方插件的按需加载
- 适当采用 keep-alive 缓存组件
- 防抖、节流的运用
- 服务端渲染 SSR or 预渲染
vue 内置指令
Vue的父子组件生命周期钩子函数执行顺序
- 加载渲染过程
父beforeCreate -> 父created -> 父beforeMount -> 子beforeCreate -> 子created -> 子beforeMount -> 子mounted -> 父mounted
- 更新过程
父beforeUpdate -> 子beforeUpdate -> 子updated -> 父updated
- 销毁过程
父beforeDestroy -> 子beforeDestroy -> 子destroyed -> 父destroyed
Vue渲染/更新过程
渲染过程:
- 解析模板为render函数(或在开发环境已完成,vue-loader)
- 触发响应式,监听data属性getter setter
- 执行render函数,生成vnode,patch(elem,vnode)
更新过程:
- 修改data,触发setter(此前在getter中已被监听)
- 重新执行render函数,生成newVnode
- patch(vnode,newVnode)
HTTP协议
当输入www.google.com时,页面发生了哪些事情:
- 域名解析:域名解析检查顺序为:浏览器自身DNS缓存---》OS自身的DNS缓存--》读取host文件--》本地域名服务器--》权限域名服务器--》根域名服务器。如果有且没有过期,则结束本次域名解析。域名解析成功之后,进行后续操作
- tcp3次握手建立连接
- 建立连接后,发起http请求
- 服务器端响应http请求,浏览器得到http请求的内容;
- 浏览器解析html代码,并请求html代码中的资源
- 浏览器对页面进行渲染,展现在用户面前。
- 连接结束
说一下什么是Http协议?
HTTP是基于TCP/IP协议的应用层协议。它不设计数据包(packet)传输,主要规定了客户端和服务器之间的通信格式,默认使用80端口
什么是Http协议无状态协议?怎么解决Http协议无状态协议?
-
无状态协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息
-
无状态协议解决办法: 通过1、Cookie 2、通过Session会话保存。
Session的概念
Session 是存放在服务器端的,类似于Session结构来存放用户数据,当浏览器 第一次发送请求时,服务器自动生成了一个Session和一个Session ID用来唯一标识这个Session,并将其通过响应发送到浏览器。当浏览器第二次发送请求,会将前一次服务器响应中的Session ID放在请求中一并发送到服务器上,服务器从请求中提取出Session ID,并和保存的所有Session ID进行对比,找到这个用户对应的Session。
Session的客户端实现形式(即Session ID的保存方法)
一般浏览器提供了两种方式来保存,还有一种是程序员使用html隐藏域的方式自定义实现:
- 使用Cookie来保存
- 使用URL附加信息的方式
Session与Cookie区别和联系:
- Cookies是属于Session对象的一种。但有不同,Cookies不会占服务器资源,是存在客服端内存或者一个cookie的文本文件中
- cookie数据保存在客户端,session数据保存在服务器端。
- session会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能,如果主要考虑到减轻服务器性能方面,应当使用COOKIE ;
- cookie和session都是用来跟踪浏览器用户身份的会话方式。
- cookie不是很安全,别人可以分析存放在本地的COOKIE并进行COOKIE欺骗
考虑到安全应当使用session。
Token
- Token是服务端生成的一串字符串,以作客户端进行请求的一个令牌
- 最简单的token组成:uid(用户唯一的身份标识)、time(当前时间的时间戳)、sign(签名,由token的前几位+盐以哈希算法压缩成一定长的十六进制字符串,可以防止恶意第三方拼接token请求服务器)。
- Token的目的是为了减轻服务器的压力,减少频繁的查询数据库,使服务器更加健壮。
session与token的区别
- Session 是一种HTTP存储机制,目的是为无状态的HTTP提供的持久机制。所谓Session 认证只是简单的把User 信息存储到Session 里,因为SID 的不可预测性,暂且认为是安全的。
- 而Token ,如果指的是OAuth Token 或类似的机制的话,提供的是 认证 和 授权 ,认证是针对用户,授权是针对App 。其目的是让 某App有权利访问 某用户 的信息
Cookie、sessionStorage、localStorage的区别
- 共同点:都是保存在浏览器端,并且是同源的
类型 | 说明 |
---|---|
Cookie | cookie数据始终在同源的http请求中携带(即使不需要),即cookie在浏览器和服务器间来回传递 |
sessionStorage | 不会自动把数据发给服务器,仅在本地保存;仅在当前浏览器窗口关闭前有效,自然也就不可能持久保持 |
localStorage | 不会自动把数据发给服务器,仅在本地保存;始终有效,窗口或浏览器关闭也一直保存,因此用作持久数据 |
Cookie如何防范XSS攻击
-
XSS(跨站脚本攻击)是指攻击者在返回的HTML中嵌入javascript脚本,为了减轻这些攻击,需要在HTTP头部配上,set-cookie:httponly-这个属性可以防止XSS,它会禁止javascript脚本来访问cookie。
-
secure - 这个属性告诉浏览器仅在请求为https的时候发送cookie。
-
结果应该是这样的:
Set-Cookie=<cookie-value>.....
说一下Http协议中302状态
-
http协议中,返回状态码302表示重定向。
-
这种情况下,服务器返回的头部信息中会包含一个 Location 字段,内容是重定向到的url。
HTTP常见状态码
范围 | 分类 | |
---|---|---|
1XX | 100-101 | 信息提示 |
2XX | 200-206 | 成功 |
3XX | 300-305 | 重定向 |
4XX | 400-415 | 客户端错误 |
5XX | 500-505 | 服务器错误 |
状态码 | 状态消息 | 含义 | 实例 |
---|---|---|---|
200 | OK | 服务器成功处理了请求(这个是我们见到最多的) | HTTP协议详解-200 |
201 | Created(已创建) | 对于那些要服务器创建对象的请求来说,资源已创建完毕。 | |
202 | Accepted(已接受) | 请求已接受, 但服务器尚未处理 | |
203 | Non-Authoritative Information(非权威信息) | 服务器已将事务成功处理,只是实体Header包含的信息不是来自原始服务器,而是来自资源的副本。 | |
204 | No Content(没有内容) | Response中包含一些Header和一个状态行, 但不包括实体的主题内容(没有response body) | 状态码204 |
205 | Reset Content(重置内容) | 另一个主要用于浏览器的代码。意思是浏览器应该重置当前页面上所有的HTML表单。 | |
206 | Partial Content(部分内容) | 部分请求成功 | 状态码206 |
状态码 | 状态消息 | 含义 | 实例 |
---|---|---|---|
300 | Multiple Choices(多项选择) | 客户端请求了实际指向多个资源的URL。这个代码是和一个选项列表一起返回的,然后用户就可以选择他希望的选项了 | |
301 | Moved Permanently(永久移除) | 请求的URL已移走。Response中应该包含一个Location URL, 说明资源现在所处的位置 | 状态码301 |
302 | Found(已找到) | 与状态码301类似。但这里的移除是临时的。 客户端会使用Location中给出的URL,重新发送新的HTTP request | HTTP协议详解-302 |
303 | See Other(参见其他) | 类似302 | |
304 | Not Modified(未修改) | 客户的缓存资源是最新的, 要客户端使用缓存 | HTTP协议之缓存-304 |
305 | Use Proxy(使用代理) | 必须通过代理访问资源, 代理的地址在Response 的Location中 | |
306 | 未使用 | 这个状态码当前没使用 | |
307 | Temporary Redirect(临时重定向 | 类似302 |
状态码 | 状态消息 | 含义 | 实例 |
---|---|---|---|
400 | Bad Request(坏请求) | 告诉客户端,它发送了一个错误的请求。 | 状态码400 |
401 | Unauthorized(未授权) | 需要客户端对自己认证 | HTTP协议之基本认证-401 |
402 | Payment Required(要求付款) | 这个状态还没被使用, 保留给将来用 | |
403 | Forbidden(禁止) | 请求被服务器拒绝了 | 状态码403 |
404 | Not Found(未找到) | 未找到资源 | HTTP协议详解-404 |
405 | Method Not Allowed(不允许使用的方法) | 不支持该Request的方法。 | 状态码405 |
406 | Not Acceptable(无法接受) | ||
407 | Proxy Authentication Required(要求进行代理认证) | 与状态码401类似, 用于需要进行认证的代理服务器 | HTTP协议之代理-407 |
408 | Request Timeout(请求超时) | 如果客户端完成请求时花费的时间太长, 服务器可以回送这个状态码并关闭连接 | |
409 | Conflict(冲突) | 发出的请求在资源上造成了一些冲突 | |
410 | Gone(消失了) | 服务器曾经有这个资源,现在没有了, 与状态码404类似 | |
411 | Length Required(要求长度指示) | 服务器要求在Request中包含Content-Length。 | 状态码411 |
412 | Precondition Failed(先决条件失败) | ||
413 | Request Entity Too Large(请求实体太大) | 客户端发送的实体主体部分比服务器能够或者希望处理的要大 | 状态码413 |
414 | Request URI Too Long(请求URI太长) | 客户端发送的请求所携带的URL超过了服务器能够或者希望处理的长度 | 状态码414 |
415 | Unsupported Media Type(不支持的媒体类型) | 服务器无法理解或不支持客户端所发送的实体的内容类型 | |
416 | Requested Range Not Satisfiable(所请求的范围未得到满足) |
状态码 | 状态消息 | 含义 | 实例 |
---|---|---|---|
500 | Internal Server Error(内部服务器错误) | 服务器遇到一个错误,使其无法为请求提供服务 | 状态码500 |
501 | Not Implemented(未实现) | 客户端发起的请求超出服务器的能力范围(比如,使用了服务器不支持的请求方法)时,使用此状态码。 | 状态码501 |
502 | Bad Gateway(网关故障) | 代理使用的服务器遇到了上游的无效响应 | 状态码502 |
503 | Service Unavailable(未提供此服务) | 服务器目前无法为请求提供服务,但过一段时间就可以恢复服务 | |
504 | Gateway Timeout(网关超时) | 与状态吗408类似, 但是响应来自网关或代理,此网关或代理在等待另一台服务器的响应时出现了超时 | |
505 | HTTP Version Not Supported(不支持的HTTP版本) | 服务器收到的请求使用了它不支持的HTTP协议版本。 有些服务器不支持HTTP早期的HTTP协议版本,也不支持太高的协议版本 | 状态码505 |
Http协议有什么组成?
请求报文包含三部分:
- 请求行:包含请求方法、URI、HTTP版本信息
- 请求首部字段
- 请求内容实体
响应报文包含三部分:
- 状态行:包含HTTP版本、状态码、状态码的原因短语
- 响应首部字段
- 响应内容实体
Http协议中有那些请求方式?
方式 | 说明 |
---|---|
GET | 用于请求访问已经被URI(统一资源标识符)识别的资源,可以通过URL传参给服务器 |
POST | 用于传输信息给服务器,主要功能与GET方法类似,但一般推荐使用POST方式。 |
PUT | 传输文件,报文主体中包含文件内容,保存到对应URI位置。 |
HEAD | 获得报文首部,与GET方法类似,只是不返回报文主体,一般用于验证URI是否有效。 |
DELETE | 删除文件,与PUT方法相反,删除对应URI位置的文件。 |
OPTIONS | 查询相应URI支持的HTTP方法。 |
TRACE | 请求服务器回送收到的请求信息,主要用语测试或诊断 |
Http协议中Http1.0与1.1区别?
- 在http1.0中,当建立连接后,客户端发送一个请求,服务器端返回一个信息后就关闭连接,当浏览器下次请求的时候又要建立连接,显然这种不断建立连接的方式,会造成很多问题。
- 在http1.1中,引入了持续连接的概念,通过这种连接,浏览器可以建立一个连接之后,发送请求并得到返回信息,然后继续发送请求再次等到返回信息,也就是说客户端可以连续发送多个请求,而不用等待每一个响应的到来。
GET与POST请求区别?
- get重点在从服务器上获取资源。
- post重点在向服务器发送数据。
- get传输数据是通过URL请求,以field(字段)= value的形式,置于URL后,并用"?"连接,多个请求数据间用"&"连接,如http://127.0.0.1/Test/login.action?name=admin&password=admin,这个过程用户是可见的。
- post传输数据通过Http的post机制,将字段与对应值封存在请求实体中发送给服务器,这个过程对用户是不可见的。
- Get传输的数据量小,因为受URL长度限制,但效率较高。
- Post可以传输大量数据,所以上传文件时只能用Post方式。
- get是不安全的,因为URL是可见的,可能会泄露私密信息,如密码等。
- post较get安全性较高。
- get方式只能支持ASCII字符,向服务器传的中文字符可能会乱码。
- post支持标准字符集,可以正确传递中文字符。
请求头
类型 | 说明 |
---|---|
Content-Type | 是返回消息中非常重要的内容,表示后面的文档属于什么MIME类型。Content-Type: [type]/[subtype]; parameter。例如最常见的就是text/html,它的意思是说返回的内容是文本类型,这个文本又是HTML格式的。原则上浏览器会根据Content-Type来决定如何显示返回的消息体内容 |
Host | 指定请求资源的Intenet主机和端口号,必须表示请求url的原始服务器或网关的位置。HTTP/1.1请求必须包含主机头域,否则系统会以400状态码返回 |
Accept | 浏览器可接受的MIME类型 |
Accept-Charset | 浏览器可接受的字符集 |
Accept-Encoding | 浏览器能够进行解码的数据编码方式,比如gzip。Servlet能够向支持gzip的浏览器返回经gzip编码的HTML页面。许多情形下这可以减少5到10倍的下载时间 |
Accept-Language | 浏览器所希望的语言种类,当服务器能够提供一种以上的语言版本时要用到 |
Authorization | 授权信息,通常出现在对服务器发送的WWW-Authenticate头的应答中 |
Connection | 表示是否需要持久连接。如果Servlet看到这里的值为“Keep- Alive”,或者看到请求使用的是HTTP1.1(HTTP 1.1默认进行持久连接),它就可以利用持久连接的优点,当页面包含多个元素时(例如Applet,图片),显著地减少下载所需要的时间。要实现这一点,Servlet需要在应答中发送一个Content-Length头,最简单的实现方法是:先把内容写入 ByteArrayOutputStream,然后在正式写出内容之前计算它的大小 |
Content-Length | 表示请求消息正文的长度 |
Cookie | 这是最重要的请求头信息之一 |
From | 请求发送者的email地址,由一些特殊的Web客户程序使用,浏览器不会用到它 |
Host | 初始URL中的主机和端口 |
If-Modified-Since | 只有当所请求的内容在指定的日期之后又经过修改才返回它,否则返回304“Not Modified”应答 |
Pragma | 指定“no-cache”值表示服务器必须返回一个刷新后的文档,即使它是代理服务器而且已经有了页面的本地拷贝 |
Referer | 包含一个URL,用户从该URL代表的页面出发访问当前请求的页面 |
User-Agent | 浏览器类型,如果Servlet返回的内容与浏览器类型有关则该值非常有用 |
常见的MIME类型如下:
- text/html : HTML格式
- text/plain :纯文本格式
- text/xml : XML格式
- image/gif :gif图片格式
- image/jpeg :jpg图片格式
- image/png:png图片格式
以application开头的媒体格式类型:
- application/xhtml+xml :XHTML格式
- application/xml : XML数据格式
- application/atom+xml :Atom XML聚合格式
- application/json : JSON数据格式
- application/pdf :pdf格式
- application/msword : Word文档格式
- application/octet-stream : 二进制流数据(如常见的文件下载)
另外一种常见的媒体格式是上传文件之时使用的:
- multipart/form-data : 需要在表单中进行文件上传时,就需要使用该格式
Http与Https优缺点?
- 通信使用明文不加密,内容可能被窃听,也就是被抓包分析。
- 不验证通信方身份,可能遭到伪装
- 无法验证报文完整性,可能被篡改
- HTTPS就是HTTP加上加密处理(一般是SSL安全通信线路)+认证+完整性保护
Http优化
-
利用负载均衡优化和加速HTTP应用
-
利用HTTP Cache来优化网站
Http协议有那些特征?
-
支持客户/服务器模式
-
简单快速
-
灵活
-
无连接、无状态。
HTTP与HTTPS区别
- https协议需要到ca申请证书,一般免费证书较少,因而需要一定费用。
- http是超文本传输协议,信息是明文传输,https则是具有安全性的ssl加密传输协议。
- http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。
- http的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。
说一下http2.0
首先补充一下,http和https的区别,相比于http,https是基于ssl加密的http协议
-
简要概括:http2.0是基于1999年发布的http1.0之后的首次更新。
-
提升访问速度(可以对于,请求资源所需时间更少,访问速度更快,相比http1.0)
-
允许多路复用:多路复用允许同时通过单一的HTTP/2连接发送多重请求-响应信息。改善了:在http1.1中,浏览器客户端在同一时间,针对同一域名下的请求有一定数量限制(连接数量),超过限制会被阻塞。
-
二进制分帧:HTTP2.0会将所有的传输信息分割为更小的信息或者帧,并对他们进行二进制编码
首部压缩
服务器端推送
fetch发送2次请求的原因
原因很简单,因为你用fetch的post请求的时候,导致fetch 第一次发送了一个Options请求,询问服务器是否支持修改的请求头,如果服务器支持,则在第二次中发送真正的请求。
一句话概括RESTFUL
就是用URL定位资源,用HTTP描述操作
AJAX
什么是ajax
AJAX 是一种用于创建快速动态网页的技术。 通过在后台与服务器进行少量数据交换,AJAX 可以使网页实现异步更新
为什么要用ajax
- 通过异步模式,提升了用户体验
- 优化了浏览器和服务器之间的传输,减少不必要的数据往返,减少了带宽占用
- Ajax引擎在客户端运行,承担了一部分本来由服务器承担的工作,从而减少了大用户量下的服务器负载。
AJAX最大的特点是什么
- Ajax可以实现动态不刷新(局部刷新)
请介绍一下XMLHttprequest对象
- Ajax的核心是JavaScript对象XmlHttpRequest。该对象在Internet Explorer 5中首次引入,它是一种支持异步请求的技术。简而言之,XmlHttpRequest使您可以使用JavaScript向服务器提出请求并处理响应,而不阻塞用户。通过XMLHttpRequest对象,Web开发人员可以在页面加载以后进行页面的局部更新。
AJAX技术体系的组成部分有哪些
HTML,css,dom,xml,xmlHttpRequest,javascript
原生js ajax请求有几个步骤?分别是什么
//创建 XMLHttpRequest 对象
var ajax = new XMLHttpRequest();
//规定请求的类型、URL 以及是否异步处理请求。
ajax.open('GET',url,true);
//发送信息至服务器时内容编码类型
ajax.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
//发送请求
ajax.send(null);
//接受服务器响应数据
ajax.onreadystatechange = function () {
if (obj.readyState == 4 && (obj.status == 200 || obj.status == 304)) {
}
什么是JSON?
JSON是一种轻量级的数据交换格式。
什么情况造成跨域?
同源策略限制 不同源会造成跨域,以下任意一种情况不同,都是不同源:
同源:协议 域名 端口号全部相同 只要有一个不相同就是非同源策略
跨域解决方案有哪些
- 动态创建script
原理:动态创建一个script标签。利用script标签的src属性不受同源策略限制。因为所有的src属性和href属性都不受同源策略限制。可以请求第三方服务器数据内容。
步骤:
-
去创建一个script标签
-
script的src属性设置接口地址
-
接口参数,必须要带一个自定义函数名 要不然后台无法返回数据。
-
通过定义函数名去接收后台返回数据
//去创建一个script标签
var script = document.createElement("script");
//script的src属性设置接口地址 并带一个callback回调函数名称
script.src = "http://127.0.0.1:8888/index.php?callback=jsonpCallback";
//插入到页面
document.head.appendChild(script);
//通过定义函数名去接收后台返回数据function jsonpCallback(data){
//注意 jsonp返回的数据是json对象可以直接使用
//ajax 取得数据是json字符串需要转换成json对象才可以使用。
}
- CORS:跨域资源共享
服务器设置Access-Control-Allow-OriginHTTP响应头之后,浏览器将会允许跨域请求
限制:浏览器需要支持HTML5,可以支持POST,PUT等方法兼容ie9以上
需要后台设置
Access-Control-Allow-Origin: * //允许所有域名访问,或者
Access-Control-Allow-Origin: http://a.com //只允许所有域名访问
-
JSONP
原理:jsonp 技术就是利用了
<script>
可以任意跨域的特点实现的。即dataType 为jsonp 的请求,实际是创建一个script 标签,通过该标签来实现访问,所以无论设置怎样的请求类型,从浏览器调试窗口可以看到,实际还是为GET 类型的。 -
postMessage
window.postMessage(message,targetOrigin) 方法是html5新引进的特性,可以使用它来向其它的window对象发送消息,无论这个window对象是属于同源或不同源,目前IE8+、FireFox、Chrome、Opera等浏览器都已经支持window.postMessage方法。
-
window.name + iframe
window对象有个name属性,该属性有个特征:即在一个窗口(window)的生命周期内,窗口载入的所有的页面都是共享一个window.name的,每个页面对window.name都有读写的权限。(window.name的值只能是字符串的形式,这个字符串的大小最大能允许2M左右甚至更大的一个容量,具体取决于不同的浏览器,一般够用了。)
-
document.domain + iframe
只有在主域相同的时候才能使用该方法
AJAX都有哪些优点和缺点?
- 最大的一点是页面无刷新,用户的体验非常好
- 使用异步方式与服务器通信,具有更加迅速的响应能力
- 可以把以前一些服务器负担的工作转嫁到客户端,利用客户端闲置的能力来处理,减轻服务器和带宽的负担,节约空间和宽带租用成本。并且减轻服务器的负担,ajax的原则是“按需取数据”,可以最大程度的减少冗余请求,和响应对服务器造成的负担
- 基于标准化的并被广泛支持的技术,不需要下载插件或者小程序
ajax的缺点
- ajax不支持浏览器back按钮
- 安全问题 AJAX暴露了与服务器交互的细节
- 对搜索引擎的支持比较弱
- 破坏了程序的异常机制
- 不容易调试
浏览器兼容性
innerText在IE中能正常工作,但在FireFox中却不行
解决方法:
需用textContent
if(navigator.appName.indexOf("Explorer")>-1){
document.getElementById('element').innerText = "my text";
}else{
document.getElementById('element').textContent = "my text";
}
CSS透明
IE:filter:alpha(opacity=60)。
FF:opacity:0.6。
盒子模型
标准 w3c 盒子模型的范围包括 margin、border、padding、content,并且 content 部分不包含其他部分; IE盒子模型的范围也包括
margin、border、padding、content
和标准 w3c 盒子模型不同的是:ie 盒子模型的 content 部分包含了 border 和 padding。
因此,问题主要表现在css中的width是否计算border和padding的问题,这个是默认的盒子解析模型不同导致的。
IE6:中包括border和padding(box-sizing: border-box;)
IE7和非IE:width宽度不包括border和padding(box-sizing: content-box;)
解决方式: 根据使用方式,写好box-sizing属性。
块属性标签float后,又有横行的margin情况下,在IE6显示margin比设置的大
问题症状:常见症状是IE6中后面的一块被顶到下一行
解决方案:在float的标签样式控制中加入 display:inline;将其转化为行内属性
图片默认有间距
问题症状:几个img标签放在一起的时候,有些浏览器会有默认的间距。
解决方案:使用float属性为img布局
万能清除浮动
.clearfix:after {
content:".";
display:block;
height:0;
clear:both;
visibility:hidden;
}
**溢出显示省略号 **
select {
-o-text-overflow:ellipsis;
text-overflow:ellipsis;
white-space:nowrap;
overflow:hidden;
width:200px;
}
js里面的兼容性问题
- const问题
说明:Firefox下,可以使用const关键字或var关键字来定义常量;
IE下,只能使用var关键字来定义常量.
解决方法:统一使用var关键字来定义常量.
HTML
什么是事件委托?
事件委托时通过利用事件冒泡方式,只指定一个事件处理来管理某一类型的所有事件。
事件委托的作用
在JavaScript中添加页面上的事件处理程序的个数直接关系到整个页面的性能,每个处理事件函数都是一个对象,对象会占用内存。
JavaScript实现
<body>
<ul id="list">
<li id="o">第一个</li>
<li id="t">第二个</li>
<li id="s">第三个</li>
</ul>
<script>
//通过ID获取
var list = document.getElementById('list')
//打印list
console.log('list',list)
//监听
list.addEventListener('click',function(e) {
// e.target是被点击的元素
// 点击元素是否为li元素
if (e.target&&e.target.nodeName.toLowerCase() === 'li') {
// 找到,输出id
console.log(e.target.id)
}
})
</script>
</body>
jQuery实现
<body>
<ul id="list">
<li id="o">第一个</li>
<li id="t">第二个</li>
<li id="s">第三个</li>
</ul>
<script>
$("#list").on('click','li',function(e) {
console.log(e.target.id) //o/t/s
})
</script>
</body>
CSS
未知宽高水平垂直居中
**方法1:**使用position=absolute,top=50%,left=50%以及 transform: translate(-50%,-50%);
#div-parent{
background-color: #00DD60;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%,-50%);
}
**方法2:**margin:auto法
div{
width: 400px;
height: 400px;
position: relative;
border: 1px solid #465468;
}
img{
position: absolute;
margin: auto;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
方法3:table-cell(未脱离文档流的)设置父元素的display:table-cell,并且vertical-align:middle,这样子元素可以实现垂直居中。
div{
width: 300px;
height: 300px;
border: 3px solid #555;
display: table-cell;
vertical-align: middle;
text-align: center;
}
img{
vertical-align: middle;
}
**方法4:**利用flex,将父元素设置为display:flex,并且设置align-items:center;justify-content:center;
.container{
width: 300px;
height: 200px;
border: 3px solid #546461;
display: -webkit-flex;
display: flex;
-webkit-align-items: center;
align-items: center;
-webkit-justify-content: center;
justify-content: center;
}
.inner{
border: 3px solid #458761;
padding: 20px;
}
已知宽高
#div-parent{
background-color: #00DD60;
width: 200px;
height: 250px;
position: absolute;
top: 50%;
left: 50%;
margin-top: -125px;
margin-left: -100px;
}
什么是BFC?
BFC是一个块级格式化上下文,它是一个独立的容器,里面的子元素不会影响到外面的元素。
产生BFC方式
- float不为none
- position为absolute或fixed
- display为inline-block,table-cell,table-caption,fixed,inline-flex
- overflow不为visible
清除浮动
- 使用clear:both清除浮动:它的优点是简单,方便兼容性好,但是一般情况下不建议使用该方法,因为会造成结构混乱,不利于后期维护
<div style="clear: both"></div>
- :after伪元素:给父级元素添加了一个:after伪元素,通过清除伪元素的浮动,达到撑起父元素高度的目的
clearfix:after{
content:"";
display:block;
visibility:hidden;
clear:both;
}
- overflow属性:当给父元素设置了overflow样式,不管是overflow:hidden或overflow:auto都可以清除浮动只要它的值不为visible就可以了,它的本质就是建构了一个BFC,这样使得达到撑起父元素高度的效果
.box{
border:1px solid #ccc; background:#eff2f4;
overflow: auto
}
画一条0.5px的线
- 采用meta viewport方式:viewport只针对于移动端,只在移动端上才能看到效果
<meta name="viewport" content="width=device-width, initial-scale=0.5, minimum-scale=0.5, maximum-scale=0.5"/>
-
采用transform:scale()方式
transform: scale(0.5,0.5);
- 采用border-image的方式
link标签与import标签的区别
- link属于html标签,但是import是css提供的
- 页面加载时,link会同时被加载,但是import引用的css会等到页面加载完成后才加载。
- link是HTML标签,所有没有兼容性,但是import只有在IE5以上才会被识别。
- link的样式权重要高于import。
transition和animation的区别
-
相同:都是随时间改变元素的属性值
-
不同:transition需要触发一个事件才能改变属性;
而animation则是反之,并且transition为2帧,从from…to,但是animation则是一帧一帧的。
flex:1代表什么意思
首先,flex是flex-grow、flex-shrink、flex-basic的缩写,flex:1; 就是代表均匀分配元素
v-show与v-if的区别
- 控制手段不同
- 编译过程不同
- 编译条件不同
控制手段:v-show
隐藏则是为该元素添加css--display:none
,dom
元素依旧还在。v-if
显示隐藏是将dom
元素整个添加或删除
编译过程:v-if
切换有一个局部编译/卸载的过程,切换过程中合适地销毁和重建内部的事件监听和子组件;v-show
只是简单的基于css切换
编译条件:v-if
是真正的条件渲染,它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。只有渲染条件为假时,并不做操作,直到为真才渲染
v-show
由false
变为true
的时候不会触发组件的生命周期v-if
由false
变为true
的时候,触发组件的beforeCreate
、create
、beforeMount
、mounted
钩子,由true
变为false
的时候触发组件的beforeDestory
、destoryed
方法
性能消耗:v-if
有更高的切换消耗;v-show
有更高的初始渲染消耗;
v-show与v-if的使用场景
v-if
与 v-show
都能控制dom
元素在页面的显示
v-if
相比 v-show
开销更大的(直接操作dom
节点增加与删除)
如果需要非常频繁地切换,则使用 v-show 较好