答案:true true falsejavascript
第一个输出:将[]转为布尔类型,为true。只有如下类型转为布尔值为false:Boolean(null)、Boolean(undefined)、Boolean(0)、Boolean(‘’)、Boolean(NaN)css
第二和第三:其余类型与布尔类型比较,将两个类型转为数字再进行比较。Number([])=0,Number(false)=0,因此第二个输出true。Number({})=NaN,NaN==0 --> false,因此第三个输出falsehtml
Number([2]) //2
Number(['x']) //NaN
Number(undefined); //NaN
Number(null); //0
null+1=1
undefined+1=NaN
undefined==false; //false
false=='' //true
null==undefined; //true,都转为boolean类型
null=undefined; //false,不强制转换
复制代码
HTML部分
<form action="" id='form'>
<label for="">
姓名: <input type="text" name="name">
</label>
<label for="">
文件:<input id="file" type="file" name="file">
</label>
<label for="">
<input type="button" value="保存">
</label>
</form>
复制代码
JS部分
var form=document.getElementById('form');
var formdata=new FormData(form);
var name=formdata.get(name);
var file=formdata.get(file);
formdata.append('token','sdcsdc');
//提交数据
var xhr=new XMLHttpRequest();
xhr.open("post","http://127.0.0.1/adv");
xhr.send(formdata);
xhr.onload=function(){
if(xhr.readyState==4&&xhr.status==200){
//...
}
}
复制代码
var formdata=new FormData();
formdata.append('name','jack');
formdata.append('name','rose');
formdata.get('name'); //jack
formdata.getAll('name'); //[jack,rose]
复制代码
formData.append("k1", "v1");
formData.append("k1", "v2");
formData.append("k2", "v1");
var i = formData.entries();
i.next(); // {done:false, value:["k1", "v1"]}
i.next(); // {done:fase, value:["k1", "v2"]}
i.next(); // {done:fase, value:["k2", "v1"]}
i.next(); // {done:true, value:undefined}
//返回的对象的value属性以数组的形式,第一个是key,第二个为value
//以循环的形式
for(var pair of formData.entries()){ //pair是每次迭代获取到的对象里的value
console.log(pair[0]+':'+pair[1])
//k1:v1
//k1:v2
//k2:v1
}
复制代码
js垃圾回收机制前端
答案:vue
下面输出结果是:java
var out = 25,
inner = {
out: 20,
func: function () {
var out = 30;
return this.out;
}
};
console.log((inner.func, inner.func)());
console.log(inner.func());
console.log((inner.func)());
console.log((inner.func = inner.func)());
复制代码
结果:25 20 20 25node
例子webpack
let a=1,b=2,k=0;
console.log( (a++,b++,k++) ); //输出0,注意是要把整个括起来,而后将计算后的结果返回
console.log(a); //2
console.log(b); //3
console.log(k); //1
复制代码
例子:ios
let a=3,b;
console.log(b=a); //输出3
复制代码
var a = {n: 1};
var b = a;
a.x = a = {n: 2};
console.log(a.x);
console.log(b.x);
复制代码
答案:nginx
undefined
{n:2}
复制代码
分析:
const obj = new Proxy({}, {
set(target, key, value, r) {
console.log(key, value)
if (key === 'a') Reflect.set(target, key, 'isA', r);
else Reflect.set(target, key, value, r);
}
});
obj.b = obj.a= {n: 1};
// 输出:
// "a" {n: 1}
// "b" {n: 1}
obj.a; // isA
obj.b; // {n: 1}
复制代码
能够得出 赋值的顺序是从右边开始到左边的。
并且是直接 a = {n: 1}, a.x = {n:1 },而不是 a.x = a 这样去赋值
复制代码
再借助 Proxy 来分析一开始part1这道题,用obj.a, obj.b 来代替原题目的 a和b。
var obj = new Proxy({}, {
get: function (target, key, receiver) {
console.log(`getting ${key}!`);
return Reflect.get(target, key, receiver);
},
set: function (target, key, value, receiver) {
console.log(`setting ${key}!`);
return Reflect.set(target, key, value, receiver);
}
});
obj.a = {n: 1 };// setting a;
obj.b = obj.a; // getting a; setting b;
obj.a.x = obj.a = {n:2 }; // getting a; setting a;
*****************分割线**********************************
console.log(obj.a); //{n;2}
obj.a.n=3; //getting a
console.log(obj.b); //getting b {n:1,x:{n:3}}
复制代码
第一部分:
能够看到obj.a.x = obj.a = {n: 2}这段语句执行时,会先输出一个 getting a 再输出 setting a。
这就意味着在对 obj.a.x 赋值时,程序是先获取 obj.a指向的对象的内存地址,此时触发了 getting a,
而后再对右边 obj.a 进行赋值,触发了 setting a, 赋值完最后一步才是对 obj.a.x赋值 {n:2 }。
注意:最后一步对obj.a.x赋值时,不会再去读取obj的a,则说明此时的a还是指向原来的地址-- 对象{n:1}的地址,也是obj.b指向的地址。
赋值以后,此时obj.a(和obj.b再也不指向同一地址)和obj.b.x指向的是同一地址 -- {n:2}的地址
复制代码
第二部分:
在分割线之下,obj.a.n=3; 读取的a再赋值,改变了指向的对象{n:2}的值
而obj.b.x也指向该对象
复制代码
if (!("a" in window)) {
var a = 1;
}
alert(a); //undefined
复制代码
此时代码的意思是,若是没有这个属性,则建立这个属性且赋值。
但if不是代码块,存在变量提高,因此上面代码实际为
var a;
if (!("a" in window)) {
a = 1;
}
alert(a);
复制代码
函数内属于一个做用域,try和catch不属于一个做用域
(function f() {
try{
throw new Error()
}catch (x) {
var x=1,y=2;
var t=3;
console.log(x);
}
console.log(t); //3
console.log(x); //undefined
console.log(y); //2
})();
复制代码
在f函数中,x、y、t会变量提高
在catch中,var x=1,就近原则,此时对x的赋值实际上是对形参的赋值,不是f函数里的x,因此函数f的x在catch外访问依旧是undefined
复制代码
声明变量b是一个函数,使用函数表达式
一、let b=function(){}
二、let b=function f(){} //能够为函数起一个名字,可是'f'不属于window的属性,则没法调用f(),至关于匿名
下面程序的输出结果?
var a = 1;
var b = function a(x) {
x && a(--x);
};
alert(a); //1
console.log(b(a)) //undefined 由于函数没有return,有return的话就是0
/* 在函数外部,a是number类型,在外面调用函数a会出错,但在函数内部调用不会出错 上面其实等同于 var b=function(x){...} */
复制代码
例子:
function a(){console.log('function')}
var a;
console.log(typeof a) //function
//若是变量赋值了
function a(){console.log('function')}
var a=1;
console.log(typeof a) //number
复制代码
var a = 4;
function b() {
a = 3;
console.log(a);
function a(){};
}
b(); //3
console.log(a); //4
复制代码
函数b内的函数a会提高,而后被a=3覆盖了值,因此输出3
挖坑:
var a = 4;
function b() {
a = 3;
}
console.log(a); //4
复制代码
为何不是输出3?—— 由于没有调用b()....
string、number、boolean、undefined、object、function、symbol
null与undefined
JSON.stringify转化null与undefined
JSON.stringify({1:undefined}) //“{}”
JSON.stringify({0:null}) //“{“0”:null}”
复制代码
返回undefined的状况
symbol
var sm=Symbol('xx');
console.log(sm); //Symbol('xx')
typeof sm; //'symbol'
var ss1=Symbol('xx');
console.log(ss==ss1); //false
ss=ss1; //由于是基本数据类型,因此能够用=令两个值相等
console.log(ss===ss1); //true
复制代码
object、String、Number、Boolean、Math、Array、Function、JSON
String、Number、Boolean、Object、Function、RegExp、Error、Date、Array
Date.now():返回当前时间毫秒数
var dt=new Date();
dt.getFullYear(); //年
dt.getMonth(); //月(0-11)
dt.getDate(); //日(1-31),返回一个月中的某一天
dt.getDay(); //天(0-6),返回一星期中的第几天(0表示星期日)
dt.getHours() //小时(0-23)
dt.getMinutes() //分钟(0-59)
dt.getSeconds() //秒数(0-59)
dt.getTime() //返回从1970/1/1 至今的毫秒数
##有对应的set方法
复制代码
var a={},
b={key:'b'},
c={key:'c'};
a[b]=123;
a[c]=456;
console.log(a[b]); //456
复制代码
解析:
由于键的名称只能是字符串,b/c做为键会调用toString获得的都是[object Object],因此a[b]和a[c]都是a["[object Object]"]
复制代码
function fun1(fn){ //第二步
arguments[0](3,4); //类数组枚举调用传入的fun2(3,4)
}
function fun2(){
alert(this.length); //此时this是fun1的arguments,表示fun1传入的实参个数--5
alert(arguments.length); //此时表示fun2传入的实参个数-- 2
}
fun1(fun2,5,6,7,8); //第一步
复制代码
for(var i=0;i<5;i++){
setTimeout(console.log(i),0) //第一个参数不是函数
//0
//1
//2
//3
//4
}
复制代码
parseInt(string [,radix]):解析一个字符串参数,并返回一个指定基数的整数
参数:
返回值:
若是radix为undefined或0或未指定,
一、若是string以‘0x’或‘0X’开头,则基数是16(16进制)
二、若是string以‘0’开头,则基数是8或10进制,具体是哪一个进制由实现环境决定
三、若是字符串string以其余任何值开头,则基数是10
复制代码
例子:
['10','10','10','10','10'].map(parseInt);
// [10, NaN, 2, 3, 4]
复制代码
实际执行的代码是:
['10','10','10','10','10'].map((item, index) => {
return parseInt(item, index)
})
//第一个radix为0,属于上面所说的状况,此时看item,不是以0或0x开头,则基数是10,10的十进制就是10
//第二个 10,radix是1,表示10是1进制表示的数字,错误
....
复制代码
1)主线程发起一个异步请求
2)相应的工做线程接收到请求后告知主线程已收到(异步函数返回)
3)主线程能够继续执行下面的代码,同时工做线程执行异步任务。
4)等到工做线程完成任务后,通知主线程
5)主线程收到通知后,执行必定动做(调用回调函数)。
JS引擎中负责解释和运行js代码的只有一个,叫作主线程,还有其余线程,例如处理Ajax请求的线程,处理DOM事件的线程,定时器线程等工做线程(可能存在与js引擎内或外)。
综上,能够总结为两个要素:
一、发起函数(注册函数)-- 用来发起异步过程
二、执行回调函数 -- 用来处理结果
两个函数都是在主线程上调用的
异步任务执行完毕后须要通知主线程,这个通知机制是如何实现的?—— 消息队列和事件循环
异步任务执行完毕后,工做线程将回调函数封装成消息message放到消息队列中,主线程经过事件循环过程去取出消息
例子:
setTimeout(fn,2000)
复制代码
setTimeout就是发起异步过程的函数,fn就是回调函数,可是回调函数并不必定要做为发起函数的参数,如:
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = xxx; // 添加回调函数
xhr.open('GET', url); //发起异步请求的函数
xhr.send(); // 发起函数
复制代码
除了广义的同步任务和异步任务以外,还有宏任务和微任务
不一样的任务会进入不一样的事件/任务队列,好比setTimeout
和setInterval
会进入相同的Event Queue。
先执行宏任务队列的一个,而后再执行宏任务下的全部微任务,再去渲染,完成一次事件循环。而后再执行下一个宏任务。
事件循环的一次循环,就是执行事件队列里的一个任务。先执行宏任务,再执行宏任务里的微任务,这样就是一次循环,第一轮循环执行后,在执行下一次循环
例子
console.log('1');
setTimeout(function() {
console.log('2');
process.nextTick(function() {
console.log('3');
})
new Promise(function(resolve) {
console.log('4');
resolve();
}).then(function() {
console.log('5')
})
})
process.nextTick(function() {
console.log('6');
})
new Promise(function(resolve) {
console.log('7');
resolve();
}).then(function() {
console.log('8')
})
setTimeout(function() {
console.log('9');
process.nextTick(function() {
console.log('10');
})
new Promise(function(resolve) {
console.log('11');
resolve();
}).then(function() {
console.log('12')
})
})
复制代码
结果:1,7,6,8,2,4,3,5,9,11,10,12。
第一轮事件循环:
第二轮事件循环:
第三轮事件循环:
例子
process.nextTick(() => { //微任务
console.log('nextTick')
})
Promise.resolve()
.then(() => { //微任务
console.log('then')
})
setImmediate(() => { //宏任务
console.log('setImmediate')
})
console.log('end')
复制代码
运行结果:
end
nextTick
then
setImmediate
复制代码
微任务里的微任务
process.nextTick(()=>{
console.log(1)
process.nextTick(()=>{
console.log(2)
})
})
process.nextTick(()=>{
console.log(3)
})
console.log('run')
复制代码
run
1
3
2
复制代码
promise是异步编程的解决方案,比传统的异步编程解决方案【事件】和【回调函数】更加合理和强大。
promise对象用于异步操做,它表示未完成且预计将来完成的异步操做
使异步变同步
async function timeout() {
return 'hello world'
}
console.log(timeout());
console.log('虽然在后面,可是我先执行');
复制代码
结果
Promise {<resolved>: "hello world"}
test.html:85 虽然在后面,可是我先执行
复制代码
获取Promise的值
async function timeout() {
return 'hello world'
}
timeout().then(result => {
console.log(result);
})
console.log('虽然在后面,可是我先执行');
复制代码
结果
虽然在后面,可是我先执行
hello world
复制代码
await
function doubleAfter2seconds(num) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(2 * num)
}, 2000);
} )
}
async function testResult() {
let result = await doubleAfter2seconds(30);
console.log(result); //60
}
testResult();
复制代码
var xhr=new XMLHttpRequest();
xhr.open('get','/index.js',true) //true表示异步
xhr.onreadystatechange=function () {
if(xhr.readyState===4)
if(xhr.status===200){
console.log(xhr.responseText)
}
}
xhr.send(null); //将请求发送给服务端,仅用于post请求
复制代码
xhr.open()的第三个参数表示是否异步
XMLHttpRequest 对象若是要用于 AJAX 的话,其 open() 方法的 async 参数必须设置为 true
xhr.send():当使用初始化)send方法还未调用
1:(载入)已调用send方法,正在发送请求
2:(载入完成)send方法已经执行完毕,已经接收到全部响应内容
3:(交互)正在解析响应内容
4:(完成)响应内容解析完成,能够在客户端调用
复制代码
cookie
storage
共同点:
数据有效时间
做用域不一样:
DOMContentLoaded:顾名思义,就是dom内容加载完毕。那什么是dom内容加载完毕呢?咱们从打开一个网页提及。当输入一个URL,页面的展现首先是空白的,而后过一会,页面会展现出内容,可是页面的有些资源好比说图片资源还没法看到,此时页面是能够正常的交互,过一段时间后,图片才完成显示在页面。从页面空白到展现出页面内容,会触发DOMContentLoaded事件。而这段时间就是HTML文档被加载和解析完成。
onload:页面上全部的资源(图片,音频,视频等)被加载之后才会触发load事件
var str='https://www.bilibili.com/text.html'
console.log(escape(str));
console.log(encodeURI(str));
console.log(encodeURIComponent(str));
encodeURI("http://www.w3school.com.cn/My first/")
复制代码
输出
https%3A//www.bilibili.com/text.html
test.html:59 https://www.bilibili.com/text.html
test.html:60 https%3A%2F%2Fwww.bilibili.com%2Ftext.html
http://www.w3school.com.cn/My%20first/
复制代码
所以,defer与async的区别就是js加载以后什么时候执行:
它们在加载脚本都是异步执行
async在加载以后就会当即执行
若是存在多个有defer属性的脚本,那么它们是按照加载顺序执行脚本的
都只适用于外部脚本文件,对与内联的 script 标签是不起做用
复制代码
因此若是必定要将script放在头部,但又不想阻塞页面的加载,则加上defer属性
function myBrowser() {
var userAgent = navigator.userAgent; //取得浏览器的userAgent字符串
var isOpera = userAgent.indexOf("Opera") > -1;
if (isOpera) {
return "Opera"
}; //判断是否Opera浏览器
if (userAgent.indexOf("Firefox") > -1) {
return "Firefox";
} //判断是否Firefox浏览器
if (userAgent.indexOf("Chrome") > -1) {
return "Chrome";
} //判断是否Google浏览器
if (userAgent.indexOf("Safari") > -1) {
return "Safari";
} //判断是否Safari浏览器
if (userAgent.indexOf("compatible") > -1 && userAgent.indexOf("MSIE") > -1 && !isOpera) {
return "IE";
}; //判断是否IE浏览器
}
复制代码
假设:一个英文字符占用一个字节,一个中文字符占用两个字节
function getBytes(str){
var len = str.length; //获取字符串长度
var bytes = len;
for(var i = 0; i < len; i++){
if (str.charCodeAt(i) > 255) bytes++; //中文则+1
}
return bytes;
}
alert(getBytes("你好,as"));
复制代码
function f(){}
typeof f.prototype; //"object"
typeof f.__proto__; //"function"
f.__proto__===Function.prototype //true
typeof Function.prototype; //"function"
复制代码
web 前端是应用服务器处理以前的部分,前端主要包括:HTML、CSS、javascript、image 等各类资源,针对不一样的资源有不一样的优化方式。
内容优化
服务器优化
CSS 优化
javascript 优化
图像优化
应用:将节点列表 (NodeList) 转换为数组
若是你运行 document.querySelectorAll("p") 方法,它可能会返回一个 DOM 元素的数组 — 节点列表对象。 但这个对象并不具备数组的所有方法,如 sort(),reduce(), map(),filter()。 为了使用数组的那些方法,你须要把它转换为数组。
var elements = document.querySelectorAll("p"); // NodeList
var arrayElements = [].slice.call(elements); // 如今 NodeList 是一个数组
var arrayElements = Array.from(elements); // 这是另外一种转换 NodeList 到 Array 的方法
复制代码
例子
var a4 = { a: 1 }
var a3 = Object.create(a4);
console.log("a3.__proto__:", a3.__proto__); // Object {a: 1}
console.log(oo.constructor.prototype==Object.prototype)//true
console.log(a3.__proto__ === a3.constructor.prototype); //false
//通常obj.__proto__=obj.constructor.prototype,除了Object.create()建立的对象
复制代码
function out(x){
return function () {
let ins='inside'
console.log(x)
}
}
let inside=out('xx');
inside();
复制代码
打断点以后能看到Scopes里的closure对象就是闭包,里面保存着引用的变量
在JavaScript中,有一个很重要的安全性限制,被称为同源策略。这一策略对于JavaScript代码可以访问的页面内容作了很重要的限制,即JavaScript只能访问与包含它的文档在同一域下(协议+域名+端口号相同)的内容。
script、image、link都是能跨域的,是发送get请求。在页面上有三种资源是能够与页面自己不一样源的。它们是:js脚本,css样式文件,图片
总结:
浏览器将CORS请求分为两类:简单请求和非简单请求,对于非简单请求,在正式通讯以前,增长一次HTTP查询请求,称为“预检”请求。
简单请求:
一、get、post、head
二、HTTP头信息不超过如下几种字段:Accept、Accept-Language、Content-Language、Content-Type(application/x-www-form-urlencoded 或 multipart/form-data 或 text/plain)
复制代码
非简单请求:
对服务器有特殊要求的请求,好比put、delete,或者Content-Type类型是application/json
复制代码
util.js文件
//一、先定义后导出
let num1=1; let num2=2;
export {num1,num2};
//二、直接导出定义的变量
export var num=1;
export var num2=2;
main.js
//接收
import {num1,num2} from 'util.js'
//能够起别名
import {num1:n1,num2:n2} from 'util.js'
//能够一次性全接收
import * as obj from 'util.js';
console.log(obj.num1,obj.num2);
---------------------------
util.js文件
//三、导出默认数据
export default const obj={}
main.js
//接收
import data from 'util.js'; //能够随意命名一个变量
-------------------------------
//同时导出默认的和部分的,export default只能使用一次
export default const example3 = {
birthday : '2018 09 20'
}
export let name = 'my name'
export let age = 'my age'
export let getName = function(){ return 'my name'}
// 导入默认与部分
import example3, {name, age} from './example1.js'
复制代码
用ES5实现迭代器的建立
function generator(arr) {
let i=0;
return {
next(){
let done=(i>=arr.length);
let value=!done? arr[i++]:undefined
return{done,value}
}
}
}
let iterator=generator([1,2,3]); //获得迭代器对象
let obj=iterator.next();
while (!obj.done) {
console.log(obj.value);
obj=iterator.next();
}
复制代码
用ES6的生成器建立迭代器,迭代数组
function *Generator(arr) {
for(let i=0;i<arr.length;i++){
yield arr[i]
}
}
let it=Generator([1,2,3]);
let obj_=it.next();
while (!obj_.done){
console.log(obj_.value);
obj_=it.next()
}
复制代码
可迭代对象有:数组、字符串、map、set对象,这些都是含有Symbol.iterator属性
es6模块
export const person = {
name: 'lc',
friends: ['xm','xh']
};
setTimeout(() => person.name ='liangcheng', 500);
复制代码
commonjs模块
var dog = {
name: 'xsz', // 不要猜想是谁的名字
owner: ['lc']
};
module.exports = dog;
setTimeout(() => dog.name = 'xushizhou' ,500);
复制代码
引用模块
import { person } from './a1';
const dog = require('./a2');
setTimeout(() => console.log(person.name, dog.name), 1000); //liangcheng xushizhou
复制代码
三要素:封装、继承、多态
# 1.封装
// 假设须要登记学籍,分别记录小明和小红的学籍号、姓名
let name1 = "小明"
let num1 = "030578001"
let name2 = "小红"
let num2 = "030578002"
// 若是须要登记大量的数据,则弊端会很是明显,并且很差维护,那么咱们会使用如下方法来登陆,这也是面向对象的特性之一:封装
let p1 = {
name:"小明",
num:"030578001"
}
let p2 = {
name:"小红",
num:"030578002"
}
# 2.继承
// 从已有的对象上,获取属性、方法
function Person(){
this.name = "邵威儒"
}
Person.prototype.eat = function(){
console.log("吃饭")
}
let p1 = new Person()
p1.eat() // 吃饭
let p2 = new Person()
p2.eat() // 吃饭
# 3.多态
// 同一操做,针对不一样对象,会有不一样的结果
let arr = [1,2,3]
arr.toString() // 1,2,3
let obj = new Object()
obj.toString() // [object Object]
复制代码
总结: 面向对象的底层仍是面向过程,面向过程抽象成类,而后封装,方便使用就是面向对象。
(Parent.apply(this))
class Parent{
constructor(){
this.x='parent'
}
}
class Son extends Parent{
constructor(){
var s=super()
console.log(s === this); //true
}
}
var son=new Son()
复制代码
好处:
坏处:
$.fn.repaint = function () {
this.each(function (item) {
return item.clientHeight;
});
}
复制代码
CSS3动画与javascript模拟动画有如下区别:
做用:使前端实现模块化开发,可以使用require与exports,export与import
优势:
原理:一切皆为模块,因为 webpack 并不支持除 .js 之外的文件,从而须要使用 loader 转换成 webpack 支持的模块,plugin插件用于扩展 webpack 的功能,在 webpack 构建生命周期的过程在合适的时机作了合适的事情。
webpack 从构建到输出文件结果的过程
vue.js是经过Object.defineProperty以及发布订阅模式来进行数据劫持和监听
v-model实际是个语法糖,原理是用v-bind绑定表单组件的value/selected/checked,和v-on绑定input/change事件。一般用在表单控件的双向绑定。
对于添加了v-model指令的组件(若是含表单控件input),会默认利用value和input事件。但若是是其余类型的表单控件checkbox,就会利用checked和change事件,因此为了通用性,组件将会有model对象属性,定义了prop和event属性来定义绑定的值和事件类型
<base-checkbox v-model="lovingVue"></base-checkbox>
复制代码
<base-checkbox v-bind:checked="lovingVue" v-on:change="lovingVue=arguments[0]"></base-checkbox>
复制代码
Vue.component('base-checkbox', {
model: {
prop: 'checked',
event: 'change'
},
props: {
checked: Boolean
},
template: `
<input
type="checkbox"
v-bind:checked="checked"
v-on:change="$emit('change', $event.target.checked)"
//checkbox表单控件本身有change事件,它的事件监听函数触发的是这个vue组件的change事件,是父组件绑定的事件,两个change事件是属于不一样对象的
>
`
})
复制代码
综上:父组件的lovingVue传递给子组件,子组件用checked接收,注意在子组件要在props属性显示指出checked这个prop。当子组件的表单控件发生变化,触发父组件绑定的事件,将表单控件的值做为事件函数的参数传递过去,从而改变父组件的lovingVue值。实现子组件更新父组件的值。
相同:二者都是能监听/依赖一个数据,并进行相应的处理
computed:相似于过滤器,对绑定到视图的数据进行处理
watch:watch是一个侦听的动做,用来观察和响应 Vue 实例上的数据变更
<div id="watch-example">
<p>
Ask a yes/no question:
<input v-model="question">
</p>
<p>{{ answer }}</p>
</div>
复制代码
watch执行异步操做,限制咱们执行该操做的频率,并在咱们获得最终结果前,设置中间状态。这些都是计算属性没法作到的:
<!-- 由于 AJAX 库和通用工具的生态已经至关丰富,Vue 核心代码没有重复 -->
<!-- 提供这些功能以保持精简。这也可让你自由选择本身更熟悉的工具。 --> <script src="https://cdn.jsdelivr.net/npm/axios@0.12.0/dist/axios.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/lodash@4.13.1/lodash.min.js"></script> <script> var watchExampleVM = new Vue({ el: '#watch-example', data: { question: '', answer: 'I cannot give you an answer until you ask a question!' }, watch: { // 若是 `question` 发生改变,这个函数就会运行 question: function (newQuestion, oldQuestion) { this.answer = 'Waiting for you to stop typing...' this.debouncedGetAnswer() } }, created: function () { // `_.debounce` 是一个经过 Lodash 限制操做频率的函数。 // 在这个例子中,咱们但愿限制访问 yesno.wtf/api 的频率 // AJAX 请求直到用户输入完毕才会发出。想要了解更多关于 // `_.debounce` 函数 (及其近亲 `_.throttle`) 的知识, // 请参考:https://lodash.com/docs#debounce this.debouncedGetAnswer = _.debounce(this.getAnswer, 500) }, methods: { getAnswer: function () { if (this.question.indexOf('?') === -1) { this.answer = 'Questions usually contain a question mark. ;-)' return } this.answer = 'Thinking...' var vm = this axios.get('https://yesno.wtf/api') .then(function (response) { vm.answer = _.capitalize(response.data.answer) }) .catch(function (error) { vm.answer = 'Error! Could not reach the API. ' + error }) } } }) </script> 复制代码
总结:computed主要用于对同步数据的处理,watch则主要用于观测某个值的变化去完成一段开销较大的复杂业务逻辑。
有两种常见的试图改变一个prop的情形:
props: ['initialCounter'],
data: function () {
return {
counter: this.initialCounter
}
}
复制代码
props: ['size'],
computed: {
normalizedSize: function () {
return this.size.trim().toLowerCase()
}
}
复制代码
不能。
虽然Object.defineProperty()能够监听到数组下标的变化,
可是为了性能优化,vue源码并无使用Object.defineProperty()来劫持数组类型的数据:
复制代码
如何解决?
难点:比较两棵虚拟DOM树的差别
比较两棵 DOM 树的差别是 Virtual DOM 算法最核心的部分,这也是所谓的 Virtual DOM 的 diff 算法。
因此 Virtual DOM 只会对同一个层级的元素进行对比:
深度优先遍历,记录差别
在实际的代码中,会对新旧两棵树进行一个深度优先的遍历,这样每一个节点都会有一个惟一的标记:
在深度优先遍历的时候,每遍历到一个节点就把该节点和新的的树进行对比。若是有差别的话就记录到一个对象里面。
new一个Vue实例时,实例只有默认的事件和生命周期函数
最先能访问到this是:beforeCreate函数
在beforeCreate函数以后created以前:data、methods、watch、computed属性进行初始化
在created函数能访问data、methods、watch、computed属性,除了el属性
在created函数以后,beforeMount函数以前:进行模板编译
beforeMounte函数:此时内存已构建出虚拟的dom树,视图还未更新,不能访问dom上一些动态变量,此时访问el还是未各类表达式{{}}
beforeMounte函数以后:建立vm.$el,与el进行替换
Mounted函数:已将虚拟dom挂载到页面上,能访问dom上的动态变量
//父组件
<child>
<div slot="xxx">父组件动态插入数据</div>
<div slot="yyy">父组件动态插入数据</div>
</child>
//子组件
<template>
<div>
<slot name='xxx'></slot>
<div>组件肯定的标签结构</div>
<slot name='yyy'></slot>
</div>
</template>
复制代码
// component-a 子组件
export default {
data () {
return {
title: 'Vue.js'
}
},
methods: {
sayHello () {
window.alert('Hello');
}
}
}
复制代码
// 父组件
<template>
<component-a ref="comA"></component-a>
</template>
<script>
export default {
mounted () {
const comA = this.$refs.comA;
console.log(comA.title); // Vue.js
comA.sayHello(); // 弹窗
}
}
</script>
复制代码
//event-bus.js文件
export default const install=function(Vue){
Vue.prototype.$bus=new Vue({
methods:{
emit(event,...args){
this.$emit(event,...args);
},
on(event,callback){
this.$on(event,callback);
},
off(event,callback){
this.$off(event,callback)
}
}
})
}
复制代码
在入口文件main.js引入插件
import bus from 'event-bus.js'
Vue.use(bus)
复制代码
使用$bus绑定事件this.$bus.on()要注意:
一、绑定在哪一个生命周期函数,才能确保在其余组件触发事件以前已经绑定好事件,即组件间生命周期函数的执行顺序
二、在组件销毁以前将事件解除,不然当再有组件绑定该事件,添加事件监听函数时,此时事件的监听函数还保留着以前的事件监听函数,即会触发多个回调。
vuex
pubsub消息发布/订阅
//订阅消息
mounted: {
PubSub.subscribe("deleteTodo",(messageName, todosIndex)=>{
this.deleteTodo(todosIndex);
});
//发布消息
methods: {
PubSub.publish("deleteTodo", this.index);
}
复制代码
多级组件嵌套须要传递数据时,一般使用的方法是经过vuex。但若是仅仅是传递数据,而不作中间处理,使用 vuex 处理,未免有点大材小用。
$attrs
:包含了从父组件接收的(来自父做用域的),但props没有定义的数据 (class 和 style 除外)。 当一个组件没有声明任何 prop 时,$attrs这里会包含全部父做用域的绑定 (class 和 style 除外),而且能够经过 v-bind="$attrs" 传入内部组件 这是惟一一次v-bind直接与=相连,通常要传给子组件且props能接收的要v-bind:变量名=数据
$listeners
:包含了父做用域中的 (不含 .native 修饰器的) v-on 事件监听器。它能够经过 v-on="$listeners" 传入内部组件
总结:
父向子传递数据:props,$attrs/$listeners、slot(传递的是标签数据)
子向父传递数据:事件绑定和触发$emit事件回调,$ref获取组件实例
兄弟间:bus,vuex,pubsub订阅发布消息
跨级通讯:bus,vuex,pubsub订阅发布消息,$attrs/$listeners
任意级别通讯:bus、vuex、pubsub订阅发布消息
一、state是什么?
二、mapState辅助函数 mapState是语法糖,不用在computed里屡次定义函数,而后返回不一样的this.$store.state.数据名,能够直接将store的state直接添加到computed里
import { mapState } from 'vuex'
...
computed: mapState({
count: 'count', // 第一种写法,count为state里定义的,后面的count能够省略
sex: (state) => state.sex, // 第二种写法
from: function (state) { // 用普通函数this指向vue实例,要注意
return this.str + ':' + state.from
},
// 注意下面的写法看起来和上面相同,事实上箭头函数的this指针并无指向vue实例,所以不要滥用箭头函数
// from: (state) => this.str + ':' + state.from
//也能够在mapState里定义不使用到state的函数
myCmpted: function () {
// 这里不须要state,测试一下computed的原有用法
return '测试' + this.str
}
})
复制代码
三、...mapState
computed:{
//原来的继续保留
fn1(){ return ...},
fn2(){ return ...},
fn3(){ return ...}
......
//再维护vuex
...mapState( //这里的...不是省略号了,是对象扩展符
[ count ]
)
}
复制代码
语法:
Vue.directive('指令名',{
bind(el,binding,vnode){
//一开始绑定时调用,只调用一次
//el:绑定指令的dom元素,binding传递的是绑定的值,存储的是对象
},
update(el,binding,vnode){
//绑定的数据发生更新时调用,0-屡次
}
})
复制代码
<p v-focus:top="10"></p>
Vue.directive('focus',{
bind(el,binding,vnode){
el.style[binding[arg]]=binding[value]+'px'
},
update(el,binding,vnode){
//绑定的数据发生更新时调用,0-屡次
}
})
复制代码
directives:{
focus:{
binding(el,binding){
}
}
}
复制代码
当Vue用 v-for 正在更新已渲染过的元素列表是,它默认用“就地复用”策略。若是数据项的顺序被改变,Vue将不是移动DOM元素来匹配数据项的改变,而是简单复用此处每一个元素,而且确保它在特定索引下显示已被渲染过的每一个元素。
一、当使用路由参数切换路由,实现同一个路由路径+不一样的参数来展现不一样的内容,即:先后都是同一组件,但显示的内容不同
watch:{
$route(to,from){
//to:就是切换后的路由
//根据需求更改数据
}
}
复制代码
二、使用路由切换组件时,一切换以前的组件就会被销毁,再次切换到该组件时,以前的状态不会保存,由于是再次建立的
vue-router 提供的导航守卫主要用来经过跳转或取消的方式守卫导航。 有多种机会植入路由导航过程当中:全局的, 单个路由独享的, 或者组件级的。
展现一种全局的,更多的查看连接:
定义路由的时候能够配置 meta 字段,属性值是对象
meta:能够与上面的导航守卫搭配使用,好比切换到网站的主页路由时,须要用户登陆才可成功跳转,显示组件,那就在该路由加配置meta字段,当跳转路由时,就会触发router.beforeEach钩子函数,在里面进行对路由的meta里自定义的一些标志位的判断,再决定是否放行。
SPA:仅在Web页面初始化时加载相应的HTML、JavaScript、CSS,加载完成后,不会由于用户的操做而进行页面的从新加载或跳转。取而代之的是利用路由机制来实现HTML内容的变换,避免页面的从新加载。
优势:
缺点:
服务端渲染SSR的优缺点:
在vue中,不能检测到如下对数组的操做:
缘由?
vue只能监听如下八种对数组操做的方法:
push()
pop()
shift()
unshift()
splice()
sort()
reverse()
复制代码
vue中为了对对象的每层属性都实现监听,须要不断遍历对象的属性,使用Object.defineProperty()来实现数据劫持,则Object.defineProperty()只能劫持数据的属性,若是能直接劫持一个完整的对象而非对象上的属性,则无需层层遍历属性了。
Proxy:
Proxy 能够理解成,在目标对象以前架设一层“拦截”,外界对该对象的访问,都必须先经过这层拦截,所以提供了一种机制,能够对外界的访问进行过滤和改写。Proxy 这个词的原意是代理,用在这里表示由它来“代理”某些操做,能够译为“代理器”。
var proxy=new Proxy(target,handler)
参数:
返回值:
let obj = {};
let handler = {
get(target, property) {
console.log(`${property} 被读取`);
return property in target ? target[property] : 3;
},
set(target, property, value) {
console.log(`${property} 被设置为 ${value}`);
target[property] = value;
}
}
let p = new Proxy(obj, handler);
p.name = 'tom' //name 被设置为 tom
p.age; //age 被读取 3
复制代码
p 读取属性的值时,实际上执行的是 handler.get() :在控制台输出信息,而且读取被代理对象 obj 的属性。
p 设置属性值时,实际上执行的是 handler.set() :在控制台输出信息,而且设置被代理对象 obj 的属性的值。
handler能够定义多种拦截的函数:Proxy
综上:取代Object.defineProperty()的Proxy有如下两个优势
能直接劫持整个对象,无需经过递归和遍历data对象来实现对数据的监控
有多种劫持操做
复制代码