Html5新增标签和属性javascript
增长标签: 一、结构标签 (1)section:独立内容区块,能够用h1~h6组成大纲,表示文档结构,也能够有章节、页眉、页脚或页眉的其余部分; (2)article:特殊独立区块,表示这篇页眉中的核心内容; (3)aside:标签内容以外与标签内容相关的辅助信息; (4)header:某个区块的头部信息/标题; (5)hgroup:头部信息/标题的补充内容; (6)footer:底部信息; (7)nav:导航条部分信息 (8)figure:独立的单元,例如某个有图片与内容的新闻块。 二、表单标签 (1)email:必须输入邮件; (2)url:必须输入url地址; (3)number:必须输入数值; (4)range:必须输入必定范围内的数值; (5)Date Pickers:日期选择器; a.date:选取日、月、年 b.month:选取月、年 c.week:选取周和年 d.time:选取时间(小时和分钟) e.datetime:选取时间、日、月、年(UTC时间) f.datetime-local:选取时间、日、月、年(本地时间) (6)search:搜索常规的文本域; (7)color:颜色 三、媒体标签 (1)video:视频 (2)audio:音频 (3)embed:嵌入内容(包括各类媒体),Midi、Wav、AU、MP三、Flash、AIFF等。 四、其余功能标签 (1)mark:标注(像荧光笔作笔记) (2)progress:进度条;<progress max="最大进度条的值" value="当前进度条的值"> (3)time:数据标签,给搜索引擎使用;发布日期<time datetime="2014-12-25T09:00">9:00</time>更新日期<time datetime="2015- 01-23T04:00" pubdate>4:00</time> (4)ruby和rt:对某一个字进行注释;<ruby><rt>注释内容</rt><rp>浏览器不支持时如何显示</rp></ruby> (5)wbr:软换行,页面宽度到须要换行时换行; (6)canvas:使用JS代码作内容进行图像绘制; (7)command:按钮; (8)deteils :展开菜单; (9)dateilst:文本域下拉提示; (10)keygen:加密; 新增的属性: 对于js进行添加的属性。 <script defer src=".....js" onload="alert('a')"></script> <script async src=".....js" onload="alert('b')"></script> 若是没有以上两个属性的话,执行顺序为先加载(下载)第一个src,而后在执行其onload,而后在向下依次同步执行defer属性在h5以前就已经有了,输入延迟加载(推迟执行),它会先加载(下载)src中文件内容,而后等页面所有加载完成后,再加载onload中js.async属性属于异步加载,它会在加载src后,当即执行onload,同时还会继续加载页面以上执行顺序,alert显示会先显示b而后再显示a 网页中标签中加入小图标的样式代码 <link rel="icon" href="url..." type="图片名称" sizes="16*16"> 有序列表ol:新增start(列表起始值),reversed(是否倒置)menu菜单type属性(3个菜单类型)内嵌css样式:在标签内部来定义一个样式区块(scoped),只对样式标签内部才有效内嵌框架:iframe元素,新增了seamless无边距无边框,srcdoc定义了内嵌框架的内容 <iframe>新增属性: <!--seamless定义框架无边框 无边距--> <!--srcdoc的显示级别比sandbox高--> <!--sandbox用来规定一个内嵌框架的安全级别--> <!--sandbox="allow-forms:容许提交表单"--> <!--sandbox="allow-origin:容许是相同的源"--> <!--sandbox="allow-scripts:容许执行脚本"--> <!--sandbox="allow-top-navigation:容许使外面的页面进行跳转"--> manifest属性: 定义页面须要用到的离线应用文件,通常放在<html>标签里 charset属性: meta属性之一,定义页面的字符集 sizes属性: <link>新增属性,当link的rel="icon"时,用以设置图标大小 base属性: <base href="http://localhost/" target="_blank">表示当在新窗口打开一个页面时,会将href中的内容做为前缀添加到地址前 defer属性: script标签属性,表示脚本加载完毕后,只有当页面也加载完毕才执行(推迟执行) async属性: script标签属性,脚本加载完毕后立刻执行(运行过程当中浏览器会解析下面的内容),即便页面尚未加载完毕(异步执行) media属性: <a>元素属性:表示对何种设备进行优化 hreflang属性: <a>的属性,表示超连接指向的网址使用的语言 ref属性: <a>的属性,定义超连接是不是外部连接 reversed属性: <ol>的属性,定义序号是否倒叙 start属性: <ol>的属性,定义序号的起始值 scoped属性: 内嵌CSS样式的属性,定义该样式只局限于拥有该内嵌样式的元素,适用于单页开发 HTML5全局属性:对任意标签均可以使用的,如下6个 data-yourvalue 、hidden、Spenllecheck、tabindex、contenteditable、desginMode; 全局属性: 1.可直接在标签里插入的:data-自定义属性名字; hidden(直接放上去就是隐藏); spellcheck="true"(语法纠错); tabindex="1"(Tab跳转顺序); contenteditable="true"(可编辑状态,单击内容,可修改); 2.在JavaScript里插入的window.document.designMode = 'on'(JavaScript的全局属性,整个页面的文本均可以编辑了);
垂直水平居中php
仅居中元素定宽高适用 absolute + 负margin absolute + margin auto absolute + calc 居中元素不定宽高 absolute + transform lineheight writing-mode table css-table flex grid
数组去重css
function unique (arr) { return Array.from(new Set(arr)) } var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}]; console.log(unique(arr)) //[1, "true", true, 15, false, undefined, null, NaN, "NaN", 0, "a", {}, {}]
不考虑兼容性,这种去重的方法代码最少。这种方法还没法去掉“{}”空对象,后面的高阶方法会添加去掉重复“{}”的方法。html
function unique(arr){ for(var i=0; i<arr.length; i++){ for(var j=i+1; j<arr.length; j++){ if(arr[i]==arr[j]){ //第一个等同于第二个,splice方法删除第二个 arr.splice(j,1); j--; } } } return arr; } var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}]; console.log(unique(arr)) //[1, "true", 15, false, undefined, NaN, NaN, "NaN", "a", {…}, {…}] //NaN和{}没有去重,两个null直接消失了
双层循环,外层循环元素,内层循环时比较值。值相同时,则删去这个值。
想快速学习更多经常使用的ES6语法,能够看我以前的文章《学习ES6笔记──工做中经常使用到的ES6语法》。前端
function unique(arr) { if (!Array.isArray(arr)) { console.log('type error!') return } var array = []; for (var i = 0; i < arr.length; i++) { if (array .indexOf(arr[i]) === -1) { array .push(arr[i]) } } return array; } var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}]; console.log(unique(arr)) // [1, "true", true, 15, false, undefined, null, NaN, NaN, "NaN", 0, "a", {…}, {…}] //NaN、{}没有去重
新建一个空的结果数组,for 循环原数组,判断结果数组是否存在当前元素,若是有相同的值则跳过,不相同则push进数组。vue
function unique(arr) { if (!Array.isArray(arr)) { console.log('type error!') return; } arr = arr.sort() var arrry= [arr[0]]; for (var i = 1; i < arr.length; i++) { if (arr[i] !== arr[i-1]) { arrry.push(arr[i]); } } return arrry; } var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}]; console.log(unique(arr)) // [0, 1, 15, "NaN", NaN, NaN, {…}, {…}, "a", false, null, true, "true", undefined] //NaN、{}没有去重
利用sort()排序方法,而后根据排序后的结果进行遍历及相邻元素比对。java
function unique(arr) { if (!Array.isArray(arr)) { console.log('type error!') return } var arrry= []; var obj = {}; for (var i = 0; i < arr.length; i++) { if (!obj[arr[i]]) { arrry.push(arr[i]) obj[arr[i]] = 1 } else { obj[arr[i]]++ } } return arrry; } var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}]; console.log(unique(arr)) //[1, "true", 15, false, undefined, null, NaN, 0, "a", {…}] //两个true直接去掉了,NaN和{}去重
function unique(arr) { if (!Array.isArray(arr)) { console.log('type error!') return } var array =[]; for(var i = 0; i < arr.length; i++) { if( !array.includes( arr[i]) ) {//includes 检测数组是否有某个值 array.push(arr[i]); } } return array } var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}]; console.log(unique(arr)) //[1, "true", true, 15, false, undefined, null, NaN, "NaN", 0, "a", {…}, {…}] //{}没有去重
function unique(arr) { var obj = {}; return arr.filter(function(item, index, arr){ return obj.hasOwnProperty(typeof item + item) ? false : (obj[typeof item + item] = true) }) } var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}]; console.log(unique(arr)) //[1, "true", true, 15, false, undefined, null, NaN, "NaN", 0, "a", {…}] //全部的都去重了
利用hasOwnProperty 判断是否存在对象属性node
function unique(arr) { return arr.filter(function(item, index, arr) { //当前元素,在原始数组中的第一个索引==当前索引值,不然返回当前元素 return arr.indexOf(item, 0) === index; }); } var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}]; console.log(unique(arr)) //[1, "true", true, 15, false, undefined, null, "NaN", 0, "a", {…}, {…}]
function unique(arr) { var array= arr; var len = array.length; array.sort(function(a,b){ //排序后更加方便去重 return a - b; }) function loop(index){ if(index >= 1){ if(array[index] === array[index-1]){ array.splice(index,1); } loop(index - 1); //递归loop,而后数组去重 } } loop(len-1); return array; } var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}]; console.log(unique(arr)) //[1, "a", "true", true, 15, false, 1, {…}, null, NaN, NaN, "NaN", 0, "a", {…}, undefined]
function arrayNonRepeatfy(arr) { let map = new Map(); let array = new Array(); // 数组用于返回结果 for (let i = 0; i < arr.length; i++) { if(map .has(arr[i])) { // 若是有该key值 map .set(arr[i], true); } else { map .set(arr[i], false); // 若是没有该key值 array .push(arr[i]); } } return array ; } var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}]; console.log(unique(arr)) //[1, "a", "true", true, 15, false, 1, {…}, null, NaN, NaN, "NaN", 0, "a", {…}, undefined]
建立一个空Map数据结构,遍历须要去重的数组,把数组的每个元素做为key存到Map中。因为Map中不会出现相同的key值,因此最终获得的就是去重后的结果。jquery
function unique(arr){ return arr.reduce((prev,cur) => prev.includes(cur) ? prev : [...prev,cur],[]); } var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}]; console.log(unique(arr)); // [1, "true", true, 15, false, undefined, null, NaN, "NaN", 0, "a", {…}, {…}]
[...new Set(arr)] //代码就是这么少----(其实,严格来讲并不算是一种,相对于第一种方法来讲只是简化了代码)
js经常使用的模式
一.单例模式webpack
单例模式也称做为单子模式,更多的也叫作单体模式。为软件设计中较为简单可是最为经常使用的一种设计模式。 在JavaScript里,实现单例的方式有不少种,其中最简单的一个方式是使用对象字面量的方法,其字面量里能够包含大量的属性和方法。
要扩展该对象,能够添加本身的私有成员和方法,而后使用闭包在其内部封装这些变量和函数声明。样例代码以下:
2、工厂模式
工厂模式是由一个方法来决定到底要建立哪一个类的实例,而这些实例常常都拥有相同的接口。这种模式主要用在所实例化的类型在编译期并不能肯定, 而是在执行期决定的状况。
实例:
这段代码来自es5的new和构造器的相关说明, new自己只是一个对象的复制和改写过程, 而具体会生成什么是由调用ObjectFactory时传进去的参数所决定的。
3、 适配模式
适配模式主要是为了解决一些接口不兼容产生的解决方法。适配器能够在不修改这些不兼容接口的状况下给使用者提供统一的包装过的适配接口。表面上又感受和以前的门面模式比较像,均是对其余对象或者接口进行包装再呈现,而适配器模式偏向的是解决兼容性问题,门面模式则偏向方便性为原则。
好比一个简单的学生查询学科成绩的方法:
这是一个关于适配器来处理参数方面兼容的形式。 适配器模式意义上很简单 - 适配,解决兼容问题。
例子二:jquery里边的$选择器须要改为$id才能和项目搭配,将$转换成$id就很轻松了。以下:
4、外观模式
外观模式,是一种相对简单而又无处不在的模式。外观模式提供一个高层接口,这个接口使得客户端或子系统更加方便调用。 用一段再简单不过的代码来表示:
实现一个简单的订阅发布者
观察者模式,定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,全部依赖于它的对象都将获得通知。
事实上,只要你曾经在DOM节点上绑定过事件函数,那么你就曾经使用过观察者模式了!
document.body.addEventListener('click', function () {
alert(2);
});
可是这只是对观察者模式最简单的使用,在不少场景下咱们常常会实现一些自定义事件来知足咱们的需求。
举个例子:
你去一家公司应聘,谈了一顿下来,hr跟你说:"好了,你回去等通知吧!"。
这个时候,1.你会问公司的电话,而后天天打过去问一遍结果
2.把本身的手机号留给hr,而后等他给你打电话
相信不少时候呢,你们都是选择了后者。
万一你天天给hr打电话弄烦他了,或许他原本打算招你的,如今也再也不打算再鸟你啦!
那么这个时候,hr就至关于一个发布者,而你就是一个订阅者啦!
好吧,大部分叫你回去等消息的就等于没救啦......
我还遇到过一个若是你没被录取,就连通知都不通知你的公司!
那么一个简单的观察者模式应该怎么实现呢?
要指定一个发布者; 给发布者添加一个缓存列表,用于存放回调函数以便通知订阅者;(这家公司不少人来应聘) 最后发布消息的时候,发布者会遍历这个缓存列表,依次触发里面存放的订阅者回调函数;(你up or 你over)
var event = {}; //发布者(hr)
event.clietList = []; //发布者的缓存列表(应聘者列表)
event.listen = function(fn) { //增长订阅者函数
this.clietList.push(fn);
};
event.trigger = function() { //发布消息函数
for (var i = 0; i < this.clietList.length; i++) { var fn = this.clietList[i]; fn.apply(this, arguments); }
};
event.listen(function(time) { //某人订阅了这个消息
console.log('正式上班时间:' + time);
});
event.trigger('2016/10',yes); //发布消息
//输出 正式上班时间:2016/10
到这里,咱们已经实现了一个最简单的观察者模式了!
可是上面的函数其实存在一个问题,那就是发布者没办法选择本身要发布的消息类型!
好比这家公司同时在招php,web前端,若是使用上面的函数就没办法区分职位了!只能一次性把所有订阅者都发送一遍消息。
对上面的代码进行改写:
var event = {}; //发布者(hr)
event.clietList = []; //发布者的缓存列表(应聘者列表)
event.listen = function(key, fn) { //增长订阅者函数
if (!this.clietList[key]) { this.clietList[key] = []; } this.clietList[key].push(fn);
};
event.trigger = function() { //发布消息函数
var key = Array.prototype.shift.call(arguments), fns = this.clietList[key]; for (var i = 0; i < fns.length; i++) { var fn = fns[i]; fn.apply(this, arguments); }
};
event.listen('web前端', fn1 = function(time) { //小强订阅了这个消息。
console.log('姓名:小强'); console.log('正式上班时间:' + time);
});
event.listen('web前端', fn2 = function(time) { //大大强订阅了这个消息
console.log('姓名:大大强'); console.log('正式上班时间:' + time);
});
//发布者发布消息
event.trigger('web前端','小强', '2016/10'); //姓名:小强 正式上班时间:2016/10
event.trigger('php','大大强', '2016/15'); //姓名:大大强 正式上班时间:2016/15
经过添加了一个key,咱们实现了对职位的判断。
有了订阅事件,咱们怎么能少了取消订阅事件呢?
event.remove = function(key, fn) {
var fns = this.clietList[key]; if (!fns) { return false; } if (!fn) { //若是没有传入fn回调函数,直接取消key对应消息的全部订阅 this.clietList[key] = []; } else { for (var i = 0; i < fns.length; i++) { //遍历回调函数列表 var _fn = fns[i]; if (_fn === fn) { fns.splice(i, 1); //删除订阅者的回调函数 } } }
};
//这时候必须指定回调函数,不然没法在remove函数中进行对比删除。
event.listen('web前端', fn1 = function(time) { //小强订阅了这个消息。
console.log('姓名:小强'); console.log('正式上班时间:' + time);
});
event.listen('web前端', fn2 = function(time) { //大大强订阅了这个消息
console.log('姓名:大大强'); console.log('正式上班时间:' + time);
});
event.remove('web前端',fn1);
//发布者发布消息
event.trigger('web前端','2016/10');
//输出 姓名:大大强 正式上班时间:2016/10
对上面代码进行改进,建立一个全局对象来实现观察者模式,
使用闭包实现私有变量,仅暴露必须的API给使用者:
var event = (function() {
var clietList = []; //发布者的缓存列表(应聘者列表) var listen = function(key, fn) { //增长订阅者函数 if (!this.clietList[key]) { this.clietList[key] = []; } this.clietList[key].push(fn); }; var trigger = function() { //发布消息函数 var key = Array.prototype.shift.call(arguments), fns = this.clietList[key]; for (var i = 0; i < fns.length; i++) { var fn = fns[i]; fn.apply(this, arguments); } }; var remove = function(key, fn) { var fns = this.clietList[key]; if (!fns) { return false; } if (!fn) { //若是没有传入fn回调函数,直接取消key对应消息的全部订阅 this.clietList[key] = []; } else { for (var i = 0; i < fns.length; i++) { //遍历回调函数列表 var _fn = fns[i]; if (_fn === fn) { fns.splice(i, 1); //删除订阅者的回调函数 } } } }; return{ listen:listen, trigger:trigger, remove:remove }
})();
观察者模式进阶:
使用命名空间防止事件名冲突 实现先发布后订阅功能
说说weakMap
### **Objects** 咱们应该首先讨论如何使用对象。 好吧,我相信90%以上的人已经知道这部份内容了,由于你点击这篇文章是为了了解新的集合对象,但对于JavaScript的初学者来讲,咱们仍是简单说说它们吧。 1. const algorithm = { site: "leetcode" }; 2. console.log(algorithm.site); // leetcode 4. for (const key in algorithm) { 5. console.log(key, algorithm[key]); 6. } 8. // site leetcode 9. delete algorithm.site; 10. console.log(algorithm.site); // undefined 因此我作了一个 algorithm 对象,它的key和value是一个字符串类型的值,而我经过用 . 关键字来调用该值。 另外,for-in 循环也很适合在对象中循环。你能够用 [] 关键字访问其键对应的值。可是不能使用 for-of 循环,由于对象是不可迭代的。 对象的属性能够用 delete 关键字来删除。这样就能够完全摆脱对象的属性,你们要注意不要和这种方法混淆。 1. const algorithm = { site: "leetcode" }; 2. // Property is not removed!! 3. algorithm.site = undefined; 4. // Property is removed!! 5. delete algorithm.site; algorithm.site = undefined 只是将新值分配给 site。 好的,咱们已经快速讨论了有关对象的一些事项: * 如何添加属性 * 如何遍历对象 * 如何删除属性 ### **Map** Map 是JavaScript中新的集合对象,其功能相似于对象。可是,与常规对象相比,存在一些主要差别。 首先,让咱们看一个建立Map对象的简单示例。 **/ 如何添加属性 /** 1. const map = new Map(); 2. // Map(0) {} Map 不须要建立任何内容,可是添加数据的方式略有不一样。 1. map.set('name', 'john'); 2. // Map(1) {"name" => "john"} Map 有一种特殊的方法可在其中添加称为 set 的属性。它有两个参数:键是第一个参数,值是第二个参数。 1. map.set('phone', 'iPhone'); 2. // Map(2) {"name" => "john", "phone" => "iPhone"} 3. map.set('phone', 'iPhone'); 4. // Map(2) {"name" => "john", "phone" => "iPhone"} 可是,它不容许你在其中添加现有数据。若是 Map 对象中已经存在与新数据的键对应的值,则不会添加新数据。 1. map.set('phone', 'Galaxy'); 2. // Map(2) {"name" => "john", "phone" => "Galaxy"} 可是你能够用其余值覆盖现有数据。 **/ 如何遍历对象 /** Map 是一个可迭代的对象,这意味着能够使用 for-of 语句将其映射。 1. for (const item of map) { 2. console.dir(item); 3. } 4. // Array(2) ["name", "john"] 5. // Array(2) ["phone", "Galaxy"] 要记住的一件事是 Map 以数组形式提供数据,你应该解构数组或访问每一个索引以获取键或值。 要仅获取键或值,还有一些方法可供你使用。 1. map.keys(); 2. // MapIterator {"name", "phone"} 3. map.values(); 4. // MapIterator {"john", "Galaxy"} 5. map.entries(); 6. // MapIterator {"name" => "john", "phone" => "Galaxy"} 你甚至能够使用展开操做符(...)来获取Map的所有数据,由于展开操做符还能够在幕后与可迭代对象一块儿工做。 1. const simpleSpreadedMap = [...map]; 2. // [Array(2), Array(2)] **/ 如何删除属性 /** 从 Map 对象中删除数据也很容易,你所须要作的就是调用 delete。 1. map.delete('phone'); 2. // true 3. map.delete('fake'); 4. // false delete 返回布尔值,该布尔值指示 delete 函数是否成功删除了数据。若是是,则返回 true,不然返回 false。 ### WeakMap WeakMap起源于Map,所以它们彼此很是类似。可是,WeakMap具备很大的不一样。 WeakMap的名字是怎么来的呢?嗯,是由于它与它的引用连接所指向的数据对象的链接或关系没有Map的链接或关系那么强,因此它是弱的。 那么,这究竟是什么意思呢? **/ 差别1:key必须是对象 /** 1. const John = { name: 'John' }; 2. const weakMap = new WeakMap(); 3. weakMap.set(John, 'student'); 4. // WeakMap {{...} => "student"} 5. weakMap.set('john', 'student'); 6. // Uncaught TypeError: Invalid value used as weak map key 你能够将任何值做为键传入Map对象,但WeakMap不一样,它只接受一个对象做为键,不然,它将返回一个错误。 **/ 差别2:并不是Map中的全部方法都支持 /** 能够使用WeakMap的方法以下。 * delete * get * has * set 这个话题最大的不一样是WeakMap不支持迭代对象的方法。可是为何呢?下面将对此进行描述。 **/ 区别3:当GC清理引用时,数据会被删除 /** 与Map相比,这是最大的不一样。 1. let John = { major: "math" }; 3. const map = new Map(); 4. const weakMap = new WeakMap(); 6. map.set(John, 'John'); 7. weakMap.set(John, 'John'); 9. John = null; 10. /* John 被垃圾收集 */ 当John对象被垃圾回收时,Map对象将保持引用连接,而WeakMap对象将丢失连接。因此当你使用WeakMap时,你应该考虑这个功能。 ### Set Set也很是相似于Map,可是Set对于单个值更有用。 **/ 如何添加属性 /** 1. const set = new Set(); 3. set.add(1); 4. set.add('john'); 5. set.add(BigInt(10)); 6. // Set(4) {1, "john", 10n} 与Map同样,Set也阻止咱们添加相同的值。 1. set.add(5); 2. // Set(1) {5} 4. set.add(5); 5. // Set(1) {5} **/ 如何遍历对象 /** 因为Set是一个可迭代的对象,所以能够使用 for-of 或 forEach 语句。 1. for (const val of set) { 2. console.dir(val); 3. } 4. // 1 5. // 'John' 6. // 10n 7. // 5 9. set.forEach(val => console.dir(val)); 10. // 1 11. // 'John' 12. // 10n 13. // 5 **/ 如何删除属性 /** 这一部分和 Map 的删除彻底同样。若是数据被成功删除,它返回 true,不然返回 false。 1. set.delete(5); 2. // true 4. set.delete(function(){}); 5. // false; 若是你不想将相同的值添加到数组表单中,则Set可能会很是有用。 1. /* With Set */ 2. const set = new Set(); 3. set.add(1); 4. set.add(2); 5. set.add(2); 6. set.add(3); 7. set.add(3); 8. // Set {1, 2, 3} 10. // Converting to Array 11. const arr = [ ...set ]; 12. // [1, 2, 3] 14. Object.prototype.toString.call(arr); 15. // [object Array] 17. /* Without Set */ 18. const hasSameVal = val => ar.some(v === val); 19. const ar = []; 21. if (!hasSameVal(1)) ar.push(1); 22. if (!hasSameVal(2)) ar.push(2); 23. if (!hasSameVal(3)) ar.push(3); ### WeakSet 与WeakMap同样,WeakSet也将丢失对内部数据的访问连接(若是内部数据已被垃圾收集)。 1. let John = { major: "math" }; 3. const set = new Set(); 4. const weakSet = new WeakSet(); 6. set.add(John); 7. // Set {{...}} 8. weakSet.add(John); 9. // WeakSet {{...}} 11. John = null; 12. /* John 被垃圾收集 */ 一旦对象 John 被垃圾回收,WeakSet就没法访问其引用 John 的数据。并且WeakSet不支持 for-of 或 forEach,由于它不可迭代。 ### 比较总结 相同点:添加相同的值不支持。 Map vs. WeakMap:WeakMap仅接受对象做为键,而Map不接受。 **Map and Set:** * 可迭代的对象,支持 for..of,forEach 或 ... 运算符 * 脱离GC关系 **WeakMap and WeakSet:** * 不是一个可迭代的对象,不能循环。 * 若是引用数据被垃圾收集,则没法访问数据。 * 支持较少的方法。
ES6的经常使用API
var m1 = new Map(); var m2 = new Map([['a', 123], ['b', 456], [3, 'abc']]);
Symbol类型的属性取值必须是obj[xm],不能直接obj.xm
Symbol能够用来保护对象的某个属性,由于对象的Symbol属性不会被遍历出来
let s1 = Symbol('name'); let s2 = Symbol('name'); console.log( s1 === s2 ); // false
let s1 = Symbol('name'); let s2 = Symbol('name'); console.log( s1 === s2 ); // false
const obj = { foo: 'bar', baz: 42 }; Object.entries(obj); // [ ["foo", "bar"], ["baz", 42] ]
var obj1 = {a: 1, b: 2, c: {d: 4, e: 5}}; var obj2 = Object.assign({}, obj1); console.log(obj1.c === obj2.c); // ture
var obj1 = {a: 1, b: 2, c: {d: 4, e: 5}}; var obj2 = JSON.parse(JSON.stringify(obj1)); console.log(obj1.c === obj2.c); // false
class Cat { mm = 789; // 原型上的属性 constructor(n,c){ // 构造器 this.name = n; this.color = c; this.trait = function () { console.log('卖萌~'); } }; skill(){ // 原型上的方法 console.log('抓老鼠'); }; } class Dog extends Cat { // 继承 constructor(n,c,f){ super(n,c); // 构造函数继承 this.food = f; // super.skill();//super当一个对象来使用时,只能访问方法(函数) // console.log(super.abc);//不能访问属性 // console.log(this.abc);//123 // this.skill();//'抓老鼠' // console.log(super);报错 }; } var dog1 = new Dog('大黄','黑色','shi'); dog1.trait(); dog1.skill(); console.log( dog1.name ); console.log( dog1.color ); console.log( dog1.food ); console.log( dog1.mm ); console.log( dog1.constructor ); // Dog
Vue路由模式
hash与history 对于Vue 这类渐进式前端开发框架,为了构建SPA(单页面应用),须要引入前端路由系统,这也就是Vue-router存在的意义。前端路由的核心,就在于——— 改变视图的同时不会向后端发出请求。
1、为了达到这个目的,浏览器提供了如下两种支持:
一、hash ——即地址栏URL中的#符号(此hsah 不是密码学里的散列运算)。 好比这个URL:http://www.abc.com/#/hello, hash 的值为#/hello。它的特色在于:hash 虽然出现URL中,但不会被包含在HTTP请求中,对后端彻底没有影响,所以改变hash不会从新加载页面。
二、history ——利用了HTML5 History Interface 中新增的pushState() 和replaceState() 方法。(须要特定浏览器支持) 这两个方法应用于浏览器的历史记录站,在当前已有的back、forward、go 的基础之上,它们提供了对历史记录进行修改的功能。只是当它们执行修改是,虽然改变了当前的URL,但你浏览器不会当即向后端发送请求。 history模式,会出现404 的状况,须要后台配置。
2、404 错误
一、hash模式下,仅hash符号以前的内容会被包含在请求中,如 http://www.abc.com, 所以对于后端来讲,即便没有作到对路由的全覆盖,也不会返回404错误;
二、history模式下,前端的url必须和实际向后端发起请求的url 一致,如http://www.abc.com/book/id 。若是后端缺乏对/book/id 的路由处理,将返回404错误。
hash和history的实现方式
主要说一下新增的两个API history.pushState() 和 history.replaceState()
pushState() 和 history.replaceState()同样采用三个参数:状态对象,标题(当前被忽略)和(可选)URL。让咱们更详细地研究这三个参数中的每个
history.replaceState()操做彻底同样history.pushState(),只是replaceState()修改当前的历史条目,而不是建立一个新的。请注意,这不会阻止在全局浏览器历史记录中建立新条目。
replaceState() 当您想要更新当前历史记录条目的状态对象或URL以响应某些用户操做时,此功能特别有用。
不一样之处在于,pushState()会增长一条新的历史记录,而replaceState()则会替换当前的历史记录。
举一个例子
在百度页面打开控制台输入
window.history.pushState(null, null, "https://www.baidu.com/?name=history");
按下回车会发现地址栏变成这样
上面的例子中 改变url页面并无刷新,一样根据API所述,浏览器会产生浏览记录
注意pushState()的url不支持跨域
经过用户的历史记录中向后和向前移动使用作了back(),forward()和go() 方法。
咱们常常在 url 中看到 #,这个 # 有两种状况,一个是咱们所谓的锚点,好比典型的回到顶部按钮原理、Github 上各个标题之间的跳转等,路由里的 # 不叫锚点,咱们称之为 hash,大型框架的路由系统大多都是哈希实现的。
一样咱们须要一个根据监听哈希变化触发的事件 ——hashchange 事件
咱们用 window.location 处理哈希的改变时不会从新渲染页面,而是看成新页面加到历史记录中,这样咱们跳转页面就能够在 hashchange 事件中注册 ajax 从而改变页面内容。
hashchange 在低版本 IE 须要经过轮询监听 url 变化来实现,咱们能够模拟以下
(function(window) { // 若是浏览器不支持原生实现的事件,则开始模拟,不然退出。 if ( "onhashchange" in window.document.body ) { return; } var location = window.location, oldURL = location.href, oldHash = location.hash; // 每隔100ms检查hash是否发生变化 setInterval(function() { var newURL = location.href, newHash = location.hash; // hash发生变化且全局注册有onhashchange方法(这个名字是为了和模拟的事件名保持统一) if ( newHash != oldHash && typeof window.onhashchange === "function" ) { // 执行方法 window.onhashchange({ type: "hashchange", oldURL: oldURL, newURL: newURL }); oldURL = newURL; oldHash = newHash; } }, 100); })(window)
我的感受仍是hash方案好一点,由于照顾到低级浏览器,就是多了#号感受不太美观,二者兼容也是能够的,只能根据浏览器的版本给出对应的方案 不过也支持IE8+ 仍是不错的
兄弟组件的传值
注意:注册的 Bus 要在组件销毁时卸载,不然会屡次挂载,形成触发一次但多个响应的状况。
beforeDestroy () {
this.$Bus.$off('sendMessage', this.message);
}
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的全部组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
图片引用自网络:
vuex
Vuex 的具体使用。
不是方法的方法:
子组件 A 经过事件 $emit 传值传给父组件。 父组件经过属性 props 传值给子组件 B。
深层次嵌套组件传值
provide 选项容许咱们指定咱们想要提供给后代组件的数据/方法。
provide: function () {
return {
getMap: this.getMap
}
}
而后在任何后代组件里,咱们均可以使用 inject 选项来接收指定的咱们想要添加在这个实例上的属性:
inject: ['getMap']
provide 和 inject 主要为高阶插件/组件库提供用例。并不推荐直接用于应用程序代码中。
// 父级组件提供 'foo'
var Provider = {
provide: {
foo: 'bar'
},
// ...
}
// 子组件注入 'foo'
var Child = {
inject: ['foo'],
created () {
console.log(this.foo) // => "bar"
}
// ...
}
然而,依赖注入仍是有负面影响的。它将你应用程序中的组件与它们当前的组织方式耦合起来,使重构变得更加困难。同时所提供的属性是非响应式的。这是出于设计的考虑,由于使用它们来建立一个中心化规模化的数据跟使用 $root 作这件事都是不够好的。若是你想要共享的这个属性是你的应用特有的,而不是通用化的,或者若是你想在祖先组件中更新所提供的数据,那么这意味着你可能须要换用一个像 Vuex 这样真正的状态管理方案了。
这个两个属性是 2.4 新增的特性。
$attrs:
官网介绍的很累赘,暂且理解为非 props 属性集合。更多介绍。
当一个组件中没有声明任何 prop 时,this.$attrs 能够获取到全部父做用域的属性绑定 (class 和 style 除外),而且能够经过 v-bind="$attrs" 传给其内部组件 —— 在建立高级别的组件时很是有用。
inheritAttrs:
控制元素属性是否显示在 dom 上,默认值为 true。
默认状况下父做用域的不被认做 props 的特性绑定 (attribute bindings) 将会“回退”且做为普通的 HTML 特性应用在子组件的根元素上。当撰写包裹一个目标元素或另外一个组件的组件时,这可能不会老是符合预期行为。经过设置 inheritAttrs 到 false,这些默认行为将会被去掉。而经过 (一样是 2.4 新增的) 实例属性 $attrs 可让这些特性生效,且能够经过 v-bind 显性的绑定到非根元素上。
祖先组件:
<template>
<div>
<List-item :title="title" :message="message"></List-item>
</div>
</template>
<script>
import ListItem from "./List-item";
export default {
data() {
return { title: "我是title", message: "传给后代" }
},
components: {
ListItem
}
}
</script>
父组件:
<template>
<div>
<h1>{{title}}</h1> <h2>{{$attrs.message}}</h2> <!-- 经过 v-bind="$attrs" 传入后代组件--> <ListItem2 v-bind='$attrs'></ListItem2>
</div>
</template>
<script>
import ListItem2 from './List-item2'
export default {
props: {
title: String
},
components: {
ListItem2
},
// 默认为 true,若是传入的属性子组件没有 prop 接受,就会以字符串的形式做为标签的属性存在 <div message="传给后代"></div>
// 设为 false,在 dom 中就看不到这些属性 <div>...</div>
inheritAttrs: false
}
</script>
后代组件:
<template>
<div>
{{$attrs.message}}
</div>
</template>
<script>
export default {
mounted() {
console.log(this.$attrs) // {message: "传给后代"}
}
}
</script>
渲染出来的结果为:
$attrs/inheritAttrs
插槽 slot 与子组件传值
在实际项目中确实有遇到插槽后备内容 动态显示的状况,因此这里要补充一下插槽 后备内容 是如何与子组件进行通讯的。
插槽后备内容是指:写在父组件中,包含在子组件标签里的,与子组件中的 slot 对应。
<template>
<child-component>
我是插槽的后备内容
</child-component>
</template>
好比这里有一个含有 slot 的 current-user 组件,它的模版结构是这样的:
<!-- 子组件 current-user.vue -->
<template>
<div>
<div>current-user组件</div> <slot>插槽里默认显示:{{user.firstName}}</slot>
</div>
</template>
<script>
export default {
data() {
return { user: { firstName: "zhao", lastName: "xinglei" } }
}
}
</script>
它的父组件是这样的:
<!-- 父组件 Users.vue -->
<template>
<div>
<div>我是Users组件</div> <current-user> 我是插槽里的后备内容: {{user.lastName}}(我想显示为子组件中 user.lastName ) </current-user>
</div>
</template>
<script>
import CurrentUser from './Current-User.vue'
export default {
components: {
CurrentUser
}
}
</script>
咱们看到,在父组件 Users 中,为子组件 current-user 提供的后备内容中,想要显示子组件定义的 user.firstName 是不能作到的。
官网中提供一个指令 v-slot,它与 props 结合使用从而达到插槽后备内容与子组件通讯的目的。
咱们首先须要在子组件的 slot 中传递一个 props(这个props 叫作插槽props),这里咱们起名叫 user:
<!-- 子组件 current-user.vue -->
<template>
<div>
<div>current-user组件</div> <slot :user="user"> 插槽里默认显示:{{user.firstName}} </slot>
</div>
</template>
在父组件中,包含插槽后备内容的子组件标签上咱们绑定一个 v-slot 指令,像这样:
<template>
<div>
<div>我是Users组件</div> <!-- slotProps里的内容就是子组件传递过来的 props --> <!-- "user": { "firstName": "zhao", "lastName": "xinglei" } --> <current-user v-slot="slotProps"> {{slotProps}} </current-user>
</div>
</template>
最后渲染出来的结果为:
做用域插槽
官网给这种插槽起名叫作做用域插槽,更多了解。
总结
3 ref 子组件引用,在操做表单元素时会应用的到。
说说vue的响应式
Vue 的响应式原理是核心是经过 ES5 的保护对象的 Object.defindeProperty 中的访问器属性中的 get 和 set 方法,data 中声明的属性都被添加了访问器属性,当读取 data 中的数据时自动调用 get 方法,当修改 data 中的数据时,自动调用 set 方法,检测到数据的变化,会通知观察者 Wacher,观察者 Wacher自动触发从新render 当前组件(子组件不会从新渲染),生成新的虚拟 DOM 树,Vue 框架会遍历并对比新虚拟 DOM 树和旧虚拟 DOM 树中每一个节点的差异,并记录下来,最后,加载操做,将全部记录的不一样点,局部修改到真实 DOM 树上。
Mixin
混入 (mixin) 提供了一种很是灵活的方式,来分发 Vue 组件中的可复用功能。一个混入对象能够包含任意组件选项。当组件使用混入对象时,全部混入对象的选项将被“混合”进入该组件自己的选项。
// 定义一个混入对象 var myMixin = { created: function () { this.hello() }, methods: { hello: function () { console.log('hello from mixin!') } } } // 定义一个使用混入对象的组件 var Component = Vue.extend({ mixins: [myMixin] }) var component = new Component() // => "hello from mixin!"
vue的订阅发布者
##### 一、vue响应原理: vue.js采用数据劫持结合发布-订阅者模式,经过Object.defineProperty()来劫持data中各个属性的setter、getter,在数据变更时,发布消息给订阅者,触发响应的监听回调。 (setter和getter是对象的存储器属性,是一个函数,用来获取和设置值) ##### 二、发布-订阅者模式的做用: ##### 一、vue响应原理: vue.js采用数据劫持结合发布-订阅者模式,经过Object.defineProperty()来劫持data中各个属性的setter、getter,在数据变更时,发布消息给订阅者,触发响应的监听回调。 (setter和getter是对象的存储器属性,是一个函数,用来获取和设置值) ##### 二、发布-订阅者模式的做用: 处理一对多的场景,应用于不一样状况下的不一样函数调用 优势:低耦合性,易于代码维护; 缺点:若订阅的消息未发生,需消耗必定的时间和内存。 [](javascript:void(0); "复制代码") <pre><!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <title>Vue发布-订阅模式</title> </head> <body> <div id="app"> 订阅试图-1:<span class="box-1">第一个值</span> 订阅试图-2:<span class="box-2">第二个值</span> </div> <script> //订阅器模型 var Dep = { list: {}, listen: function (key, fn) { (this.list[key] || (this.list[key] = [])).push(fn); }, trigger: function () { var key = Array.prototype.shift.call(arguments); fns = this.list[key]; if (!fns || fns.length == 0) return; for (var i = 0, fn; fn = fns[i++];) { fn.apply(this, arguments);//发布消息附带的参数 } } }; //劫持的方法 Object.defineProperty方法,给对象的属性赋值 var dataHijack = function ({ data, tag, datakey, selector }) { debugger var value = ''; el = document.querySelector(selector); Object.defineProperty(data, datakey, { //拿到数据 get: function () { console.log('我获取到值了'); return value; }, //设置数据 set: function (newVal) { console.log('我设置值了'); value = newVal; Dep.trigger(tag, newVal); //发布消息,更新变化 } }) //绑定观察者 Dep.listen(tag, function (text) { el.innerHTML = text; }) }; var dataObj = {}; //数据 //数据劫持 dataHijack({ data: dataObj, tag: 'view-1', datakey: 'one', selector: '.box-1' }); dataHijack({ data: dataObj, tag: 'view-2', datakey: 'two', selector: '.box-2' }); </script> </body> </html></pre> [](javascript:void(0); "复制代码") [](javascript:void(0); "复制代码") <pre> // jquery中的发布-订阅者 //建立一个事件池 $.Callback() let $pond= $.Callback(); $('.submit').click(function(){ //发布 点击的时候通知事件池中的方法执行,同时传递实参 $pond.fire(100,200); }); let fn1=function(){console.log(1)} let fn2=function(){console.log(2)} let fn3=function(n,m){console.log(3,n+m)} //把须要作的事情添加到事件池中 //事件池至关于一个登记册,把全部订阅者收集到上面 $pond.add(fn1); $pond.add(fn2); $pond.add(fn3); </pre>
常见的状态码
各种别常见状态码: 2xx (3种) 200 OK:表示从客户端发送给服务器的请求被正常处理并返回; 204 No Content:表示客户端发送给客户端的请求获得了成功处理,但在返回的响应报文中不含实体的主体部分(没有资源能够返回); 206 Patial Content:表示客户端进行了范围请求,而且服务器成功执行了这部分的GET请求,响应报文中包含由Content-Range指定范围的实体内容。 3xx (5种) 301 Moved Permanently:永久性重定向,表示请求的资源被分配了新的URL,以后应使用更改的URL; 302 Found:临时性重定向,表示请求的资源被分配了新的URL,但愿本次访问使用新的URL; 301与302的区别:前者是永久移动,后者是临时移动(以后可能还会更改URL) 303 See Other:表示请求的资源被分配了新的URL,应使用GET方法定向获取请求的资源; 302与303的区别:后者明确表示客户端应当采用GET方式获取资源 304 Not Modified:表示客户端发送附带条件(是指采用GET方法的请求报文中包含if-Match、If-Modified-Since、If-None-Match、If-Range、If-Unmodified-Since中任一首部)的请求时,服务器端容许访问资源,可是请求为知足条件的状况下返回改状态码; 307 Temporary Redirect:临时重定向,与303有着相同的含义,307会遵守浏览器标准不会从POST变成GET;(不一样浏览器可能会出现不一样的状况); 4xx (4种) 400 Bad Request:表示请求报文中存在语法错误; 401 Unauthorized:未经许可,须要经过HTTP认证; 403 Forbidden:服务器拒绝该次访问(访问权限出现问题) 404 Not Found:表示服务器上没法找到请求的资源,除此以外,也能够在服务器拒绝请求但不想给拒绝缘由时使用; 5xx (2种) 500 Inter Server Error:表示服务器在执行请求时发生了错误,也有多是web应用存在的bug或某些临时的错误时; 503 Server Unavailable:表示服务器暂时处于超负载或正在进行停机维护,没法处理请求;
Webpack性能优化
`懒加载:`也叫延迟加载,即在须要的时候进行加载,随用随载。 使用懒加载的缘由: `vue`是单页面应用,使用`webpcak`打包后的文件很大,会使进入首页时,加载的资源过多,页面会出现白屏的状况,不利于用户体验。运用懒加载后,就能够按需加载页面,提升用户体验。
import Vue from 'vue' import Router from 'vue-router' Vue.use(Router) export default new Router({ mode: 'history', routes: [ { path: '/', component: resolve => require(['@/components/DefaultIndex'],resolve), children: [ { path: '', component: resolve => require(['@/components/Index'],resolve) }, { path: '*', redirect: '/Index' } ] }) 复制代码
import Vue from 'vue' import Router from 'vue-router' import DefaultIndex from '@/components/DefaultIndex' import Index from '@/components/Index' Vue.use(Router) export default new Router({ mode: 'history', routes: [ { path: '/', component: 'DefaultIndex ', children: [ { path: '', component: 'Index' }, { path: '*', redirect: '/Index' } ] }) 复制代码
通常在`vue`项目中用`webpack`打包时,会根据`webpack.base.conf.js`中`url-loader`中设置`limit`大小来对图片处理,对小于`limit`的图片转化为`base64`格式,其他的不作操做。因此对有些较大的图片资源,在请求资源的时候,加载会很慢,能够用`image-webpack-loader`来压缩图片。
npm install image-webpack-loader --save-dev 复制代码
在`webpack.base.conf.js`文件中引入配置(此项目我用的是脚手架搭建的,因此是`webpack.base.conf.js`)
1: 引入: require("image-webpack-loader") 2:配置: module: { rules: [ ...(config.dev.useEslint ? [createLintingRule()] : []), { test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, loader: 'url-loader', options: { loader: 'image-webpack-loader', options: { bypassOnDebug: true, } } }, 或者也可配置为: { test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, use:[ { loader: 'url-loader', options: { limit: 10000, name: utils.assetsPath('img/[name].[hash:7].[ext]') } }, { loader: 'image-webpack-loader', options: { bypassOnDebug: true, } } ] } 复制代码
因为`webpack`打包后的`js`过大,以致于在加载资源时间过长。因此将文件打包成多个`js`文件,在须要的时候按需加载。
entry:{ main:'xxx.js' } plugins:{ new commonsChunkPlugin({ name:'commons', minChunks:function(module){ // 下边return参考的vue-cli配置 // any required modules inside node_modules are extracted to vendor return ( module.resource && /\.js$/.test(module.resource) && module.resource.indexOf( path.join(__dirname, '../node_modules') ) === 0 ) } }) , // 如下才是关键 new commonsChunkPlugin({ name:'charts', chunks:['commons'] minChunks:function(module){ return ( module.resource && /\.js$/.test(module.resource) && module.resource.indexOf( path.join(__dirname, '../node_modules') ) === 0 && ['jquery.js', 'highcharts.js','echarts'].indexOf( module.resource.substr(module.resource.lastIndexOf('/')+1).toLowerCase() ) != -1 ) } }) } 复制代码
1:打包时,将一些没必要要的插件能够去掉,以防止打包一些无用的资源,致使打包后的文件过大,影响性能。 2:在引入第三方插件的时候,若是该插件的组件过大,能够按需引入,如`element-ui`。 3:使用`webpack.optimize.UglifyJsPlugin`插件压缩混淆[js](http://lib.csdn.net/base/javascript "JavaScript知识库")代码,使用方法以下:
plugins: [//webpack.config.jsnew webpack.optimize.UglifyJsPlugin({ warnings: false, compress: { join_vars: true, warnings: false, }, toplevel: false, ie8: false, ] 复制代码
web前端项目,静态资源放在`cdn`上比较多,`gzip`的压缩是很是必要的,它直接改变了`js`文件的大小,减小两到三倍。 参考[加速nginx: 开启gzip和缓存](https://link.jianshu.com/?t=https%3A%2F%2Fwww.darrenfang.com%2F2015%2F01%2Fsetting-up-http-cache-and-gzip-with-nginx%2F),`nginx`的`gzip`配置很是简单,在你对应的域名底下,添加下面的配置,重启服务便可。`gzip_comp_level`的值大于`2`的时候并不明显,建议设置在`1或者2`之间。
# 开启gzip gzip on; # 启用gzip压缩的最小文件,小于设置值的文件将不会压缩 gzip_min_length 1k; # gzip 压缩级别,1-10,数字越大压缩的越好,也越占用CPU时间,后面会有详细说明 gzip_comp_level 2; # 进行压缩的文件类型。javascript有多种形式。其中的值能够在 mime.types 文件中找到。 gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png; # 是否在http header中添加Vary: Accept-Encoding,建议开启 gzip_vary on; # 禁用IE 6 gzip gzip_disable "MSIE [1-6]\."; 复制代码
这种方法我 没有使用过,有用过的亲,能够留言,沟通一下下。
为了提升服务器获取数据的速度,`nginx`缓存着静态资源是很是必要的。若是是测试服务器对`html`应该不设置缓存,而`js`等静态资源环境由于文件尾部会加上一个`hash`值,这能够有效实现缓存的控制。
location ~* ^.+\.(ico|gif|jpg|jpeg|png)$ { access_log off; expires 30d; } location ~* ^.+\.(css|js|txt|xml|swf|wav)$ { access_log off; expires 24h; } location ~* ^.+\.(html|htm)$ { expires 1h; }
Angular和vue的区别
angularjs和vue的区别:
一、vueJS简单易学,而angularJS的上手较高;
二、vue专一于View层,主打的是快速便捷,而angularJS功能则比较全面,固然体量也较大,相对没有vue那么便捷;
三、angularJS的指令都是ng-xxx,而vueJS的指令都是v-xxx;
四、angularJS的全部指令和方法都是绑定在$scope上的,而vueJS是new出来一个实例,全部的方法和指令都在这个实例上,一个页面上能够有多个vue实例,可是angularJS的对象只能有一个;
五、angularJS是由google开发和维护的,vueJS是由我的维护的;
六、vueJS通常用于移动端的开发,而angularJS通常应用于大型的项目
Post和get请求区别
GET 被强制服务器支持 浏览器对URL的长度有限制,因此GET请求不能代替POST请求发送大量数据 GET请求发送数据更小 GET请求是安全的 GET请求是幂等的 POST请求不能被缓存 POST请求相对GET请求是「安全」的
Css动画跟js动画的区别
CSS动画 优势: (1)浏览器能够对动画进行优化。 一、 浏览器使用与 requestAnimationFrame 相似的机制,requestAnimationFrame比起setTimeout,setInterval设置动画的优点主要是:1)requestAnimationFrame 会把每一帧中的全部DOM操做集中起来,在一次重绘或回流中就完成,而且重绘或回流的时间间隔牢牢跟随浏览器的刷新频率,通常来讲,这个频率为每秒60帧。2)在隐藏或不可见的元素中requestAnimationFrame不会进行重绘或回流,这固然就意味着更少的的cpu,gpu和内存使用量。 二、强制使用硬件加速 (经过 GPU 来提升动画性能) (2)代码相对简单,性能调优方向固定 (3)对于帧速表现很差的低版本浏览器,CSS3能够作到天然降级,而JS则须要撰写额外代码 缺点: 一、 运行过程控制较弱,没法附加事件绑定回调函数。CSS动画只能暂停,不能在动画中寻找一个特定的时间点,不能在半路反转动画,不能变换时间尺度,不能在特定的位置添加回调函数或是绑定回放事件,无进度报告 二、 代码冗长。想用 CSS 实现稍微复杂一点动画,最后CSS代码都会变得很是笨重。 JS动画 优势: (1)JavaScript动画控制能力很强, 能够在动画播放过程当中对动画进行控制:开始、暂停、回放、终止、取消都是能够作到的。 (2)动画效果比css3动画丰富,有些动画效果,好比曲线运动,冲击闪烁,视差滚动效果,只有JavaScript动画才能完成 (3)CSS3有兼容性问题,而JS大多时候没有兼容性问题 缺点: (1)JavaScript在浏览器的主线程中运行,而主线程中还有其它须要运行的JavaScript脚本、样式计算、布局、绘制任务等,对其干扰致使线程可能出现阻塞,从而形成丢帧的状况。 (2)代码的复杂度高于CSS动画 总结:若是动画只是简单的状态切换,不须要中间过程控制,在这种状况下,css动画是优选方案。它可让你将动画逻辑放在样式文件里面,而不会让你的页面充斥 Javascript 库。然而若是你在设计很复杂的富客户端界面或者在开发一个有着复杂UI状态的 APP。那么你应该使用js动画,这样你的动画能够保持高效,而且你的工做流也更可控。因此,在实现一些小的交互动效的时候,就多考虑考虑CSS动画。对于一些复杂控制的动画,使用javascript比较可靠。 css 动画和 js 动画的差别 1. 代码复杂度,js 动画代码相对复杂一些 2. 动画运行时,对动画的控制程度上,js 可以让动画,暂停,取消,终止,css动画不能添加事件 3. 动画性能看,js 动画多了一个js 解析的过程,性能不如 css 动画好
Hash跟history的区别
vue-router 中hash模式和history模式。 在vue的路由配置中有mode选项,最直观的区别就是在url中hash 带了一个很丑的 # ,而history是没有#的。vue默认使用hash。 mode:"hash"; mode:"history"; hash —— 即地址栏 URL 中的 # 符号(此 hash 不是密码学里的散列运算)。 好比这个 URL:http://www.aaa.com/#/hello,hash 的值为 #/hello。它的特色在于:hash 虽然出如今 URL 中,但不会被包括在 HTTP 请求中,对后端彻底没有影响,所以改变 hash 不会从新加载页面。 history —— 利用了 HTML5 History Interface 中新增的 pushState() 和 replaceState() 方法。(须要特定浏览器支持) 这两个方法应用于浏览器的历史记录栈,在当前已有的 back、forward、go 的基础之上,它们提供了对历史记录进行修改的功能。只是当它们执行修改时,虽然改变了当前的 URL,但浏览器不会当即向后端发送请求。 所以能够说,hash 模式和 history 模式都属于浏览器自身的特性,Vue-Router 只是利用了这两个特性(经过调用浏览器提供的接口)来实现前端路由。 history模式的问题 经过history api,咱们丢掉了丑陋的#,可是它也有个问题:不怕前进,不怕后退,就怕刷新,f5,(若是后端没有准备的话),由于刷新是实实在在地去请求服务器的。 在hash模式下,前端路由修改的是#中的信息,而浏览器请求时不会将 # 后面的数据发送到后台,因此没有问题。可是在history下,你能够自由的修改path,当刷新时,若是服务器中没有相应的响应或者资源,则会刷新出来404页面。
跨域问题
1) 在www.a.com/a.html中:
document.domain = 'a.com';var ifr = document.createElement('iframe');ifr.src = 'http://www.script.a.com/b.html';ifr.display = none;document.body.appendChild(ifr);ifr.onload = function(){ var doc = ifr.contentDocument || ifr.contentWindow.document; //在这里操做doc,也就是b.html ifr.onload = null;};
2) 在www.script.a.com/b.html中:
document.domain = 'a.com';
这个没什么好说的,由于script标签不受同源策略的限制。
function loadScript(url, func) { var head = document.head || document.getElementByTagName('head')[0]; var script = document.createElement('script'); script.src = url; script.onload = script.onreadystatechange = function(){ if(!this.readyState || this.readyState=='loaded' || this.readyState=='complete'){ func(); script.onload = script.onreadystatechange = null; } }; head.insertBefore(script, 0);}window.baidu = { sug: function(data){ console.log(data); }}loadScript('http://suggestion.baidu.com/su?wd=w',function(){console.log('loaded')});//咱们请求的内容在哪里?//咱们能够在chorme调试面板的source中看到script引入的内容
原理是利用location.hash来进行传值。
假设域名a.com下的文件cs1.html要和cnblogs.com域名下的cs2.html传递信息。
1) cs1.html首先建立自动建立一个隐藏的iframe,iframe的src指向cnblogs.com域名下的cs2.html页面
2) cs2.html响应请求后再将经过修改cs1.html的hash值来传递数据
3) 同时在cs1.html上加一个定时器,隔一段时间来判断location.hash的值有没有变化,一旦有变化则获取获取hash值
注:因为两个页面不在同一个域下IE、Chrome不容许修改parent.location.hash的值,因此要借助于a.com域名下的一个代理iframe
代码以下:
先是a.com下的文件cs1.html文件:
function startRequest(){ var ifr = document.createElement('iframe'); ifr.style.display = 'none'; ifr.src = 'http://www.cnblogs.com/lab/cscript/cs2.html#paramdo'; document.body.appendChild(ifr);} function checkHash() { try { var data = location.hash ? location.hash.substring(1) : ''; if (console.log) { console.log('Now the data is '+data); } } catch(e) {};}setInterval(checkHash, 2000);
cnblogs.com域名下的cs2.html:
//模拟一个简单的参数处理操做switch(location.hash){ case '#paramdo': callBack(); break; case '#paramset': //do something…… break;} function callBack(){ try { parent.location.hash = 'somedata'; } catch (e) { // ie、chrome的安全机制没法修改parent.location.hash, // 因此要利用一个中间的cnblogs域下的代理iframe var ifrproxy = document.createElement('iframe'); ifrproxy.style.display = 'none'; ifrproxy.src = 'http://a.com/test/cscript/cs3.html#somedata'; // 注意该文件在"a.com"域下 document.body.appendChild(ifrproxy); }}
a.com下的域名cs3.html
//由于parent.parent和自身属于同一个域,因此能够改变其location.hash的值parent.parent.location.hash = self.location.hash.substring(1);
window.name 的美妙之处:name 值在不一样的页面(甚至不一样域名)加载后依旧存在,而且能够支持很是长的 name 值(2MB)。
1) 建立a.com/cs1.html
2) 建立a.com/proxy.html,并加入以下代码
<head> <script> function proxy(url, func){ var isFirst = true, ifr = document.createElement('iframe'), loadFunc = function(){ if(isFirst){ ifr.contentWindow.location = 'http://a.com/cs1.html'; isFirst = false; }else{ func(ifr.contentWindow.name); ifr.contentWindow.close(); document.body.removeChild(ifr); ifr.src = ''; ifr = null; } }; ifr.src = url; ifr.style.display = 'none'; if(ifr.attachEvent) ifr.attachEvent('onload', loadFunc); else ifr.onload = loadFunc; document.body.appendChild(iframe); }</script></head><body> <script> proxy('http://www.baidu.com/', function(data){ console.log(data); }); </script></body>
3 在b.com/cs1.html中包含:
<script> window.name = '要传送的内容';</script>
1) a.com/index.html中的代码:
<iframe id="ifr" src="b.com/index.html"></iframe><script type="text/javascript">window.onload = function() { var ifr = document.getElementById('ifr'); var targetOrigin = 'http://b.com'; // 若写成'http://b.com/c/proxy.html'效果同样 // 若写成'http://c.com'就不会执行postMessage了 ifr.contentWindow.postMessage('I was there!', targetOrigin);};</script>
2) b.com/index.html中的代码:
<script type="text/javascript"> window.addEventListener('message', function(event){ // 经过origin属性判断消息来源地址 if (event.origin == 'http://a.com') { alert(event.data); // 弹出"I was there!" alert(event.source); // 对a.com、index.html中window对象的引用 // 但因为同源策略,这里event.source不能够访问window对象 } }, false);</script>
CORS背后的思想,就是使用自定义的HTTP头部让浏览器与服务器进行沟通,从而决定请求或响应是应该成功,仍是应该失败。
IE中对CORS的实现是xdr
var xdr = new XDomainRequest();xdr.onload = function(){ console.log(xdr.responseText);}xdr.open('get', 'http://www.baidu.com');......xdr.send(null);
其它浏览器中的实现就在xhr中
var xhr = new XMLHttpRequest();xhr.onreadystatechange = function () { if(xhr.readyState == 4){ if(xhr.status >= 200 && xhr.status < 304 || xhr.status == 304){ console.log(xhr.responseText); } }}xhr.open('get', 'http://www.baidu.com');......xhr.send(null);
实现跨浏览器的CORS
function createCORS(method, url){ var xhr = new XMLHttpRequest(); if('withCredentials' in xhr){ xhr.open(method, url, true); }else if(typeof XDomainRequest != 'undefined'){ var xhr = new XDomainRequest(); xhr.open(method, url); }else{ xhr = null; } return xhr;}var request = createCORS('get', 'http://www.baidu.com');if(request){ request.onload = function(){ ...... }; request.send();}
JSONP包含两部分:回调函数和数据。
回调函数是当响应到来时要放在当前页面被调用的函数。
数据就是传入回调函数中的json数据,也就是回调函数的参数了。
function handleResponse(response){ console.log('The responsed data is: '+response.data);}var script = document.createElement('script');script.src = 'http://www.baidu.com/json/?callback=handleResponse';document.body.insertBefore(script, document.body.firstChild);/*handleResonse({"data": "zhe"})*///原理以下://当咱们经过script标签请求时//后台就会根据相应的参数(json,handleResponse)//来生成相应的json数据(handleResponse({"data": "zhe"}))//最后这个返回的json数据(代码)就会被放在当前js文件中被执行//至此跨域通讯完成
jsonp虽然很简单,可是有以下缺点:
1)安全问题(请求代码中可能存在安全隐患)
2)要肯定jsonp请求是否失败并不容易
web sockets是一种浏览器的API,它的目标是在一个单独的持久链接上提供全双工、双向通讯。(同源策略对web sockets不适用)
web sockets原理:在JS建立了web socket以后,会有一个HTTP请求发送到浏览器以发起链接。取得服务器响应后,创建的链接会使用HTTP升级从HTTP协议交换为web sockt协议。
只有在支持web socket协议的服务器上才能正常工做。
var socket = new WebSockt('ws://www.baidu.com');//http->ws; https->wsssocket.send('hello WebSockt');socket.onmessage = function(event){ var data = event.data;}
vue 的params和query的区别
初学vue的时候,不知道如何在方法中跳转界面并传参,百度事后,了解到两种方式,params 与 query。而后,错误就这么来了:
router文件下index.js里面,是这么定义路由的:
<pre>{
path:"/detail", name:"detail", component:home }</pre>
我想用params来传参,是这么写的,嗯~
this.$router.push({
path:"/detail",
params:{
name:'nameValue',
code:10011
}
});
结果可想而知,接收参数的时候:
<pre>this.$route.params.code //undefined</pre>
这是由于,params只能用name来引入路由,下面是正确的写法:
<pre>this.$router.push({
name:"detail",
params:{
name:'nameValue', code:10011
}
});</pre>
这回就对了,能够直接拿到传递过来的参数nameValue了。
说完了个人犯傻,下面整理一下这二者的差异:
一、用法上的
刚才已经说了,query要用path来引入,params要用name来引入,接收参数都是相似的,分别是this.$route.query.name和this.$route.params.name。
注意接收参数的时候,已是$route而不是$router了哦!!
二、展现上的
query更加相似于咱们ajax中get传参,params则相似于post,说的再简单一点,前者在浏览器地址栏中显示参数,后者则不显示
原型,原型链,闭包
全部函数都有一个特别的属性:
prototype
: 显式原型属性全部实例对象都有一个特别的属性:
__proto__
: 隐式原型属性显式原型与隐式原型的关系
原型链
变量提高与函数提高
理解
分类:
生命周期
包含哪些属性:
全局 :
函数
原型链继承 : 获得方法
function Parent(){} Parent.prototype.test = function(){}; function Child(){} Child.prototype = new Parent(); // 子类型的原型指向父类型实例 Child.prototype.constructor = Child var child = new Child(); //有test()
借用构造函数 : 获得属性
function Parent(xxx){this.xxx = xxx} Parent.prototype.test = function(){}; function Child(xxx,yyy){ Parent.call(this, xxx);//借用构造函数 this.Parent(xxx) } var child = new Child('a', 'b'); //child.xxx为'a', 但child没有test()
组合
function Parent(xxx){this.xxx = xxx} Parent.prototype.test = function(){}; function Child(xxx,yyy){ Parent.call(this, xxx);//借用构造函数 this.Parent(xxx) } Child.prototype = new Parent(); //获得test() var child = new Child(); //child.xxx为'a', 也有test()
new一个对象背后作了些什么?
执行上下文建立和初始化的过程
全局:
函数:
理解:
分类:
做用
区别做用域与执行上下文
理解:
做用:
写一个闭包程序
function fn1() { var a = 2; function fn2() { a++; console.log(a); } return fn2; } var f = fn1(); f(); f();
闭包应用:
缺点:
解决:
内存溢出
内存泄露
常见的内存泄露:
箭头函数和普通函数的区别
箭头函数:
let fun = () => {
console.log('lalalala');
}
普通函数:
function fun() {
console.log('lalla');
}
箭头函数至关于匿名函数,而且简化了函数定义。箭头函数有两种格式,一种只包含一个表达式,连{ ... }和return都省略掉了。还有一种能够包含多条语句,这时候就不能省略{ ... }和return。
let FunConstructor = () => {
console.log('lll');
}
let fc = new FunConstructor();
function A(a){
console.log(arguments);
}
A(1,2,3,4,5,8); // [1, 2, 3, 4, 5, 8, callee: ƒ, Symbol(Symbol.iterator): ƒ]
let B = (b)=>{
console.log(arguments);
}
B(2,92,32,32); // Uncaught ReferenceError: arguments is not defined
let C = (...c) => {
console.log(c);
}
C(3,82,32,11323); // [3, 82, 32, 11323]
var obj = {
a: 10,
b: () => {
console.log(this.a); // undefined console.log(this); // Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, frames: Window, …}
},
c: function() {
console.log(this.a); // 10 console.log(this); // {a: 10, b: ƒ, c: ƒ}
}
}
obj.b();
obj.c();
var obj = {
a: 10,
b: function(){
console.log(this.a); //10
},
c: function() { return ()=>{
console.log(this.a); //10
}
}
}
obj.b();
obj.c()();
let obj2 = { a: 10, b: function(n) { let f \= (n) => n + this.a; return f(n); }, c: function(n) { let f \= (n) => n + this.a; let m \= { a: 20 }; return f.call(m,n); } }; console.log(obj2.b(1)); // 11 console.log(obj2.c(1)); // 11
var a = ()=>{ return 1; } function b(){ return 2; } console.log(a.prototype); // undefined console.log(b.prototype); // {constructor: ƒ}
在JavaScript中我们之前主要用关键var来定义变量,ES6以后,新增了定义变量的两个关键字,分别是let和const。
对于变量来讲,在ES5中var定义的变量会提高到做用域中全部的函数与语句前面,而ES6中let定义的变量则不会,let声明的变量会在其相应的代码块中创建一个暂时性死区,直至变量被声明。
let和const都可以声明块级做用域,用法和var是相似的,let的特色是不会变量提高,而是被锁在当前块中。
一个很是简单的例子:
function test() { if(true) { console.log(a)//TDZ,俗称临时死区,用来描述变量不提高的现象 let a = 1 } } test() // a is not defined function test() { if(true) { let a = 1 } console.log(a) } test() // a is not defined
惟一正确的使用方法:先声明,再访问。
function test() { if(true) { let a = 1 console.log(a) } } test() // 1
const
声明常量,一旦声明,不可更改,并且常量必须初始化赋值。
const虽然是常量,不容许修改默认赋值,但若是定义的是对象Object,那么能够修改对象内部的属性值。
const type = { a: 1 } type.a = 2 //没有直接修改type的值,而是修改type.a的属性值,这是容许的。 console.log(type) // {a: 2}
const和let的异同点
相同点:const和let都是在当前块内有效,执行到块外会被销毁,也不存在变量提高(TDZ),不能重复声明。
不一样点:const不能再赋值,let声明的变量能够重复赋值。
const实际上保证的,并非变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动。对于简单类型的数据(数值、字符串、布尔值),值就保存在变量指向的那个内存地址,所以等同于常量。但对于复合类型的数据(主要是对象和数组),变量指向的内存地址,保存的只是一个指向实际数据的指针,const只能保证这个指针是固定的(即老是指向另外一个固定的地址),至于它指向的数据结构是否是可变的,就彻底不能控制了。所以,将一个对象声明为常量必须很是当心。
块级做用域的使用场景
除了上面提到的经常使用声明方式,咱们还能够在循环中使用,最出名的一道面试题:循环中定时器闭包的考题
在for循环中使用var声明的循环变量,会跳出循环体污染当前的函数。
for(var i = 0; i < 5; i++) { setTimeout(() => { console.log(i) //5, 5, 5, 5, 5 }, 0) } console.log(i) //5 i跳出循环体污染外部函数 //将var改为let以后 for(let i = 0; i < 5; i++) { setTimeout(() => { console.log(i) // 0,1,2,3,4 }, 0) } console.log(i)//i is not defined i没法污染外部函数
在实际开发中,咱们选择使用var、let仍是const,取决于咱们的变量是否是须要更新,一般咱们但愿变量保证不被恶意修改,而使用大量的const。使用const声明,声明一个对象的时候,也推荐使用const,当你须要修改声明的变量值时,使用let,var能用的场景均可以使用let替代。
symbol
ES6 之前,咱们知道5种基本数据类型分别是Undefined,Null,Boolean,Number以及String,而后加上一种引用类型Object构成了JavaScript中全部的数据类型,可是ES6出来以后,新增了一种数据类型,名叫symbol,像它的名字表露的同样,意味着独一无二,意思是每一个 Symbol类型都是独一无二的,不与其它 Symbol 重复。
能够经过调用 Symbol() 方法将建立一个新的 Symbol 类型的值,这个值独一无二,不与任何值相等。
var mySymbol=Symbol(); console.log(typeof mySymbol) //"symbol"
ES6字符串新增的方法
UTF-16码位:ES6强制使用UTF-16字符串编码。关于UTF-16的解释请自行百度了解。
codePointAt():该方法支持UTF-16,接受编码单元的位置而非字符串位置做为参数,返回与字符串中给定位置对应的码位,即一个整数值。
String.fromCodePoiont():
做用与codePointAt相反,检索字符串中某个字符的码位,也能够根据指定的码位生成一个字符。
normalize():提供Unicode的标准形式,接受一个可选的字符串参数,指明应用某种Unicode标准形式。
在ES6中,新增了3个新方法。每一个方法都接收2个参数,须要检测的子字符串,以及开始匹配的索引位置。
模板字符串
字符串是JavaScript中基本类型之一,应该算是除了对象以外是使用最为频繁的类型吧,字符串中包含了例如substr,replace,indexOf,slice等等诸多方法,ES6引入了模板字符串的特性,用反引号来表示,能够表示多行字符串以及作到文本插值(利用模板占位符)。
// 之前的多行字符串咱们这么写: console.log("hello world 1\n\ hello cala"); // "hello world // hello cala" //有了模板字符串以后 console.log(`hello world string text line 2`); // "hello world // hello cala"
能够用${}来表示模板占位符,能够将你已经定义好的变量传进括弧中,例如:
var name="cala"; var age=22; console.log(`hello,I'am ${name},my age is ${age}`) //hello,I'am cala,my age is 22
includes(str, index):若是在字符串中检测到指定文本,返回true,不然false。
let t = 'abcdefg' if(t.includes('cde')) { console.log(2) } //true
startsWith(str, index):若是在字符串起始部分检测到指定文本,返回true,不然返回false。
let t = 'abcdefg' if(t.startsWith('ab')) { console.log(2) } //true
endsWith(str, index):若是在字符串的结束部分检测到指定文本,返回true,不然返回false。
let t = 'abcdefg' if(t.endsWith('fg')) { console.log(2) } //true
若是你只是须要匹配字符串中是否包含某子字符串,那么推荐使用新增的方法,若是须要找到匹配字符串的位置,使用indexOf()。
函数的默认参数
在ES5中,咱们给函数传参数,而后在函数体内设置默认值,以下面这种方式。
function a(num, callback) { num = num || 6 callback = callback || function (data) {console.log('ES5: ', data)} callback(num * num) } a() //ES5: 36,不传参输出默认值 //你还能够这样使用callback a(10, function(data) { console.log(data * 10) // 1000, 传参输出新数值 })
在ES6中,咱们使用新的默认值写法
function a(num = 6, callback = function (data) {console.log('ES6: ', data)}) { callback(num * num) } a() //ES6: 36, 不传参输出默认值 a(10, function(data) { console.log(data * 10) // 1000,传参输出新数值 })
(箭头函数比较重要,如今简单提一下,迟一点有空专门写一篇箭头函数的文章。)
const arr = [5, 10] const s = arr.reduce((sum, item) => sum + item) console.log(s) // 15
箭头函数中this的使用跟普通函数也不同,在JavaScript的普通函数中,都会有一个本身的this值,主要分为:
普通函数:
一、函数做为全局函数被调用时,this指向全局对象
二、函数做为对象中的方法被调用时,this指向该对象
三、函数做为构造函数的时候,this指向构造函数new出来的新对象
四、还能够经过call,apply,bind改变this的指向
箭头函数:
一、箭头函数没有this,函数内部的this来自于父级最近的非箭头函数,而且不能改变this的指向。
二、箭头函数没有super
三、箭头函数没有arguments
四、箭头函数没有new.target绑定。
五、不能使用new
六、没有原型
七、不支持重复的命名参数。
箭头函数的简单理解
一、箭头函数的左边表示输入的参数,右边表示输出的结果。
const s = a => a console.log(s(2)) // 2
二、在箭头函数中,this属于词法做用域,直接由上下文肯定,对于普通函数中指向不定的this,箭头函数中处理this无疑更加简单,以下:
//ES5普通函数 function Man(){ this.age=22; return function(){ this.age+1; } } var cala=new Man(); console.log(cala())//undefined //ES6箭头函数 function Man(){ this.age=22; return () => this.age+1; } var cala=new Man(); console.log(cala())//23
三、箭头函数中没有arguments(咱们能够用rest参数替代),也没有原型,也不能使用new 关键字,例如:
//没有arguments var foo=(a,b)=>{return arguments[0]*arguments[1]} console.log(foo(3,5)) //arguments is not defined //没有原型 var Obj = () => {}; console.log(Obj.prototype); // undefined //不能使用new 关键字 var Obj = () => {"hello world"}; var o = new Obj(); // TypeError: Obj is not a constructor
四、箭头函数给数组排序
const arr = [10, 50, 30, 40, 20] const s = arr.sort((a, b) => a - b) console.log(s) // [10,20,30,40,50]
尾调用优化
尾调用是指在函数return的时候调用一个新的函数,因为尾调用的实现须要存储到内存中,在一个循环体中,若是存在函数的尾调用,你的内存可能爆满或溢出。
ES6中,引擎会帮你作好尾调用的优化工做,你不须要本身优化,但须要知足下面3个要求:
一、函数不是闭包
二、尾调用是函数最后一条语句
三、尾调用结果做为函数返回
尾调用实际用途——递归函数优化
在ES5时代,咱们不推荐使用递归,由于递归会影响性能。
可是有了尾调用优化以后,递归函数的性能有了提高。
//新型尾优化写法 "use strict"; function a(n, p = 1) { if(n <= 1) { return 1 * p } let s = n * p return a(n - 1, s) } //求 1 x 2 x 3的阶乘 let sum = a(3) console.log(sum) // 6
Object.assign()
Object.assign()方法用于将全部可枚举属性的值从一个或多个源对象复制到目标对象。它将返回目标对象。
Object.assign 方法只会拷贝源对象自身的而且可枚举的属性到目标对象。该方法使用源对象的[[Get]]和目标对象的[[Set]],因此它会调用相关 getter 和 setter。所以,它分配属性,而不只仅是复制或定义新的属性。若是合并源包含getter,这可能使其不适合将新属性合并到原型中。为了将属性定义(包括其可枚举性)复制到原型,应使用Object.getOwnPropertyDescriptor()和Object.defineProperty() 。
String类型和 Symbol 类型的属性都会被拷贝。
合并对象
var o1 = { a: 1 }; var o2 = { b: 2 }; var o3 = { c: 3 }; var obj = Object.assign(o1, o2, o3); console.log(obj); // { a: 1, b: 2, c: 3 } console.log(o1); // { a: 1, b: 2, c: 3 }, 注意目标对象自身也会改变。
合并具备相同属性的对象
var o1 = { a: 1, b: 1, c: 1 }; var o2 = { b: 2, c: 2 }; var o3 = { c: 3 }; var obj = Object.assign({}, o1, o2, o3); console.log(obj); // { a: 1, b: 2, c: 3 }
Map和Set都叫作集合,可是他们也有所不一样。Set常被用来检查对象中是否存在某个键名,Map集合常被用来获取已存的信息。
Set是有序列表,含有相互独立的非重复值。
Array和Set对比
都是一个存储多值的容器,二者能够互相转换,可是在使用场景上有区别。以下:
Array的indexOf方法比Set的has方法效率低下
Set不含有重复值(能够利用这个特性实现对一个数组的去重)
Set经过delete方法删除某个值,而Array只能经过splice。二者的使用方便程度前者更优
Array的不少新方法map、filter、some、every等是Set没有的(可是经过二者能够互相转换来使用)
Object和Map对比
Object是字符串-值,Map是值-值
Object键为string类型,Map的键是任意类型
手动计算Object尺寸,Map.size能够获取尺寸
Map的排序是插入顺序
Object有原型,因此映射中有一些缺省的键。能够理解为Map=Object.create(null)
Set操做集合
let set = new Set() // Set转化为数组 let arr = Array.from(set) let arr = [...set] // 实例属性(继承自Set) set.constructor === Set set.size // 操做方法 set.add(1) // 添加一个值 set.delete(1) //删除一个值 set.has(1) //判断是否有这个值(Array中的indexOf) set.clear() //清除全部值 // 获取用于遍历的成员方法(Set的遍历顺序就是插入顺序) set.keys() // 返回键名的遍历器 set.values() // 返回键值得遍历器 set.entries() // 返回键值对的遍历器 set.forEach() // 循环遍历每一个值(和Array的方法一致) for (let key of set.keys()){} for (let val of set.values()){} for (let entry of set.entries()){} // 使用数组方法来处理set值 set = new Set(arr) set = new Set([...set].map((x) => x = x * 2)) set = new Set([...set].filter((x) => x > 2))
Map的方法集合
let map = new Map() // 实例属性(继承自Map) map.constructor === Map map.size // 操做方法 map.set(1,2) map.get(1) map.delete(1) map.has(1) map.clear() // 遍历方法 map.keys() map.values() map.entries() map.forEach() // Map和数组的转换 map = new Map([['key','val'],[2,1]]) // 要求双成员数组 let arr = [...map] // 值得注意的是Map的键是跟内存绑定的 map.set([1], 's') map.get([1]) let arr = [1] let arr1 = [1] map.set(arr, 's') map.get(arr) map.set(arr1, 's') map.get(arr1)
想要深刻理解Set和Map,能够查看《深刻理解:ES6中的Set和Map数据结构,Map与其它数据结构的互相转换》
一、entries() 返回迭代器:返回键值对
//数组 const arr = ['a', 'b', 'c']; for(let v of arr.entries()) { console.log(v) } // [0, 'a'] [1, 'b'] [2, 'c'] //Set const arr = new Set(['a', 'b', 'c']); for(let v of arr.entries()) { console.log(v) } // ['a', 'a'] ['b', 'b'] ['c', 'c'] //Map const arr = new Map(); arr.set('a', 'a'); arr.set('b', 'b'); for(let v of arr.entries()) { console.log(v) } // ['a', 'a'] ['b', 'b']
二、values() 返回迭代器:返回键值对的value
//数组 const arr = ['a', 'b', 'c']; for(let v of arr.values()) { console.log(v) } //'a' 'b' 'c' //Set const arr = new Set(['a', 'b', 'c']); for(let v of arr.values()) { console.log(v) } // 'a' 'b' 'c' //Map const arr = new Map(); arr.set('a', 'a'); arr.set('b', 'b'); for(let v of arr.values()) { console.log(v) } // 'a' 'b'
三、keys() 返回迭代器:返回键值对的key
//数组 const arr = ['a', 'b', 'c']; for(let v of arr.keys()) { console.log(v) } // 0 1 2 //Set const arr = new Set(['a', 'b', 'c']); for(let v of arr.keys()) { console.log(v) } // 'a' 'b' 'c' //Map const arr = new Map(); arr.set('a', 'a'); arr.set('b', 'b'); for(let v of arr.keys()) { console.log(v) } // 'a' 'b'
虽然上面列举了3种内建的迭代器方法,可是不一样集合的类型还有本身默认的迭代器,在for of中,数组和Set的默认迭代器是values(),Map的默认迭代器是entries()。
for of循环解构
对象自己不支持迭代,可是咱们能够本身添加一个生成器,返回一个key,value的迭代器,而后使用for of循环解构key和value。
const obj = { a: 1, b: 2, *[Symbol.iterator]() { for(let i in obj) { yield [i, obj[i]] } } } for(let [key, value] of obj) { console.log(key, value) } // 'a' 1, 'b' 2
字符串迭代器
const str = 'abc'; for(let v of str) { console.log(v) } // 'a' 'b' 'c'
一、find():传入一个回调函数,找到数组中符合当前搜索规则的第一个元素,返回它,而且终止搜索。
const arr = [1, "2", 3, 3, "2"] console.log(arr.find(n => typeof n === "number")) // 1
二、findIndex():传入一个回调函数,找到数组中符合当前搜索规则的第一个元素,返回它的下标,终止搜索。
const arr = [1, "2", 3, 3, "2"] console.log(arr.findIndex(n => typeof n === "number")) // 0
三、fill():用新元素替换掉数组内的元素,能够指定替换下标范围。
arr.fill(value, start, end)
四、copyWithin():选择数组的某个下标,从该位置开始复制数组元素,默认从0开始复制。也能够指定要复制的元素范围。
arr.copyWithin(target, start, end) const arr = [1, 2, 3, 4, 5] console.log(arr.copyWithin(3)) // [1,2,3,1,2] 从下标为3的元素开始,复制数组,因此4, 5被替换成1, 2 const arr1 = [1, 2, 3, 4, 5] console.log(arr1.copyWithin(3, 1)) // [1,2,3,2,3] 从下标为3的元素开始,复制数组,指定复制的第一个元素下标为1,因此4, 5被替换成2, 3 const arr2 = [1, 2, 3, 4, 5] console.log(arr2.copyWithin(3, 1, 2)) // [1,2,3,2,5] 从下标为3的元素开始,复制数组,指定复制的第一个元素下标为1,结束位置为2,因此4被替换成2