事件传播指的是发生事件时传播的过程。一共按顺序分为如下三个阶段。 javascript
捕获阶段:从window对象传导到目标节点(从上到下)的过程,直到目标的元素,为截获事件提供机会css
目标阶段:在当前目标触发的过程 ,目标接受事件html
冒泡阶段:从目标节点传导回到windowd对象(从下到上)的过程,在这个阶段对事件作出响应。前端
ES5组合继承vue
//定义一个父类:人
function Person(cai) {
this.cai = cai;
this.emotion = ['喜', '怒', '哀', '乐']; //人都有喜怒哀乐
}
//定义原型类方法 将 Person 类中需共享的方法放到 prototype 中,实现复用
Person.prototype.eat = function () {
console.log('吃' + this.cai);
}
//定义子类:学生,继承了“人”这个类
function Student(cai, studentID) {
//先调用父类构造器 子类继承父类的属性 须要将this指向父类中的cai
Person.call(this, cai);
this.studentID = studentID; // studentID是子类本身的属性
}
Student.prototype = new Person(); //子类继承父类的方法此时 Student.prototype 中的 constructor 被重写了,会致使 stu1.constructor === Person
Student.prototype.constructor = Student; //将 Student 原型对象的 constructor 指针从新指向 Student 自己
//建立子类的实例
var stu1 = new Student('西兰花', 1001);
console.log(stu1.emotion); //['喜', '怒', '哀', '乐']
console.log(stu1.cai); // 西兰花
stu1.eat(); //吃西兰花
console.log(stu1.constructor); //Student复制代码
结合原型链继承和借用构造函数继承即组合继承是javascript最经常使用的继承模式,不过,它也有本身的不足:组合继承不管在什么状况下,都会调用两次父类构造函数。 一次是在建立子类原型的时候,另外一次是在子类构造函数内部.子类最终会包含父类对象的所有实例属性,但咱们不得不在调用子类构造函数时重写这些属性。
java
继承实质的区别:node
**ES5:**先创造子类的实例对象this,而后再将父类的方法添加到this上面(Parent.apply(this))。 nginx
**ES6:**先创造父类的实例对象this,(因此必须先调用super方法)而后再用子类的构造函数修改this。
web
ES6继承面试
class Person {
constructor(cai) {
this.cai = cai;
this.emotion = ['喜', '怒', '哀', '乐']; //人都有喜怒哀乐
}
eat() {
console.log('吃' + this.cai);
}
}
class Student extends Person {
constructor(cai, studentID) {
// 指向父类的构造函数
super(cai);
this.studentID = studentID;
}
showStudentID() {
console.log(this.studentID)
}
}
var stu1 = new Student('西兰花', 1001);
console.log(stu1.emotion);
stu1.eat(); //吃西兰花
stu1.showStudentID() //1001复制代码
注:
ES6 里的 Class 是经过关键字 extends 实现继承
子类必须在 constructor 方法中调用 super 方法 ,super的关键字在这里表示父类的构造函数,用来创造父类的this对象, 而子类是没有本身的 this 对象的,须要调用 super 方法,来继承父类的 this 对象,而后对其加工 ,故可知只有调用了 super 以后才可使用 this 关键字,不然会报错
参考连接 https://juejin.im/post/59ac1c4ef265da248e75892b#heading-2
引子:
Js里有两种数据类型,基本数据类型和引用数据类型。深拷贝、浅拷贝通常都是针对引用数据类型的。
基本数据类型主要是:undefined,boolean,number,string,null
var a = 1;
var b = a;
a = 2;
console.log(a); // 2
console.log(b); // 1复制代码
var arr1 = [1,2,3,4];
var arr2 = arr1;
arr1.push(5);
console.log(arr1); // [1,2,3,4,5]
console.log(arr2); // [1,2,3,4,5]
arr2.push(6);
console.log(arr1); // [1,2,3,4,5,6]
console.log(arr2); // [1,2,3,4,5,6]复制代码
然而,对于引用数据类型的赋值操做,arr2 仅仅是复制了 arr1的引用(也能够称之为指向 arr1 内存地址的指针)。简单来讲,就是 arr1 与 arr2 指向了同一个内存空间
浅拷贝:
若是属性是基本类型,拷贝的就是基本类型的值;若是属性是内存地址(引用类型),拷贝的就是内存地址 ,所以若是其中一个对象改变了这个地址,就会影响到另外一个对象。
function shallowCopy(copyTarget) {
var obj = {};
for (var key in copyTarget) {
obj[key] = copyTarget[key];
}
return obj;
}
var json1 = {
'name': '张三',
'family': {
'children': '张三三',
'wife': '李四'
}
}
var json2 = shallowCopy(json1);
// before
console.log(json2);
// after
json1.family['father'] = '张一'
console.log(json1);
console.log(json2);复制代码
由此能够看出,浅拷贝仅仅拷贝了基本类型的数据,对于引用类型数据,则指向被复制的内存地址,若原地址中的对象发生改变,那么浅复制出来的对象也会相应改变。
深拷贝:
深拷贝可归纳为:为引用类型数据成员另辟了一个独立的内存空间,实现真正内容上的拷贝。
function deepCopy(copyTarget) {
var obj = {};
for(var key in copyTarget) {
// 先判断obj[key]是否为对象
if(typeof copyTarget[key] === "object"){
// 递归
obj[key] = deepCopy(copyTarget[key]);
} else {
// 若是不是对象,直接赋值便可
obj[key] = copyTarget[key];
}
}
return obj;
}
var json1 = {
'name': '张三',
'family': {
'children': '张三三','wife': '李四'
}
}
var json2 = deepCopy(json1);
// before
console.log(json2);
// after
json1.family['father'] = '张一'
console.log(json1);
console.log(json2);复制代码
拓展
深复制能够用JSON的方式:JSON.parse(JSON.stringify(obj))
可是JSON复制会忽略掉值为undefined以及函数表达式。
var obj = {
a: 1,
b: 2,
c: undefined,
sum: function() { return a + b; }
};
var obj2 = JSON.parse(JSON.stringify(obj));
console.log(obj2); //Object {a: 1, b: 2}复制代码
浏览器中 Event Loop:
浏览器中, js引擎线程会循环从 任务队列 中读取事件而且执行, 这种运行机制称做 Event Loop (事件循环).
每一个浏览器环境,至多有一个event loop。 一个event loop能够有1个或多个task queue(任务队列) 先执行同步的代码,而后js会跑去消息队列中执行异步的代码,异步完成后,再轮到回调函数,而后是去下个事件循环中执行setTimeout 它从script(总体代码)开始第一次循环。以后全局上下文进入函数调用栈。直到调用栈清空(只剩全局),而后执行全部的micro-task(微任务)
。当全部可执行的micro-task(微任务)
执行完毕以后。循环再次从macro-task 宏任务
开始,找到其中一个任务队列执行完毕,而后再执行全部的micro-task,这样一直循环下去。
从规范上来说,setTimeout有一个4ms的最短期,也就是说无论你设定多少,反正最少都要间隔4ms才运行里面的回调。而Promise的异步没有这个问题。Promise所在的那个异步队列优先级要高一些 Promise是异步的,是指他的then()和catch()方法,Promise自己仍是同步的 Promise的任务会在当前事件循环末尾中执行,而setTimeout中的任务是在下一次事件循环执行
总结:
setTimeout、onClick
等等的一些操做,咱们会将他的执行结果放入队列,此期间主线程不阻塞event loop
在队列里面从头开始取,在执行栈中执行event loop
永远不会断Event Loop
(事件循环机制)setTimeout(function(){
console.log(4)
},0);
new Promise(function(resolve){
console.log(1)
for( var i=0 ; i<10000 ; i++ ){
i===9999 && resolve()
}
console.log(2)
}).then(function(){
console.log(5)
});
console.log(3);
//依次输出 1 2 3 5 4
复制代码
经过Vue传递参数能够分为两种方式: params参数 query参数
params参数传递方式分两种:(1)经过router-link进行跳转 (2)经过编程导航进行路由跳转
params传值
<div
v-for="item in list"
@click="getDescribe(item)"
>
{{item}}
</div>复制代码
data() {
return {
list: ['101','102','103']
}
}复制代码
对应路的由配置以下:
{
path: '/two/:id',
name: 'two',
component: () => import('@/components/two.vue'),
meta: {
title: 'two'
}
}复制代码
push 后面能够是对象,也能够是字符串:
对象:
getDescribe(item) {
this.$router.push({
path: `/two/${item}`
})
}复制代码
字符串:
getDescribe(item) {
this.$router.push(`/two/${item}`)
}复制代码
命名的路由:
getDescribe(item) {
this.$router.push({ name: 'two', params: { id: `${item}` }})
}复制代码
注意: 须要注意的是使用params必须和name属性一块儿使用,不然要跳转的目标路由页面没法经过params获取到传递过来的参数。params: 至关于 post 请求,请求参数不会体如今地址栏中。 这种方法会出现以下问题: 若是子页面点击【刷新】按钮时,刚才传过来的参数并不会同步。由于参数没有同步到网页地址栏中。可是若是要让他体如今地址栏中,能够在配置路由时,写在path属性中。 以下能够解决刷新的问题:
{
path: '/two/:id',
name: 'two',
component: () => import('@/components/two.vue'),
meta: {
title: 'two'
}
}复制代码
读取参数:
console.log(this.$route.params)复制代码
query传值。 相似get传值
query: 至关于 get 请求,请求参数会体如今地址栏中。
this.$router.push({
path: `/two`,
query: {
id:item
}
})复制代码
http://localhost:8888/#/two?id=101
console.log(this.$route.query)复制代码
this.$router 和this.$route有何区别?
上面提到的编程式导航中用this.$router.push()来改变路由,用this.$route.params来获得参数值
1.$router为VueRouter实例,想要导航到不一样URL,则使用$router.push方法。
2.$route为当前router跳转对象,里面能够获取name、path、query、params等
computed特性
1.是计算值,
2.应用:就是简化tempalte里面{{}}计算和处理props或$emit的传值
3.具备缓存性,页面从新渲染值不变化,计算属性会当即返回以前的计算结果,而没必要再次执行函数
注意:computed的值在getter执行后是会缓存的,只有在它依赖的属性值改变以后,下一次获取computed的值时才会从新调用对应的getter来计算
watch特性
1.是观察的动做,
2.应用:监听props,$emit或本组件的值执行异步操做
3.无缓存性,页面从新渲染时值不变化也会执行
注意:watch在每次监听的值变化时,都会执行回调。其实从这一点来看,都是在依赖的值变化以后,去执行回调
总结:若是一个值依赖多个属性(多对一),用computed确定是更加方便的。若是一个值变化后会引发一系列操做,或者一个值变化会引发一系列值的变化(一对多),用watch更加方便一些。
watch的回调里面会传入监听属性的新旧值,经过这两个值能够作一些特定的操做。computed一般就是简单的计算。 watch和computed并无哪一个更底层,watch内部调用的是vm.$watch,它们的共同之处就是每一个定义的属性都单独创建了一个Watcher对象
Vue.js 提供了一个方法 watch,它用于观察Vue实例上的数据变更。
<template>
<div>
<input
type="text"
v-model="age"
>
</div>
</template>
<script>
export default {
data() {
return {
age:18
}
},
watch: {
age: (newAge,oldAge) => {
console.log('新值为:'+newAge+',旧值为:'+oldAge);
}
},
methods: {
},
components: {
},
computed: {
},
mounted() {
}
}
复制代码
1、vdom是什么?
vdom是虚拟DOM(Virtual DOM)的简称,指的是用JS模拟的DOM结构,将DOM变化的对比放在JS层来作。换而言之,vdom就是JS对象。
以下真实DOM
<ul id="list">
<li class="item">Item1</li>
<li class="item">Item2</li>
</ul>复制代码
映射成虚拟DOM就是这样:
{
tag: "ul",
attrs: {
id: "list"
},
children: [
{
tag: "li",
attrs: { className: "item" },
children: ["Item1"]
}, {
tag: "li",
attrs: { className: "item" },
children: ["Item2"]
}
]
} 复制代码
2、为何要用vdom?
采用JS对象模拟的方法,将DOM的比对操做放在JS层,减小浏览器没必要要的重绘,提升效率。
固然有人说虚拟DOM并不比真实的DOM快,其实也是有道理的。当一个table中的每一条数据都改变时,显然真实的DOM操做更快,由于虚拟DOM还存在js中diff算法的比对过程。因此,性能优点仅仅适用于大量数据的渲染而且改变的数据只是一小部分的状况。
虚拟DOM更加优秀的地方在于:
一、它打开了函数式的UI编程的大门,即UI = f(data)这种构建UI的方式。
二、能够将JS对象渲染到浏览器DOM之外的环境中,也就是支持了跨平台开发,好比ReactNative。
3、diff算法
一、父组件向子组件通讯
使用props,父组件可使用props向子组件传递数据。
father.vue
<template>
<div>
<Child :name="msg"></Child>
</div>
</template>
<script>
import Child from '@/components/child.vue'
export default {
components: {
Child
},
data() {
return {
msg: 'feixuan'
}
},
computed: {
},
mounted() {
},
methods: {
}
}
</script>复制代码
child.vue
<template>
<div>
{{name}}
</div>
</template>
<script>
export default {
props:{
name:{
type:String,
default:''
}
},
data() {
return {
}
},
components: {
},
computed: {
},
mounted() {
},
methods: {
}
}
</script>复制代码
二、子组件向父组件通讯
方法一: 使用vue事件 父组件向子组件传递事件方法,子组件经过$emit触发事件,回调给父组件。
father.vue
<template>
<div>
<Child @msgFunc="msgEvent"></Child>
{{message}}
</div>
</template>
<script>
import Child from '@/components/child.vue'
export default {
components: {
Child
},
data() {
return {
message: ''
}
},
methods: {
msgEvent(msg) {
console.log(msg)
this.message = msg
}
},
computed: {
},
mounted() {
}
}
</script>复制代码
child.vue
<template>
<div>
<button @click="handleClick">点我</button>
</div>
</template>
<script>
export default {
data() {
return {
}
},
methods: {
handleClick() {
this.$emit('msgFunc','我是来自子组件的消息');
}
},
components: {
},
computed: {
},
mounted() {
}
}
</script>复制代码
三、非父子组件通讯
对于两个组件不是父子关系,那么又该如何实现通讯呢?在项目规模不大的状况下,彻底可使用中央事件总线 EventBus
的方式。若是你的项目规模是大中型的,那么可使用vuex状态管理
EventBus
经过新建一个 Vue
事件 bus
对象,而后经过 bus.$emit
触发事件,bus.$on
监听触发的事件。
在使用vue.js的时候,有时候由于一些特定的业务场景,不得不去操做DOM,好比这样:
<template>
<div>
<div ref="test">{{test}}</div>
<button @click="handleClick">点击</button>
</div>
</template>
<script>
export default {
data() {
return {
test: 'begin'
}
},
methods: {
handleClick () {
this.test = 'end';
console.log(this.$refs.test.innerText);//打印“begin”
}
},
components: {
},
computed: {
},
mounted() {
}
}
</script>
复制代码
打印的结果是begin,为何咱们明明已经将test设置成了“end”,获取真实DOM节点的innerText却没有获得咱们预期中的“end”,而是获得以前的值“begin”呢?
缘由
Vue.js源码的Watch实现。当某个响应式数据发生变化的时候,它的setter函数会通知闭包中的Dep,Dep则会调用它管理的全部Watch对象。触发Watch对象的update实现。咱们来看一下update的实现。
update () {
/* istanbul ignore else */
if (this.lazy) {
this.dirty = true
} else if (this.sync) {
/*同步则执行run直接渲染视图*/
this.run()
} else {
/*异步推送到观察者队列中,下一个tick时调用。*/
queueWatcher(this)
}
}复制代码
Vue是一个MVVM框架,数据绑定简单来讲,就是当数据发生变化时,相应的视图会进行更新,当视图更新时,数据也会跟着变化。
实现数据绑定的方式大体有如下几种:
- 一、发布者-订阅者模式(backbone.js)
- 二、脏值检查(angular.js)
- 三、数据劫持(vue.js)复制代码
Vue.js则是经过数据劫持以及结合发布者-订阅者来实现的,数据劫持是利用ES5的Object.defineProperty(obj, key, val)来劫持各个属性的的setter以及getter,在数据变更时发布消息给订阅者,从而触发相应的回调来更新视图。
二进制 → 十进制
方法:二进制数从低位到高位(即从右往左)计算,第0位的权值是2的0次方,第1位的权值是2的1次方,第2位的权值是2的2次方,依次递增下去,把最后的结果相加的值就是十进制的值了。
例:将二进制的(101011)B转换为十进制的步骤以下:
1. 第0位 1 x 2^0 = 1;
2. 第1位 1 x 2^1 = 2;
3. 第2位 0 x 2^2 = 0;
4. 第3位 1 x 2^3 = 8;
5. 第4位 0 x 2^4 = 0;
6. 第5位 1 x 2^5 = 32;
7. 读数,把结果值相加,1+2+0+8+0+32=43,即(101011)B=(43)D。
复制代码
八进制 → 十进制
方法:八进制数从低位到高位(即从右往左)计算,第0位的权值是8的0次方,第1位的权值是8的1次方,第2位的权值是8的2次方,依次递增下去,把最后的结果相加的值就是十进制的值了。 八进制就是逢8进1,八进制数采用 0~7这八数来表达一个数。
例:将八进制的(53)O转换为十进制的步骤以下:
1. 第0位 3 x 8^0 = 3;
2. 第1位 5 x 8^1 = 40;
3. 读数,把结果值相加,3+40=43,即(53)O=(43)D。复制代码
十进制 → 二进制
方法:除2取余法,即每次将整数部分除以2,余数为该位权上的数,而商继续除以2,余数又为上一个位权上的数,这个步骤一直持续下去,直到商为0为止,最后读数时候,从最后一个余数读起,一直到最前面的一个余数。
例:将十进制的(43)D转换为二进制的步骤以下:
1. 将商43除以2,商21余数为1;
2. 将商21除以2,商10余数为1;
3. 将商10除以2,商5余数为0;
4. 将商5除以2,商2余数为1;
5. 将商2除以2,商1余数为0;
6. 将商1除以2,商0余数为1;
7. 读数,由于最后一位是通过屡次除以2才获得的,所以它是最高位,读数字从最后的余数向前读,101011,即(43)D=(101011)B。复制代码
函数防抖(debounce)
防抖函数 debounce 指的是某个函数在某段时间内,不管触发了多少次回调,都只执行最后一次。假如咱们设置了一个等待时间 3 秒的函数,在这 3 秒内若是遇到函数调用请求就从新计时 3 秒,直至新的 3 秒内没有函数调用请求,此时执行函数,否则就以此类推从新计时。
原理及实现 :
实现原理就是利用定时器,函数第一次执行时设定一个定时器,以后调用时发现已经设定过定时器就清空以前的定时器,并从新设定一个新的定时器,若是存在没有被清空的定时器,当定时器计时结束后触发函数执行。
函数节流(throttle)
规定在一个单位时间内,只能触发一次函数。若是这个单位时间内触发屡次函数,只有一次生效,
即每隔一段时间后执行一次,也就是下降频率,将高频操做优化成低频操做,
一般使用场景: 滚动条事件 或者 resize 事件,一般每隔 100~500 ms执行一次便可。
复制代码
总结 :
函数防抖和函数节流都是防止某一时间频繁触发,可是这两兄弟之间的原理却不同。 函数防抖是某一段时间内只执行一次,而函数节流是间隔时间执行。
结合应用场景
debounce :
search搜索联想,用户在不断输入值时,用防抖来节约请求资源。 window触发resize的时候,不断的调整浏览器窗口大小会不断的触发这个事件,用防抖来让其只触发一次
throttle :
鼠标不断点击触发,mousedown(单位时间内只触发一次) 监听滚动事件,好比是否滑到底部自动加载更多,用throttle来判断
斐波那契数列也叫黄金分割数列,也叫兔子数列
原理:假定一对大兔子每个月能生一对小兔子,且每对新生的小兔子通过一个月能够长成一对大兔子,若是不发生死亡,且每次均生下一雌一雄,问一年后共有多少对兔子?
月份 | 兔子状况 | 总数 |
---|---|---|
第0个月 | a(小兔子) | 1 |
第1个月 | a(具有繁殖能力) | 1 |
第2个月 | b(生啦生啦)+ a(他父母) | 2 |
第3个月 | b(2月份出生的具有繁殖能力,正跃跃欲试) + b2(他父母又生二胎了) +a(他父母) | 3 |
第4个月 | c(2月份的兔子b喜当爹)+b(二月份出生的兔子) + b2(二胎具有繁殖能力,准备生娃) +a(他父母)+d(a生三胎) | 5 |
… | … | … |
一、1 、二、三、五、八、1三、2一、3四、5五、89……
因此规律就是 fn(n)=fn(n-1)+fn(n-2)
迭代 方式
/*
*i 月份
*/
function fn(i){
var a=[];
/*0个月什么都不存在*/
a[0]=0;
a[1]=1;
for(var j = 2; j<= i;j++){
a[j]=a[j-1] + a[j-2];
}
return a[i]
}复制代码
递归方式
/*
* i 月份
*/
function fn(i){
if(i < 2){return i === 0 ? 0 : 1;}
return fn(i-1)+fn(i-2)
}复制代码
总结: 针对这个例子来讲,这里的递归会进行太屡次的调用(比迭代多),因此简洁的背后牺牲的是性能
这个是阿里的一道面试题
核心原理: 更新视图但不从新请求页面。
vue-router实现单页面路由跳转,提供了三种方式:hash模式、history模式、abstract模式,根据mode参数来决定采用哪种方式。
路由模式
vue-router 提供了三种运行模式:
hash: 使用 URL hash 值来做路由。默认模式。
history: 依赖 HTML5 History API 和服务器配置。查看 HTML5 History 模式。
abstract: 支持全部 JavaScript 运行环境,如 Node.js 服务器端
Hash模式:
hash即浏览器url中#后面的内容,包含#。hash是URL中的锚点,表明的是网页中的一个位置,单单改变#后的部分,浏览器只会加载相应位置的内容,不会从新加载页面。 也就是说 即#是用来指导浏览器动做的,对服务器端彻底无用,HTTP请求中,不包含#。 每一次改变#后的部分,都会在浏览器的访问历史中增长一个记录,使用”后退”按钮,就能够回到上一个位置。 因此说Hash模式经过锚点值的改变,根据不一样的值,渲染指定DOM位置的不一样数据。
History模式:
HTML5 History API提供了一种功能,能让开发人员在不刷新整个页面的状况下修改站点的URL,就是利用 history.pushState API 来完成 URL 跳转而无须从新加载页面; 因为hash模式会在url中自带#,若是不想要很丑的 hash,咱们能够用路由的 history 模式,只须要在配置路由规则时,加入"mode: 'history'",这种模式充分利用 history.pushState API 来完成 URL 跳转而无须从新加载页面。 有时,history模式下也会出问题: eg: hash模式下:xxx.com/#/id=5 请求地址为 xxx.com,没有问题。 history模式下:xxx.com/id=5 请求地址为 xxx.com/id=5,若是后端没有对应的路由处理,就会返回404错误; 为了应对这种状况,须要后台配置支持: 在服务端增长一个覆盖全部状况的候选资源:若是 URL 匹配不到任何静态资源,则应该返回同一个 index.html 页面,这个页面就是你 app 依赖的页面。
abstract模式:
abstract模式是使用一个不依赖于浏览器的浏览历史虚拟管理后端。 根据平台差别能够看出,在 Weex 环境中只支持使用 abstract 模式。 不过,vue-router 自身会对环境作校验,若是发现没有浏览器的 API,vue-router 会自动强制进入 abstract 模式,因此 在使用 vue-router 时只要不写 mode 配置便可,默认会在浏览器环境中使用 hash 模式,在移动端原生环境中使用 abstract 模式。 (固然,你也能够明确指定在全部状况下都使用 abstract 模式)。
async 及 await
async 是让方法变成异步。 await 是等待异步方法执行完成。
注意:await 必须在 async 方法中才可使用由于await 访问自己就会形成程序中止堵塞,因此必须在异步方法中才可使用
使用await语法的时候,后面的函数须要返回Promise对象
async test1() {
let result = await this.$http.get('/api/job',{})
console.log(result)
}复制代码
冒泡排序:
两两元素进行比较,较大者放置后头,相似水中的气泡较大者浮在水的上端
function bubble(arr) {
for(var i = 0; i < arr.length; i++) {
for(var j = 0; j < arr.length - 1- i; j++) {
if(arr[j] > arr[j + 1]) {
var max = arr[j];
arr[j] = arr[j+1];//交换数据
arr[j+1] = max;//交换数据
}
}
}
console.log(arr)
}
bubble([1,4,6,2,7,2])// [1, 2, 2, 4, 6, 7]复制代码
快速排序:
取一个元素为基准,把序列分红两部分,小于基准的放到它的左面,大于等于的放到它的右面,而后在把左面和右面的子序列再进行上述的拆分,直到子序列不可再分割(小于2个元素),最终达到整个序列有序
function quickSort(arr) {
if(arr.length < 2) {
return arr;
} else {
const pivot = arr[0]; // 基准值
const pivotArr = []; // 同样大的放中间
const lowArr= []; // 小的放左边
const hightArr = []; // 大的放右边
arr.forEach(current => {
if(current === pivot) pivotArr.push(current);
else if(current > pivot) hightArr.push(current);
else lowArr.push(current);
})
return quickSort(lowArr).concat(pivotArr).concat(quickSort(hightArr));
}
}
console.log(quickSort([4,6,2,3,1,5,7,8])) // [1, 2, 3, 4, 5, 6, 7, 8]复制代码
简单选择排序:
首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置。 再从剩余未排序元素中继续寻找最小(大)元素,而后放到已排序序列的末尾。 重复第二步,直到全部元素均排序完毕。
const selectionSort = array => {
const len = array.length;
let minIndex, temp;
for (let i = 0; i < len - 1; i++) {
minIndex = i;
for (let j = i + 1; j < len; j++) {
if (array[j] < array[minIndex]) {
// 寻找最小的数
minIndex = j; // 将最小数的索引保存
}
}
temp = array[i];
array[i] = array[minIndex];
array[minIndex] = temp;
console.log('array: ', array);
}
return array;
};
selectionSort([6,4,3,8])//[3, 4, 6, 8]复制代码
异步加载js有三种 : defer 、 async 、 动态建立script标签 、 按需异步载入js
async : 并行加载脚本文件,下载完毕当即解释执行代码,不会按照页面上的script顺序执行。 defer : 并行下载js,会按照页面上的script标签的顺序执行,而后在文档解析完成以后执行脚本
defer 与 async 的相同点是采用并行下载,在下载过程当中不会产生阻塞。区别在于执行时机,async 是加载完成后自动执行,而 defer 须要等待页面完成后执行。
解析:
<script src="script.js"></script>
没有 defer 或 async,浏览器会当即加载并执行指定的脚本,“当即”指的是在渲染该 script 标签之下的文档元素以前,也就是说不等待后续载入的文档元素,读到就加载并执行。
<script async src="script.js"></script>
有 async,加载和渲染后续文档元素的过程将和 script.js 的加载与执行并行进行(异步)。
<script defer src="myscript.js"></script>
有 defer,加载后续文档元素的过程将和 script.js 的加载并行进行(异步),可是 script.js 的执行要在全部元素解析完成以后,DOMContentLoaded 事件触发以前完成。
Load 事件触发表明页面中的 DOM,CSS,JS,图片已经所有加载完毕。DOMContentLoaded 事件触发表明初始的 HTML 被彻底加载和解析,不须要等待 CSS,JS,图片加载。
call 和 apply 是为了动态改变 this 而出现的,当一个 object 没有某个方法,可是其余的有,咱们能够借助 call 或 apply 用其它对象的方法来操做。
知乎简单易懂答案:
猫吃鱼狗吃肉,奥特曼打怪兽
有一天,狗想吃鱼了
猫.吃鱼.call(狗,鱼)
狗就吃到鱼了
猫成精了,想打怪兽
奥特曼.打怪兽.call(猫,小怪兽)
obj.call(thisObj, arg1, arg2, ...)
obj.apply(thisObj, [arg1, arg2, ...])
二者做用一致,都是把obj(即this)绑定到thisObj,这时候thisObj具有了obj的属性和方法。
或者说thisObj继承了obj的属性和方法。惟一区别是apply接受的是数组参数,call接受的是连续参数。复制代码
例子:
var obj1 = {
value: 1
}
function say() {
console.log(this.value)
}
say() // 输出undefined
say.call(obj1) // 输出1复制代码
注意两点 : call 改变了 this 的指向,此时的 this 指到了 obj1 say 函数执行了
bind方法的特殊性:
之因此把bind方法单独放出来是由于bind方法和前面二者仍是有不小的区别的,虽然都是动态改变this的值,举个例子
var obj = {
x: 81,
};
var foo = {
getX: function() {
return this.x;
}
}
console.log(foo.getX.bind(obj)()); //81
console.log(foo.getX.call(obj)); //81
console.log(foo.getX.apply(obj)); //81复制代码
有没有注意到使用bind方法时候后面还要多加上一对括号,由于使用bind只是返回了对应函数并无当即执行,而call和apply方法是当即执行的
beforeCreate:
vue实例的挂载元素el和数据对象data都为undefined,还未初始化
created:
实例建立完成 vue实例的数据对象data有了,el尚未。并已经完成如下配置:数据观测(data observer),属性和方法的运算, watch/event 事件回调
此时能够调用methods中定义的方法,修改data的数据,而且可触发响应式变化、computed值从新计算,watch到变动等
还未挂载到DOM,不能访问到$el属性,$ref属性内容为空数组
beforeMount:
vue 实例的$el 和 data 都初始化了,但仍是挂载以前为虚拟的 dom 节点,data.message 还未替换
mounted:
实例挂载到DOM上,此时能够经过DOM API获取到DOM节点,$ref属性能够访问,data.message 成功渲染
beforeUpdate:
这里的更新对象是模板,即须要虚拟 DOM 从新渲染和打补丁,beforeUpdate发生在以上两个流程以前,此时新的虚拟DOM已经生成
若是发生变动的数据在模板中并无使用(包括直接和间接,间接:好比某个依赖该数据的计算属性在模板中使用了),则不会触发更新流程!!!
updated:
因为数据更改致使的虚拟 DOM 从新渲染和打补丁,在这以后会调用该钩子。
当这个钩子被调用时,组件 DOM 已经更新,能够执行依赖于 DOM 的操做
beforeDestroy:
实例销毁以前调用。在这一步,实例仍然彻底可用,this仍能获取到实例
通常在这一步中进行:销毁定时器、解绑全局事件、销毁插件对象等操做
destroyed:
Vue 实例销毁后调用。调用后,Vue 实例指示的全部东西都会解绑定,全部的事件监听器会被移除,全部的子实例也会被销毁
参考 https://segmentfault.com/a/1190000000652891
闭包就是可以读取其余函数内部变量的函数。因为在Javascript语言中,只有函数内部的子函数才能读取局部变量,所以能够把闭包简单理解成“定义在一个函数内部的函数”。
要理解闭包,首先必须理解Javascript特殊的变量做用域。
变量的做用域无非就是两种:全局变量和局部变量。
Javascript语言的特殊之处,就在于函数内部能够直接读取全局变量。
var n = 999;
function f() {
console.log(n)
}
f(); // 999
复制代码
另外一方面,在函数外部天然没法读取函数内的局部变量。
function f() {
var n= 999;
}
console(n); //Uncaught ReferenceError: n is not defined
复制代码
这里有一个地方须要注意,函数内部声明变量的时候,必定要使用var命令。若是不用的话,你实际上声明了一个全局变量!
function f() {
n = 999;
}
f();
console.log(n); // 999复制代码
出于种种缘由,咱们有时候须要获得函数内的局部变量。可是,前面已经说过了,正常状况下,这是办不到的,只有经过变通方法才能实现。
那就是在函数的内部,再定义一个函数。
function f1() {
var n = 999;
function f2(){
console.log(n); // 999
}
}复制代码
在上面的代码中,函数f2就被包括在函数f1内部,这时f1内部的全部局部变量,对f2都是可见的。可是反过来就不行,f2内部的局部变量,对f1 就是不可见的。这就是Javascript语言特有的“链式做用域”结构(chain scope),
子对象会一级一级地向上寻找全部父对象的变量。因此,父对象的全部变量,对子对象都是可见的,反之则不成立。
既然f2能够读取f1中的局部变量,那么只要把f2做为返回值,咱们不就能够在f1外部读取它的内部变量了吗!
function f1() {
n = 999;
function f2(){
console.log(n);
}
return f2;
}
var result=f1();
result();// 999复制代码
闭包能够用在许多地方。它的最大用处有两个,一个是前面提到的能够读取函数内部的变量,另外一个就是让这些变量的值始终保持在内存中
function f1() {
var n = 999;
nAdd = function() {
n += 1
}
function f2() {
console.log(n);
}
return f2;
}
var result = f1();
result(); // 999
nAdd();
result(); // 1000复制代码
在这段代码中,result实际上就是闭包f2函数。它一共运行了两次,第一次的值是999,第二次的值是1000。这证实了,函数f1中的局部变量n一直保存在内存中,并无在f1调用后被自动清除。
为何会这样呢?缘由就在于f1是f2的父函数,而f2被赋给了一个全局变量,这致使f2始终在内存中,而f2的存在依赖于f1,所以f1也始终在内存中,不会在调用结束后,被垃圾回收机制(garbage collection)回收。
这段代码中另外一个值得注意的地方,就是“nAdd=function(){n+=1}”这一行,首先在nAdd前面没有使用var关键字,所以 nAdd是一个全局变量,而不是局部变量。其次,nAdd的值是一个匿名函数(anonymous function),而这个
匿名函数自己也是一个闭包,因此nAdd至关因而一个setter,能够在函数外部对函数内部的局部变量进行操做。
特性:
1.函数嵌套函数
2.函数内部能够引用外部的参数和变量
3.参数和变量不会被垃圾回收机制回收
闭包的缺点就是常驻内存,会增大内存使用量,使用不当很容易形成内存泄露。
为何要使用闭包:
为了设计私有方法和变量,避免全局变量污染 但愿一个变量长期驻扎在内存中
利用数组的 indexOf 属性
let arr = [5,7,8,8]
console.log(arr.indexOf(5))//0
console.log(arr.indexOf(8))//2
console.log(arr.indexOf(9))//-1
function unique(origin) {
var result = [];
for (var i = 0; i < origin.length; i++) {
var item = origin[i];
if (result.indexOf(item) === -1) {
result.push(item);
}
}
return result;
}
console.log(unique(arr))//[5, 7, 8]复制代码
数组的 filter 属性和 indexOf 属性
let arr = [5,7,8,8]
function unique(origin) {
var result = origin.filter(function(item, index, array) {
// 获取元素在源数组的位置,只返回那些索引等于当前元素索引的值。
return array.indexOf(item) === index;
});
return result;
}
console.log(unique(arr))//[5, 7, 8]复制代码
利用 ES6 Set
ES6 提供了新的数据结构 Set,它相似于数组,可是成员的值都是惟一的,没有重复的值。向 Set 加入值的时候,不会发生类型转变,因此 5 和 ‘5’ 是两个不一样的值。Set 内部判断两个值是否相同,用的是相似于 “===”的算法,可是区别是,在 set 内部认为 NaN 等于 NaN
let arr = [5,7,8,8]
function unique(origin) {
return Array.from(new Set(origin));
}
console.log(unique(arr))//[5, 7, 8]复制代码
首先盒子模型是由content、padding、border、margin 4部分组成
分类: 标准盒子模型 和 ie盒子模型
区别:
可是目前已经统一去起来了
<!DOCTYPE html>复制代码
CSS3提供了能够切换盒子模型模式的属性:box-sizing,它有2个值,分别是content-box和border-box
content-box:
让元素维持W3C的标准盒模型。元素的宽度/高度由border + padding + content的宽度/高度决定,设置width/height属性指的是content部分的宽/高
border-box:
让元素维持IE传统盒模型(IE6如下版本和IE6~7的怪异模式)。设置width/height属性指的是border + padding + content
1.对www.baidu.com这个网址进行DNS域名解析,获得对应的IP地址
2.根据这个IP,找到对应的服务器,发起TCP的三次握手
3.创建TCP链接后发起HTTP请求
4.服务器响应HTTP请求,浏览器获得html代码
5.浏览器解析html代码,并请求html代码中的资源(如js、css图片等)(先获得html代码,才能去找这些资源)
6.浏览器对页面进行渲染呈现给用户
三个主要过程:
最后一步对前端很重要 浏览器是如何对页面进行渲染的?
a: 解析html文件构成 DOM树,
b: 解析CSS文件构成渲染树,
c: 边解析,边渲染 ,
d: JS 单线程运行,JS有可能修改DOM结构,意味着JS执行完成前,后续全部资源的下载是没有必要的,因此JS是单线程,会阻塞后续资源下载
function Animal(name) {
this.name = name;
}
Animal.prototype.run = function() {
console.log(this.name + 'can run...');
}
var cat = new Animal('cat');
cat.run() //catcan run...
console.log(cat.__proto__ === Animal.prototype); // true复制代码
new共经历了四个过程。
var fn = function () { };
var fnObj = new fn();复制代码
一、建立了一个空对象
var obj = new object();复制代码
二、设置原型链
obj._proto_ = fn.prototype;复制代码
三、让fn的this指向obj,并执行fn的函数体
var result = fn.call(obj);复制代码
四、判断fn的返回值类型,若是是值类型,返回obj。若是是引用类型,就返回这个引用类型的对象。
if (typeof(result) == "object"){
fnObj = result;
} else {
fnObj = obj;
} 复制代码
为啥须要Symbol:
一个新规则的提出,必然是由于有需求,熟悉ES5的人都知道,ES5里面对象的属性名都是字符串,若是你须要使用一个别人提供的对象,你对这个对象有哪些属性也不是很清楚,但又想为这个对象新增一些属性,那么你新增的属性名就极可能和原来的属性名发送冲突,显然咱们是不但愿这种状况发生的。因此,咱们须要确保每一个属性名都是独一无二的,这样就能够防止属性名的冲突了。所以,ES6里就引入了Symbol,用它来产生一个独一无二的值。
Symbol是什么:
Symbol其实是ES6引入的一种原始数据类型,除了Symbol,JavaScript还有其余5种数据类型,分别是Undefined、Null、Boolean、String、Number,这5种数据类型都是ES5中就有的。
怎么生成一个Symbol类型的值:
既然咱们已经知道了Symbol是一种原始的数据类型,那么怎么生成这种数据类型的值呢?Symbol值是经过Symbol函数生成的,以下:
let s = Symbol();
console.log(s); // Symbol()
typeof s; // "symbol"复制代码
上面代码中,s就是一个Symbol类型的值,它是独一无二的。
Symbol函数前不能用new
Symbol函数不是一个构造函数,前面不能用new操做符。因此Symbol类型的值也不是一个对象,不能添加任何属性,它只是一个相似于字符型的数据类型。若是强行在Symbol函数前加上new操做符,会报错,以下:
let s = new Symbol();// Uncaught TypeError: Symbol is not a constructor(…)复制代码
Symbol函数的参数
1.字符串做为参数
用上面的方法生成的Symbol值很差进行区分,Symbol函数还能够接受一个字符串参数,来对产生的Symbol值进行描述,方便咱们区分不一样的Symbol值。
let s1 = Symbol('s1');
let s2 = Symbol('s2');
console.log(s1); // Symbol(s1)
console.log(s2); // Symbol(s2)
s1 === s2; // false
let s3 = Symbol('s2');
s2 === s3; // false
复制代码
从上面代码能够看出:
2.对象做为参数
若是Symbol函数的参数是一个对象,就会调用该对象的toString方法,将其转化为一个字符串,而后才生成一个Symbol值。因此,说到底,Symbol函数的参数只能是字符串。
set数据集合:相似于数组
set:是一个集合,相似于数组,与数组的主要区别是没有重复的元素。主要的做用能够进行去重。
重点:一个属性,四个方法
一、size属性:返回set数组的长度,相似于lenght
二、四个方法:add,delete,clear,has
let set =new Set([1,2,3,4,1]);
console.log(set.size); //4
//添加元素
set.add(7);
console.log(set); // {1, 2, 3, 4, 7}
//删除数组中的某个元素
set.delete(3);
console.log(set); // {1, 2, 4, 7}
//检测数组中是否含有某个元素,返回值为布尔
console.log(set.has(2)); //true
//清空数组
set.clear();
console.log(set) //{}复制代码
map数据集合:相似于对象
let obj={a:1};
const map = new Map([
['name','java'],
['feel','今每天气贼好'],
['jieguo','适合看帅哥'],
[obj,'是!']
]);
console.log(map);//{"name" => "java", "feel" => "今每天气贼好", "jieguo" => "适合看帅哥", {…} => "是!"}复制代码
Map与Object的区别
一、Map的size属性能够直接获取键值对个数
二、二者都是对应的keys-value键值对, 可是Object的keys只能是字符串或者 Symbols, Map的key能够是任意值,好比数组、对象等
总结:Map 对象保存键值对。一个对象的键只能是字符串或者 Symbols,但一个 Map 的键能够是任意值。 Set 对象容许你存储任何类型的惟一值,Set对象是值的集合,Set中的元素只会出现一次 Symbol 是一种特殊的、不可变的数据类型,能够做为对象属性的标识符使用(Symbol([description]) )
<template>
<div>
{{list[2]}}
</div>
</template>
<script>
export default {
data() {
return {
list: [1,3,5]
}
},
methods: {
},
components: {
},
computed: {
},
mounted() {
this.list[2] = 8 // 没有效果
this.$set(this.list, 2, 8); //有效果
}
}
</script>复制代码
对于set这个方法的解释:
this.$set(数组或者对象,修改的下标或者对象属性名,修改的值)
一、不改变原数组,返回新数组
一、concat() 链接两个或多个数组,两边的原始数组都不会变化,返回被链接数组的一个副本。
二、join() 把数组中全部元素放入一个字符串中,返回字符串。
三、slice() 从开始到结束(不包括结束)选择数组的一部分浅拷贝到一个新数组。
四、map() 建立一个新数组并返回,其中新数组的每一个元素由调用原始数组中的每个元素执行提供的函数得来,原始数组不会改变。
五、every() 对数组中的每一个元素都执行一次指定的回调函数,直到回调函数返回false,此时every()返回false并再也不继续执行。若是回调函数对每一个元素都返回true,那么every()将返回true。
六、some() 对数组中的每一个元素都执行一次指定的回调函数,直到回调函数返回true,此时some()返回true并再也不继续执行。若是回调函数对每一个元素都返回false,那么some()将返回false。
七、filter() 建立一个新数组, 其包含经过所提供函数实现的测试的全部元素。
二、改变原数组
一、forEach() 针对每个元素执行提供的函数。会修改原来的数组,不会返回执行结果,返回undefined。
二、pop() 删除数组最后一个元素,返回被删除的元素。若是数组为空,则不改变数组,返回undefined。
三、push() 向数组末尾添加一个或多个元素,返回改变后数组的长度。 reverse() 颠倒数组中元素的位置,返回该数组的引用。
四、shift() 从数组中删除第一个元素,并返回该元素的值。此方法更改数组的长度。
五、unshift() 将一个或多个元素添加到数组的开头,并返回新数组的长度。
六、sort() 对数组的元素进行排序,并返回数组。排序不必定是稳定的。默认排序顺序是根据字符串Unicode码点。
七、splice() 向数组中添加/删除项目,而后返回被删除项目的新数组
1.GET使用URL或Cookie传参,而POST将数据放在BODY中,这个是由于HTTP协议用法的约定。并不是它们的自己区别。
4.GET和POST最大的区别主要是GET请求是幂等性的,POST请求不是。(幂等性:对同一URL的多个请求应该返回一样的结果。)由于get请求是幂等的,在网络很差的隧道中会尝试重试。若是用get请求增数据,会有重复操做的风险,而这种重复操做可能会致使反作用
一、全局的钩子
beforeEach(to,from,next)(全局前置守卫) 页面加载以前
afterEach(to,from,next)(全局后置守卫) 页面加载以后,有两个参数:to/from ,全局后置钩子在全部路由跳转结束的时候调用这些钩子不会接受 next 函数也不会改变导航自己
vue router.beforeResolve(全局解析守卫)注:beforeEach和afterEach都是vue-router实例对象的属性,每次跳转前,beforeEach和afterEach都会执行。
二、组建内的导航钩子:
beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave,直接在路由组件内部进行定义的
beforeRouteEnter(to, from, next) :
在渲染该组件的对应路由被确认
前调用,用法和参数与beforeEach相似,next须要被主动调用
注意:
beforeRouteEnter (to, from, next) {
// 这里还没法访问到组件实例,this === undefined
next( vm => {
// 经过 `vm` 访问组件实例
})
}复制代码
vm
访问组件实例进行赋值等操做beforeRouteUpdate (to, from, next) :
在当前路由改变,而且该组件被复用时调用,能够经过this访问实例, next须要被主动调用,不能传回调
beforeRouteLeave (to, from, next) :
航离开该组件的对应路由时调用,能够访问组件实例 this
,next须要被主动调用,不能传回调 用途:清除当前组件中的定时器,避免占用内存;当页面中有未关闭的窗口, 或未保存的内容时, 阻止页面跳转;保存相关内容到Vuex中或Session中
三、路由内的导航钩子
主要用于写某个指定路由跳转时须要执行的逻辑有三个参数:to/from/next
{
path:'/',
name:'Login',
component:Login,
beforeEnter:(to,from,next)=>{
consloe.log("即将进入Login");
next();
}
}复制代码