注意:
1.事件冒泡
2.关键字
(1)如何判断对象是否属于某个类
使用instanceof关键字可以判断某个对象是否被另一个类构造,即是否是该类的实例化对象
使用constructor判断一个对象是否是类的构造函数。
使用typeof判断变量的数据类型。
/ /vue项目中的config下面的index.js中dev:{
}的proxyTable中配置
proxyTable: {
// 路由接口代理配置
'/api':{
target:'http://localhost:8443',//这里是跨域请求的接口的域名
changeOrigin:true, //changeOrigin这个参数为true的时候会虚拟出一个服务端来接收请求,并且代发用户的这个请求,以此来解决跨域的问题
pathRewrite:{
'^/api':'' //这里是用'/api'来代替 target中的地址,例如要调用“http://localhost:8443/qml/?greate=100”那么直接写成'/api/qml/?greate=100'
},
secure:true //如果是https则添加这个属性。
6.设置代理: 通过代理服务器通信。
div {
width:0px;
height:0px;
border-top:10px solid red;
border-right:10px solid transparent;
border-bottom:10px solid transparent;
border-left:10px solid transparent;
}
<script>
function client() {
if(window.innerWidth != null) // ie9 + 最新浏览器
{
return {
width: window.innerWidth,
height: window.innerHeight
}
}else if(document.compatMode === "CSS1Compat") // 标准浏览器
{
return {
width: document.documentElement.clientWidth,
height: document.documentElement.clientHeight
}
}
return {
// 怪异浏览器
width: document.body.clientWidth,
height: document.body.clientHeight
}
}
document.write(client().width);
</script>
valueOf()会把数据类型转换成原始类型;也就是原来是什么类型转换后还是什么类型,日期类型除外
toString()会把数据类型转换成string类型,也就是说不管原来是什么类型,转换后一律是string类型。
这两个方法的区别总结如下:
1、valueOf()偏向于运算,toString()偏向于显示
2、对象转换时,优先调用toString()
3、强转字符串的情况下,优先调用toString()方法;强转数字的情况下优先调用valueOf()
4、正常情况下,优先调用toString()
5、在有运算操作符的情况下valueOf()的优先级高于toString(),这里需要注意的是当调用valueOf()方法无法运算后还是会再调toString()方法
@给数组添加一个原型方法
@深入理解JavaScript中的class
通过类来创建对象,使得开发者不必写重复的代码,以达到代码复用的目的。它基于的逻辑是,两个或多个对象的结构功能类似,可以抽象出一个模板,依照模板复制出多个相似的对象。
class Person {
constructor(name){
this.name = name
}
hello(){
console.log('Hello, my name is ' + this.name + '.');
}
}
var person1 = new Person('xiaoMing');
person1.hello() // Hello, my name is xiaoMing.
class并不是es6中新增的数据类型,本质也是function
其实是把es5中prototype属性(通过原型链模拟实现继承);模拟es5中实现继承如下:
function Person() {
this.name = name
}
// 1. 首先给 Person.prototype 原型对象添加了 describe 方法 。
Person.prototype.describe = function(){
console.log('Hello, my name is ' + this.name + '.');
}
// 2. 实例化对象的 __proto__ 指向 Person.prototype
var jane = new Person('jane');
jane.__proto__ === Person.prototype;
// 3. 读取 describe 方法时,实际会沿着原型链查找到 Person.prototype 原型对象上。
jane.describe() // Hello, my name is jane.
原理解释:
箭头函数是es6中新增的匿名函数写法;语法简单便于书写;不会改变this的绑定。
//es5 如果要解决就需要let self = this,找个临时变量保存this
function Counter() {
this.num = 0;
this.timer = setInterval(function add() {
this.num++;
console.log(this.num);
}, 1000);
}
var b = new Counter();
// NaN
// NaN
// NaN
// 箭头函数不会改变this的绑定,
// 通过Counter构造函数绑定的this将会被保留,新实例化的b也会有原来的this指向
function Counter() {
this.num = 0;
this.timer = setInterval(() => {
this.num++;
console.log(this.num);
}, 1000);
}
var b = new Counter();
@JavaScript异步
1.异步方案一: 回调函数
容易产生回调地狱,且每次都只有一个回调函数
function f1(callback){
setTimeout(function () {
// f1的任务代码
callback();
}, 1000);
}
// 执行
f1(f2)
2.异步方案二: 采用事件驱动模式,设置监听函数。
任务的执行不取决代码的顺序,而取决于某一个事件是否发生。
监听函数有:on,bind,listen,addEventListener,observe
function f1(){
settimeout(function(){
//f1的任务代码
f1.trigger('done');
},1000);
}
f1.trigger(‘done’) 表示,执行完成后,立即触发done事件,从而开始执行f2.
优点:比较容易理解,可以绑定多个事件,每一个事件可以指定多个回调函数,
而且可以去耦合,有利于实现模块化。
缺点:整个程序都要变成事件驱动型,运行流程会变得不清晰。
3.异步方案三 采用发布订阅模式
function f1(){
setTimeout(function () {
// f1的任务代码
jQuery.publish("done");
}, 1000);
}
//f1 发布信号,f2订阅主题
jQuery.subscribe("done", f2);
4.异步方案四 创建promise对象
promise对象是commonJS工作组提出的一种规范,一种模式,目的是为了异步编程提供统一接口。
Promise 对象代表一个异步操作,其不受外界影响,有三种状态:
Pending(进行中、未完成的)
Resolved(已完成,又称 Fulfilled)
Rejected(已失败)
这个对象的状态不能修改,只能被异步操作的结果改变。
function f1(){
var dfd=$.deferred();
settimeout(function(){
//f1的任务代码
dfd.resolve();
},500);
return dfd.promise;
}
每个异步任务都返回一个promise对象,该对象有对应的then方法
f1.then(f2); 有多个异步任务可以有多个then 最后还有 .fail方法。
es6封装的优雅使用promise对象:
1.async await
//egg项目中使用
async sendMsg() {
const {
ctx, service } = this;
const payload = ctx.request.body || {
};
const result= await service.message.sendMsg(payload);
ctx.body = result;
}
2.generator对象的next函数获取yield/return的值
function* helloGenerator() {
yield "hello";
yield "generator";
return;
}
var h = helloGenerator();
console.log(h.next());//{ value: 'hello', done: false }
console.log(h.next());//{ value: 'generator', done: false }
console.log(h.next());//{ value: 'undefined', done: true }
3.自己的总结
//@ https://www.cnblogs.com/dirkhe/p/7384743.html
//@ https://blog.csdn.net/aiden_Jerray/article/details/83443223
// 在egg中用的async await==========》
async update() {
const {
ctx, service } = this;
const {
id } = ctx.query;
const payload = ctx.request.body || {
};
const result = await service.user.update(id,payload);
ctx.body = result;
}
//async 声明是异步函数,await后面跟的东西会执行完后返回,
//这个vue中关于async await的介绍 https://www.cnblogs.com/SamWeb/p/8417940.html
//安防========》
var getExamineOrder = function(params) {
var deferred = $q.defer(); //生成异步对象
var url = appConfig.AFOrderEnum.getQualityCheckAndAuditOrder;
console.log($scope.orderData);
$http.post(url, $scope.orderData).then(
function(res) {
console.log(res);
deferred.resolve({
data: res.data.result,
count: res.data.count
});
},
function(err) {
}
);
return deferred.promise;
//正确返回promise 异步中发生错误会返回reject,这里应该是只返回了正确情况下的回调
}
//这个网上能搜到
var deferred = $q.defer();
//生成deferred异步对象
deferred.resolve(rtns);
//执行到这里时,改变deferred状态为执行成功,返回rtns为从后台取到的数据,可以继续执行then,done
deferred.reject(data);
//执行到这里时,改变deferred状态为执行失败,返回data为报错,可以继续执行fail
return deferred.promise;
//起到保护作用,不允许函数外部改变函数内的deferred状态
//在别的函数中用这个异步函数
getExamineOrder().then(function(res) {
console.log("res", res);
});
实例userArray 的_proto_永远指向构造他的对象的对象的prototype
var userArray = new Array()
userArray.proto = Array.prototype
https://www.cnblogs.com/alichengyin/p/4852616.html
https://blog.csdn.net/yucihent/article/details/79424506
和 :after :before 伪元素配合使用用于插入内容
▪ 顶行写CACHE MANIFEST
▪ CACHE: 换行 指定我们需要缓存的静态资源,如.css、image、js等
▪ NETWORK: 换行 指定需要在线访问的资源,可使用通配符
▪ FALLBACK: 当前页面无法访问时退回的页面(回退; 后退)
▪ CACHE: 可以省略,这种情况下将需要缓存的资源写在CACHE MANIFEST
▪ 可以指定多个CACHE: NETWORK: FALLBACK:,无顺序限制
▪ #表示注释,只有当demo.appcache文件内容发生改变时或者手动清除缓存后,才会重新缓存。
▪ chrome 可以通过chrome://appcache-internals/工具和离线(offline)模式来调试管理应用缓存。
//一:同步阻塞代码
// 这是一个阻塞式函数, 将一个文件复制到另一个文件上
function copyBigFile(afile, bfile){
var result = copyFileSync(afile,bfile);
return result;
}
//调用这个”copyBigFile()”函数,将一个大文件复制到另一个文件上,将耗时1小时。意味着这个函数的将在一个小时之后返回。
//这是一段程序
console.log("start copying ... ");
var a = copyBigFile('A.txt', 'B.txt'); //这行程序将耗时1小时
console.log("Finished"); // 这行程序将在一小时后执行
console.log("处理一下别的事情"); // 这行程序将在一小时后执行
console.log("Hello World, 整个程序已加载完毕,请享用"); // 这行程序将在一小时后执行
//同步阻塞的例子,因为copyFileSync函数返回值的过程需要漫长的时间,所以线程也无法继续执行下去,只能等待。
//二:同步非阻塞代码
// 这是一个非阻塞式函数
// 如果复制已完成,则返回 true, 如果未完成则返回 false
function copyBigFile(afile,bfile){
var copying = copyFileAsync(afile, bfile);
var isFinished = !copying;
return !isFinished;
}
调用这个函数将立刻返回结果,然后你的程序就可以写成
console.log("start copying ... ");
while( a = copyBigFile('A.txt', 'B.txt')){
console.log("在这之间还可以处理别的事情");
} ;
console.log("Finished"); // 这行程序将在一小时后执行
console.log("Hello World, 整个程序已加载完毕,请享用"); // 这行程序将在一小时后执行
//一个非阻塞式的函数,给你的编程带来了更多的便利,你可以在长IO操作的同时,写点其他的程序。
//注意:同步非阻塞其实是利用循环不断去轮询结果,程序会给执行很多遍。
//三:异步非阻塞代码
//非阻塞式的有异步通知能力的函数
//以下不需要看懂,只用知道这个函数会在完成copy操作之后,执行success
function copyBigFile(afile,bfile, callback){
var copying = copyFileAsync(afile, bfile, function(){
callback();});
var isFinished = !copying;
return !isFinished;
}
//这个函数不同于上一个同步非阻塞函数的地方在于,它具有通知功能,
//也就是说,它能够在完成操作之后主动地通知程序,“我完成了”。于是有程序如下,
console.log("start copying ... ");
copyBigFile("A.txt","B.txt", function(){
console.log("Finished"); //一个小时后被执行
console.log("Hello World, 整个程序已加载完毕,请享用"); //一个小时后被执行
})
console.log("干别的事情");
console.log("做一些别的处理");
//程序在调用copyBigFile函数之后,可以立即获得返回值,线程没有被阻塞住,
//于是还可以去干些别的事情,然后当copyBigFile完成之后,会执行指定的函数。
document.getElementById('box').innerHTML = "123123", //设置内容
var html = document.getElementById('box').innerHTML //获取内容
@不完全函数
改变函数内部this的指向的方法有三种:call()、apply()、bind()。
都是在函数执行的时候动态的改变函数的作用域。也可以说特定的作用域中调用函数,等于设置函数体内this对象的值,以扩充函数赖以运行的作用域。
一般来说,this总是指向调用某个方法的对象,但是使用call()和apply()方法时,就会改变this的指向。
1.call() 用法示例:
语法: 函数.call(this, arg1, arg2, arg3, arg4)
第一个参数用来指定函数内部的this指向,后面的参数是函数执行时所需的实参,并且这些参数需要一个一个列举出来。
<script>
//示例一
window.color = 'red';
document.color = 'yellow';
var s1 = {
color: 'blue' };
function changeColor(){
console.log(this.color);
}
changeColor.call(); //red (默认传递参数window)
changeColor.call(window); //red
changeColor.call(document); //yellow
changeColor.call(this); //red
changeColor.call(s1); //blue
//示例二
var Pet = {
words : '...',
speak : function (say) {
console.log(say + ''+ this.words)
}
}
Pet.speak('Speak'); // 结果:Speak...
var Dog = {
words:'Wang'
}
//将this的指向改变成了Dog
Pet.speak.call(Dog, 'Speak'); //结果: SpeakWang
</script>
2.apply()用法示例:
apply()方法使用示例:
语法: 函数.apply(this, []);
apply()方法 接收两个参数,一个是函数运行的作用域(this),另一个是参数数组。
形式:apply([thisObj [,argArray] ]);,调用一个对象的一个方法,另一个对象替换当前对象。
第一个参数用来指定函数内部的this指向;
第二个参数要求是一个数组或者伪数组,apply会把它平铺然后传入对应的函数中。
<script>
//示例一
window.number = 'one';
document.number = 'two';
var s1 = {
number: 'three' };
function changeColor(){
console.log(this.number);
}
changeColor.apply(); //one (默认传参)
changeColor.apply(window); //one
changeColor.apply(document); //two
changeColor.apply(this); //one
changeColor.apply(s1); //three
//示例二
function Pet(words){
this.words = words;
this.speak = function () {
console.log( this.words)
}
}
function Dog(words){
//Pet.call(this, words); //结果: Wang
Pet.apply(this, arguments); //结果: Wang
}
var dog = new Dog('Wang');
dog.speak();
</script>
3.bind()用法示例
//例一
ar counter = {
count: 0,
inc: function () {
this.count++;
}
};
var func = counter.inc.bind(counter); //传入的对象是counter
func();
counter.count // 1
//示例二
var counter = {
count: 0,
inc: function () {
this.count++;
}
};
var obj = {
count: 100
};
var func = counter.inc.bind(obj);
//此时把obj当作绑定的对象,this.count便指向obj中的count。
func();
console.log('obj.count',obj.count);// 101
console.log('counter.count',counter.count);// 0
补充:@this指向的各种场景
@六种前端构建工具
@grunt、gulp和webpack
@全局安装和本地安装
在示例js中,即项目中的js,寻找模块的路径显示Node-path规定的路径和项目里面的node-modules;
全局安装的依赖不能直接require去获取,需要手动解决包路径问题,也可以直接把全局的node-modules复制一份到项目中。反正打包时会压缩,没用过的不会被放进去。
npm 和bower都是包管理工具。
@安装bower
@前端常用缓存技术
1.localstorage:
localStorage 方法存储的数据没有时间限制。主要不主动或被动清空缓存一直存在。
存储方式:以键值对(Key-Value)的方式存储,永久存储,永不失效,除非手动删除。
大小:每个域名5M。
getItem //取记录
setItem//设置记录
removeItem//移除记录
key//取key所对应的值
clear//清除记录
2.sessionstoeage:
HTML5 的本地存储 API 中的 localStorage 与 sessionStorage 在使用方法上是相同的,区别在于 sessionStorage 在关闭页面后即被清空,而 localStorage 则会一直保存。
3.本地离线存储:
本地离线存储,把需要离线存储在本地的文件列在一个manifest配置文件
4.cookie: cookie是纯文本,没有可执行代码
CSRF: 跨站请求伪造,可以理解为攻击者盗用了用户的身份,以用户的名义发送了恶意请求,比如用户登录了一个网站后,立刻在另一个tab页面访问量攻击者用来制造攻击的网站,这个网站要求访问刚刚登陆的网站,并发送了一个恶意请求,这时候CSRF就产生了,比如这个制造攻击的网站使用一张图片,但是这种图片的链接却是可以修改数据库的,这时候攻击者就可以以用户的名义操作这个数据库,防御方式的话:使用验证码,检查https头部的refer,使用token。
XSS: 跨站脚本攻击,是说攻击者通过注入恶意的脚本,在用户浏览网页的时候进行攻击,比如获取cookie,或者其他用户身份信息,可以分为存储型和反射型,存储型是攻击者输入一些数据并且存储到了数据库中,其他浏览者看到的时候进行攻击,反射型的话不存储在数据库中,往往表现为将攻击代码放在url地址的请求参数中,防御的话为cookie设置httpOnly属性,对用户的输入进行检查,进行特殊字符过滤。
@JavaScript所有循环
@所有循环
1.for循环:
2.foreach:
它是数组的原型方法(甚至是map和set)。forEach()方法根据索引顺序index,每次使用数组中的每个元素调用一个给定的函数(或回调函数)。
注意,forEach()不会对没有值的数组元素运行给定的函数,比如数组只有第一个和第十个有值时,其他循环会从0到9的位置依次索引,但foreach知道数组真正有几个元素。
//语法
array.forEach(function(currentValue, index, array){
// 函数主体
})
//forEach()方法以函数作为参数。该函数由三个参数组成:
//currentValue:保存正在处理的当前值
//index:保存该特定数组中的值的索引
//array:保存了整个数组
你可以使用forEach()遍历一个集合set,也可以使用它迭代一个映射map。
//示例
var array = [10, 20, "hi", , {
}, function () {
console.log('我是一个函数')}]
array.forEach(function(item, index){
console.log('array [' + index + '] 是: '+ item)
})
3.map循环:
map()
map是数组的另一个原型方法。map()方法将创建一个新的数组,该数组的返回值由一个给定的数组的函数执行生成。
//语法
var newArray= oldArray.map(function (currentValue, index, array){
//Return element for the newArray
});
//map()方法以函数作为参数,该函数有三个参数:
currentValue: 在数组中处理的当前元素
index:数组中正在处理的当前元素的索引值
array:数组映射的调用
//示例
var num = [1, 5, 10, 15];
var doubles = num.map(function(x) {
return x * 2;
});
4.while:
while语句生成一个循环,在条件为true的情况下,在一个特定的语句块中执行该循环。每次执行代码块之前,条件都会被检查。
5.do.while:
do-while和while略有不同,因为它包含一个额外的特性,最少执行一次。
6.for…in:
这个方法主要是对象的迭代。for…in在语句中迭代对象的可枚举属性。对于每个不同的属性,可以执行语句。
因为我们知道数组也是一种特殊的对象,所以数组也能用这个来进行迭代,但是非常不推荐,因为它会返回所有可枚举熟属性,就是可能会不按照顺序访问元素。
//语法
for (variableName in object) {
Block of code
}
//示例
var obj = {
a: 1, b: 2, c: 3};
for (var prop in obj) {
console.log('obj.'+prop+'='+obj[prop]);
};
7.for…of:
是ES6中新引入的一种循环类型提供了访问所有数据结构的统一操作接口了,使用for…of语句,你可以遍历任何可迭代的对象,比如Array、String、Map、WeakMap、Set、参数对象、TypeArray,甚至一般对象。
可以和break;continue;return配合使用。
for…of都是关于对象自己value的迭代,而for…in将会考虑原型和继承的属性。如果你想在对象上迭代(而不是迭代)。for…of将会考虑它自己的所有属性,但迭代的情况下,它只会考虑适合这种迭代的元素。
Vue双向绑定有弊端,或者说不是Vue弊端而是受现代 JavaScript 的限制(以及废弃Object.observe)Vue 不能检测到对象属性的添加或删除。由于 Vue 会在初始化实例时对属性执行 getter/setter 转化过程,所以属性必须在 data 对象上存在才能让 Vue 转换它,这样才能让它是响应的。例如:
vm.set 如果对象是响应式的,确保属性被创建后也是响应式的,同时触发视图更新,这个方法主要用于避开Vue不能检测到属性被添加的限制.
解决办法:通过vm.set 主动通知Vue;用splice或返回的新数组覆盖旧数组等触发数据变化。
@简书vue双向绑定原理
@博客园Vue双向绑定
1.vue是如实现双向绑定的?
vue.js 则是采用数据劫持结合发布者-订阅者模式的方式,通过==Object.defineProperty()==来劫持各个属性的setter,getter,并且在其中封装了消息订阅器Dep用来管理所有的订阅者,比如添加订阅者,是否需要添加订阅者;通知订阅者notify方法,在数据变动时发布消息给订阅者,订阅者在触发update反方法进行更新。
observer用来实现对每个vue中的data中定义的属性循环用Object.defineProperty()实现数据劫持,以便利用其中的setter和getter,然后通知订阅者,订阅者会触发它的update方法,对视图进行更新。
/ /比如定义数据可以直接是一个对象,
/ /此时可以通过person.name直接修改访问数据,但是并不能动态知道这种变化。
let person = {
name:'小明',age:18}
var obj = {
};
Object.defineProperty(obj, 'name', {
get: function() {
console.log('获取了')
return val;
},
set: function (newVal) {
console.log('设置了')
}
})
obj.name = 'yzg'; / /在给obj设置name属性的时候,触发了set这个方法
var val = obj.name; / /在得到obj的name属性,会触发get方法
vue中就是每当有这样的可能用到双向绑定的指令,就在一个Dep中增加一个订阅者,其订阅者只是更新自己的指令对应的数据,
function defineReactive (obj, key, val) {
var dep = new Dep();
Object.defineProperty(obj, key, {
get: function() {
//添加订阅者watcher到主题对象Dep
if(Dep.target) {
// JS的浏览器单线程特性,保证这个全局变量在同一时间内,
//只会有同一个监听器使用
dep.addSub(Dep.target);
}
return val;
},
set: function (newVal) {
if(newVal === val) return;
val = newVal;
console.log(val);
/ / 作为发布者发出通知
dep.notify();/ /通知后dep会循环调用各自的update方法更新视图
}
})
}
function observe(obj, vm) {
Object.keys(obj).forEach(function(key) {
defineReactive(vm, key, obj[key]);
})
}
2.vue中的Compile
compile的目的就是解析各种指令称真正的html。
遍历对象属性获取vue的各种指令并且把触发相对应的get;set;watcher方法
每个需要双向绑定的会为他添加dep订阅者集合管理数组,编译时为dep添加订阅者,订阅者通知触发对应的方法。
3.Vue双向绑定大概原理
首先我们为每个vue属性用Object.defineProperty()实现数据劫持,为每个属性分配一个订阅者集合的管理数组dep;然后在编译的时候在该属性的数组dep中添加订阅者,v-model会添加一个订阅者,{ {}}也会,v-bind也会,只要用到该属性的指令理论上都会,接着为input会添加监听事件,修改值就会为该属性赋值,触发该属性的set方法,在set方法内通知订阅者数组dep,订阅者数组循环调用各订阅者的update方法更新视图。
4.angular双向绑定原理
angular在scope模型上设置了一个监听队列,用来监听数据并更新视图,每当有绑定的东西时angular就会在队列中插入一条$watch,用来检测他监控的内容是否有变化吗,浏览器接收到可以被angular context处理的内容时 $digest就会触发,用来更新视图。
1.使用数组原型的方法 :var arg = Array.prototype.slice.call(arguments,0);
2.使用es6新方法 var arg = Array.from(arguments);
3.伪数组可以被遍历,for循环push到一个空数组
4.使用toArray 和markArray
var aDiv = arguments.toArray(); //实例方法
var aDiv = $.makeArray(arguments);//静态方法
deferred可以延迟脚本加载,先解析HTML
@类型检测
instanceof可以判断一个对象是否是类的实例化对象;
constructor可以判断一个对象是否是类的构造函数。
@数组扁平化
/ /方法一
/ /直接遍历整个数组,元素直接存,还是数组则递归调用
function flat(arr) {
let res = []; //储存结果集
for (let i = 0;i<arr.length;i++){
if(Array.isArray(arr[i])){
res.push(...flat(arr[i]))
}else {
res.push(arr[i])
}
}
return res;
}
const arr = [[3, 12, 1, 2, 2], [2, 3, 5, 5], [6, 7, 8, [11, 12, [12, 13, [14]]]]];
var result = flat(arr);
console.log(result);
/ / 方法二
//每次都从arr头部选元素,不是数组直接存储结果集,是的话遍历这个数组,
//数值储存结果集,若还是数组则存储在arr尾部,以便再次用flat处理。
function flat(arr) {
let res = []; //储存结果集
while (arr.length >0){
let data = arr.pop(); //拿走第一个元素
if(Array.isArray(data)){
for(const item of data){
if(Array.isArray(item)){
arr.push(item); //里面还是数组,存储在arr尾部
}else {
res.push(item); //是具体元素可以直接存储
}
}
}else {
res.push(data); //是具体元素可以直接存储
}
}
return res;
}
const arr = [[3, 12, 1, 2, 2], [2, 3, 5, 5], [6, 7, 8, [11, 12, [12, 13, [14]]]]];
var result = flat(arr);
console.log(result);
//方法三
//es6中数据解构+map循环
function flat(arr) {
return [].concat(...arr.map(v => Array.isArray(v) ? [].concat(flat(v)) : v));
}
const arr = [[3, 12, 1, 2, 2], [2, 3, 5, 5], [6, 7, 8, [11, 12, [12, 13, [14]]]]];
var result = flat(arr);
console.log(result);
var data = [1,9,5,4,6,7,3,2,8,45,65,21,48,666,99,27,13]
console.log(Array.from(new Set(data)).sort((a, b) => a - b))
传统 Ajax 指的是 XMLHttpRequest(XHR), 最早出现的发送后端请求技术,隶属于原始js中,核心使用XMLHttpRequest对象,多个请求之间如果有先后关系的话,就会出现回调地狱。
Ajax的交互模型:
第一步检查XMLhttpRequest对象对否存在;
第二步告诉创建好的XHR对象那个函数会处理的改变,就是在onReadyChange里面对不同的XHR.status进行不同的处理
第三步XHR.open指定请求方法,地址,还有是否异步
第四步XHR.send发送数据,然后接受结果进行 对应的业务处理。
JQuery ajax 是对原生XHR的封装,除此以外还增添了对JSONP的支持。经过多年的更新维护,真的已经是非常的方便了,优点无需多言;如果是硬要举出几个缺点,那可能只有:
1.本身是针对MVC的编程,不符合现在前端MVVM的浪潮
2.基于原生的XHR开发,XHR本身的架构不清晰。
3.JQuery整个项目太大,单纯使用ajax却要引入整个JQuery非常的不合理(采取个性化打包的方案又不能享受CDN服务)
4.不符合关注分离(Separation of Concerns)的原则
5.配置和调用方式非常混乱,而且基于事件的异步模型不友好。
//=========ajax
$.ajax({
url: '/getUsers',
type: 'get',
dataType: 'json',
data: {
//'a': 1,
//'b': 2,
},
success: function (response) {
console.log(response);
}
})
//===========axios
axios({
method: 'post',
url: '/user/12345',
data: {
firstName: 'Fred',
lastName: 'Flintstone'
}
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
但是axios是利用promise对Ajax的进一步封装,比如在Vue中我使用了axios插件:
/ /http.js中再次手动封装axios添加拦截器功能
import axios from 'axios';
import router from './router';
//axios配置
axios.defaults.timeout = 6000;
//axios.defaults.baseURL = 'https://api.github.com';
// axios.defaults.headers.common['token'] = localStorage.getItem('token');
//request 拦截器
axios.interceptors.request.use(
);
//respone拦截器
axios.interceptors.response.use(
);
export default axios;
/ / Vue的入口文件main.js中挂载axios
Vue.prototype.$md5 = md5; //使用md5加密技术
Vue.prototype.$http = axios; //解决跨域调接口
axios 是一个基于Promise 用于浏览器和 nodejs 的 HTTP 客户端,本质上也是对原生XHR的封装,只不过它是Promise的实现版本,符合最新的ES规范,它本身具有以下特征:
1.从浏览器中或node中创建 XMLHttpRequest
2.支持 Promise API
3.客户端支持防止CSRF
4.提供了一些并发请求的接口(重要,方便了很多的操作)
5.从 node.js 创建 http 请求
6.拦截请求和响应
7.转换请求和响应数据
8.取消请求
9.自动转换JSON数据
进程是资源分配的最小单位,线程是程序执行的最小单位
线程是程序执行时的最小单位,它是进程的一个执行流,是CPU调度和分派的基本单位,一个进程可以由很多个线程组成,线程间共享进程的所有资源,每个线程有自己的堆栈和局部变量。线程由CPU独立调度执行,在多CPU环境下就允许多个线程同时运行。
同样多线程也可以实现并发操作,每个请求分配一个线程来处理。
线程和进程各自有什么区别和优劣呢?
进程有自己的独立地址空间,每启动一个进程,系统就会为它分配地址空间,建立数据表来维护代码段、堆栈段和数据段,这种操作非常昂贵。而线程是共享进程中的数据的,使用相同的地址空间,因此CPU切换一个线程的花费远比进程要小很多,同时创建一个线程的开销也比进程要小很多。
线程之间的通信更方便,同一进程下的线程共享全局变量、静态变量等数据,而进程之间的通信需要以通信的方式(IPC)进行。
进程通信:PC的方式通常有管道(包括无名管道和命名管道)、消息队列、信号量、共享存储、Socket、Streams等。其中 Socket和Streams支持不同主机上的两个进程IPC
管道(pipe),流管道(s_pipe)和有名管道(FIFO)
信号(signal)
信号量
消息队列
共享内存(使用操作两大,读写频率高)
套接字(socket,适用于分布式开发)
不过如何处理好同步与互斥是编写多线程程序的难点。
但是多进程程序更健壮,多线程程序只要有一个线程死掉,整个进程也死掉了,而一个进程死掉并不会对另外一个进程造成影响,因为进程有自己独立的地址空间。
keep-alive是Vue提供的一个抽象组件,用来对组件进行缓存,从而节省性能,由于是一个抽象组件,所以在v页面渲染完毕后不会被渲染成一个DOM元素。
使用该组件的方法是现在router.js中的meta标签中把keep-alive标签设置为true,然后界面中把需要被缓存的组件用标签包裹起来,
由于组件被缓存后不会再触发created 等生命周期钩子函数,但是被缓存的组件额外增加了两个生命周期钩子函数:activesd和deactivied,
activited是缓存的组件被再次激活时触发,
deactivited是组件销毁时,离开组件时触发。
我们在创建一个router实例的时候,可以提供一个scrollBehavior(to,from,savedposition)方法,该方法会在用户切换路由时触发,列表到一百条时仍然返回原来浏览过的位置。
include:需要缓存的
exclude:不需要缓存的
keep-alive
创建期间的生命周期函数:
beforeCreate:实例刚在内存中被创建出来,此时,还没有初始化好 data 和 methods 属性
运行期间的生命周期函数:
销毁期间的生命周期函数:
防抖(debounce)(比如频繁点击开关)
所谓防抖,就是指触发事件后在 n 秒内函数只能执行一次,如果在 n 秒内又触发了事件,则会重新计算函数执行时间。
防抖函数分为非立即执行版和立即执行版。
节流(比如点击按键出特效)
所谓节流,就是指连续触发事件但是在 n 秒中只执行一次函数。节流会稀释函数的执行频率。
对于节流,一般有两种方式可以实现,分别是时间戳版和定时器版。
@防抖和节流代码实现例子
重绘: 是当节点需要更改外观而不会影响布局的,比如改变 color、background-color、visibility等就叫称为重绘
回流: 是 布局或者几何属性需要改变 就称为回流。
注意: 回流必定会发生重绘,重绘不一定会引发回流。回流所需的成本比重绘高的多,改变深层次的节点很可能导致父节点的一系列回流。
减少重绘和回流的方法:
cdn是一种用空间来交换时间的策略。
@前端CDN
前端优化CDN
后缀表达式求解 代码
人工计算后缀表达式
1.纯手工去重
利用两层循环,外层遍历元素,内层判断有无重复,又重复就删除,或者先建立一个空数组,只把空数组push进去
2.利用排序(如果是元素类型是数字)
先把数组排序,排序后利用一层循环,判断左右两个是否相等,相等就删除那个元素。
3.利用map和set的数据结构
@前端三大框架