- 合理的title、description、keywords:搜索对着三项的权重逐个减小,title值强调重点即可,重要关键词出现不要超过2次,而且要靠前,不同页面title要有所不同;description把页面内容高度概括,长度合适,不可过分堆砌关键词,不同页面description有所不同;keywords列举出重要关键词即可
- 语义化的HTML代码,符合W3C规范:语义化代码让搜索引擎容易理解网页
- 重要内容HTML代码放在最前:搜索引擎抓取HTML顺序是从上到下,有的搜索引擎对抓取长度有限制,保证重要内容一定会被抓取
- 重要内容不要用js输出:爬虫不会执行js获取内容
- 少用iframe:搜索引擎不会抓取iframe中的内容
- 非装饰性图片必须加alt
- 提高网站速度:网站速度是搜索引擎排序的一个重要指标
- title通常当鼠标滑动到元素上的时候显示
- alt是img的特有属性,是图片内容的等价描述,用于图片无法加载时显示、读屏器阅读图片。可提图片高可访问性,除了纯装饰图片外都必须设置有意义的值,搜索引擎会重点分析。
GET方法:
发送一个请求来取得服务器上的某一资源
POST方法:
向URL指定的资源提交数据或附加新的数据
PUT方法:
跟POST方法很像,也是想服务器提交数据。但是,它们之间有不同。PUT指定了资源在服务器上的位置,而POST没有
HEAD方法:
只请求页面的首部
- 浏览器将请求的url地址交给DNS域名解析,找到真实的IP,向服务器发起请求
- 服务器交给后台处理完成后返回数据,浏览器接收文件(HTML,JS,CSS);
- 浏览器对加载到的资源(HTML,JS,CSS)进行语法解析,建立相应的内部数据结构(如
HTML
的DOM
)- 载入解析到的资源,渲染页面,完成
content
方面
- 减少
HTTP
请求:合并文件、CSS
精灵、inline Image
- 减少
DNS
查询:DNS
缓存、将资源分布到恰当数量的主机名- 减少
DOM
元素数量
Server
方面
- 使用
CDN
- 配置
ETag
- 对组件使用
Gzip
压缩
Cookie
方面
- 减小
cookie
大小
css
方面
- 将样式表放到页面顶部
- 不使用
CSS
表达式- 使用
不使用
@import
Javascript
方面
- 将脚本放到页面底部
- 将
javascript
和css
从外部引入- 压缩
javascript
和css
- 删除不需要的脚本
- 减少
DOM
访问
- 优化图片:根据实际颜色需要选择色深、压缩
- 优化
css
精灵- 不要在
HTML
中拉伸图片
- 用正确的标签做正确的事情!
html
语义化就是让页面的内容结构化,便于对浏览器、搜索引擎解析;- 在没有样式
CSS
情况下也以一种文档格式显示,并且是容易阅读的。- 搜索引擎的爬虫依赖于标记来确定上下文和各个关键字的权重,利于
SEO
。- 使阅读源代码的人对网站更容易将网站分块,便于阅读维护理解
- 主要分成两部分:渲染引擎(
layout engineer
或Rendering Engine
)和JS
引擎- 渲染引擎:负责取得网页的内容(
HTML
、XML
、图像等等)、整理讯息(例如加入CSS
等),以及计算网页的显示方式,然后会输出至显示器或打印机。浏览器的内核的不同对于网页的语法解释会有不同,所以渲染的效果也不相同。所有网页浏览器、电子邮件客户端以及其它需要编辑、显示网络内容的应用程序都需要内核JS
引擎则:解析和执行javascript
来实现网页的动态效果- 最开始渲染引擎和
JS
引擎并没有区分的很明确,后来JS引擎越来越独立,内核就倾向于只指渲染引擎
- 埋点的作用就是用来做动作行为分析的
- 在产品流程关键部位植相关统计代码,用来追踪每次用户的行为,统计关键流程的使用程度。
cookies
,sessionStorage
和 localStorage
的区别?
cookie
是网站为了标示用户身份而储存在用户本地终端(Client Side)上的数据(通常经过加密)- cookie数据始终在同源的http请求中携带(即使不需要),记会在浏览器和服务器间来回传递
sessionStorage
和localStorage
不会自动把数据发给服务器,仅在本地保存
有效期
cookie
设置的cookie
过期时间之前一直有效,即使窗口或浏览器关闭localStorage
存储持久数据,浏览器关闭后数据不丢失除非主动删除数据sessionStorage
数据在当前浏览器窗口关闭后自动删除
- document.write是直接写入到页面的内容流,如果在写之前没有调用document.open, 浏览器会自动调用open。每次写完关闭之后重新调用该函数,会导致页面被重写。
- innerHTML则是DOM页面元素的一个属性,代表该元素的html内容。你可以精确到某一个具体的元素来进行更改。如果想修改document的内容,则需要修改document.documentElement.innerElement。innerHTML将内容写入某个DOM节点,不会导致页面全部重绘
XHTML 是更严格更纯净的 HTML 代码。
- XHTML 元素必须被正确地嵌套。
- XHTML 元素必须被关闭。
- 标签名必须用小写字母。
- XHTML 文档必须拥有根元素。
- Node的一些方法,返回值为Node,比如说文本节点,注释节点之类的
- Element的一些方法,返回值则一定是Element
- Element 继承于 Node,具有Node的方法,同时又拓展了很多自己的特有方法
联系:
它们都能让元素不可见
区别:
- display:none;会让元素完全从渲染树中消失,渲染的时候不占据任何空间;visibility: hidden;不会让元素从渲染树消失,渲染时元素继续占据空间,只是内容不可见
- display: none;是非继承属性,子孙节点消失由于元素从渲染树消失造成,通过修改子孙节点属性无法显示;visibility: hidden;是继承属性,子孙节点消失由于继承了hidden,通过设置visibility: visible;可以让子孙节点显式
- 修改常规流中元素的display通常会造成文档重排。修改visibility属性只会造成元素的重绘。
- 读屏器不会读取display: none;元素内容;会读取visibility: hidden;元素内容
- link是HTML方式, @import是CSS方式
- link最大限度支持并行下载,@import过多嵌套导致串行下载,出现FOUC
- link可以通过rel="alternate stylesheet"指定候选样式
- 浏览器对link支持早于@import,可以使用@import对老浏览器隐藏样式
- @import必须在样式规则之前,可以在css文件中引用其他文件
- 总体来说:link优于@import
absolute
:生成绝对定位的元素,相对于static
定位以外的第一个父元素进行定位fixed
:生成绝对定位的元素,相对于浏览器窗口进行定位relative
:生成相对定位的元素,相对于其正常位置进行定位static
默认值。没有定位,元素出现在正常的流中inherit
规定从父元素继承position
属性的值
- 浮动后,行内元素不会成为块状元素,但是可以设置宽高。行内元素要想变成块状元素,占一行,直接设置
display:block
;。但如果元素设置了浮动后再设置display:block
;那就不会占一行。
双飞翼布局
sss sss
左边
右边
三栏等高
- 左边内容1111多少度撒多撒多撒多撒房东房东的
- 右边内容多右边内容多右边内容多右边内容多右边内容多右边内容多右边内容多右边内容多
- sss sss sss sss sss sss sss sss sss sss ssssss sss sss
页面上的一个隔离的渲染区域,容器里面的子元素不会在布局上影响到外面的元素
如何产生BFC?
float的值不为none、overflow的值不为visible、position的值不为relative和static、display的值为table-cell, table-caption, inline-block中的任何一个。
比如常见的多栏布局,结合块级别元素浮动,里面的元素则是在一个相对隔离的环境里运行。
水平垂直居中
绝对定位方法:
不确定当前div的宽度和高度,采用 transform: translate(-50%,-50%);
当前div的父级添加相对定位(position: relative;)
.app1 {
position: relative;
height: 1000px;
background-color: #ccc;
}
.app1 .box1 {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
background-color: cyan;
}
绝对定位方法:
确定了当前div的宽度,margin值为当前div宽度一半的负值
.box1{
position: absolute;
width: 600px;
height: 600px;
background-color: darkblue;
left: 50%;
top: 50%;
margin-left: -300px;
margin-top: -300px;
}
绝对定位方法:
绝对定位下top left right bottom 都设置0
.box1 {
position: absolute;
width: 600px;
height: 600px;
background-color: rgb(196, 156, 24);
top: 0;
left: 0;
right: 0;
bottom: 0;
margin: auto;
}
flex布局方法:
当前div的父级添加flex css样式
.app1 {
height: 1000px;
background-color: #ccc;
display: flex;
justify-content: center;
align-items: center;
}
.app1 .box1 {
width: 600px;
height: 600px;
background-color: rgb(12, 206, 22);
}
div {
display: flex;
justify-content: center;
width: 100px;
height: 100px;
background-color: aqua;
}
单行文字居中,多行文字居左显示
- 有两种,
IE
盒子模型、W3C
盒子模型;- 盒模型: 内容(content)、填充(
padding
)、边界(margin
)、 边框(border
);- 区 别:
IE
的content
部分把border
和padding
计算了进去;
与后代选择器相比,子元素选择器(Child selectors)只能选择作为某元素直接/一级子元素的元素
My name is Donald
I live in Duckburg.
相邻兄弟选择器(Adjacent sibling selector)可选择紧接在另一元素后的元素,且二者有相同父元素。
如果需要选择紧接在另一个元素后的元素,而且二者有相同的父元素,可以使用相邻兄弟选择器
DIV 内部段落。
DIV 之后的第一个 P 元素。
//这一行的样式生效
后续兄弟选择器选取所有指定元素之后的相邻兄弟元素。
段落 1。 在 div 中。
段落 2。 在 div 中。
段落 3。不在 div 中。
//样式生效
段落 4。不在 div 中。
//样式生效
pointer-events: none;
parseInt(string,radix)函数解析一个字符串参数,并返回一个指定基数的整数 (数学系统的基础)。
radix 一个介于2和36之间的整数(即进制数 )默认是10进制。
例如如果为3的话,则表示该字符串是以3进制来表示的
map()方法创建一个新数组,其结果是该数组中的每个元素都调用一个提供的函数后返回的结果。
const arr = [1, 2, 3];
arr.map((num) => num + 1); // [2, 3, 4]
['1', '2', '3'].map((item, index) => {
return parseInt(item, index)
})
//即返回的值分别为:
parseInt('1', 0) // 1 默认是十进制
parseInt('2', 1) // NaN
parseInt('3', 2) // NaN, 3 不是二进制 二进制只包含0或 1
['10','10','10','10','10'].map(parseInt);
//返回的结果为 [10,NaN,2,3,4]
函数节流:throttle
指定时间间隔内只会执行一次任务;
函数防抖:debounce
任务频繁触发的情况下,只有任务触发的间隔超过指定间隔的时候,任务才会执行。
vue写法:
//vue写法
//toolFun.js公共方法
export default function debounceFn(fn,delay=200){ //这里需要定义成函数
// e.stopPropagation()
console.log("外层",fn);
var timer = 0;
return function(){
console.log("timer",timer);
if(timer){
clearTimeout(timer)
}
timer = setTimeout(()=>{
fn.apply(this,arguments) //透传this 和参数
timer = 0
},delay)
// timer = setTimeout(fn,delay)
}
}
react写法:
//react 写法
state = {
name:'hahaha'
}
render() {
return (
Home
{/*这里不能用箭头函数 */}
);
}
debounceFn = (fn,delay=200) =>{
// e.stopPropagation()
console.log("外层",fn);
var timer = 0;
return function(){
console.log("timer",timer);
if(timer){
clearTimeout(timer)
}
timer = setTimeout(()=>{
fn.apply(this,arguments) //透传this 和参数
timer = 0
},delay)
// timer = setTimeout(fn,delay)
}
}
fn = ()=>{
console.log("打印数据",this.state.name)
}
- export与export default均可用于导出常量、函数、文件、模块等,
- 你可以在其它文件或模块中通过
import+(常量 | 函数 | 文件 | 模块)名
的方式,将其导入,以便能够对其进行使用- 但在一个文件或模块中,export、import可以有多个,export default仅有一个。
- export 导出后,在import导入时可以加{xx1,xx2 } 导出多个
- export default 导出后,在使用import导入时,不能加{ } ,,,而且只能只有一个
闭包是指有权访问另一个函数作用域中的变量的函数。(闭包 = 函数 + 函数能够访问的自由变量)
缺点:闭包使用过多,会占用较多的内存,这也是一个副作用,内存泄漏。
优点:变量长期驻扎在内存中;避免全局变量的污染;能够实现封装和缓存等;
有一个函数,参数是一个函数,返回值也是一个函数,返回的函数功能和入参的函数相似,但这个函数只能执行3次,再次执行无效,如何实现:
function sayHi() {
console.log('hi')
}
function threeTimes(fn) {
let times = 0
return () => {
if (times++ < 3) {
fn()
}
}
}
const newFn = threeTimes(sayHi)
newFn()//hi
newFn()//hi
newFn()//hi
newFn()//不执行
newFn() // 后面两次执行都无任何反应
实现add函数,让add(a)(b)和add(a,b)两种调用结果相同(柯里化)
let sum = add(1)(2);
console.log(sum);
let sum2 = add(1);
console.log(sum2(2))
function add(a, b) {
if (b === undefined) {
return function (x) {
return a + x
}
}
return a + b
}
let sum = add(1)(2);//执行的是return a+x
console.log(sum)
let sum2 = add(1, 2);//执行的是return a+b
console.log(sum2)
https://blog.csdn.net/weixin_44486539/article/details/102793904
- script 标签的 async 属性;该属性是HTML5中新增的异步支持,这种加载方式执行完之前会阻止onload事件的触发,会阻塞部分页面的初始化处理
- script 的 defer 属性;defer 属性规定是否对脚本执行进行延迟,直到页面加载为止;只支持IE。
- onload时的异步加载;这种方法只是把插入script的方法放在一个函数里面,然后放在window的onload方法里面执行,这样就解决了阻塞onload事件触发的问题
https://blog.csdn.net/fu983531588/article/details/108734278?utm_medium=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.channel_param&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.channel_param
只复制指向某个对象的指针,而不复制对象本身,新旧对象共享一块内存;
用 = 号赋值引用地址:
let obj = {
name: '李四',
age: 20,
sex: '男',
school: {
colleges: "野鸡烧烤工程系",
grade: 2020,
name: '野鸡大学',
stuNo: 2020123456,
},
};
let obj2 = obj;
obj2.name = 'Rose';
obj2.sex = '女';
obj2.school.name = '野鸡变凤凰';
console.log('obj', obj);
console.log('obj2', obj2);
//{age: 20,name: "Rose",school:{colleges: "野鸡烧烤工程系",grade: 2020,name: "野鸡变凤凰",stuNo: 2020123456},sex: "女"}
for…in 被循环的对象存在嵌套对象时为浅拷贝(如代码中的school):
let obj = {
name: '李四',
age: 20,
sex: '男',
school: {
name: '野鸡大学',
grade: 2020,
stuNo: 2020123456,
colleges: '野鸡烧烤工程系',
},
};
let obj2 = {};
for (let key in obj) {
obj2[key] = obj[key];
}
obj2.name = '小六';
obj2.sex = '女';
obj2.school.name = '野鸡变凤凰';
obj2.school.colleges = '凤凰系';
console.log('obj', obj);
//{age: 20,name: "李四",school:{colleges: "凤凰系",grade: 2020,name: "野鸡变凤凰",stuNo: 2020123456},sex: "男"}
console.log('obj2', obj2);
//{age: 20,name: "小六",school:{colleges: "凤凰系",grade: 2020,name: "野鸡变凤凰",stuNo: 2020123456},sex: "女"}
复制并创建一个一摸一样的对象,不共享内存,修改新对象,旧对象保持不变。
JSON.parse(JSON.stringify())
这种方式无法拷贝 正则表达式,undefine,function
let obj2 = JSON.parse(JSON.stringify(obj));
**for.. in ** 被循环的对象不存在嵌套对象时为深拷贝
Object.assign():
只复制源对象中可枚举的属性和对象自身的属性。如果目标对象中的属性具有相同的键,则属性将被源中的属性覆盖:
let obj = {
name: '李四',
age: 20,
sex: null,
tel: /^1[345789]\d{9}$/,
address: undefined,
flag: true,
say: () => {
console.log('哇塞')
}
};
let obj2 = {
name: '小二',
};
Object.assign(obj2, obj);
obj2.name = '小六';
console.log('obj', obj);//还是原来的值,不受影响
console.log('obj2', obj2);//name 为小六,其他的和obj 一样
递归方法:
function deepClone(targetObj = {}, map = new Map()) {
if (typeof targetObj !== 'object') {
return targetObj
}
if (map.get(targetObj)) {
return map.get(targetObj)
}
let result = {}
if (targetObj instanceof Array || Object.prototype.toString(targetObj) === "[object Array]") {
result = []
}
map.set(targetObj, result)
for (const key in targetObj) {
if (targetObj.hasOwnProperty(key)) {
//递归使用
result[key] = deepClone(targetObj[key], map)
}
}
return result
}
let obj = {
a: 1,
b: undefined,
c: null,
d: {
d1: 'ddddddddddd'
},
e: [111111111111, 333333333333]
};
let newObj = deepClone(obj)
newObj.e = [9999, 333333]
console.log(obj, newObj)
利用 ES6的set 方法:
function unique10(arr) {
//Set数据结构,它类似于数组,其成员的值都是唯一的
return Array.from(new Set(arr)); // 利用Array.from将Set结构转换成数组
}
console.log(unique10([1, 1, 2, 3, 5, 3, 1, 5, 6, 7, 4]));
// 结果是[1, 2, 3, 5, 6, 7, 4]
利用for嵌套for,然后splice去重(ES5中最常用):
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]
console.log(unique(arr)) //{}和NaN没有被去重
//[1, 'true', {}, {}, true, 15, false, undefined, null, NaN, NaN]
indexOf 去重:
新建一个空的结果数组,for循环原数组,判断结果数组是否存在当前元素,如果有则跳过,没有则push进数组。indexOf() 方法可返回某个指定的字符串值在字符串中首次出现的位置。
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]
console.log(unique(arr)) //{}和NaN没有被去重
// [1, 'true', {}, {}, true, 15, false, undefined, null, NaN, NaN]
利用includes:
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]
console.log(unique(arr)) //{}没有被去重
//[1, 'true', {}, {}, true, 15, false, undefined, null, NaN]
利用filter:
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];
console.log(unique(arr)) //{}没有去重,NaN被忽略
//[1, 'true', true, 15, false, undefined, null]
事件委托
把原本需要绑定的事件委托给父元素,让父元素担当事件监听的职务。
- 1
- 2
- 3
- 4
- 5
页面A跳转到页面B及URL携带参数
传递参数:window.location.href = "../list.html?id="+id;
接收参数:url = location.search
通过localStorage 和 sessionStorage 先存本地存储数据
setItem来存数据:
window.localStorage.setItem("data", "kevin");
window.sessionStorage.setItem("data", "kevin");
用getItem来取数据:
//取数据
window.localStorage.getItem("data");
window.sessionStorage.getItem("data");
DOM中提供preventDefault()方法来取消事件默认行为
DOM中提供stopPropagation()方法来阻止事件冒泡
防止移动端穿透 :body滚动 + 弹层内部滚动[js-检测touchmove的target]
const {
a1,
b1
} = {
a1: '这是第一个值',
b1: '这是第二个值'
}
console.log(a1);//'这是第一个值'
var a1 = '这是第三个值';
console.log(a1);//报错'a1' has already been declared
Ajax的原理简单来说是在用户和服务器之间加了—个中间层(AJAX引擎),通过XMLHttpRequest对象来向服务器发异步请求,从服务器获得数据,然后用javascript来操作DOM而更新页面。使用户操作与服务器响应异步化。这其中最关键的一步就是从服务器获得请求数据
优点:
缺点:
function f(url) {
let promise = new Promise(function (resolve, reject) {
$.ajax({
url: url,
dataType: 'json',
success: function (data) {
console.log(data);
if (data.erros === 0) {
resolve(data.msg);
} else {
reject("失败")
}
}
})
})
return promise
}
f("http://39.102.61.13:66/insucate.php?act=list&page=1&size=2").then(function (data) {
console.log(data)
}, function (fail) {
console.log(fail);
})
var value = 1;
function foo() {
console.log(value);
}
function bar() {
var value = 2;
foo();
}
bar(); //1
var scope = "gloab scope"
function a() {
var scope = "local scope"
function fn() {
return scope
}
return fn()
}
a() //'local scope'
构造继承
原型继承
实例继承
拷贝继承
原型prototype机制或apply和call方法去实现较简单,建议使用构造函数与原型混合方式
function Parent() {
this.name = 'wang';
}
function Child() {
this.age = 28;
}
Child.prototype = new Parent();//继承了Parent,通过原型
var demo = new Child();
alert(demo.age);
alert(demo.name);//得到被继承的属性
this
变量引用该对象,同时还继承了该函数的原型this
引用的对象中this
所引用,并且最后隐式的返回 this
offsetWidth/offsetHeight返回值包含content + padding + border,效果与e.getBoundingClientRect()相同
clientWidth/clientHeight返回值只包含content + padding,如果有滚动条,也不包含滚动条
scrollWidth/scrollHeight返回值包含content + padding + 溢出内容的尺寸
JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式
它是基于JavaScript的一个子集。数据格式简单, 易于读写, 占用带宽小
JSON字符串转换为JSON对象:
var obj = str.parseJSON()
var obj =eval('('+ str +')');
JSON对象转换为JSON字符串:
var last=obj.toJSONString();
instanceof方法
instanceof
运算符是用来测试一个对象是否在其原型链原型构造函数的属性
ES5新增方法isArray()
constructor方法
constructor
属性返回对创建此对象的数组函数的引用,就是返回对象相对应的构造函数
意外的全局变量引起的内存泄露
function leak(){ leak="xxx";//leak成为一个全局变量,不会被回收 }
闭包引起的内存泄露
没有清理的DOM元素引用
// 空数组初始化后,即使数组arr中没有元素,也是一个object
//用于判断条件时就会被转化为true。
console.log([] ? true : false) //true
//但是与布尔值相比较的话,会被先转换为Number。[]=>0,true=>1
console.log(([] == true) ? true : false) //false
console.log(([] == false) ? true : false) //true
console.log({} ? true : false) //true
//与布尔值比较时,同样被转为Number ,{}=>NaN,所以与false 、true 都不相等
console.log(({} == false) ? true : false) //false
UI:界面美观,要有个性,考虑用户使用的逻辑要简单,用起来舒适自由。使用习惯要符合大部分用户的习惯,比如少让用户输入,采用选择的方式,提供搜索和提示功能。
安全性:
高性能:
第一步:创建ajax对象(XMLHttpRequest/ActiveXObject(Microsoft.XMLHttp))
第二步:判断数据传输方式(GET/POST)
第三步:打开链接 open()
第四步:发送 send()
数据接收完成,判断http响应状态(status)200-300之间或者304(缓存)执行回调函数
依照 Promise/A+ 的定义,Promise 有四种状态:
pending:初始状态
fulfilled:成功的操作
rejected: 失败的操作.
settled:Promise已被fulfilled或rejected
Promise:对象用来进行延迟(deferred) 和异步(asynchronous) 计算
Promise:实例拥有 then 方法
接收两个函数作为参数
面试题
setTimeout(() => {
console.log(1); //最后输出1
}, 0)
new Promise(function (resolve) {
console.log(2); //第一个输出2
for (var i = 0; i < 1000; i++) {
i = 9999 && resolve();
}
console.log(3) //第二个输出3
}).then(function (data) {
console.log(4); //第四个输出4
})
console.log(5) //第三个输出5
//当前主线程的任务完成后,会读取任务队列(task queue)中的是事件。
//setTimeout会放到任务队列中,代码继续往下走
//promise中的then操作是放在执行栈,也就是主线程的最后
let a = {};
let b = '0',
c = 0;
a[b] = 'bbb';//a['0'] = 'bbb'
a[c] = 'ccc';//a[0] = 'ccc'
// 因为 '0' == 0 所以值会被覆盖
console.log(a[b]); //ccc
console.log(a); //{0:'ccc'}
//Symbol 创建唯一值
let i = {},
j = Symbol(1),
k = Symbol(1);
i[j] = 'jjj';
i[k] = 'kkk';
console.log(i[j]); //jjj
console.log(i);//{Symbol(1): "jjj",Symbol(1): "kkk"}
// 对象属性
let d = {},
e = {
n: 1
},
f = {
n: 2
};
d[e] = 'eee';
d[f] = 'fff';
console.log(d[e]); //fff
console.log(d); //{[object Object]: "fff"}
var a1 = 0,
b1 = 0;
function A(a1) {
A = function (b1) {
alert(a1 + b1++);
}
alert(a1++);
}
A(1); //'1' 第一次执行 alert(a1++);a1 = 2 此时A 的堆内存指针已经被替换
A(2) //'4' 第二次执行的是 alert(a1 + b1++);此时b1 = 2;a1的值往上找,a1=2
function foo() {
console.log(this);//{a3: 23 }
console.log(this.a);//23
}
var obj = {
a: 23
}
var a = 33;
var bar = foo.bind(obj)
bar.call(window);
btn.onclick = function () {
//创建ajax对象
var xml = new XMLHttpRequest();
// 2.创建一个新的http请求,(打开浏览器,输入请求地址)
// xhr.open(请求的方式(get/post),请求的地址[,异步同步请求])
xml.open('get', 'http://localhost/index.php', true);
// 3.发送请求
// xhr.send(传递post请求的数据/ get请求设置 null)
xml.send(null);
//监听状态变化事件,获取后台返回数据
xml.onreadystatechange = function () {
//readyState 表示ajax的状态
//ajax一共有5种状态
//0 ==> 创建ajax对象完毕
//1 ==> 调用了open()方法
//2 ==> 调用了send()方法
//3 ==> 服务器端只返回了一部分数据
//4 ==> 服务器端数据全部返回,ajax请求完成
// 304 not modified ,文件没有改变
if (xml.readyState == 4) {
if (xml.status >= 200 && xml.status < 300 || xml.status == 304) {
alert(xml.responseText)
}
}
}
}
出于浏览器的同源策略限制
当一个请求url的协议、域名、端口三者之间任意一个与当前页面url不同即为跨域
var m = +prompt('请输入斐波那契列数');
document.write(FiboNaQie(m));
function FiboNaQie(n) {
if (n === 1) return 1;
if (n === 2) return 1;
else {
return FiboNaQie(n - 1) + FiboNaQie(n - 2);
}
}
+遇到字符串时为拼接,- 无论时什么情况下都是减
console.log("10"+2-"1")
//输出数字 101
事件捕获=》事件处理=》事件冒泡
https://www.jianshu.com/p/24825a2683e6 (什么是token)
https://blog.csdn.net/qq_18549249/article/details/81329654?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-1.control&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-1.control 前后端分离-通过header传递token实现认证
Token的定义:Token是服务端生成的一串字符串,以作客户端进行请求的一个令牌,当第一次登录后,服务器生成一个Token便将此Token返回给客户端,以后客户端只需带上这个Token前来请求数据即可,无需再次带上用户名和密码。
使用Token的目的:Token的目的是为了减轻服务器的压力,减少频繁的查询数据库,使服务器更加健壮。
主要使用token的基本流程如下:
var name = 'zzj';
var objects = {
name: 'xiaoming',
getName: function () {
return function () {
return this.name
}
}
}
console.log(objects.getName())
//输出ƒ () {return this.name }
console.log(objects.getName()()) //此时this指向的是window输出zzj
var arr = [];
function Round(start, end) {
var temp = end - start + 1 //加1是为能取到100
// Math.random() 随机抽取[0,1)的值
//floor 是向下取整
var res = Math.floor(Math.random() * temp + start)
return res;
}
for (var i = 0; i < 10; i++) {
arr.push(Round(10, 100))
}
//排序若 a 大于 b,则返回一个大于 0 的值。
arr.sort(function (a, b) {
return a - b;
})
// 方法一
function toThousand(str) {
var len = str.length;
var num = parseInt(len / 3);
console.log(num)
var head = str.slice(0, len - num * 3);
head += ',';
var body = str.slice(len - num * 3, len)
console.log(body)
var array = []
for (var i = 0; i < num; i++) {
array.push(body.slice(i * 3, i * 3 + 3))
}
console.log(array)
body = array.join(','); //数组转字符串
if (len % 3 == 0) {
//刚好位数是3的倍数
str = body
} else {
str = head + body;
}
return str
}
console.log(toThousand("123456789"))
// 方法二
function format(n) {
n = Math.floor(n);
const s = n.toString()
const arr = s.split('').reverse() //字符串转成数组变翻转
return arr.reduce((pre, val, index) => {
console.log(pre, val, index)
if (index % 3 === 0) {
if (pre) {
return val + ',' + pre
} else {
return val
}
} else {
return val + pre
}
}, '')
}
const n = 12345678;
console.log(format(n))
// 方法三
function format2(n) {
n = Math.floor(n);
const s = n.toString()
let res = ''
for (let i = s.length - 1; i >= 0; i--) {
let j = s.length - i;
console.log(j)
if (j % 3 === 0) {
if (i === 0) {
res = s[i] + res
} else {
res = ',' + s[i] + res
}
} else {
res = s[i] + res
}
}
return res
}
const n = 12345678;
console.log(format2(n))
对未声明的变量执行 typeof 不会报错,会返回 undefined
放在运算符中的函数声明在执行阶段时找不到的 。
var test = 1;
if (function f() { }) {
test += typeof f;
}
console.log(test);//1undefined
将一个嵌套多层的数组转换为只有一层的数组
function flat(arr, depth = 1) {
if (depth > 0) {
return arr.reduce((pre, cur) => {
//cur 当前值
return pre.concat(Array.isArray(cur) ? flat(cur, depth - 1) : cur)
}, []);
}
return arr.slice()
}
let arr1 = [11, 22, [55, 999], [{ id: 1, number: 8888 }, { id: 2, number: 6666666666 }]];
flat(arr1, 2) // [11,22,55,999,{id:1,number:8888},{id:2,number:6666666666}]
setTimeout(function () {
console.log(1);
}, 0);
console.log(2);
async function s1() {
console.log(7)
await s2(); //后面的代码被挂起
console.log(8);
};
async function s2() {
console.log(9);
}
s1();
new Promise((resolve, reject) => {
console.log(3);
resolve();
console.log(6);
}).then(() => console.log(4))
console.log(5);
// 输出 2 7 9 3 6 5 8 4 1
//await表示遇到await关键字后先将这块代码的asyncContext挂起并执行上级上下文
交替执行,多个fulfilled promise 实例,同时执行then
//交替执行,多个fulfilled promise 实例,同时执行then
Promise.resolve().then(res => {
console.log("1")
}).then(res => {
console.log("2")
}).then(res => {
console.log("3")
})
Promise.resolve().then(res => {
console.log("10")
}).then(res => {
console.log("20")
}).then(res => {
console.log("30")
})
//打印输出 1 10 2 20 3 30
then 中执行promise 实例
Promise.resolve().then(res => {
console.log("1")
return Promise.resolve(5)
}).then(res => {
console.log("res", res)
}).then(res => {
console.log("2")
}).then(res => {
console.log("3")
}).then(res => {
console.log("4")
})
Promise.resolve().then(res => {
console.log("10")
}).then(res => {
console.log("20")
}).then(res => {
console.log("30")
}).then(res => {
console.log("40")
})
//输出 1 10 20 30 5 40 2 3 4
setInterval
返回一个唯一的 id。此 id 可被用于 clearInterval
函数来取消定时。
function* bar() {
//生成器
console.log("1111");
const result = yield new Promise((resolve, reject) => {
setTimeout(() => {
resolve('Hello')
}, 2000)
})
console.log(result)
}
//迭代器 ,使用.next()方法
const it = bar()
it.next().value.then(res => {
it.next(res)
})
//先打印 1111
//间隔两秒后打印 Hello
1:
const add = () => {
const cache = {};
return num => {
if (num in cache) {
return `From cache! ${cache[num]}`;
} else {
const result = num + 10;
cache[num] = result;
return `Calculated! ${result}`;
}
};
};
const addFunction = add();
console.log(addFunction(10));
console.log(addFunction(10));
console.log(addFunction(5 * 2));
//add函数是一个记忆函数。 通过记忆化,我们可以缓存函数的结果,以加快其执行速度。上述情况,我们创建一个cache对象,用于存储先前返回过的值。
//输出Calculated! 20 From cache! 20 From cache! 20
2:
const handler = {
set: () => console.log("Added a new property!"),
get: () => console.log("Accessed a property!")
};
const person = new Proxy({}, handler);
person.name = "Lydia";
person.name;
//输出 Added a new property! Accessed a property!
使用 Proxy 对象,我们可以给一个对象添加自定义行为 设置属性值时 set 被调用,每当我们获取属性值时 get 被调用 3:
const name = "Lydia Hallie";
const age = 21;
console.log(Number.isNaN(name)); //false
console.log(Number.isNaN(age)); //false
//方法 Number.isNaN,你可以检测你传递的值是否为 数字值 并且是否等价于 NaN
console.log(isNaN(name)); //true
console.log(isNaN(age)); //false
4:
const add = x => y => z => {
console.log(x, y, z);
return x + y + z;
};
add(4)(5)(6); //4 5 6
5:
const name = "Lydia Hallie";
console.log(!typeof name === "object"); //false -----!typeof name 返回false
console.log(!typeof name === "string"); //false
6:
const config = {
languages: [],
set language(lang) {
return this.languages.push(lang);
}
};
console.log(config.language); //undefined
//方法 language 是一个 setter。Setters 并不保存一个实际值,它们的使命在于 修改 属性。当调用方法 setter, 返回 undefined
7:
console.log(`${(x => x)('I love')} to program`)
//I love to program
8:
const colorConfig = {
red: true,
blue: false,
green: true,
black: true,
yellow: false,
}
const colors = ["pink", "red", "blue"]
console.log(colorConfig.colors[1]) //TypeError
9:
// index.js
console.log('running index.js');
import { sum } from './sum.js';
console.log(sum(1, 2));
// sum.js
console.log('running sum.js');
export const sum = (a, b) => a + b;
//running sum.js, running index.js, 3
/**
*import命令是编译阶段执行的,在代码运行之前。因此这意味着被导入的模块会先运行,而导入模块的文件会
*后执行。 这是CommonJS中require()和import之间的区别。使用require(),您可以在运行代码时根据需要
*加载依赖项。 如果我们使用require而不是import,running index.js,running sum.js,3会被依次打印
*/
10:
const person = { name: "Lydia" };
Object.defineProperty(person, "age", { value: 21 });
console.log(person);
console.log(Object.keys(person));
//{ name: "Lydia", age: 21 }, ["name"]
/**
* 通过defineProperty方法,我们可以给对象添加一个新属性,或者修改已经存在的属性。
* 而我们使用defineProperty方法给对象添加了一个属性之后,属性默认为 不可枚举(not enumerable).
* Object.keys方法仅返回对象中 可枚举(enumerable) 的属性,因此只剩下了"name".
* 用defineProperty方法添加的属性默认不可变。你可以通过writable, configurable 和 enumerable属性来改变这一行为。
* 这样的话, 相比于自己添加的属性,defineProperty方法添加的属性有了更多的控制权。
*
*/
11:
const set = new Set([1, 1, 2, 3, 4]);
console.log(set);
//{1,2,3,4} Set对象是独一无二的值的集合:
12:
function greeting() {
throw "Hello world!Hello world!"; //抛出错误信息
}
function sayHi() {
try {
const data = greeting();
console.log("It worked!", data);
} catch (e) {
console.log("Oh no an error:", e);
}
}
sayHi(); //Oh no an error: Hello world!
//通过throw语句,我么可以创建自定义错误。 而通过它,我们可以抛出异常
13:
function getInfo(member, year) {
member.name = "Lydia";
year = "1998";
}
const person = { name: "Sarah" };
const birthYear = "1997";
getInfo(person, birthYear);
console.log(person, birthYear);
//{ name: "Lydia" }, "1997"
//对象作为参数传参时,相应的属性被修改时,原始数据也会改变
14:
[1, 2, 3].map(num => {
if (typeof num === "number") return;
return num * 2;
});
//[undefined,undefined,undefined]
15.遍历一个任意长度的list中的元素并依次创建异步任务,如何获取所有任务的执行结果?
const p1 = new Promise((resolve, reject) => {
resolve("成功了")
})
const p2 = Promise.resolve('success')
const p3 = Promise.reject('错误')
Promise.all([p1, p2]).then((result) => {
console.log(result)['成功了', 'success']
}).catch((err) => {
})
Promise.all([p1, p2, p3]).then((result) => {
console.log(result)
}).catch((err) => {
console.log(err) //错误 只会抛出错误的那个任务
})
//可用于并行执行独立的异步操作,并收集这些操作的结果
Promise.allSettled([p1, p2, p3]).then((result) => {
console.log(result)
//[{status: 'fulfilled', value: '成功了'},{status: 'fulfilled', value: 'success'}{status: 'rejected', value: '错误'}]
}).catch((err) => {
console.log(err) //错误 只会抛出错误的那个任务
})
XSS是一种经常出现在web应用中的计算机安全漏洞,它允许恶意web用户将代码植入到提供给其它用户使用的页面中。
ref方法:
{{ age }}
active-class属于vue-router的样式方法 当routerlink标签被点击时将会应用这个样式 使用有两种方法routerLink标签内使用
computed:是计算属性,依赖其它属性值,并且computed的值有缓存,只有它依赖的属性值发生改变,下一次获取computed的值时才会重新计算computed的值
·当我们需要进行数值计算,并且依赖于其它数据时,应该使用computed,因为可以利用computed的缓存特性,避免每次获取值时,都要重新计算;
watch:更多的是[观察]的作用,类似于某些数据的监听回调,每当监听的数据变化时都会执行回调进行后续操作;
watch: {
bufferInfoList: {
handler(newValue, oldValue) {
// console.log("newValue", newValue);
},
immediate: true,
deep: true
}
},
同一轮事件循环中的所有数据改变
。在缓冲时会除去重复的操作
,等到下一轮事件循环时,才开始更新。
{{msg}}
Message got outside $nextTick: {{msg1}}
Message got inside $nextTick: {{msg2}}
Message got outside $nextTick: {{msg3}}
主要应用的场景:
created()
钩子函数进行的DOM操作一定要放在Vue.nextTick()
的回调函数中router-link
// query传参数 (类似get,url后面会显示参数)
// 路由可不配置 path: "/home/:id"
// html 取参 $route.query.id
// script 取参 this.$route.query.id
this.$router.push() (函数里面调用)
//query传参
this.$router.push({name:'home',query: {id:'1'}})
// html 取参 $route.query.id
// script 取参 this.$route.query.id
//params传参
this.$router.push({name:'home',params: {id:'1'}})// 只能用 name
// 路由配置 path: "/home/:id" 或者 path: "/home:id" ,
// 不配置path ,第一次可请求,刷新页面id会消失
// 配置path,刷新页面id会保留
// html 取参 $route.params.id
// script 取参 this.$route.params.id
query和params区别
$route 和 $
router的区别
beforeCreate, created, beforeMount, mounted
总共分为8个阶段创建前/后,载入前/后,更新前/后,销毁前/后。
创建前/后:
在beforeCreate阶段,vue实例的挂载元素$el和数据对象data都为undefined,还未初始化。在**created**阶段,vue实例的数据对象data有了,$
el还没有,如果要调用 methods 中的方法,或者操作 data 中的数据,最早可以在这个阶段中操作
载入前/后:
在beforeMount阶段,vue实例的$el和data都初始化了,但还是挂载之前为虚拟的dom节点,data.message还未替换。在mounted阶段,vue实例挂载完成,data.message成功渲染, 如果我们想要通过插件操作页面上的DOM节点,最早可以在和这个阶段中进行
beforeUpdate:
当执行这个钩子时,页面中的显示的数据还是旧的,data中的数据是更新后的, 页面还没有和最新的数据保持同步 updated:页面显示的数据和data中的数据已经保持同步了,都是最新的。
销毁前/后:
在执行destroy方法后,对data的改变不会再触发周期函数,说明此时vue实例已经解除了事件监听以及和dom的绑定,但是dom结构依然存在。
父组件先初始化 ->父组件渲染完成 ->子组件开始初始化 -> 子组件挂载完成(mount) -> 父组件挂载完毕(mount)
运用懒加载则可以将页面进行划分,需要的时候加载页面,可以有效的分担首页所承担的加载压力,减少首页加载用时
{
// 我的
path: 'my',
name: 'my',
component: () => import( /* webpackChunkName: 'my' */ "../views/my/my.vue")
},
//使用这种方法,每个组件打包成一个js文件,当进入组件时才会加载
索引、数组长度,,数组的这两个发生变化,vue 检测不到变化
使用$set方法:三个参数分别为:操作的数组,索引,改变后的值
vm.$set(vm.names, 0, "luluba");
使用数组的原型方法splice
vm.names.splice(0, 1, "luluba");
全局前置守卫
router.beforeEach((to, from, next) => {
// ...
})
//to: Route: 即将要进入的目标 路由对象
//from: Route: 当前导航正要离开的路由
//next: Function: 一定要调用该方法来 resolve 这个钩子。执行效果依赖 next 方法的调用参数
路由独享守卫
routes: [
{
path: '/foo',
component: Foo,
beforeEnter: (to, from, next) => {
// ...
}
}
]
组件内的守卫
beforeRouteEnter(to, from, next) {
// 在渲染该组件的对应路由被 confirm 前调用
// 不!能!获取组件实例 `this`
// 因为当守卫执行前,组件实例还没被创建
},
beforeRouteUpdate(to, from, next) {
// 在当前路由改变,但是该组件被复用时调用
// 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
// 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
// 可以访问组件实例 `this`
},
beforeRouteLeave(to, from, next) {
// 导航离开该组件的对应路由时调用
// 可以访问组件实例 `this`
}
答:需要使用key来给每个节点做一个唯一标识,Diff算法就可以正确的识别此节点。 作用主要是为了高效的更新虚拟DOM。
组件中的data写成一个函数,数据以函数返回值{ }的形式定义,这样每次复用组件的时候,都会返回一份新的data,相当于每个组件实例都有自己私有的数据空间,它们只负责各自维护的数据,不会造成混乱。而单纯的写成对象形式,就是所有的组件实例共用了一个data,这样改一个全都改了。
http://caibaojian.com/vuex-interview.html
vuex有哪几种属性?
有五种,分别是 State、 Getter、Mutation 、Action、 Module
我们可以在组件中触发 Action,Action 则会提交 Mutation,Mutaion 会对 State 进行修改,组件再根据 State 、Getter 渲染页面
通过父组件接收某一个子组件的事件,对数据进行修改,从而会影响到全部的子组件
通过vuex
// store/index.js
state: {
name: '嗨喽'
},
mutations: {
changeName(state) {
state.name = "hello"
}
},
//子组件2
vuex 方法
nsme:{{ $store.state.name }}