vue首屏性能优化,解决页面加载时间过长(白屏)方法
import ShowBlogs from '@/components/ShowBlogs' routes:[ path: 'Blogs', name: 'ShowBlogs', component: ShowBlogs ]
- 改为:
routes:[ path: 'Blogs', name: 'ShowBlogs', component: () => import('./components/ShowBlogs.vue') ]
- 如果是在 vuecli 3中,我们还需要多做一步工作
因为 vuecli 3默认开启 prefetch(预先加载模块),提前获取用户未来可能会访问的内容
在首屏会把这十几个路由文件,都一口气下载了
所以我们要关闭这个功能,在 vue.config.js中设置: - ui框架按需加载
在 .babelrc / babel.config.js文件中添加( vue-cli 3要先安装 babel-plugin-component):
plugins: [ [ "component", { "libraryName": "element-ui", "styleLibraryName": "theme-chalk" } ] ]
cnpm i compression-webpack-plugin -D
在 vue.config.js中引入并修改 webpack配置:
const CompressionPlugin = require('compression-webpack-plugin') configureWebpack: (config) => { if (process.env.NODE_ENV === 'production') { // 为生产环境修改配置... config.mode = 'production' return { plugins: [new CompressionPlugin({ test: /\.js$|\.html$|\.css/, //匹配文件名 threshold: 10240, //对超过10k的数据进行压缩 deleteOriginalAssets: false //是否删除原文件 })] } }
在服务器我们也要做相应的配置
如果发送请求的浏览器支持 gzip,就发送给它 gzip格式的文件。
https://segmentfault.com/a/1190000020383064
Vue 响应式原理
数据劫持 (变化侦听+依赖收集)
数据劫持: vue.js 则是采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调。Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性, 并返回这个对象。
vue中通过observe
方法实现所有的数据属性的劫持.
- vue在实例化的时候会将data数据中的属性都做数据劫持。(2.0之前)
- 如果是对象,也会迭代本身属性将全部属性都实现数据劫持
- 当赋值的时候,如果是
newVal
是对象,也会去迭代newVal
的属性实现全部属性的数据劫持
Array对象
是没有办法通过上述方法实现数据劫持的:
vue中实现的方法实际是对数组的属性重写,重写过后的方法不仅能实现原有的功能,还能发布消息给订阅者。
如果要更新 Array
某个索引对应的值得时候,要用Vue.set方式实现
Vue.set是对数据进行拦截,实际就是数据劫持处理,并发布一次消息
手写bind
//context为需要被绑定上的对象,arguments是参数 Function.prototype.bind = function(context){ var self = this; //this => Function return function(){ return self.apply(context,arguments)// } }
http与https区别
- https协议需要到ca申请证书,一般免费证书较少,因而需要一定费用。
- http是超文本传输协议,信息是明文传输,https则是具有安全性的ssl加密传输协议。
- http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。
- http的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。
HTTPS 连接过程
服务器发送它的证书给浏览器,浏览器确认证书正确,并检查证书中对应的主机名是否正确,如果正确则双方对数据用公钥加密后发给对方,对方再用私钥进行解密,保证数据是不透明的。
非对称加密算法:加密用公钥,解密用私钥,公钥是公开的,私钥是不会传播的。常见的算法如:RSA。
- 客户端在使用HTTPS方式与Web服务器通信时有以下几个步骤,如图所示。
- 客户使用https url访问服务器,则要求web 服务器建立ssl链接。
- web服务器接收到客户端的请求之后,会将网站的证书(证书中包含了公钥),返回或者说传输给客户端。
- 客户端和web服务器端开始协商SSL链接的安全等级,也就是加密等级。
- 客户端浏览器通过双方协商一致的安全等级,建立会话密钥,然后通过网站的公钥来加密会话密钥,并传送给网站。
- web服务器通过自己的私钥解密出会话密钥。
- web服务器通过会话密钥加密与客户端之间的通信。
SSL原理
es6之扩展/剩余运算符 三个点(…)
- 对象中的扩展运算符(…)用于取出参数对象中的所有可遍历属性,拷贝到当前对象之中
前端跨脚本攻击xss
恶意攻击者往Web页面里插入恶意的Script代码,当用户浏览该页之时,嵌入其中Web里面的Script代码会被执行,从而达到恶意攻击用户的目的。
XSS也称为跨脚本攻击,是一种恶意脚本,可以获取用户得cookie、token、session.
分为存储性(持久型)、反射型(非持久型)、基于DOM
- 尽量少使用
appenChild
、innerHTML
、outerHTML
等标签,而使用innerText
、textContent
、setAttribute
- 前端过滤:url: 可以使用
encodeURIComponent
进行转义 - 通过 Content-Security-PolicyHTTP头来开启CSP:
- 通过JavaScript的 Document.cookie API无法访问带有 HttpOnly 标记的Cookie,它们只应该发送给服务端。如果 Cookie 不想被客户端 JavaScript 脚本调用,那么就应该为其设置 HttpOnly 标记。
CSRF跨站请求伪造
CSRF 攻击是攻击者借助受害者的 Cookie 骗取服务器的信任,可以在受害者毫不知情的情况下以受害者名义伪造请求发送给受攻击服务器.
由于同源策略,跨域的Ajax请求不会带上Cookie,然而script/iframe/img等标签却是支持跨域的。所以在请求的时候是会带上cookie的。
- Get 请求不对数据进行修改/写
- 请求时附带验证信息,如验证码或Token,可以在 HTTP 请求中以参数的形式加入一个随机产生的 token,并在服务器端建立一个拦截器来验证这个 token。
- 不让第三方网站访问到用户Cookie
- 阻止第三方网站请求接口
XSS与CSRF的区别:
- XSS是利用用户对指定网站的信任
- CSRF是利用网站对用户的信任
call apply bind区别
- call、apply、bind的作用是改变函数运行时this的指向
- call 方法第一个参数是要绑定给this的值,后面传入的是一个参数列表。当第一个参数为null、undefined的时候,默认指向window。
- apply接受两个参数,第一个参数是要绑定给this的值,第二个参数是一个参数数组。当第一个参数为null、undefined的时候,默认指向window。
- bind和call很相似,第一个参数是this的指向,从第二个参数开始是接收的参数列表。区别在于bind方法返回值是函数以及bind接收的参数列表的使用。bind返回对应函数, 便于稍后调用; apply, call则是立即调用。
- 箭头函数体内的 this 对象, 就是定义时所在的对象, 而不是使用时所在的对象
++1 和i++哪个效率高
++i的效率高些,++i在运算过程中不产生临时对象,返回的就是i,是个左值,类似++i=1这样的表达式是合法的,而i++在运算的过程中会产生临时对象,返回的是临时对象的值,是个右值,像i++=1这样的表达式是非法的.
对于内置类型,单独的i++和++i语句,现在的编译器基本上都会优化成++i,所以就没什么区别了
webpack热更新 HMR
为 Webpack 开发环境开启热更新,要做两件事:
- 使用
HotModuleReplacementPlugin
插件 - 打开
webpack-dev-server
的热更新开关
每次 compiler 的 ‘done’ 钩子函数被调用的时候就会要求客户端去检查模块更新,如果客户端不支持 HMR,那么就会全局加载。通过 webpack-dev-server 提供的 websocket 服务端代码通知 websocket 客户端发送的 ok 和 warning 信息的时候会要求更新。如果支持 HMR 的情况下就会要求检查更新,同时发送过来的还有服务器端本次编译的 compilation 的 hash 值。如果不支持 HMR,那么要求刷新页面。如果 ok 则调用reloadApp方法,而 reloadApp 方法 判断如果开启了 HMR 模式, 通过hotEmitter 执行webpackHotUpdate方法,如果不是 Hotupdate 那么直接 reload刷新网页。所以"webpack/hot/only-dev-server"的文件内容就是检查哪些模块更新了(通过 webpackHotUpdate 事件完成,而该事件依赖于compilation的 hash 值),其中哪些模块更新成功,而哪些模块由于某种原因没有更新成功。
sass less
事件流,冒泡捕获
从页面中接收事件的顺序称为事件流.
IE的事件流叫做事件冒泡(event bubbling),即事件开始时由最具体的元素(文档中嵌套层次最深的那个节点)接收,然后逐级向上传播到较为不具体的节点(文档)。
Netscape Communicator团队提出的另一种事件流叫做事件捕获(event capturing)。事件捕获是不太具体的节点应该更早接收到事件,而最具体的节点应该最后接收到事件。事件捕获的用意在于在事件到达预期目标之前捕获它。
“DOM2级事件”规定的事件流包括三个阶段:事件捕获阶段、处于目标阶段和事件冒泡阶段。
首先发生的是事件捕获,为截获事件提供了机会。然后是实际的目标接收到事件。最后一个阶段是冒泡阶段,可以在这个阶段对事件作出响应。
先捕获,后冒泡
事件流比较典型应用是事件委托。事件委托利用了事件冒泡,只指定一个事件处理程序,就可以管理一类型的所有事件。
强缓存,协商缓存
- 强缓存:强缓存是利用http的返回头中的
Expires
或者Cache-Control
两个字段来控制的,用来表示资源的缓存时间。
Cache-Control与Expires可以在服务端配置同时启用,同时启用的时候Cache-Control优先级高。 - 协商缓存:协商缓存就是由服务器来确定缓存资源是否可用,所以客户端与服务器端要通过某种标识来进行通信,从而让服务器判断请求资源是否可以缓存访问。
这主要涉及到下面两组header字段,这两组搭档都是成对
出现的,即第一次请求的响应头带上某个字段**(Last-Modified
或者Etag
),则后续请求则会带上对应的请求字段**(If-Modified-Since
或者If-None-Match
),若响应头没有Last-Modified或者Etag字段,则请求头也不会有对应的字段。Etag/If-None-Match返回的是一个校验码。ETag可以保证每一个资源是唯一的,资源变化都会导致ETag变化。与Last-Modified不一样的是,当服务器返回304 Not Modified的响应时,由于ETag重新生成过,response header中还会把这个ETag返回,即使这个ETag跟之前的没有变化。
etag标签(实体标签)
ETag是HTTP1.1中才加入的一个属性,用来帮助服务器控制Web端的缓存验证。
- 原理是这样的:当浏览器请求服务器的某项资源(A)时, 服务器根据A算出一个哈希值(3f80f-1b6-3e1cb03b)并通过 ETag 返回给浏览器,浏览器把"3f80f-1b6-3e1cb03b" 和 A 同时缓存在本地,当下次再次向服务器请求A时,会通过类似
If-None-Match: "3f80f-1b6-3e1cb03b"
的请求头把ETag发送给服务器,服务器再次计算A的哈希值并和浏览器返回的值做比较,如果发现A发生了变化就把A返回给浏览器(200),如果发现A没有变化就给浏览器返回一个304未修改。这样通过控制浏览器端的缓存,可以节省服务器的带宽,因为服务器不需要每次都把全量数据返回给客户端。
ETag`HTTP响应头是资源的特定版本的标识符。这可以让缓存更高效,并节省带宽,因为如果内容没有改变,Web服务器不需要发送完整的响应。而如果内容发生了变化,使用ETag有助于防止资源的同时更新相互覆盖(“空中碰撞”)。
如果给定URL中的资源更改,则一定要生成新的Etag值。 因此Etags类似于指纹,也可能被某些服务器用于跟踪。 比较etags能快速确定此资源是否变化,但也可能被跟踪服务器永久存留。
装饰者模式
装饰模式指的是在不必改变原类文件和使用继承的情况下,**动态地扩展一个对象的功能。**它是通过创建一个包装对象,也就是装饰来包裹真实的对象。
适用场景:当遇到新的功能或需求需要对原来的操作做出更改时,若原来的操作比较复杂,可以把原来的操作原封不动地放在装饰者中,然后再添加新功能。
function Person() { } Person.prototype.sayHello = function() { console.log('Hello, Alice!'); }; function Decorator(param) { this.person = param; } Decorator.prototype.sayHello = function() { this.person.sayHello(); console.log('Hello, Bruce!'); }; new Decorator(new Person()).sayHello();
WebView调用Android本地组件
webview有两个方法:setWebChromeClient 和 setWebClient
setWebClient:主要处理解析,渲染网页等浏览器做的事情
setWebChromeClient:辅助WebView处理Javascript的对话框,网站图标,网站title,加载进度等
WebViewClient就是帮助WebView处理各种通知、请求事件的。
原型与原型链
http://www.lucklnk.com/godaddy/details/aid/150268326
https://www.jianshu.com/p/ddaa5179cda6
https://segmentfault.com/a/1190000015642813
https://segmentfault.com/a/1190000018511025
构造函数需要使用new关键字来调用。
原型prototype
在JavaScript中,每当定义一个函数数据类型(普通函数、类)时候,都会天生自带一个prototype属性,这个属性指向函数的原型对象,并且这个属性是一个对象数据类型的值。
每个函数对象都有一个 prototype 属性**,每一个JavaScript对象(null除外)在创建的时候就会与之关联另一个对象,这个对象就是我们所说的原型,每一个对象都会从原型"继承"属性。
- proto:每一个JavaScript对象(除了 null )都具有的一个属性,叫proto,这个属性会指向该对象的原型。
- constructor:每个原型都有一个 constructor 属性指向关联的构造函数,实例原型指向构造函数
使用原型的好处就是,可以让所有对象实例共享它包含的属性和方法。即不用在构造函数中定义对象实例的属性和方法,而是直接把这些信息添加到原型对象上面。
使用hasOwnProperty()方法可以检查一个属性是存在于实例中,还是存在原型中?
只在给定属性存在于对象实例中时,才会返回true。
原型链
__proto__
和constructor
每一个对象数据类型(普通的对象、实例、prototype…)也天生自带一个属性__proto__,属性值是当前实例所属类的原型(prototype)。原型对象中有一个属性constructor, 它指向函数对象。
在JavaScript中万物都是对象,对象和对象之间也有关系,并不是孤立存在的。对象之间的继承关系,在JavaScript中是通过prototype对象指向父类对象,直到指向Object对象为止,这样就形成了一个原型指向的链条,专业术语称之为原型链。
举例说明:person → Person → Object ,普通人继承人类,人类继承对象类
当我们访问对象的一个属性或方法时,它会先在对象自身中寻找,如果有则直接使用,如果没有则会去原型对象中寻找,如果找到则直接使用。如果没有则去原型的原型中寻找,直到找到Object对象的原型,Object对象的原型没有原型,如果在Object原型中依然没有找到,则返回undefined。
我们可以使用对象的hasOwnProperty()
来检查对象自身中是否含有该属性;使用in
检查对象中是否含有某个属性时,如果对象中没有但是原型中有,也会返回true
JavaScript 默认并不会复制对象的属性,相反,JavaScript 只是在两个对象之间创建一个关联,这样,一个对象就可以通过委托访问另一个对象的属性和函数,所以与其叫继承,委托的说法反而更准确些。
MVVM
Model–View–ViewModel(MVVM) 是一个软件架构设计模式,由微软 WPF 和 Silverlight 的架构师 Ken Cooper 和 Ted Peters 开发。MVVM 源自于经典的 Model–View–Controller(MVC)模式,MVVM 的核心是 ViewModel 层,它就像是一个中转站(value converter),负责转换 Model 中的数据对象来让数据变得更容易管理和使用,该层向上与视图层进行双向数据绑定,向下与 Model 层通过接口请求进行数据交互,起呈上启下作用。
iframe优缺点
- 优点
1.iframe能够原封不动的把嵌入的网页展现出来。
2.如果遇到加载缓慢的第三方内容如图标和广告,这些问题可以由iframe来解决 - 缺点
很多的移动设备(PDA 手机)无法完全显示框架,设备兼容性差。
iframe框架页面会增加服务器的http请求,对于大型网站是不可取的
JS数组slice()和splice()的区别
- slice(start,end)定义:从已有的数组中返回你选择的某段数组元素;:start可以为负数,此时它规定从数组尾部开始算起的位置。也就是-1 ,指最后一个元素,
- slice()方法不会修改数组本身,而是返回所选取范围的数组元素
- 如果想删除数组中的某一个元素,需要使用splice()。splice()定义:从数组中添加或删除元素,然后返回被删除的数组元素。splice()方法会改变原始数组
- **splice()语法:**arrayObject.splice(index,howmany,item1,…,itemX).howmany表示删除的元素数量,如果为0,则表示不删除数组元素
defineproperty 与 proxy的区别
- Object.defineProperty() 无法监控到数组下标的变化
- proxy可以劫持整个对象,并返回一个新对象,有13种劫持操作,Proxy是es6提供的,兼容性不好,无法用polyfill磨平
- proxy在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写,我们可以这样认为,Proxy是
Object.defineProperty
的全方位加强版.
懒加载和预加载
- 懒加载也叫延迟加载,指的是在长网页中延迟加载图像,是一种很好优化网页性能的方式
- 懒加载原理:首先将页面上的图片的 src 属性设为空字符串,而图片的真实路径则设置在data-original属性中,
当页面滚动的时候需要去监听scroll事件,在scroll事件的回调中,判断我们的懒加载的图片是否进入可视区域,如果图片在可视区内将图片的 src 属性设置为data-original 的值,这样就可以实现延迟加载。 - 预加载简单来说就是将所有所需的资源提前请求加载到本地,这样后面在需要用到时就直接从缓存取资源。
两者主要区别是一个是提前加载,一个是迟缓甚至不加载。
懒加载对服务器前端有一定的缓解压力作用;
预加载则会增加 服务器前端压力。
解决setTimeout中的this指向问题
在setInterval和setTimeout中传入函数时,函数中的this会指向window对象。
解决办法:
推荐用下面两种写法:
- 将bind换成call,apply也会导致立即执行,延迟效果会失效
window.setTimeout(this.declare.bind(this), 2000);
- 使用es6中的箭头函数,因为在箭头函数中this是固定的.箭头函数可以让setTimeout里面的this,绑定定义时所在的作用域
对象字面量vs构造函数创建对象对比
- 字面量的优势
- 它的代码量更少,更易读;
- 它可以强调对象就是一个简单的可变的散列表,而不必一定派生自某个类
- 对象字面量运行速度更快,因为它们可以在解析的时候被优化,它们不需要"作用域解析(scope resolution)";因为存在我们创建了一个同名的构造函数Object()的可能,当我们调用Object()的时候,解析器需要顺着作用域链从当前作用域开始查找,如果在当前作用域找到了名为Object()的函数就执行,如果没找到,就继续顺着作用域链往上照,直到找到全局Object()构造函数为止
- Object()构造函数可以接收参数,通过这个参数可以把对象实例的创建过程委托给另一个内置构造函数,并返回另外一个对象实例,而这往往不是你想要的。Object()构造函数的这种特性会导致一些意想不到的结果,特别是当参数不确定的时候.
Content-Type
互联网媒体类型,在HTTP协议消息头中,使用Content-Type来表示请求和响应中的媒体类型信息。它用来告诉服务端如何处理请求的数据,以及告诉客户端(一般是浏览器)如何解析响应的数据,比如显示图片,解析并展示html等等。
- application/x-www-form-urlencoded
HTTP会将请求参数用key1=val1&key2=val2的方式进行组织,并放到请求实体里面,注意如果是中文或特殊字符如"/“、”,“、“:” 等会自动进行URL转码。不支持文件,一般用于表单提交。
- multipart/form-data
与application/x-www-form-urlencoded不同,这是一个多部分多媒体类型。首先生成了一个 boundary 用于分割不同的字段,在请求实体里每个参数以------boundary开始,然后是附加信息和参数名,然后是空行,最后是参数内容。多个参数将会有多个boundary块。如果参数是文件会有特别的文件域。最后以------boundary–为结束标识。multipart/form-data支持文件上传的格式,一般需要上传文件的表单则用该类型。
- application/json
以“键-值”对的方式组织的数据。这个使用这个类型,**需要参数本身就是json格式的数据,**参数会被直接放到请求实体里,不进行任何处理。服务端/客户端会按json格式解析数据(约定好的情况下)。
- application/xml 和 text/xml
与application/json类似,这里用的是xml格式的数据,text/xml的话,将忽略xml数据里的编码格式。
vue-router
两种模式
- hash —— 即地址栏 URL 中的 # 符号:它的特点在于:hash 虽然出现在 URL 中,但不会被包括在 HTTP 请求中,对后端完全没有影响,因此改变 hash 不会重新加载页面。
- history —— 利用了 HTML5 History Interface 中新增的 pushState() 和 replaceState() 方法。(需要特定浏览器支持)。这两个方法应用于浏览器的历史记录栈,在当前已有的 back、forward、go 的基础之上,它们提供了对历史记录进行修改的功能。只是当它们执行修改时,虽然改变了当前的 URL,但浏览器不会立即向后端发送请求。hash 模式和 history 模式都属于浏览器自身的特性,
钩子函数
- **全局钩子:主要包括
beforeEach
(前置守卫)和aftrEach
(后置钩子),一般用来判断权限,**以及页面丢失时候需要执行的操作 - 单个路由里面的钩子:主要用于写某个指定路由跳转时需要执行的逻辑
- 组件路由:主要包括
beforeRouteEnter
和beforeRouteUpdate
,beforeRouteLeave
,这几个钩子是路由导航守卫,都是写在组件里面
vuex的原理
Vuex:可以理解为Vue的状态管理模式,它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
- State:数据仓库。代表数据的唯一来源,一般Vuex所有的数据都会存储在State中。State本身是一种json对象。
- getter:用来获取数据的。
- Mutation:用来修改数据的。通过commit Mutation修改。Mutation里面的操作要同步的方式。
- Action:用来提交Mutation的。可以进行异步操作。
- State由Mutations驱动变化
- 通过Actions与后端交互
- 通过State渲染前端
- 前端触发Actions来提交Mutations
闭包
闭包可以理解为“定义在一个函数内部的函数”,本质上,闭包是将函数内部和外部连接起来的一座桥梁。
父函数内部定义的子函数,如果没有引用父函数作用域中的变量,那么这个子函数不是闭包。也就是说,闭包是由函数和它所在的环境构成的,缺一不可。
闭包会让函数中的变量都被保存在内存中,内存消耗大,所以不能滥用闭包,可以在不使用该变量的时候将其delete
闭包就是能够读取其他函数内部变量的函数。函数a的内部函数b,被函数a外部的一个变量引用的时候,就创建了一个闭包。(闭包只有在被调用时才执行操作)
闭包的特性:
①.封闭性:外界无法访问闭包内部的数据,如果在闭包内声明变量,外界是无法访问的,除非闭包主动向外界提供访问接口;
②.持久性:一般的函数,调用完毕之后,系统自动注销函数,而对于闭包来说,在外部函数被调用之后,闭包结构依然保存在系统中,闭包中的数据依然存在,从而实现对数据的持久使用。
Promise
1、主要用于异步计算
2、可以将异步操作队列化,按照期望的顺序执行,返回符合预期的结果
3、可以在对象之间传递和操作promise,帮助我们处理队列
在JavaScript的世界中,所有代码都是**单线程执行的。**由于这个“缺陷”,导致JavaScript的所有网络操作,浏览器事件,都必须是异步执行。异步执行可以用回调函数实现.异步操作会在将来的某个时间点触发一个函数调用.
避免 回调地域问题(嵌套层次深)。
promise是一个对象,对象和函数的区别就是对象可以保存状态,函数不可以**(闭包除外)**
Promise最大的好处是在异步执行的流程中,把执行代码和处理结果的代码清晰地分离了.
- resolve作用是,将Promise对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;
- reject作用是,将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected)
promise有三个状态:
1、pending[待定]初始状态
2、fulfilled[实现]操作成功
3、rejected[被否决]操作失败
Promise对象的状态改变,只有两种可能:
从pending变为fulfilled
从pending变为rejected。
这两种情况只要发生,状态就凝固了,不会再变了。
Promise.prototype.finally()不改变Promise状态,在promise结束时,无论结果是fulfilled或者是rejected,都会执行指定的回调函数。这为在Promise
是否成功完成后都需要执行的代码提供了一种方式。这避免了同样的语句需要在then()
和catch()
中各写一次的情况。
Promise还可以做更多的事情,比如,有若干个异步任务,需要先做任务1,如果成功后再做任务2,任何任务失败则不再继续并执行错误处理函数。
job1.then(job2).then(job3).catch(handleError);//其中,job1、job2和job3都是Promise对象。
Object.toString和Object.prototype.toString的区别
Object构造函数
本身没有toString
方法。
依照原型链关系,Object构造函数
的上游原型链是Function.prototype
。调用Object.toString.call(param)
本质上是调用Function.prototype.toString.call(param)
,
原型链终端Object.prototype对象上的toString()确实可以被继承下来,可以用来判断数据类型, 但是并不能满足所有的需求,作为子代的包装类和数组就重写了自己的toString(), 因此当我们调用toString()时,会调用自身原型上重写的toString()。
- Object.toString()是Object构造函数上/原型的方法,返回的是对应的函数
- Object.prototype.toString()是Object原型对象上的方法,返回的是代表该对象的字符串
- Object对象和它的原型链上各自有一个toString()方法,第一个返回的是一个函数,第二个返回的是值类型。
- 每个对象都有一个
toString()
方法,当该对象被表示为一个文本值时,或者一个对象以预期的字符串方式引用时自动调用。默认情况下,toString()
方法被每个Object
对象继承。如果此方法在自定义对象中未被覆盖,toString()
返回 “[object type]”,其中type
是对象的类型。–MDN
var obj = {}; Object.toString(obj);//"function Object() { [native code] }" Object.prototype.toString() // [object Type] Object.prototype.toString.call(obj);//"[object Object]"通过call将this上下文切换到Object,为了每个对象都能通过 Object.prototype.toString() 来检测,需要以 Function.prototype.call() 或者 Function.prototype.apply() 的形式来调用,传递要检查的对象作为第一个参数,称为 thisArg。
- 为什么Array、String、Number、Boolean等不能直接调用toString()
Array,Function,Date虽然是基于Object进行创建的,但是他们继承的是Object.toString(),而不是Object.prototype.toString()。
因为Array、String、Number、Boolean、RegExp、Date等类型都 重写了 toString(),如果直接调用则因为自身的原型对象上已有toString()方法,就不会调用到Object原型对象上的toString()方法了。
- 一般情况下,js中对象的toString(),返回字符串,内容与函数声明语法有关,例如[1,2,3].toString()//“1,2,3”
- 大多数都返回函数的完整源码,Array.toString()//“function Array() { [native code] }”
- 内置函数往往返回一个类似"[native code]“的函数体,需要配合call方法,比如Object.prototype.toString.call([1,2,3])//”[object Array]"
实现弹幕
播放时,设置其translateX
和transform
。
暂停时,把当前的位置写为translateX
,transform
为0,这个时候弹幕就保持在那个位置不动了。
再次播放时,重新设置translateX和transform,当然,transform-duration肯定是依据弹幕长度,当前位置和终点位置(播放器宽度)计算出来的。
播放和暂停的逻辑全都由js处理。
弹幕到位了以后,dom会被移除。
再加上will-change
的GPU加速
B站另外还做了一个操作就是复用dom,弹幕走到头以后不会移除,而是在有下一个新弹幕的时候把原来的dom节点复用,修改style,使其重新播放一次,这个也是性能提高的一个处理办法
https://blog.csdn.net/qq2712193/article/details/51725705
含对象的数组怎么排序
var str=[ {name:"a",age:50}, {name:"b",age:20}, {name:"c",age:40}, {name:"d",age:30}, ]; function compare(key){ return function(value1,value2){ var val1=value1[key]; var val2=value2[key]; return val1-val2; } } str.sort(compare('age'));// console.log(str);
请求报文&响应报文
- 请求报文:请求行:请求方法,HTTP协议版本; 请求头部:Content-Type; 空行;请求体
- 响应报文:状态行:状态码; 响应头部:Content-Type、cache-control、etag; 空行; 响应体;
引入css方式
@import(url(demo.css)) @import完全是CSS提供的一种方式
1.基本不使用,因为页面会先加载html,然后再去加载css,这样就会造成页面样式的延迟。(同步方式)
当一个页面被加载的时候,link引用的CSS会同时被加载,而**@import引用的CSS会等到页面全部被下载完再被加载**
link在支持CSS的浏览器上都支持而**@import只在5.0以上的版本有效**
深拷贝及实现深拷贝的三种方法
- 递归
//使用递归实现深拷贝函数 function deepClone(obj) { var objClone = Array.isArray(obj) ? [] : {} if (obj && typeof obj === 'object') { for (key in obj) { if (obj.hasOwnProperty(key)) {// //判断obj的子元素是否为object对象,如果是则就递归拷贝 if (obj[key] && typeof obj[key] === 'object') { objClone[key] = deepClone(obj[key]) } else { //如果不为对象就直接拷贝 objClone[key] = obj[key] }}}} return objClone }
obj1 = JSON.stringify(obj) + JSON.parse(obj1)
- Jquery:
$.extend(true,[],object)
- 第三方函数库 lodsh
数组和链表的区别
链表通过指针来连接元素与元素,数组则是把所有元素按次序依次存储。
- 链表的插入删除元素相对数组较为简单,不需要移动元素,且较为容易实现长度扩充,但是寻找某个元素较为困难;
-数组寻找某个元素较为简单,但插入与删除比较复杂,由于最大长度需要再编程一开始时指定,故当达到最大长度时,扩充长度不如链表方便。
(1) 从逻辑结构角度来看
- 数组必须事先定义固定的长度(元素个数),不能适应数据动态地增减的情况。当数据增加时,可能超出原先定义的元素个数;当数据减少时,造成内存浪费。
- 链表动态地进行存储分配,可以适应数据动态地增减的情况,且可以方便地插入、删除数据项。(数组中插入、删除数据项时,需要移动其它数据项)
(2) 从内存存储角度来看
- (静态)数组从栈中分配空间, 对于程序员方便快速,但自由度小。
- 链表从堆中分配空间, 自由度大但申请管理比较麻烦.
Array.push原理
- 逐个赋值
- 修改数组长度
var ary=[1,2,3,4,5,6,7,8]; //push() 向数组的末尾添加一个或更多元素,并返回新的长度。 Array.prototype.push=function () { for (var i=0;i<arguments.length;i++){ this[this.length]=arguments[i] } return this.length; }; console.log(ary.push(1, 2, 3)); //pop() 删除并返回数组的最后一个元素 Array.prototype.pop=function () { //this :当前实例 var num=this[this.length-1];//数组最后一个元素 this.length--;//修改长度 return num;//返回原数组最后一个元素 }; console.log(ary.pop()); //shift() 删除并返回数组的第一个元素 //unshift()向数组的开头添加一个或更多元素,并返回新的长度。 //slice(start,end) 从某个已有的数组返回选定的元素 //splice(index,howmany,item1,.....,itemX) 删除元素,并向数组添加新元素。splice() 方法与 slice() 方法的作用是不同的,splice() 方法会直接对数组进行修改。 index: 必需。整数,规定添加/删除项目的位置,使用负数可从数组结尾处规定位置。 howmany: 必需。要删除的项目数量。如果设置为 0,则不会删除项目。 item1, ..., itemX: 可选。向数组 添加 的新项目。