前端技术汇总

154 阅读10分钟

一、ajax

1.1 什么是ajax?

ajax是快速搭建动态网页的技术,通过与后台进行数据的传输,可以使网页进行异步刷新

1.2 ajax优势 && 为什么要用ajax

1.3 ajax的特点

局部刷新

1.4 介绍一下Xhr对象

1.5 手写ajax请求

const xhr = new XMLHttpRequest()
xhr.open('get', 'url', true)
xhr.send()
xhr.onreadystatechange = function() {
    if(xhr.readystate === 4 && xhr.status === 200) {
        console.log(xhr.responseText)
    }
}

1.6 json字符串转换集json对象、json对象转换json字符串

1.7 ajax传递方式有哪些?有什么区别?

常用的post、get、put、delete post和get的区别:

  1. get比post快
  2. post比get安全
  3. get的参数是放在url上,post则是放在请求的实体体中

1.8 什么情况下会造成跨域

https: // www .baidu.com
协议 域名 端口
浏览器有同源策略,只要以上三个不一致,就会造成跨域

1.9 跨域的解决方案

  1. 利用script标签,src属性和href属性不受同源策略限制
  2. CORS跨域
  3. jsonp
  4. window.iframe
  5. document.domain
  6. 代理(proxy目前没有系统学习,需要完善)

1.9.1 什么是CORS

1. 两种请求
1.1 简单请求:

(1) 请求方法是以下三种方法之一:

  • HEAD
  • GET
  • POST (2)HTTP的头信息不超出以下几种字段:
  • Accept
  • Accept-Language
  • Content-Language
  • Last-Event-ID
  • Content-Type:只限于三个值application/x-www-form-urlencodedmultipart/form-datatext/plain

2. 简单请求的解决方式

  1. 请求头中会origin: "https://..." 字段
  2. 在服务器添加请求头
  • Access-Control-Allow-Origin: "*"
  • Access-Coltrol-Allow-Credentials: true 表示是否发送cookie,只可以设置为true
  • Access-Control-Expose-Header: 表示服务器可以获取哪个字段信息
  1. 浏览器:
  • WithCrendentials: true 表示传送cookie
3. 非简单请求

非简单请求会在正式发送请求之前发送一次,称为预检请求

3.1 预检请求
预检请求目的

询问服务器,该网页所在域名是否在服务器的许可名单之中,以及使用哪些http动词和信息字段

预检请求方式

只能为options

浏览器发送字段
  • Access-Control-Request-Header: 表示服务器返回的头信息中包含哪些字段
  • Access-Control-Request-Method: 表示cors请求会用到哪些方法(put、post...)
服务器返回字段
  1. 必须要有的:
  • Access-Control-Allow-Origin: "*"
  1. 其他的:
  • Access-Control-Allow-Methods: GET, POST, PUT
  • Access-Control-Allow-Headers: X-Custom-Header
  • Access-Control-Allow-Credentials: true
  • Access-Control-Max-Age: 1728000 //说明多长时间之内不用发送预检请求
正式回应

发送请求中含有origin: * 回应请求中含有: Access-Control-Allow-Origin: "*"

二、原型与原型链

2.1 明确对象定义

在JS中万物皆对象,分为普通对象和函数对象

2.1.1 何为函数对象?

通过function() / new function建立的, 其prototype都为Object

2.2 prototype属性

所有的函数对象都有prototype,指向的是他的一个实例即普通对象

  • Array.prototype === array
  • Object.prototype === object
  • Function.prototype = function (比较特殊)

2.3__proto__属性

所有对象都有__proto__属性,指向的是其构造函数的prototype

2.3.1 构造函数

简单来说就是上一级, array的constructor函数为Array

特例: Object.prototype.__ proto __ === null##

(详细都在这里 转载自:www.jianshu.com/p/dee9f8b14…

2.4 构造器

所有构造器的__ proto__值为Function.prototype

  • Array.__ proto__ = Function.prototype
  • Function.__ proto__ = Function.prototype
  • Object.__ proto__ = Function.prototype

2.4 面试案例

2.4.1 如何准确判断一个变量是数组类型

const arr = [1,2,3]
console.log(arr instanceof Array)

2.4.2 写一个原型链继承的例子 实现DOM封装(还包括其他继承)

/**
  * 注意点:
  * 1.使用new来创建div实例
  * 2.使用document自带的函数,比如addEventListener
  */
function getElement(idName) {
  this.element = document.getElementById(idName)
}

getElement.prototype.html = function(content) {
  if(content) {
    this.element.innerHTML = content
    return this
  } else {
    return this.element.innerHTML
  }
}

getElement.prototype.on = function(type,callback) {
  this.element.addEventListener(type,callback)
}

const div = new getElement('div')
// console.log(div.html())
div.html('11111')
div.on('click',() => {
  alert(div.html())
})

2.4.3 描述 new 一个对象的过程

/**
 * 手写new
 * 1.创建一个新的对象
 * 2.继承父级原型上的方法
 * 3.让该对象this指向父级,同时赋予参数
 * 4.如果返回值类型为object返回该值,否则返回空对象
 * */
function _new(obj, ...args) {
    let o = {}
    o.__proto__ = obj.prototype
    const res = obj.call(o, ...args)
    return typeof res == 'object' ? res : o 
}
function Person(name) {
    this.name = name
    return 'aaa'
}
Person.prototype.getName = function() {
    return this.name
}

const p = _new(Person, 'lixin')
console.log(p)

2.4.4 观察下面函数,f中包含啊a、b方法吗?

var F = function () {}
Object.prototype.a = function () {}
Function.prototype.b = function () {}

var f = new F()

答:只含有a方法,观察下面的原型链

  • f.__ proto__ = F.prototype
  • F.prototype = object
  • F.prototype.__ proto__ = Object.prototype 所以,只含有a方法

2.4.5 手写instanceof实现原理

instanceof与typeof的区别

  • typeof只有undefied、object、string、number、symbol、function、boolean
  • instanceof只对引用类型有用
  • typeof null = object (遗留的bug);typeof function = object
  • Object.prototype.toString.call() 最精准,但是麻烦
// obj的__proto__会指向其构造函数的prototype
function Instanceof(obj, cons) {
    let o = obj.__proto__
    let c = cons.prototype
    while(true) {
      if(o === null){
        return false
      }
      if(c === o) {
        return true
      }
      o = o.__proto__
    }
  }
/**
* 什么是Object.prototype.toString.call()
每个对象都有toString方法,这个方法可以获得对象类型,所有的
*/
const arr = []
const obj = {}
const func = function() {}
const date = new Date()
const str = ''
Object.prototype.toString.call(arr)
Object.prototype.toString.call(obj)
Object.prototype.toString.call(func)
Object.prototype.toString.call(date)
Object.prototype.toString.call(str)
// 输出 后面的便是对象类型
[object Array]
[object Function]
[object Object]
[object Date]
[object String]

2.4.6 this

  • this分为普通函数和箭头函数
2.4.6.1 普通函数
  1. 单独调用,指向window
  2. 闭包指向window
  3. 隐式绑定谁执行就指向谁(obj.foo2() 函数作为参数)
  4. 显式绑定 使用bind call apply 绑定谁指向谁
  5. new定义 谁是实例就指向谁
2.4.6.2 箭头函数
  1. 向上找到作用域,谁调用就指向谁
  2. call、bind、apply不好使
  3. 匿名函数同样向上找作用域
  4. new定义同理,但是普通对象定义的是找对象的上层作用域
var obj = { 
    name: 'obj', 
    foo1: () => { 
        console.log(this.name) 
    }, 
    foo2: function () { 
        console.log(this.name) 
        return () => { 
            console.log(this.name) 
        } 
    } 
} 
var name = 'window' 
obj.foo1() 
obj.foo2()()
// 答案
window // 普通对象昂向上找作用域
obj
obj

例题在这里面,很牛逼!!佩服大大!讲的很透彻

2.4.6.3 手写bind apply call

2.4.7 判断对象里是否存在某个属性

A.call(B, args) //把A的this指向是B

    1. 使用in
    1. 使用hasOwnProperty() 缺点:获取不到继承属性
    1. 使用undefined 缺点:如果值为undefined时,结果不准确
    1. 使用if判断
const a = {
   x: '111',
   z: undefined
}
console.log('x' in a) // true
console.log('y' in a) // false
console.log('toString' in a) // true
console.log(a.hasOwnProperty('x')) // true
console.log(a.hasOwnProperty('toString')) // false
console.log(a.x == undefined) // false
console.log(a.z == undefined) // true

三、 数组

3.1 数组去重

let arr = [1,2,3,4,2,3]
console.log(new Set(arr))

3.2 数组扁平化

// ES5:
let arr = [1,2,[3,4,[3,4,5]]]
let a = []
function flatten (arr) {
  arr.forEach(element => {
    if(typeof element === 'object') {
      flatten(element)
    } else {
      a.push(element)
    }
  });
}
flatten(arr)
console.log(a)

// ES6:
let arr = [1,2,[3,4,[3,4,5]]]
function flatten(arr) {
  while(arr.some(item => Array.isArray(item))) {
    arr = [].concat(...arr)  
  }
  return arr
}
console.log(flatten(arr))

3.3 深浅拷贝

深浅拷贝只针对引用对象而言,其他的对象都是浅拷贝,但是不涉及值的变化

  • 深拷贝:改变复制值时原始值不变
  • 浅拷贝:改变复制值时原始值改变

基础知识

浅拷贝是放在栈中,栈中即是内容。深拷贝栈中则是堆的地址。 使用深拷贝(三种方法):

  1. const obj2 = JSON.parse(JSON.stringfy(obj1))
  2. Object.assign(ob1)
  3. 使用递归 优缺点 JSON.parse()
  4. 不可以复制正则、undefined、函数等
  5. Object.assign Object.assign()
  6. 可以复制正则、函数等
  7. 如果有数组、对象等,还是浅拷贝 递归
  8. 复杂一点

3.3.1 js写浅拷贝

3.4 类型引用

在js中分为基本类型和引用类型

  • 基本类型包括string、number、boolean、symbol、bigInt、null、undefined
  • 引用类型是object,array、date等

3.4.1 存储方式

  1. 基本类型是栈存储,会把值都放在栈中
  2. 引用类型栈储存的是值指向的地址,堆里面是值

3.4.2 面试题

let x = [12,23]
function  fn(y){
  y[0] = 100;
  y = [100];
  y[1]  = 200;
  console.log(y);
}
fn(x)
console.log(x);
// 解析
function  fn(y){
  y[0] = 100; // 此时地址没有变,x是[100,23]
  y = [100]; // x的地址改变了,x是[100]
  y[1]  = 200; // x是[100, 200]
  console.log(y); // [100,200]
}
fn(x)
console.log(x) // 记住x的地址
  1. 0.1 + 0.2 为什么不等于0.3 答: 在js中十进制先转为二进制,但是某些小数转为二进制是无穷的,另外number类型有精度不准确的问题,0.1转为二进制时无穷,由于精度的问题,会有一个小数残留
// 如何让a == 1 && a == 2 && a == 3
/** 知识点: 
* 引用类型转换为`Number`类型,先调用`valueOf`,再调用`toString`
* 引用类型转换为`String`类型,先调用`toString`,再调用`valueOf`
*/
const a = {
    value: [3,2,1],
    toString: function() {
        return this.value.pop()
    }
}
console.log(a == 1 && a == 2 && a == 3)

3.5 连续赋值

从左到右解析,从右到左赋值

(function() {var a=b=1;})();
console.log(b);
console.log(a);
// 从右到左赋值
// 从左到右解析
//相当于如下
var a
b = 1 // b是全局变量
a = b // a是局部变量
// 输出结果: 1, undefined
var a = {n: 1};
var b = a;
a.x = a = {n: 2};
console.log(a);
console.log(b);
console.log(a.x);
console.log(b.x);
/**
* .运算符的优先级大于= 所以,先执行 a.x={n:2} a=a.x
*/
{n: 2}
{n: 1, x: {n: 1}}
undefined
{n: 1}

3.6 数组自身方法

3.6.1 sclice(start, end) 不改变原数组

  • slice对数组进行部分截取,从0开发,到end结束,不算end(无论正负)
  • start和end为负值,从后往前数
var fruits = ['a', 'b', 'c', 'd', 'r'];
console.log(fruits.slice(1,3)) // bc
console.log(fruits.slice(1, -1)) // bcd
console.log(fruits.slice(-2)) // dr

3.6.2 splice 改变原数组

四、js声明提升

  1. 以下的声明提升针对var定义(输出undefined),是指把该变量定义提到该作用域下的最高位置
  2. 分为变量提升、函数提升

4.1 变量提升(直接上题)

console.log(v1);
var v1 = 100;
function foo() {
    console.log(v1);
    var v1 = 200;
    console.log(v1);
}
foo();
console.log(v1);
// 提升完之后是:
var v1
console.log(v1)
v1 = 100
function foo() {
    var v1
    console.log(v1)
    v1 = 200
    console.log(v1)
}
foo()
console.log(v1)
// 结果输出:undefined undefined 200 100

4.2 函数提升

函数定义有两种:

  • function foo() {}
  • const foo = function() {} 变量提升是对第一种情况而言

五、 promise

5.1 构造函数

const p1 = new Promise((reslove, reject) => {
    reslove(4)
})
p1.then(res => {
    console.log(res)
})

5.2 宏队列与微队列

在new Promise中都是宏队列的,只有遇到了then才加入到微队列中。setTimeout则是单独的,多个之间(无论里面是有promise还是什么其他的)不干扰

async function async1 () {
  console.log('async1 start')
  await async2();
  console.log('async1 end')
}
 
async function async2 () {
  console.log('async2')
}
 
console.log('script start')
 
setTimeout(function () {
  console.log('setTimeout')
}, 0)
 
async1();
 
new Promise (function (resolve) {
  console.log('promise1')
  resolve();
}).then (function () {
  console.log('promise2')
})
console.log('script end')
// 知识点 await后面的方法相当于then方法,遇到await就是阻塞
// 记住,只有then才可以进入微队列中
// 输出结果
script start
async1 start
async2
promise1
script end
async1 end
promise2
setTimeout
const promise = new Promise((resolve, reject) => {
        setTimeout(() => {
             console.log('once')
             resolve('success')
        }, 1000)
 })
promise.then((res) => {
       console.log(res)
     })
promise.then((res) => {
     console.log(res)
 })
 // 输出 then可以执行多次,但是第二次是直接返回结果,不会有异步等待的时间,
 once success success 

在浏览器上,下面的程序会一次输出哪些内容?

const p1 = () => (new Promise((resolve, reject) => {
	console.log(1);
	let p2 = new Promise((resolve, reject) => {
		console.log(2);
		const timeOut1 = setTimeout(() => {
			console.log(3);
			resolve(4);
		}, 0)
		resolve(5);
	});
	resolve(6);
	p2.then((arg) => {
		console.log(arg);
	});

}));
const timeOut2 = setTimeout(() => {
	console.log(8);
	const p3 = new Promise(reject => {
		reject(9);
	}).then(res => {
		console.log(res)
	})
}, 0)


p1().then((arg) => {
	console.log(arg);
});
console.log(10);
// 答案

六、路由

6.1 history和hash

  • 在mode中定义使用history还是hash
  • hash多了一个锚点(#),hash是改变#后面的数据,来进行路由的变化。特点:#虽然包含在url里面,但是不在http请求里面,同时改变他也不会发生路由变化
  • history则是利用h5的特性,需要在特定的浏览器里使用。

五、http与https

5.1 http、https概念

http

5.2 http与https的区别

5.3 http运行方式

5.4 http1.0 http1.1 http2.0

5.5 http的缓存策略。

5.6 说下https,证书是如何校验的?

5.7 说下http2,你觉得阻碍http2发展的问题是什么?(

六、说下输入一个url地址的全过程

  • DNS解析找到IP地址
  • 根据IP建立TCP请求(三次握手)
  • 建立成功发送http请求
  • 服务器响应http请求
  • 浏览器解析html资源和1请求静态资源js、css
  • 关闭TCP连接(四次挥手)
  • 浏览器渲染页面
三次握手和四次挥手都是什么?
什么叫静态资源?
  • js(.js .jsx .coffee .ts)
  • css(.css .less .sass .scss)
  • image (.jpg .png .gif .bmp .svg)
  • 字体文件 (.svg .ttf .eot .woff .woff2)
  • 模板文件 (.ejs .jade .vue)

六、闭包

七、CRSF攻击

八、跨域解决

IPV4、IPV6什么意思