前端知识终结-javascript篇

一、同源策略

同源策略可防止 JavaScript 发起跨域请求。源被定义为 URI、主机名和端口号的组合。此策略可防止页面上的恶意脚本通过该页面的文档对象模型,访问另一个网页上的敏感数据。

二、跨域

原因
浏览器的同源策略导致了跨域
作用
用于隔离潜在恶意文件的重要安全机制
解决
jsonp ,允许 script 加载第三方资源
反向代理(nginx 服务内部配置 Access-Control-Allow-Origin *)
cors 前后端协作设置请求头部,Access-Control-Allow-Origin 等头部信息
iframe 嵌套通讯,postmessage
https://zhuanlan.zhihu.com/p/41479807
跨域资源共享 CORS 阮一峰

三、JSONP
参考: https://blog.csdn.net/qq_30627241/article/details/104848405.

四、域名收敛

PC 时代为了突破浏览器的域名并发限制。有了域名发散。
浏览器有并发限制,是为了防止DDOS攻击。
域名收敛:就是将静态资源放在一个域名下。减少DNS解析的开销。
域名发散:是将静态资源放在多个子域名下,就可以多线程下载,提高并行度,使客户端加载静态资源更加迅速。
域名发散是pc端为了利用浏览器的多线程并行下载能力。而域名收敛多用与移动端,提高性能,因为dns解析是是从后向前迭代解析,如果域名过多性能会下降,增加DNS的解析开销。

五、事件绑定的方式

嵌入dom
按钮
直接绑定
btn.onclick = function(){}
事件监听
btn.addEventListener(‘click’,function(){})
事件委托

事件委托利用了事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。所有用到按钮的事件(多数鼠标事件和键盘事件)都适合采用事件委托技术, 使用事件委托可以节省内存。

document.querySelector(‘ul’).onclick = (event) => {
let target = event.target
if (target.nodeName ===LI) {
console.log(target.innerHTML)
}
}

// bad
document.querySelectorAll(‘li’).forEach((e) => {
e.onclick = function() {
console.log(this.innerHTML)
}
})

六、事件循环

事件循环是一个单线程循环,用于监视调用堆栈并检查是否有工作即将在任务队列中完成。如果调用堆栈为空并且任务队列中有回调函数,则将回调函数出队并推送到调用堆栈中执行。

七、事件模型

DOM0
直接绑定

btn.onclick = function() {}
btn.onclick = null

DOM2

//DOM2级事件可以冒泡和捕获 
//通过addEventListener绑定 通过
//removeEventListener解绑
// 绑定
btn.addEventListener(‘click’, sayHi)
// 解绑
btn.removeEventListener(‘click’, sayHi)

DOM3
DOM3具有更多事件类型 DOM3级事件在DOM2级事件的基础上添加了更多的事件类型,全部类型如下:
UI事件,当用户与页面上的元素交互时触发,如:load、scroll
焦点事件,当元素获得或失去焦点时触发,如:blur、focus
鼠标事件,当用户通过鼠标在页面执行操作时触发如:dbclick、mouseup
滚轮事件,当使用鼠标滚轮或类似设备时触发,如:mousewheel
文本事件,当在文档中输入文本时触发,如:textInput
键盘事件,当用户通过键盘在页面上执行操作时触发,如:keydown、keypress
合成事件,当为IME(输入法编辑器)输入字符时触发,如:compositionstart
变动事件,当底层DOM结构发生变化时触发,如:DOMsubtreeModified

八、如何自定义事件

原生提供了3个方法实现自定义事件
createEvent,设置事件类型,是 html 事件还是 鼠标事件
initEvent 初始化事件,事件名称,是否允许冒泡,是否阻止自定义事件
dispatchEvent 触发事件

九、target和currentTarget区别

event.target
返回触发事件的元素
event.currentTarget
返回绑定事件的元素

十、prototype和proto的关系是什么

所有的对象都拥有proto属性,它指向对象构造函数的prototype属性

let obj = {}
obj.proto === Object.prototype // true

function Test(){}
test.proto == Test.prototype // true
所有的函数都同时拥有proto和protytpe属性 函数的proto指向自己的函数实现 函数的protytpe是一个对象 所以函数的prototype也有proto属性 指向Object.prototype

function func() {}
func.prototype.proto === Object.prototype // true
Object.prototype.proto指向null

Object.prototype.proto // null

十一、原型继承

所有的JS对象都有一个prototype属性,指向它的原型对象。当试图访问一个对象的属性时,如果没有在该对象上找到,它还会搜寻该对象的原型,以及该对象的原型的原型,依次层层向上搜索,直到找到一个名字匹配的属性或到达原型链的末尾。

继承

JS高程第3版 第6章 继承 寄生组合式继承

function SuperType(name) {
this.name = name
this.colors = [‘red’]
}

SuperType.prototype.sayName = function() {
console.log(this.name)
}
// 继承实例属性
function SubType(name, age) {
SuperType.call(this, name)
this.age = age
}

function inheritPrototype(subType, superType) {
let prototype = Object.create(superType.prototype)
prototype.constructor = subType
subType.prototype = prototype
}
// 继承原型方法
inheritPrototype(SubType, SuperType)

// 定义自己的原型方法
SubType.prototype.sayAge = function() {
console.log(this.age)
}

十二、闭包

闭包是指有权访问另一个函数作用域中的变量的函数。

function sayHi(name) {
return () => {
console.log(Hi! ${name})
}
}
const test = sayHi(‘xiaoming’)
test() // Hi! xiaoming
虽然sayHi函数已经执行完毕,但是其活动对象也不会被销毁,因为test函数仍然引用着sayHi函数中的变量name,这就是闭包。
但也因为闭包引用着另一个函数的变量,导致另一个函数已经不使用了也无法销毁,所以闭包使用过多,会占用较多的内存,这也是一个副作用。

有一个函数,参数是一个函数,返回值也是一个函数,返回的函数功能和入参的函数相似,但这个函数只能执行3次,再次执行无效,如何实现

这个题目是考察闭包的使用

function sayHi() {
console.log(‘hi’)
}

function threeTimes(fn) {
let times = 0
return () => {
if (times++ < 3) {
fn()
}
}
}

const newFn = threeTimes(sayHi)
newFn()
newFn()
newFn() // 后面两次执行都无任何反应

通过闭包变量 times 来控制函数的执行

实现add函数,让add(a)(b)和add(a,b)两种调用结果相同

function add(a, b) {
	if (b === undefined) {
	return function(x) {
	return a + x
}
}

return a + b
1
}

十三、Ajax

Ajax(asynchronous JavaScript and XML)是使用客户端上的许多 Web 技术,创建异步 Web 应用的一种 Web 开发技术。借助 Ajax,Web 应用可以异步(在后台)向服务器发送数据和从服务器检索数据,而不会干扰现有页面的显示和行为。通过将数据交换层与表示层分离,Ajax 允许网页和扩展 Web 应用程序动态更改内容,而无需重新加载整个页面。实际上,现在通常将 JSON 替换为 XML,因为 JavaScript 对 JSON 有原生支持优势。
XMLHttpRequest API 经常用于异步通信。此外还有最近流行的fetch API。

let xmlhttp
if (window.XMLHttpRequest) {
// IE7+, Firefox, Chrome, Opera, Safari 浏览器执行代码
xmlhttp = new XMLHttpRequest()
} else {
// IE6, IE5 浏览器执行代码
xmlhttp = new ActiveXObject(“Microsoft.XMLHTTP)
}
xmlhttp.onreadystatechange = () => {
if (xmlhttp.readyState === 4 && xmlhttp.status === 200) {
document.getElementById(“myDiv”).innerHTML = xmlhttp.responseText
}
}
xmlhttp.open(GET,/ajax/test.txt”, true)
xmlhttp.send()

十四、使用Ajax的优缺点分别是什么

优点

交互性更好。来自服务器的新内容可以动态更改,无需重新加载整个页面。
减少与服务器的连接,因为脚本和样式只需要被请求一次。
状态可以维护在一个页面上。JavaScript 变量和 DOM 状态将得到保持,因为主容器页面未被重新加载。
基本上包括大部分 SPA 的优点。
缺点

动态网页很难收藏。
如果 JavaScript 已在浏览器中被禁用,则不起作用。
有些网络爬虫不执行 JavaScript,也不会看到 JavaScript 加载的内容。
基本上包括大部分 SPA 的缺点。
https://github.com/yangshun/front-end-interview-handbook/blob/master/questions/javascript-questions.md

十五、Ajax和Fetch区别

ajax是使用XMLHttpRequest对象发起的,但是用起来很麻烦,所以ES6新规范就有了fetch,fetch发一个请求不用像ajax那样写一大堆代码。
使用fetch无法取消一个请求,这是因为fetch基于Promise,而Promise无法做到这一点。
在默认情况下,fetch不会接受或者发送cookies
fetch没有办法原生监测请求的进度,而XMLHttpRequest可以
fetch只对网络请求报错,对400,500都当做成功的请求,需要封装去处理
fetch由于是ES6规范,兼容性上比不上XMLHttpRequest
变量提升

var会使变量提升,这意味着变量可以在声明之前使用。let和const不会使变量提升,提前使用会报错。 变量提升(hoisting)是用于解释代码中变量声明行为的术语。使用var关键字声明或初始化的变量,会将声明语句“提升”到当前作用域的顶部。 但是,只有声明才会触发提升,赋值语句(如果有的话)将保持原样。

十六、使用let、var和const创建变量有什么区别

用var声明的变量的作用域是它当前的执行上下文,它可以是嵌套的函数,也可以是声明在任何函数外的变量。let和const是块级作用域,意味着它们只能在最近的一组花括号(function、if-else 代码块或 for 循环中)中访问。

function foo() {
// 所有变量在函数中都可访问
var bar = ‘bar’;
let baz = ‘baz’;
const qux = ‘qux’;

console.log(bar); // bar
console.log(baz); // baz
console.log(qux); // qux
}

console.log(bar); // ReferenceError: bar is not defined
console.log(baz); // ReferenceError: baz is not defined
console.log(qux); // ReferenceError: qux is not defined
if (true) {
var bar = ‘bar’;
let baz = ‘baz’;
const qux = ‘qux’;
}

// 用 var 声明的变量在函数作用域上都可访问
console.log(bar); // bar
// let 和 const 定义的变量在它们被定义的语句块之外不可访问
console.log(baz); // ReferenceError: baz is not defined
console.log(qux); // ReferenceError: qux is not defined
var会使变量提升,这意味着变量可以在声明之前使用。letconst不会使变量提升,提前使用会报错。

console.log(foo); // undefined

var foo = ‘foo’;

console.log(baz); // ReferenceError: can’t access lexical declaration ‘baz’ before initialization

let baz = ‘baz’;

console.log(bar); // ReferenceError: can’t access lexical declaration ‘bar’ before initialization

const bar = ‘bar’;var重复声明不会报错,但letconst会。

var foo = ‘foo’;
var foo = ‘bar’;
console.log(foo); // “bar”

let baz = ‘baz’;
let baz = ‘qux’; // Uncaught SyntaxError: Identifier ‘baz’ has already been declared
letconst的区别在于:let允许多次赋值,而const只允许一次。

// 这样不会报错。
let foo = ‘foo’;
foo = ‘bar’;

// 这样会报错。
const baz = ‘baz’;
baz = ‘qux’;

十六、对象浅拷贝和深拷贝有什么区别

在 JS 中,除了基本数据类型,还存在对象、数组这种引用类型。 基本数据类型,拷贝是直接拷贝变量的值,而引用类型拷贝的其实是变量的地址。

let o1 = {a: 1}
let o2 = o1

在这种情况下,如果改变 o1 或 o2 其中一个值的话,另一个也会变,因为它们都指向同一个地址。

o2.a = 3
console.log(o1.a) // 3

而浅拷贝和深拷贝就是在这个基础之上做的区分,如果在拷贝这个对象的时候,只对基本数据类型进行了拷贝,而对引用数据类型只是进行了引用的传递,而没有重新创建一个新的对象,则认为是浅拷贝。反之,在对引用数据类型进行拷贝的时候,创建了一个新的对象,并且复制其内的成员变量,则认为是深拷贝。

十七、怎么实现对象深拷贝

let o1 = {a:{
b:1
}
}
let o2 = JSON.parse(JSON.stringify(o1))

另一种方法

function deepCopy(s) {
	const d = {}
	for (let k in s) {
		if (typeof s[k] == ‘object’) {
			d[k] = deepCopy(s[k])
		} else {
			d[k] = s[k]
		}
}

return d
	1
}

十八、数组去重

ES5

function unique(arry) {
	const temp = []
	arry.forEach(e => {
		if (temp.indexOf(e) == -1) {
		temp.push(e)
	}
})

return temp
	1
}

ES6

function unique (arr) {
	return Array.from(new Set(arr))
}

十九、数据类型

Undefined
Null
Boolean
Number
String
Object
symbol(ES6新增)
内置函数(原生函数)

String
Number
Boolean
Object
Function
Array
Date
RegExp
Error
Symbol
原始值 “I am a string” 并不是一个对象,它只是一个字面量,并且是一个不可变的值。 如果要在这个字面量上执行一些操作,比如获取长度、访问其中某个字符等,那需要将其 转换为 String 对象。 幸好,在必要时语言会自动把字符串字面量转换成一个 String 对象,也就是说你并不需要 显式创建一个对象。

二十、如何判断数组与对象

Array.isArray([]) // true
Array.isArray({}) // false

typeof [] // “object”
typeof {} // “object”

Object.prototype == [].proto // false
Object.prototype == {}.proto // true
Array.prototype == [].proto // true
Array.prototype == {}.proto // false

二十一、自动分号

有时 JavaScript 会自动为代码行补上缺失的分号,即自动分号插入(Automatic SemicolonInsertion,ASI)。
因为如果缺失了必要的 ; ,代码将无法运行,语言的容错性也会降低。ASI 能让我们忽略那些不必要的 ; 。
请注意,ASI 只在换行符处起作用,而不会在代码行的中间插入分号。
如果 JavaScript 解析器发现代码行可能因为缺失分号而导致错误,那么它就会自动补上分 号。并且,只有在代码行末尾与换行符之间除了空格和注释之外没有别的内容时,它才会 这样做。

二十二、浮点数精度

https://www.css88.com/archives/7340

二十三、cookie、localStorage、sessionStorage区别

|特性 | cookie | localStorage | sessionStorage| |-|-|-|-| |由谁初始化 | 客户端或服务器,服务器可以使用Set-Cookie请求头。 | 客户端 | 客户端 | |数据的生命周期|一般由服务器生成,可设置失效时间,如果在浏览器生成,默认是关闭浏览器之后失效 |永久保存,可清除 | 仅在当前会话有效,关闭页面后清除| |存放数据大小|4KB|5MB|5MB| |与服务器通信|每次都会携带在HTTP头中,如果使用cookie保存过多数据会带来性能问题|仅在客户端保存|仅在客户端保存| |用途|一般由服务器生成,用于标识用户身份|用于浏览器缓存数据|用于浏览器缓存数据| | 访问权限 | 任意窗口 | 任意窗口 | 当前页面窗口 |

二十四、自执行函数?用于什么场景?好处?

自执行函数:1、声明一个匿名函数2、马上调用这个匿名函数。
作用:创建一个独立的作用域。

好处:防止变量弥散到全局,以免各种js库冲突。隔离作用域避免污染,或者截断作用域链,避免闭包造成引用变量无法释放。利用立即执行特性,返回需要的业务函数或对象,避免每次通过条件判断来处理

场景:一般用于框架、插件等场景

多个页面之间如何进行通信

有如下几个方式: cookie web worker * localeStorage和sessionStorage

二十五、css动画和js动画的差异

代码复杂度,js 动画代码相对复杂一些
动画运行时,对动画的控制程度上,js 能够让动画,暂停,取消,终止,css动画不能添加事件
动画性能看,js 动画多了一个js 解析的过程,性能不如 css 动画好
https://zhuanlan.zhihu.com/p/41479807

二十六、如何实现文件断点续传

断点续传最核心的内容就是把文件“切片”然后再一片一片的传给服务器,但是这看似简单的上传过程却有着无数的坑。

首先是文件的识别,一个文件被分成了若干份之后如何告诉服务器你切了多少块,以及最终服务器应该如何把你上传上去的文件进行合并,这都是要考虑的。

因此在文件开始上传之前,我们和服务器要有一个“握手”的过程,告诉服务器文件信息,然后和服务器约定切片的大小,当和服务器达成共识之后就可以开始后续的文件传输了。

前台要把每一块的文件传给后台,成功之后前端和后端都要标识一下,以便后续的断点。

当文件传输中断之后用户再次选择文件就可以通过标识来判断文件是否已经上传了一部分,如果是的话,那么我们可以接着上次的进度继续传文件,以达到续传的功能。 有了HTML5 的 File api之后切割文件比想想的要简单的多的多。

只要用slice 方法就可以了

var packet = file.slice(start, end);
参数start是开始切片的位置,end是切片结束的位置 单位都是字节。通过控制start和end 就可以是实现文件的分块

file.slice(0,1000);
file.slice(1000,2000);
file.slice(2000,3000);
// …

在把文件切成片之后,接下来要做的事情就是把这些碎片传到服务器上。 如果中间掉线了,下次再传的时候就得先从服务器获取上一次上传文件的位置,然后以这个位置开始上传接下来的文件内容。

参考:https://www.cnblogs.com/zhwl/p/3580776.html

二十七、new一个对象经历了什么

function Test(){}
const test = new Test()
创建一个新对象:
const obj = {}
设置新对象的constructor属性为构造函数的名称,设置新对象的proto属性指向构造函数的prototype对象
obj.constructor = Test
obj.proto = Test.prototype
使用新对象调用函数,函数中的this被指向新实例对象
Test.call(obj)
将初始化完毕的新对象地址,保存到等号左边的变量中
bind、call、apply的区别

call和apply其实是一样的,区别就在于传参时参数是一个一个传或者是以一个数组的方式来传。
call和apply都是在调用时生效,改变调用者的this指向。

let name = ‘Jack’
const obj = {name: ‘Tom’}
function sayHi() {console.log('Hi! ’ + this.name)}

sayHi() // Hi! Jack
sayHi.call(obj) // Hi! Tom
bind也是改变this指向,不过不是在调用时生效,而是返回一个新函数。

const newFunc = sayHi.bind(obj)
newFunc() // Hi! Tom
请简述JavaScript中的this。

JS 中的this是一个相对复杂的概念,不是简单几句能解释清楚的。粗略地讲,函数的调用方式决定了this的值。我阅读了网上很多关于this的文章,Arnav Aggrawal 写的比较清楚。this取值符合以下规则:

在调用函数时使用new关键字,函数内的this是一个全新的对象。
如果apply、call或bind方法用于调用、创建一个函数,函数内的 this 就是作为参数传入这些方法的对象。
当函数作为对象里的方法被调用时,函数内的this是调用该函数的对象。比如当obj.method()被调用时,函数内的 this 将绑定到obj对象。
如果调用函数不符合上述规则,那么this的值指向全局对象(global object)。浏览器环境下this的值指向window对象,但是在严格模式下(‘use strict’),this的值为undefined。
如果符合上述多个规则,则较高的规则(1 号最高,4 号最低)将决定this的值。
如果该函数是 ES2015 中的箭头函数,将忽略上面的所有规则,this被设置为它被创建时的上下文。
想获得更深入的解释,请查看他在 Medium 上的文章。

https://github.com/yangshun/front-end-interview-handbook/blob/master/Translations/Chinese/questions/javascript-questions.md#%E8%AF%B7%E7%AE%80%E8%BF%B0javascript%E4%B8%AD%E7%9A%84this

二十八、如何确定this指向

如果要判断一个运行中函数的 this 绑定,就需要找到这个函数的直接调用位置。找到之后就可以顺序应用下面这四条规则来判断 this 的绑定对象。 1. 由 new 调用?绑定到新创建的对象。 2. 由 call 或者 apply (或者 bind )调用?绑定到指定的对象。 3. 由上下文对象调用?绑定到那个上下文对象。 4. 默认:在严格模式下绑定到 undefined ,否则绑定到全局对象。

一定要注意,有些调用可能在无意中使用默认绑定规则。如果想“更安全”地忽略 this 绑定,你可以使用一个 DMZ 对象,比如 ø = Object.create(null) ,以保护全局对象。
ES6 中的箭头函数并不会使用四条标准的绑定规则,而是根据当前的词法作用域来决定this ,具体来说,箭头函数会继承外层函数调用的 this 绑定(无论 this 绑定到什么)。这其实和 ES6 之前代码中的 self = this 机制一样

参考:《你不知道的JavaScript》

二十九、== 和 ===的区别是什么

是抽象相等运算符,而=是严格相等运算符。==运算符是在进行必要的类型转换后,再比较。=运算符不会进行类型转换,所以如果两个值不是相同的类型,会直接返回false。使用时,可能发生一些特别的事情,例如:

1 ==1; // true
1 == [1]; // true
1 == true; // true
0 == ‘’; // true
0 ==0; // true
0 == false; // true

如果你对和=的概念不是特别了解,建议大多数情况下使用===

三十、箭头函数和普通函数有什么区别

函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象,用callapplybind也不能改变this指向
不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。
不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。
不可以使用yield命令,因此箭头函数不能用作 Generator 函数。
箭头函数没有原型对象prototype

三十一、首屏时间、白屏时间

Performance 接口可以获取到当前页面中与性能相关的信息。
该类型的对象可以通过调用只读属性 Window.performance 来获得。
白屏时间:

performance.timing.responseStart - performance.timing.navigationStart
首屏时间

window.onload = () => {
new Date() - performance.timing.responseStart
}
https://developer.mozilla.org/zh-CN/docs/Web/API/Performance

三十二、当你在浏览器输入一个地址后发生了什么

https://github.com/skyline75489/what-happens-when-zh_CN/blob/master/README.rst?utm_medium=social&utm_source=wechat_session&from=timeline&isappinstalled=0

三十三、页面大量图片,如何优化加载,优化用户体验

图片懒加载。在页面的未可视区域添加一个滚动事件,判断图片位置与浏览器顶端的距离与页面的距离,如果前者小于后者,优先加载。
如果为幻灯片、相册等,可以使用图片预加载技术,将当前展示图片的前一张和后一张优先下载。
如果图片为css图片,可以使用CSSsprite,SVGsprite等技术。
如果图片过大,可以使用特殊编码的图片,加载时会先加载一张压缩的特别厉害的缩略图,以提高用户体验。
如果图片展示区域小于图片的真实大小,应在服务器端根据业务需要先进行图片压缩,图片压缩后大小与展示一致。
https://www.jianshu.com/p/5d82bba9e1a1

三十四、js网络请求性能优化之防抖与节流

防抖(debounce)
在函数需要频繁触发时,只有当有足够空闲的时间时,才执行一次。就好像在百度搜索时,每次输入之后都有联想词弹出,这个控制联想词的方法就不可能是输入框内容一改变就触发的,他一定是当你结束输入一段时间之后才会触发。
节流(thorttle)
预定一个函数只有在大于等于执行周期时才执行,周期内调用不执行。就好像你在淘宝抢购某一件限量热卖商品时,你不断点刷新点购买,可是总有一段时间你点上是没有效果,这里就用到了节流,就是怕点的太快导致系统出现bug。
区别
在发生持续触发事件时,防抖设置事件延迟并在空闲时间去触发事件,而节流则是隔一定的时间触发一次。
具体请看:
https://blog.csdn.net/jacoox/article/details/80719456

三十五、如何做到修改url参数页面不刷新

HTML5引入了 history.pushState() 和 history.replaceState() 方法,它们分别可以添加和修改历史记录条目。

let stateObj = {
foo: “bar”,
};

history.pushState(stateObj, “page 2, “bar.html”);

假设当前页面为 foo.html,执行上述代码后会变为 bar.html,点击浏览器后退,会变为 foo.html,但浏览器并不会刷新。 pushState() 需要三个参数: 一个状态对象, 一个标题 (目前被忽略), 和 (可选的) 一个 URL. 让我们来解释下这三个参数详细内容:

状态对象 — 状态对象 state 是一个 JavaScript 对象,通过 pushState () 创建新的历史记录条目。无论什么时候用户导航到新的状态,popstate 事件就会被触发,且该事件的 state 属性包含该历史记录条目状态对象的副本。 状态对象可以是能被序列化的任何东西。原因在于 Firefox 将状态对象保存在用户的磁盘上,以便在用户重启浏览器时使用,我们规定了状态对象在序列化表示后有640k的大小限制。如果你给 pushState() 方法传了一个序列化后大于 640k 的状态对象,该方法会抛出异常。如果你需要更大的空间,建议使用
sessionStorage 以及 localStorage.
标题 — Firefox 目前忽略这个参数,但未来可能会用到。传递一个空字符串在这里是安全的,而在将来这是不安全的。二选一的话,你可以为跳转的 state 传递一个短标题。
URL — 该参数定义了新的历史URL记录。注意,调用 pushState() 后浏览器并不会立即加载这个 URL,但可能会在稍后某些情况下加载这个 URL,比如在用户重新打开浏览器时。新URL不必须为绝对路径。如果新URL是相对路径,那么它将被作为相对于当前 URL 处理。新 URL 必须与当前URL同源,否则 pushState() 会抛出一个异常。该参数是可选的,缺省为当前 URL。
格式化金钱,每千分位加逗号

function format(str) {
	let s = ‘’
	let count = 0
	for (let i = str.length - 1; i >= 0; i–) {
		s = str[i] + s
		count++
		if (count % 3 == 0 && i != 0) {
			s =,+ s
	}
}
return s
}
function format(str) {
	return str.replace(/(\d)(?=(?:\d{3})+$)/g, ‘$1,)
}

三十六、请用js去除字符串空格

去除所有空格

str.replace(/\s/g, ‘’)
去除两边空格

str.replace(/^\s+|\s+$/g, ‘’)
// 原生方法
str.trim()

三十七、HTTP问题

RESTful

REST 指的是一组架构约束条件和原则。满足这些约束条件和原则的应用程序或设计就是 RESTful。

GET
get方法在Rest中主要用于获取资源,能够发送参数,不过有限制,且参数都会以?开头的形 式附加在URL尾部。 规范的get方法处理器应该是幂等的,也就是说对一个资源不论发送多少次get请求都不会更改数据或造成破坏。
POST
post方法在Rest请求中主要用于添加资源,参数信息存放在请求报文的消息体中相对安全,且可发送较大信息
PUT
put方法在Rest中主要用于更新资源,因为大多数浏览器不支持put和delete,会自动将put和delete请求转化为get和post. 因此为了使用put和delete方法, 需要以post发送请求,在表单中使用隐藏域发送真正的请求。 put方法的参数是同post一样是存放在消息中的,同样具有安全性,可发送较大信息。 put方法是幂等的,对同一URL资源做出的同一数据的任意次put请求其对数据的改变都是一致的。
DELETE
Delete在Rest请求中主要用于删除资源,因为大多数浏览器不支持put和delete,会自动将put和delete请求转化为get和post。 因此为了使用put和delete方法,需要以post发送请求,在表单中使用隐藏域发送真正的请求。 Delete方法的参数同post一样存放在消息体中,具有安全性,可发送较大信息 Delete方法是幂等的,不论对同一个资源进行多少次delete请求都不会破坏数据
https://blog.csdn.net/jnshu_it/article/details/80203696

GET和POST的区别

GET产生一个TCP数据包;POST产生两个TCP数据包。
GET在浏览器回退时是无害的,而POST会再次提交请求。
GET产生的URL地址可以被Bookmark,而POST不可以。
GET请求会被浏览器主动cache,而POST不会,除非手动设置。
GET请求只能进行url编码,而POST支持多种编码方式。
GET请求参数会被完整保留在浏览器历史记录里,而POST中的参数不会被保留。
GET请求在URL中传送的参数是有长度限制的,而POST么有。
对参数的数据类型,GET只接受ASCII字符,而POST没有限制。
GET比POST更不安全,因为参数直接暴露在URL上,所以不能用来传递敏感信息。
GET参数通过URL传递,POST放在Request body中。
Accept和Content-Type

Accept 请求头用来告知客户端可以处理的内容类型,这种内容类型用MIME类型来表示。 服务器使用 Content-Type 应答头通知客户端它的选择。

Accept: text/html
Accept: image/*
Accept: text/html, application/xhtml+xml, application/xml;q=0.9, /;q=0.8
1.Accept属于请求头, Content-Type属于实体头。
Http报头分为通用报头,请求报头,响应报头和实体报头。
请求方的http报头结构:通用报头|请求报头|实体报头
响应方的http报头结构:通用报头|响应报头|实体报头

2.Accept代表发送端(客户端)希望接受的数据类型。
比如:Accept:text/xml;
代表客户端希望接受的数据类型是xml类型

Content-Type代表发送端(客户端|服务器)发送的实体数据的数据类型。
比如:Content-Type:text/html;
代表发送端发送的数据格式是html。

二者合起来,
Accept:text/xml;
Content-Type:text/html
即代表希望接受的数据类型是xml格式,本次请求发送的数据的数据格式是html。

状态码

| 状态码 | 类别 | 描述 | | – | – | – | | 1xx | Informational(信息状态码) | 接受请求正在处理 | | 2xx | Success(成功状态码) | 请求正常处理完毕 | | 3xx | Redirection(重定向状态码) | 需要附加操作已完成请求 | | 4xx | Client Error(客户端错误状态码) | 服务器无法处理请求 | | 5xx | Server Error(服务器错误状态码) | 服务器处理请求出错 |

HTTP缓存

https://segmentfault.com/a/1190000010690320

如何处理不让别人盗用你的图片,访问你的服务器资源

http header, 对refer做判断看来源是不是自己的网站,如果不是就拒绝
通过session校验,如果不通过特定服务生成cookie和session就不能请求得到资源
Http与Https的区别

HTTP 的URL 以http:// 开头,而HTTPS 的URL 以https:// 开头
HTTP 是不安全的,而 HTTPS 是安全的
HTTP 标准端口是80 ,而 HTTPS 的标准端口是443
在OSI 网络模型中,HTTP工作于应用层,而HTTPS 的安全传输机制工作在传输层
HTTP 无法加密,而HTTPS 对传输的数据进行加密
HTTP无需证书,而HTTPS 需要CA机构wosign的颁发的SSL证书
https://zhuanlan.zhihu.com/p/33778904

什么是Http协议无状态协议?怎么解决Http协议无状态协议?

无状态协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息也就是说,
当客户端一次HTTP请求完成以后,客户端再发送一次HTTP请求,HTTP并不知道当前客户端是一个”老用户“。

可以使用Cookie来解决无状态的问题,Cookie就相当于一个通行证,第一次访问的时候给客户端发送一个Cookie,
当客户端再次来的时候,拿着Cookie(通行证),那么服务器就知道这个是”老用户“。

https://zhuanlan.zhihu.com/p/33778904

常用的HTTP方法有哪些

GET:用于请求访问已经被URL(统一资源标识符)识别的资源,可以通过URL传参给服务器。
POST:用于传输信息给服务器,主要功能与Get方法类似,但一般推荐POST方式。
PUT:传输文件,报文主体包含文件内容,保存到对应URL位置。
HEAD:获取报文首部,与GET方法类似,只是不返回报文主体,一般用于验证URL是否有效。
DELET:删除文件,与PUT方法相反,删除对应URL位置的文件。OPTIONS:查询相应URL支持的HTTP方法。
一次完整的HTTP请求所经历的7个步骤

HTTP通信机制是在一次完整的HTTP通信过程中,Web浏览器与Web服务器之间将完成下列7个步骤:

建立TCP连接
在HTTP工作开始之前,Web浏览器首先要通过网络与Web服务器建立连接,该连接是通过TCP来完成的,该协议与IP协议共同构建 Internet,即著名的TCP/IP协议族,因此Internet又被称作是TCP/IP网络。HTTP是比TCP更高层次的应用层协议,根据规则, 只有低层协议建立之后才能,才能进行更层协议的连接,因此,首先要建立TCP连接,一般TCP连接的端口号是80。

Web浏览器向Web服务器发送请求行
一旦建立了TCP连接,Web浏览器就会向Web服务器发送请求命令。例如:GET /sample/hello.jsp HTTP/1.1。

Web浏览器发送请求头
浏览器发送其请求命令之后,还要以头信息的形式向Web服务器发送一些别的信息,之后浏览器发送了一空白行来通知服务器,它已经结束了该头信息的发送。

Web服务器应答
客户机向服务器发出请求后,服务器会客户机回送应答, HTTP/1.1 200 OK ,应答的第一部分是协议的版本号和应答状态码。

Web服务器发送应答头
正如客户端会随同请求发送关于自身的信息一样,服务器也会随同应答向用户发送关于它自己的数据及被请求的文档。

Web服务器向浏览器发送数据
Web服务器向浏览器发送头信息后,它会发送一个空白行来表示头信息的发送到此为结束,接着,它就以Content-Type应答头信息所描述的格式发送用户所请求的实际数据。

Web服务器关闭TCP连接
一般情况下,一旦Web服务器向浏览器发送了请求数据,它就要关闭TCP连接,然后如果浏览器或者服务器在其头信息加入了这行代码:

Connection:keep-alive
TCP连接在发送后将仍然保持打开状态,于是,浏览器可以继续通过相同的连接发送请求。保持连接节省了为每个请求建立新连接所需的时间,还节约了网络带宽。

建立TCP连接->发送请求行->发送请求头->(到达服务器)发送状态行->发送响应头->发送响应数据->断TCP连接

https://juejin.im/post/5a8102e0f265da4e710f5910

三十八、Vue问题

什么是mvvm

MVVM最早由微软提出来,它借鉴了桌面应用程序的MVC思想,在前端页面中,把Model用纯JavaScript对象表示,View负责显示,两者做到了最大限度的分离 把Model和View关联起来的就是ViewModel。
ViewModel负责把Model的数据同步到View显示出来,还负责把View的修改同步回Model
View 和 Model 之间的同步工作完全是自动的,无需人为干涉(由viewModel完成,在这里指VUE)
因此开发者只需关注业务逻辑,不需要手动操作DOM, 不需要关注数据状态的同步问题,复杂的数据状态维护完全由 MVVM 来统一管理

ViewModel如何编写?

需要用JavaScript编写一个通用的ViewModel,这样,就可以复用整个MVVM模型了

一个MVVM框架和jQuery操作DOM相比有什么区别? 我们先看用jQuery实现的修改两个DOM节点的例子:

Hello, Bart!

You are 12.

Hello, Bart!

You are 12.
用jQuery修改name和age节点的内容:

var name = ‘Homer’;
var age = 51;

$(’#name’).text(name);
$(’#age’).text(age);
如果我们使用MVVM框架来实现同样的功能,我们首先并不关心DOM的结构,而是关心数据如何存储。最简单的数据存储方式是使用JavaScript对象:

var person = {
name: ‘Bart’,
age: 12
}
我们把变量person看作Model,把HTML某些DOM节点看作View,并假定它们之间被关联起来了。

要把显示的name从Bart改为Homer,把显示的age从12改为51,我们并不操作DOM,而是直接修改JavaScript对象:

person.name = ‘Homer’;
person.age = 51;
执行上面的代码,我们惊讶地发现,改变JavaScript对象的状态,会导致DOM结构作出对应的变化!这让我们的关注点从如何操作DOM变成了如何更新JavaScript对象的状态,而操作JavaScript对象比DOM简单多了!

这就是MVVM的设计思想:关注Model的变化,让MVVM框架去自动更新DOM的状态,从而把开发者从操作DOM的繁琐步骤中解脱出来! 下图可以很好的解释view viewModel model之间的关系

Vue的优点是什么

mvvm的优点即是vue的优点,在这里再总结一下:
数据和视频之间的同步工作完全是自动的,无需人为干涉,所以开发者只需关注业务逻辑,不需要手动操作DOM, 不需要关注数据状态的同步问题, 复杂的数据状态维护完全由 MVVM 来统一管理,节省了很多精力。

对于生命周期的理解

创建一个Vue实例,是一个漫长的过程,要经历初始化,数据合并,模板解析,数据渲染等等一系列过程。 所以,为了能实现在这个过程里面插入自己想要提前做的事情,就有了生命周期钩子函数。

举个例子:

一辆公交车,从出发点A站到终点站B,中间有很多站点,公交车每到一个站点,就得停下来,
等待客人上车,然后再驶往下一个站点,一直到终点站为止。
A和B之间的站点,就像是这个路程的生命周期。每一个站点都是一个不同的生命周期(站点名不同),
只要到了站点,就得执行该站点对应的生命周期函数,
只不过每个站点的生命周期函数都是一样的(等待客人上车)。
Vue中的生命周期也是一样,对应了Vue实例从创建到结束之间的每一个过程。 例如,Vue的beforeCreate周期,指的就是Vue在实例初始化之后,数据观测 (data observer) 和 event/watcher 事件配置之前被调用。

至于Vue具体的生命周期函数有哪些,请看官网API文档

组件传值

父组件通过prop向子组件传值
子组件通过事件向父组件传值
子组件与子组件之间不能直接传值,需要通过父组件来做间接传值,在这种情况下推荐使用vuex
具体例子请看官方文档

vue数据绑定原理

Vue的数据双向绑定都是依据Object.defineProperty()这一方法来做的
Object.defineProperty到底有什么作用呢? MDN

Object.defineProperty(obj, prop, descriptor)

obj
要在其上定义属性的对象。

prop
要定义或修改的属性的名称。

descriptor
将被定义或修改的属性描述符。
简单来说 这个方法可以定义一个对象某个属性的描述符

我们需要用到的就是描述符当中的getter和setter

const obj = {a:1}
obj.a // 1

obj.a = 2
像上面代码中的两个操作 读取和赋值 就是在访问obj.a的getter和setter
当我们输入obj.a时 就是在访问obj对象a属性的getter 当输入obj.a = 2 时就是在访问obj对象a属性的setter

Object.defineProperty(obj, ‘a’, {
get : function(){
return val
},
set : function(newValue){
val = newValue
},
enumerable : true,
configurable : true
})
getter和setter都是一个函数 我们还可以这样做 例如

get: function() {
// 每次访问obj.a时都会执行这段代码
console.log(‘hello, 你在读取a的值’)
return val
}
set: function(newValue) {
val = newValue
// 每次给obj.a赋值时都会执行这段代码
console.log(‘你设置了a的值’)
}
Vue的双向数据绑定就是根据上面的原理来实现的 只要在读取值时收集观察者 在赋值时触发观察者更新函数 就可以实现数据变更 从而实现DOM重新渲染

说到这可能还不是很明白 不要急 慢慢来 先看一下这段代码 复制放到HTML文件里自己运行一下 然后打开网页 在控制台里输入data.user.name看看 会有惊喜

vue-router原理
说简单点,vue-router的原理就是通过对URL地址变化的监听,继而对不同的组件进行渲染。
每当URL地址改变时,就对相应的组件进行渲染。原理是很简单,实现方式可能有点复杂,主要有hash模式和history模式。
如果想了解得详细点,建议百度或者阅读源码。

vuex原理

vuex的原理其实非常简单,它为什么能实现所有的组件共享同一份数据?
因为vuex生成了一个store实例,并且把这个实例挂在了所有的组件上,所有的组件引用的都是同一个store实例。
store实例上有数据,有方法,方法改变的都是store实例上的数据。由于其他组件引用的是同样的实例,所以一个组件改变了store上的数据, 导致另一个组件上的数据也会改变,就像是一个对象的引用。
如果对vuex的实现有兴趣,可以看看我自己造的一个vue轮子对应的vuex插件。它实现了除vuex模块外的所有功能。

v-if和v-show的区别

v-if 是“真正”的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。

v-if 也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。

相比之下,v-show 就简单得多——不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 进行切换。

一般来说,v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用 v-show 较好;如果在运行时条件很少改变,则使用v-if 较好。

https://cn.vuejs.org/v2/guide/conditional.html#v-if-vs-v-show

vue怎么实现页面的权限控制

利用 vue-router 的 beforeEach 事件,可以在跳转页面前判断用户的权限(利用 cookie 或 token),是否能够进入此页面,如果不能则提示错误或重定向到其他页面,在后台管理系统中这种场景经常能遇到。

keep-alive有什么作用

在 Vue 中,每次切换组件时,都会重新渲染。如果有多个组件切换,又想让它们保持原来的状态,避免重新渲染,这个时候就可以使用 keep-alive。 keep-alive 可以使被包含的组件保留状态,或避免重新渲染。

计算属性有什么作用

先来看一下计算属性的定义:
当其依赖的属性的值发生变化的时,计算属性会重新计算。反之则使用缓存中的属性值。
计算属性和vue中的其它数据一样,都是响应式的,只不过它必须依赖某一个数据实现,并且只有它依赖的数据的值改变了,它才会更新。

route和 route和route和router的区别

$route 是路由信息对象,包括path,params,hash,query,fullPath,matched,name 等路由信息参数。

而 $router 是路由实例对象,包括了路由的跳转方法,钩子函数等

watch的作用是什么

watch 主要作用是监听某个数据值的变化。和计算属性相比除了没有缓存,作用是一样的。

借助 watch 还可以做一些特别的事情,例如监听页面路由,当页面跳转时,我们可以做相应的权限控制,拒绝没有权限的用户访问页面。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

warm-summer

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值