这里写目录标题
1.开发中遇到的困难?
- 问题:才开始做项目的时候,以为插件使用简单,vue项目中轮播图的使用,swiper插件的使用,查看官方的文档是一定要有节点DOM(相应的结构)渲染出来才会有对应的动态效果。
vue中mounted生命周期函数–>组件挂载完毕就会调用,HTML已经被渲染到了页面上,这个阶段可以执行dom操作,可以进行数据请求,但是还是v-for在遍历来自于Vuex(数据:通过ajax向服务器发请求,存在异步)并且v-for的遍历也是需要时间的遍历数据渲染结构的(没法保证v-for的遍历完成), - 解决:思考了一下vue生命周期函数以及vue中的封装好的函数方法。nextTick函数:在下次DOM更新, 循环结束之后,执行延迟回调。在 修改数据之后 立即使用这个方法,获取更新后的DOM
- 收获:组件实例的$nextTick+watch方法,在今后经常使用,经常结合第三方插件使用,获取更新后的DOM节点
2. Css的盒子模型
- 标准盒模型(W3C):box-sizeing:content-box(默认值)
标准盒模型总宽度/高度:margin+border+padding+内容区宽度/高度( 即 width/height 不包含 padding 和 border 值 )) - 怪异盒模型(IE盒模型):box-sizing:border-box
标准盒模型总宽度/高度:margin+ (内容区宽度/高度 + padding + border) ( 即 width/height 包含 padding 和 border 值 ))
3. Pull和fetch的区别
- git pull将远程的仓库的变化下载下来,与和本地的分支做合并
- git fetch将远程的仓库的变化下载下来,并没有和本地的分支做合并
git在项目中常用命令流程
1.配置author信息
git config --global user.name “你的名字”
git config --global user.email “你的邮箱”
2.git clone 远程仓库链接
3.git stash “信息"本地保存自己的创建的代码
4.git pull 拉取远程的仓库的代码和本地分支做合并
5.解决冲突
6.git add 仓库(目录)中的所有文件添加进git管理
7.git commit 提交到本地仓库
8.git push 提交到远程仓库
4. SPA单页面项目
-
概念:SPA( single-page application )仅在 Web 页面初始化时加载相应的 HTML、JavaScript 和 CSS实现页面的渲染加载。一旦页面加载完成,SPA 不会因为用户的操作而进行页面的重新加载或跳转;取而代之的是利用路由机制实现 HTML 内容的变换,UI 与用户的交互、避免页面的重新加载。
-
优点:
- 用户体验好、快,内容的改变不需要重新加载整个页面,避免了不必要的跳转和重复渲染;
- 基于上面一点,SPA 相对服务器压力小;
- 前后端职责分离,架构清晰,前端进行交互逻辑,后端负责数据处理;
-
缺点:
- 初次加载耗时多:为实现单页 Web 应用功能及显示效果,需要在加载页面的时候将 JavaScript、CSS 统一加载,部分页面按需加载
- 前进后退路由管理:由于单页应用在一个页面中显示所有的内容,所以不能使用浏览器的前进后退功能,所有的页面切换需要自己建立堆栈管理;
- SEO 难度较大:由于所有的内容都在一个页面中动态替换显示,所以在 SEO 上其有着天然的弱势。
-
劣势解决:
- SPA单页面应用实现前进与后退功能
前言:SPA单页面应用:在使用ajax与服务器数据交互过程中,实现局部数据的更新时,浏览器地址栏的url链接并没有发生改变,浏览器因为不会保存更新记录(浏览器会记录地址栏的url资源链接,如果url链接发生变化,浏览器会把该url链接保存到一个特殊的数据结构中,这样当用户点击返回与前进按钮时,会快速访问已经被记录的url链接资源。)。这时候再去返回就会出现非期待性的结果
- 解决方案:设置window.location.hash(location.hash)+ 监听window.onhashchange
//在ajax与服务器进行交互时,设置window.location.hash的值:
function updateView (attr){
$.ajax({
type:...,
url:...,
data:{attr : attr},
success:function(datas){
//设置hash值
window.location.hash = "#"+attr;
//do somthing
},
error:function(status){
//do somthing
}
});
}
//attr 这个值最好是该次请求所需的参数 设置onhashchange事件监听
window.onhashchange=function(){
var attr=window.location.hash.replace("#","");
updateView (attr);
}
//但是上述这样单纯的只要用户点击第一页的视图,ajax请求成功后,会主动改变hash值,这时候又触发onhashchange,又一次更新视图,两次访问服务器。
//解决办法
//设置一个全局变量,记录hash值的改变是怎样引起的:
var innerDocClick;
$('body').on('mouseleave',function(){
innerDocClick=false;//鼠标在页面外(点击了返回按钮)
});
$('body').on('mouseover',function(){
innerDocClick=true;//鼠标在页面内(没有点击返回按钮)
});
window.onhashchange=function(){
if(!innerDocClick)//若点击了返回按钮 加条件判断去更改hash值
{
var attr=window.location.hash.replace("#","");
updateView (attr);
}
}
- 第二种解决方法是在页面中嵌入一个隐藏 iframe,由于浏览器可以对 DOM 树中 iframe 节点的 src 属性进行历史记录跟踪,这样通过在逻辑上建立一条“页面 URL – 页面内 iframe URL – 页面状态”的对应链,同样可以在 IE 中建立片段标识符与页面状态的联系。
5.SEO优化
- 概念:seo是搜索引擎优化。是一种利用搜索引擎的规律提高网站在有关搜索引擎的排名,是一种网络营销方式。
搜索引擎的规律:是靠搜索关键字来让自己的网站在搜索栏的第一页内,展示在用户的最前面,能让用户第一时间看到自己的网站。
- 站内优化:
META标签的优化:例如:title,keywords,description等的优化;(logo图片)
网站文章的更新:每天保持站内文章的更新; - 站外优化:
友链互换:与一些和你网站相关性比较高,整体质量比较好的网站交换链接,巩固稳定关键词排名。
补充
高质量网站有哪些特点
1.内容受众
2.时效性强
3.设计与布局整洁干净
4.无虚假违法信息
6.BOM浏览器对象模型
- window对象
- 概念:BOM的核心对象是window,它表示浏览器的一个实例,它也是ECMAScript规定的Globle对象,也就是说网页中任何一个对象都是在window这个对象里面的
常用的方法:
1.alert()
2.定时器
- location对象
概念: location属性用于获得当前窗口中加载的文档有关的信息或者设置窗体的URL,并且可以解析URL,因为返回的是一个对象,因此可以叫做location对象,还有一些导航功能,值得注意的是location既是window对象的属性,又是document对象的属性,既window.location和document.location 引用的是同一个对象。
URL: Uniform Resource Locator 统一资源定位符 :是互联网上标准的地址。互联网上的每个文件都有唯一的URL,包含 通过地址属性可以得到不同的url的信息图片location对象的属性
常用的方法:
location.href = “http://www.666.com”; //页面跳转到该网址
location.assign(“http://www.666.com”); //页面跳转到该网址
location.hash用于设置页面的标签值
- navigator对象:
概念:该对象里面保存着浏览器的各种信息,判断浏览器的各种信息就是从该对象里的属性来读取,具体属性如下图:
常用的方法:
navigator.cookicEnablcd:判断是否启用cookic
navigator.userAgent:判断浏览器的名称和版本号
navigator.plugins:保存浏览器中所有插件信息的集合
<script>
function hasPlugin(name) {
name = name ? name.toLocaleLowerCase() : '';
console.log('name = ' + name);
var plugins = navigator.plugins;
for (var i = 0; i < plugins.length; i++) {
//console.log(typeof plugins[i]);
console.log(plugins[i].name);
return plugins[i].name.toLocaleLowerCase().indexOf(name) !== -1 ? true : false;
}
}
var r = hasPlugin('pdf');
console.log(r);
- history对象:
- 概念:该对象保存着用户上网的历史记录,从窗口被打开的那一刻算起
常用方法:
1.前进:history.forward();history.go(1);
2.后退:history.back();history.go(-1);
3.获取记录个数:history.length:
7.数组、对象、字符串中的一些方法
8.解构赋值
- 概念:变量的解构赋值,ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称- - 为解构。这种方式的出现大大提高了代码的扩展性
常见的使用场景
- 交换变量的值
let x = 1;
let y = 2;
[x, y] = [y, x];
- 从函数返回多个值
//返回一个数组,用结构赋值可以非常方便的取到对应值
function example() {
return [1, 2, 3];
}
let [a, b, c] = example();
//返回一个对象,获取对应的属性值
function example() {
return {
foo: 1,
bar: 2
};
}
let { foo, bar } = example();
- 取 JSON 数据
解构赋值对提取 JSON 对象中的数据
let jsonData = {
id: 42,
status: "OK",
data: [867, 5309]
};
let { id, status, data: number } = jsonData;
console.log(id, status, number);
// 42, "OK", [867, 5309]
// 上面代码可以快速提取 JSON 数据的值。
- 对象的解构赋值
- 数组的解构赋值
1.同时赋值多个变量
let arr = [a,b,c,d] = [1,2,3,4]
1
2.解构嵌套数组
//解构嵌套数组,就是模式匹配,只要等号两边的层级结构相同,就可以拿到对应位置的值
const arr = [1, [2, 3, [4, 5, 6]]];
const [a, [b, c, [d, e, f]]] = arr;
1
2
3
3 相同“模式”的不完全解构
let [a, b, c] = [1, 2, 3, 4]; // 1 2 3
let [a, b, c, d] = [1, 2, 3]; // 1 2 3 undefined
let [a, [b, c, [d, e]]] = [1, [2, 3, [4, 5, 6]]]; // 1 2 3 4 5
1
2
3
4.解构的默认值
let [a = true] = [];
a // true
1
2
注意
数组的解构是根据它的位置(模式)对应的
解构操作允许有默认值,但一定是已经声明的。
如果等号的右边不是数组(或者严格地说,不是可遍历的结构)那么将会报错
9…args剩余参数(扩展运算符)
展开运算:允许一个表达式在某处展开。展开运算符在多个参数(用于函数调用)或多个元素(用于数组字面量)或者多个变量(用于解构赋值)的地方可以使用。
- 函数调用中使用展开运算符(传参)
在ES6之前将整个数组里面的元素依次作为实参传递给函数形参的时候使用Function.prototype.apply的特性
let arr = [1,2,3]
function test(a,b,c){}
test.apply(null,args) //通过apply特性将数值的形式转换为数组对应传递进去
ES6之后展开运算符
let arr = [1,2,3]
function test(a,b,c){}
test(..arr) //将数组展开进行传递参数
- 数组中使用展开运算符(合并数组,类数组对象变成数组)
a.合并数组
let arr = [1,2,3]
let arr1 = [...arr,4,5,6] //1,2,3,4,5,6
b.展开运算符可以用于数组的一些方法中(push函数)
let arr = [1,2,3]
let arr1 = [4,5,6]
arr1.push(...arr) //4,5,6,1,2,3
c.类数组对象变成数组
let a=new Set([1,2,3,4,5,2,1]) // a : Set(5) {1, 2, 3, 4, 5}
let b=[...a] // (5) [1, 2, 3, 4, 5]
- 解构赋值(解构赋值中展开运算符只能用在最后)
let [arg1,arg2,...arg3] = [1, 2, 3, 4]
arg1 //1
arg2 //2
arg3 //['3','4']
- 对象中的展开运算符(和数组类似)
let {x,y,...z}={x:1,y:2,a:3,b:4};
x; //1
y; //2
z; //{a:3,b:4}
let z={a:3,b:4};
let n={x:1,y:2,...z};
n; //{x:1,y:2,a:3,b:4}
let a={x:1,y:2};
let b={z:3};
let ab={...a,...b};
ab //{x:1,y:2,z:3}
剩余参数:剩余参数语法允许我们将一个不定数量的参数表示为一个数组。
- 函数调用
function test(a,b,...args){} //...args == [4,5,6]
test(1,2,3,4,5,6)
- 解构赋值(解构赋值中展开运算符只能用在最后)
let [arg1,arg2,...arg3] = [1, 2, 3, 4]
arg1 //1
arg2 //2
arg3 //['3','4']
扩展运算符的作用及使用场景:
- 对象的扩展运算符
- (…)用于取出参数对象中的所有可遍历属性,拷 贝到当前对象之中。
- 等价于Object.assign 方法用于对象的合并,将源对象(source)的所有可 枚举属性,复制到目标对象(target)。Object.assign 方法的第一个参数是目标对象,后面的参数都是源对象。(如果目标对象与源对 象有同名属性,或多个源对象有同名属性,则后面的属性会覆盖前面 的属性)
- 数组扩展运算符
- 可以将一个数组转为用逗号分隔的参数序列,且每 次只能展开一层数组
- 将数组转换为参数序列
- 复制数组,连接数组(contact)
- 将字符串转为真正的数组(任何 Iterator 接口的对象,都可以用扩展运算符转为真正的数组)
- 函数中使用扩展运算符,可以使用在形参上代替argument
10.arguments 对象
- 定义:arguments是一个对应传递给函数参数的类数组对象,arguments对象是所有非箭头函数都有的一个局部变量。你可以使用arguments对象在函数中引用函数的参数。此对象包含传递给函数的每个参数,第一个参数在索引0处。
- 特性:
arguments对象并不是一个数组,除了length和索引元素之外,任何数组的属性都没有。当然它可以被转换为数组
const args = Array.from(arguments);
const args = [...arguments];
arguments存在属性callee:
属性callee相当于调用自身函数,可以用作匿名函数的递归:
var sum = function (n) {
if (1 == n){
return 1;
} else {
return n + arguments.callee(n - 1); //6 5 4 3 2 1 } } alert(sum(6)); 输出结果:21
}
- 作用:
a.无需明确命名参数,就可以重写函数,在函数代码中,使用特殊对象 arguments,开发者无需明确指出参数名,就能访问它们
function sayHi(message) {
alert(arguments[0]); // 此处将打印message参数的值
}
b.检测参数个数( arguments.length )
function howManyArgs() {
alert(arguments.length);
}
howManyArgs("string", 45);
howManyArgs();
howManyArgs(12); // 上面这段代码将依次显示 "2"、"0" 和 "1"。
c.针对同一个方法被多处调用,但是参数数量不确定的情况下,可以更具arguments索引进行判断。
function func1() {
console.log(arguments[0]); // 1
console.log(arguments[1]); // 2
console.log(arguments[2]); // 3
}
func1(1, 2, 3)
d.模拟函数重载
用 arguments 对象判断传递给函数的参数个数,即可模拟函数重载
当只有一个参数时,doAdd() 函数给参数加 5。如果有两个参数,则会把两个参数相加,返回它们的和。所以,doAdd(10) 输出的是 “15”,而 doAdd(40, 20) 输出的是 “60”。
function doAdd() {
if(arguments.length == 1) {
alert(arguments[0] + 5);
} else if(arguments.length == 2) {
alert(arguments[0] + arguments[1]);
}
}
doAdd(10); //输出 "15"
doAdd(40, 20); //输出 "60"
11. Promise以及底层封装
补充
异步编程出现原因
js是单线程,同一时刻只允许一个代码块执行,耗时任务时,UI渲染阻塞
- 概念上:promise是一门新的技术,E6中新出的规范,是用来解决JS中进行异步编程的新的解决方案(旧的方案是单纯的使用回调函数,会造成回调地狱)
- 表达上:是回调函数的形式,主要通过promise对象用来封装一个异步操作并可以获取其成功和失败的结果值
- 优势上:指定回调函数的方式更加灵活、支持链式调用,可以解决回调地狱问题
- 状态:pending 未决定的、resloved / fullfilled 成功、rejected 失败一旦改变不会再变
- 使用场景:异步AJAX请求,node中fs模块读取文件
12.浅拷贝深拷贝
拷贝数据的方式:
(1)直接赋值给一个变量
(2)Object.assign() 浅拷贝ES6新出接口相当于扩展运算符
(3)Array.prototype.slice() 浅拷贝
(4)Array.prototype.concat() 浅拷贝
(5)扩展运算符…arr 浅拷贝
(6)JSON.parse(JSON.stringify()) 深拷贝
(7)Loadsh库中的API,如:.clone()、.cloneDeep()浅拷贝深拷贝
浅拷贝(对象,数组)
特点:拷贝的时候只是拷贝了一份引用,修改拷贝以后的数据会影响原来的数据。
浅拷贝只是拷贝一层,更深层次对象级别的值拷贝引用
如何浅拷贝:1.直接赋值 2.遍历赋值 3.ES6的语法糖,object.assign(给谁,拷贝谁)只要一层就没有问题
let a = {
age: 1,
color:[1,2,3],
like:{
sing:4,
dance:5
}
}
let b = {}
Object.assign(b, a);
b.like.sing = 2;
console.log(a.like.sing) // 原本是4现在变成了2这就是浅拷贝拷贝了复杂类型的地址
深拷贝(深度克隆)
特点:拷贝的时候会生成一份新的数据,修改拷贝以后的数据不会原数据。
拷贝多层,每一层的数据都会拷贝
let a = {
age: 1,
color:[1,2,3],
like:{
sing:4,
dance:5
}
}
let b = {}
var deepCopy = function(newObj,oldObj){
for(let k in oldObj){
// k是属性名,oldObj[k]是属性值
// 进行递归判断
let item = oldObj[k]
if(item instanceof Array){
// 设置新的对象的属性名并且声明类型
newObj[k] = []
// 再次调用
deepCopy(newObj[k],item)
}else if(item instanceof Object){
newObj[k] = {}
// 再次调用
deepCopy(newObj[k],item)
}else {
newObj[k] = item
}
}
}
deepCopy(b,a)
、、、简化代码
function deepClone(obj) {
var myObj = obj instanceof Array ? [] : {}
for (let key in obj) {
myObj[key] = typeof (obj[key]) =='object'?deepClone(obj[key]): obj[key]
}
return myObj
}
补充:深拷贝其他的实现方法
JSON.parse(JSON.stringify(obj))也可以实现深拷贝,但是弊端较多,例如无法识别undefined,symbol
lodash库里面的API_.cloneDeep(value).clone()
13.组件之间的通信
1、props和emit
2、Ref和p a r e n t / parent/parent/children
3、eventBus\emit\on 兄弟、父子组件通信
4、$attrs\listeners 隔代通信
5、Provide\inject 隔代组件通信
6、Vuex
7、Pubsub库
父子组件props/e m i t / emit/emit/parent/ref/a t t r s 兄弟组件 attrs 兄弟组件attrs兄弟组件parent/$root/eventbus/vuex
跨层级关系eventbus/vuex/provide+inject
14.JS数据类型/数据结构
数据类型区分:
-
基本数据类型
- Symbol 代表创建后独一无二且不可变的数据类型,它主要是为了 解决可能出现的全局变量冲突的问题
- BigInt 是一种数字类型的数据,它可以表示任意精度格式的整数, 使用 BigInt 可以安全地存储和操作大整数,即使这个数已经超出了 Number 能够表示的安全整数范围。
- 为什么会有 BigInt 的提案:JavaScript 中 Number.MAX_SAFE_INTEGER 表示最⼤安全数字,计算 结果是 9007199254740991,即在这个数范围内不会出现精度丢失(⼩ 数除外)。但是⼀旦超过这个范围,js 就会出现计算不准确的情况, 这在⼤数计算的时候不得不依靠⼀些第三⽅库进⾏解决,因此官⽅提 出了 BigInt 来解决此问题。
-
引用数据类型
存储位置区分
- 栈:原始数据类型直接存储在栈(stack)中的简单数据段,占据空间 小、大小固定,属于被频繁使用数据,所以放入栈中存储;
- 堆:引用数据类型存储在堆(heap)中的对象,占据空间大、大小不固 定。如果存储在栈中,将会影响程序运行的性能;引用数据类型在栈 中存储了指针,该指针指向堆中该实体的起始地址。当解释器寻找引 用值时,会首先检索其在栈中的地址,取得地址后从堆中获得实体。
数据结构中
栈中数据的存取方式为先进后出。
堆是一个优先队列,是按优先级来进行排序的,优先级可以按照大小来规定。
操作系统中,内存被分为
栈区内存由编译器自动分配释放,存放函数的参数值,局部变量的 值等。其操作方式类似于数据结构中的栈
堆区内存一般由开发者分配释放,若开发者不释放,程序结束时可 能由垃圾回收机制回收
15.对象和数组
一、区别?
-
数组表示有序数据的集合,而对象表示无序数据的集合。如果数据的顺序很重要,就用数组,否则就用对象
-
数组是种有顺序的链表,对象是无顺序的键值对。
-
数组有length属性,对象没有
二、判断一个对象为空对象
使用 JSON 自带的.stringify 方法来判断
let obj = {}
console.log(JSON.stringify(obj) == "{}"); //true
ES6 新增的方法 Object.keys()来判断
let obj = {}
console.log(Object.keys(obj).length <= 0); //true
三、
16.数据类型的检测方式(包括区分对象和数组)
- typeof(数组,null和对象都是object,不能区分),能检测undefined、Boolean、number、string、object、function
- instanceof
instanceof 可以正确判断对象的类型,其内部运行机制是判断在其 原型链中能否找到该类型的原型。instanceof 运算符可以用来测试一个对象在其原型链 中是否存在一个构造函数的 prototype 属性 - 对象的constructor属性
constructor 有两个作用,一是判断数据的类型,二是对象实例通过 constrcutor 对象访问它的构造函数。需要注意,如果创建一个对象 来改变它的原型,constructor 就不能用来判断数据类型了
var arr = [1,2,3,1];
var obj = {
a:“A”,
c:“C”
}
console.log(arr.constructor === Array)//true
console.log(obj.constructor === Object) //true
- Array.isArray([]) //true ES6语法
- Object.prototype.toString.call(arr) 利用对象原型上的的toString可以准确判断是什么类型,call()改变this指向,这里是借用Object的方法
拓展:
1.为什么不用Object.toString?
答:虽然所有对象的原型链最顶端都是Object.prototype,但同时Object本身也是一个构造函数,继承于Function,而Array、function 等类 型作为 Object 的实例,都重写了 toString 方法。因此根据原型链上的知识,采用 obj.toString() 不能得到其对象类型,只能将 obj 转换为字符串类型;因此,在想要 得到对象的具体类型时,应该调用 Object 原型上的 toString 方法。
2.num.toString()//“123”
把数字转换为字符串
num.toString(2)//转换为二进制的字符串
17.instanceof手写实现
判断构造函数的原型是否出现在对象的原型链上的任何位置
// 判断构造函数的原型是否出现在对象的原型链上的任何位置
// left对象 right构造函数
function myInstanceof (left,right){
// 1.获取对象的原型
let proto = Object.getPrototypeOf(left)
// 2.获取构造函数的prototype对象
let prototype = right.prototype
while(true){
// 对象的原型不存在则返回或者递归判断的结束条件
if(!proto) return false
// 当构造函数的原型和对象的原型相等
if(proto = prototype) return true
// 如果没有找到,就继续从原型上去找,通过Object.getPrototype方法用来获取指定对象的原型
proto = Object.getPrototypeOf(proto)
}
}
18将一个数组转换为二进制
1.let a = x % n (n为几进制)
2. res += a
3. x / n
19.项目部署
系统开发:修改端口server.port = 80 打包
服务器准备、环境配置:云服务,阿里云—云服务器ECS—实例—创建实例—购买服务器(cpu ,内存,硬盘,操作系统镜像)—网络配置(分配公网IPV4地址,带宽,安全组中Linux端口22,window端口3389)—会有公网和私网IP,用户名和密码
在操纵系统中安装目标程序所需要的环境:用FTP工具将本地上传到服务器,建立连接—主机:公网IP,协议:SFTP/SSH 端口: 用户名: 密码:
程序部署及运行:打包–上传(FTP工具)—启动
域名绑定:字母与IP绑定,访问域名相当于访问IP地址,购买域名—域名备案(ICP)—域名解析(解析,添加记录:记录值是IP地址,域名)