## 1. 深拷贝浅拷贝
```javascript
浅拷贝:
1.浅拷贝只拷贝了父对象的基本类型的拷贝
2.如果父对象的属性是个对象或者数组
3.那么子对象获取的只是一个内存地址,而不是真正的拷贝。**(存在父对象被篡改的可能)**
//=======
深拷贝
1.能够实现真正意义上的数组和对象的拷贝。
2.只要递归调用浅拷贝
<script>
// 深拷贝拷贝多层, 每一级别的数据都会拷贝.
var obj = {
id: 1,
name: 'andy',
msg: {
age: 18
},
color: ['pink', 'red']
};
var o = {};
// 封装函数
function deepCopy(newobj, oldobj) {
for (var k in oldobj) {
// 判断我们的属性值属于那种数据类型
// 1. 获取属性值 oldobj[k]
var item = oldobj[k];
// 2. 判断这个值是否是数组
if (item instanceof Array) {
newobj[k] = [];
deepCopy(newobj[k], item)
} else if (item instanceof Object) {
// 3. 判断这个值是否是对象
newobj[k] = {};
deepCopy(newobj[k], item)
} else {
// 4. 属于简单数据类型
newobj[k] = item;
}
}
}
deepCopy(o, obj);
console.log(o);
var arr = [];
console.log(arr instanceof Object);
o.msg.age = 20;
console.log(obj);
</script>
2.谈谈Promise的理解
1.从深度来说
Promise是异步编程新的解决方案。(旧的异步编程解决方法是纯回调函数)
2.具体的说
(1) 从语法上说Promise是一个构造函数
(2) 从功能上来说Promise用来封装异步操作并获取其结果。
3.Promise的工作流程图
Promise只有三种状态,两种状态变化
1.pending 变为resolved
2.pending 变为rejected
pending 初始化状态
resolved 成功的状态
rejected 失败的状态
4.React 父子传值,兄弟传值
1. 父组件将向子组件传值 属性传递 props接收 props是只读的,不能更改
2. 子组件向父组件传值 子组件调用父组件方法,通过参数进行传值的。
3.兄弟组件传值
pubsub-js
PubSub.publish("事件名","数据") 相当于vue的 ($emit) 触发事件 (发送数据)
PubSub.subscribe("事件名",(msg,data)=>{ }) 相当于vue的 ($on) 监听事件 (接收数据)
5.setState第二个参数是有什么作用?同步还是异步?调用setState后都执行了那些操作
1. setState第二个参数是
(1)setState第二个参数是一个函数。
(2)该函数会在setState函数调用完成,并且组件开始重新渲染时调用
(3)可以用该函数监听组件是否渲染完成
2. setState同步还是异步
(1)由react引发的事件处理,调用setState不会同步更新this.state
(2)setState会调用同步更新this.state
3. 调用setState后都执行了那些操作
(1)将传递给setState的对象合并到组件的当前状态
(2)这将启动一个和解的过程,构建一个新的react树
(3)与上一个元素树进行对比,从而进行最小化的重渲染
5.let块级做用域
外层作用域无法读取内层作用域的变量;
{{{
{let tmp = "hello world";}
console.log(tmp); //error
}}}
6.react生命周期函数
react生命周期分为三大周期,11个阶段
1.创建时期的5大阶段
(1)getDefaultProps 获取默认属性数据
(2)getInitialState 获取初始化状态数据
(3)componentWillMount 组件即将被构建
(4)render 渲染组件
(5)componentDidMount 组件构建完成
2.存在时期的5大阶段
(1)componentWillReceiveProps 组件即将接收新的属性数据
(2)shouldComponentUpdate 判断组件是否应该更新
(3)componentWillUpdata 组件即将更新
(4)render 渲染组件
(5)componentDidUpdate 组件更新完成
3.销毁时期的1阶段
(1)componentWillUnmount 组件即将被销毁
7. redux 数据流向
8. Es6新增特性
1. 增加了let 关键字定义块作用域的变量
2. 增加了const关键字定义常量
3. 增加了class关键字定义类,使用extends实现继承
4. 支持解构语法
5. 函数定义不再使用function,而是使用箭头函数
9. 数组去重
1.使用原始方法遍历去重
2.使用es6新增特性Set元素唯一性去重
ar arr = [1,2,3,3,3,3,14]
var set = new Set(arr)
var arr = Array.from(set)
console.log(arr) // 1,2,3,14
3.filter结合indexOf
10. 原型(p67)
1.在js是通过原型实现继承的语言
2.js中原型也是一个对象
3.用原型可以实现对象属性的继承
4.js中每一个对象都包含了一个prototype内部属性
5.这个属性所对应的就是该对象的原型
11. 原型链(p103)
1.js中每一个对象都包含了一个prototype内部属性,称为原型
2.原型的值也是一个对象
3.因此他也有自己的原型
4.这样就串联起了一条原型链
5.原型链的链头是object,他的原型比较特殊,值为null.
12. var 和let和const的区别(p186)
1. var 声明的变量不支持块级作用域 支持前置声明 可以重复定义 值可以改动
2. let 声明的变量支持块级作用域 不支持前置声明 不可以重复定义 值可以改动
3. const声明的变量支持块级作用域 不支持前置声明 不可以重复定义 值无法改动
13. react性能优化
(1)重写shouldComponentUpdate来避免不必要的DOM操作
(2) 使用production版本的react.js
(3) 使用key来帮助React 识别列表中所有子组件的最小变化。
14. 回调地狱与解决方法
1.回调函数的嵌套调用 回调地狱
2.使用peomise的链式调用 解决方法
15. this的指向(js高级编程03)
<script>
// 函数的不同调用方式决定了this 的指向不同
// 1. 普通函数 this 指向window
function fn() {
console.log('普通函数的this' + this);
}
window.fn();
// 2. 对象的方法 this指向的是对象 o
var o = {
sayHi: function() {
console.log('对象方法的this:' + this);
}
}
o.sayHi();
// 3. 构造函数 this 指向 ldh 这个实例对象 原型对象里面的this 指向的也是 ldh这个实例对象
function Star() {};
Star.prototype.sing = function() {
}
var ldh = new Star();
// 4. 绑定事件函数 this 指向的是函数的调用者 btn这个按钮对象
var btn = document.querySelector('button');
btn.onclick = function() {
console.log('绑定时间函数的this:' + this);
};
// 5. 定时器函数 this 指向的也是window
window.setTimeout(function() {
console.log('定时器的this:' + this);
}, 1000);
// 6. 立即执行函数 this还是指向window
(function() {
console.log('立即执行函数的this' + this);
})();
</script>
16. 项目流程
项目流程
1)需求分析,产品根据需求出需求文档
2)根据需求文档,出原型图
3)ui根据原型图出设计稿
4)交给开发团队进行开发
a.根据需求,划分模块
b.项目估期,各个模块时间总和
c.根据模块划分责任人
d.确认接口,后端出接口文档,前端验收接口文档
e.建立代码仓库,上传项目到代码仓库(svn,git)
下载项目到本地进行开发,后端一般比前端慢,前端mock数据
f.后端开发完,前后端联调
5) 交给测试团队,进行测试
6) 交给安全团队
7) 交给运维,上线
17 vue组件传值
.vue的父子传值?
父:v-bind传,子:props接收
子:$emit("事件名",数据)传,父:v-on绑定事件名
----------------------------------------------------
父传子
属性 props 不能改的。
验证
type required validator default
子传父
this.$emit("事件","数据")
----------------------------------------------
+ 父组件给子组件传值
+ <组件 属性="传改子组件的值"></组件>(v-bind:属性="识别数据类型和变量" 不用v-bind 传递的都是字符串)
+ 子组件通过props选项接收值
+ props:["属性"]
+ ~~~js
props:{
属性:类型
}
props:{验证
属性:{
type:类型,
required:true 必须传递这个属性
default:值 设置默认值
validator(值){
return 布尔表达式
}
}
}
~~~
+ 父组件传给子组件的值不能更改,单向数据流(可以把这个值赋值给子组件data里的变量,可以更改子组件里data里的那个变量)
+ 子组件向父组件传值
+ 父组件先监听自定义事件 v-on:自定义事件
+ 子组件发送数据 this.$emit("事件的名字",“数据“)
+ 父组件通过执行事件处理函数,通过事件对象拿到子组件传过来的值
+ 兄弟组件的传值
+ 通过公共的父组件进行传值
+ 通过vuebus传值
+ Vue的实例上有连个方法 $on $emit
1) vuebus = new Vue() 也可以用公共的父组件 this.$parent this.$root
2) 发送 vuebus.$emit("事件","数据")
3) created vuebus.$on("事件",(data)=>{ }) data 就是拿到的数据
18 vue生命周期钩子函数
beforeCreate
created
beforeMount
mounted
beforeUpdate
updated
beforeDestroy
destroyed
activated
deactivated
-----------------------------------
组件整个生命周期能被自动调用的函数,就是生命周期的钩子函数
- beforeCreate
在实例初始化之后,数据观测 (data observer) 和 event/watcher 事件配置之前被调用。
访问不到数据
- created 在实例创建完成后被立即调用 可以获取数据
- beforeMount
~~~
在挂载开始之前被调用
可以访问数据 编译模板结束,虚拟dom已经存在
~~~
- mounted
可以拿到节点和数据 常用
实例被挂载后调用
ref表示节点
this.$refs.ref的标识 就可以拿到节点了
- beforeUpdate
~~~
数据更新时调用,发生在虚拟 DOM 打补丁之前
~~~
- updated
~~~
常用的 监控数据的变化
由于数据更改导致的虚拟 DOM 重新渲染和打补丁
watch 是监控特定数据的变化,updated是监控组件里所有数据的变化
~~~
- beforeDestroy实例销毁之前调用 清理资源,防止内存的泄露
- destroyed 实例销毁后调用。该钩子被调用后
19for…in与for…of的区别
for…in是遍历数组、对象的key
let arr = [1, 2, 3];
for (let i in arr) {
console.log(i)
}
let obj = {
name: ‘wuxiaodi’,
age: 18,
};
for (let i in obj) {
console.log(i)
}
输出结果:
如果想用for…in遍历值那就把JS代码改成这样:
let arr = [1, 2, 3];
for (let i in arr) {
console.log(arr[i])
}
let obj = {
name: ‘wuxiaodi’,
age: 18,
};
for (let i in obj) {
console.log(obj[i])
}
for…of是遍历数组的value
let arr = [1, 2, 3];
for (let i of arr) {
console.log(i)
}
20 forEach, map, filter方法区别
1, forEach循环,循环数组中每一个元素, 没有返回值, 可以不用知道数组长度
2, map函数,遍历数组每个元素,需要返回值,返回值组成新的数组,原数组不变
3,filter函数,有返回值, 过滤符合条件的元素,组成一个新数组, 原数组不变
21 移动布局自适应不同屏幕的几种方式
(1)相应式布局
(2)100%布局(弹性布局)
(3)等比缩放布局
22 项目中都有哪些模块
项目总工分为5大模块
- 登录模块
- 课程
- 应用中心
- 消息
- 我的
具体模块内容如下
## 1.登录模块
(1)验证码登录
(2)密码登录
## 2.课程模块
(1)课程列表
(2)课程表
(3)课程情况
- 课程准备
- 课程授课
- 课程结束
## 3.应用中心
(1)教师签到
- 考勤签到
- 补勤
- 签到统计
- 考勤日统计
- 考勤月统计
- 加班统计
(2)访客管理
- 访客列表
- 访客审核
- 访客详情
(3)新闻
- 新闻列表
- 新闻详情
- 新闻编辑
(4)通知
- 通知列表
- 通知详情
- 通知编辑
(5)设备管控
- 按设备管控
- 按教室管控
(6)一卡通管理
- 消费情况查看
- 一卡通充值
## 4.消息
(1)消息
- 消息列表
- 我的
- 通讯录
- 二唯码
- 聊天
- 个人聊天
- 聊天页面
- 个人资料
- 聊天设置
- 群聊
- 群聊页面
- 群聊资料
- 群聊设置
- 创建群聊
- 按规则创建
- 自定义创建
(2)通知
- 通知列表
## 5.我的
(1)我的
(2)个人信息修改
- 个人信息修改
- 手机号修改
(3)密码修改
(4)角色切换
(5)检查更新
(6)设置
- 提醒设置
- 意见反馈
- 责任声明
- 关于
(7)退出登录
23 项目技术栈
1. 使用react框架,完成各项功能组件;
2. 使用ant Design Mobile组件库实现一些效果
3. 使用webpack + React + react-router + react-redux 技术栈来进行项目开发;
4. 通过react-swiper实现轮播图效果。
24 项目中遇到的问题
1.我们在做完一个功能模块后甲方需求变动了,
2.所以我们只能硬着头皮改代码,连续加班半个月,
3.这个事情本来可避免的,
4.如果我们最初能在和甲方对需求的时候能挖掘他们的潜在需求,
5.早点给他们一些正确的需求建议,这个问题可能就不存在了
25 浏览器兼容问题
我们在开发的时候会明确项目要兼容哪些浏览器的最低版本,我之前的项目要求兼容IE8.0以上的版本,Chrome 48以上,FireFox 44以上。有了这些最基本的要求,在开发中就是要考虑到CSS样式和JavaScript的在这些浏览器的兼容性了
(一)html部分
1.H5新标签在IE9以下的浏览器识别
<!--[if lt IE 9]>
<script type="text/javascript" src="js/html5shiv.js"></script>
<![endif]-->
html5shiv.js下载地址
https://github.com/aFarkas/html5shiv/releases
2.ul标签内外边距问题ul标签在IE6\IE7中,有个默认的外边距,但是在IE8以上及其他浏览器中有个默认的内边距。解决方法:统一设置ul的内外边距为0
(二)CSS样式的兼容性
1.css的hack问题:主要针对IE的不同版本,不同的浏览器的写法不同
IE的条件注释hack:
<!--[if IE 6]>此处内容只有IE6.0可见<![endif]-->
<!--[if IE 7]>此处内容只有IE7.0可见<![endif]-->
2.IE6双边距问题:IE6在浮动后,又有横向的margin,此时,该元素的外边距是其值的2倍
解决办法:display:block;
3.IE6下图片的下方有空隙
解决方法:给img设置display:block;
4.IE6下两个float之间会有个3px的bug
解决办法:给右边的元素也设置float:left;
5.IE6下没有min-width的概念,其默认的width就是min-width
6.IE6下在使用margin:0 auto;无法使其居中
解决办法:为其父容器设置text-align:center;
7.被点击过后的超链接不再具有hover和active属性
解决办法:按lvha的顺序书写css样式,
":link": a标签还未被访问的状态;
":visited": a标签已被访问过的状态;
":hover": 鼠标悬停在a标签上的状态;
":active": a标签被鼠标按着时的状态;
8.在使用绝对定位或者相对定位后,IE中设置z-index失效,原因是因为其元素依赖于父元素的z-index,但是父元素默认为0, 子高父低,所以不会改变显示的顺序
9.IE6下无法设置1px的行高,原因是由其默认行高引起的
解决办法:为期设置overflow:hidden;或者line-height:1px;
(三)JavaScript的兼容性
1.标准的事件绑定方法函数为addEventListener,但IE下是attachEvent;
2.事件的捕获方式不一致,标准浏览器是由外至内,而IE是由内到外,但是最后的结果是将IE的标准定为标准
3.window.event获取的。并且获取目标元素的方法也不同,标准浏览器是event.target,而IE下是event.srcElement
4.在低版本的IE中获取的日期处理函数的值不是与1900的差值,但是在高版本的IE中和标准浏览器保持了一致,获取的值也是与1900的差值。
比如:var year= new Date().getYear();
5.ajax的实现方式不同,这个我所理解的是获取XMLHttpRequest的不同,IE下是activeXObject
6.IE中不能操作tr的innerHtml7.获得DOM节点的父节点、子节点的方式不同
其他浏览器:parentNode parentNode.childNodes
IE:parentElement parentElement.children
26 vue双向绑定原理
1.vue数据双向绑定是通过结合发布者-订阅者模式的方式来实现的。
2.通过Object.defineProperty()为各个属性定义get、set方法。
3.在数据发生改变时给订阅者发布消息
4.触发相应的监听回调
var obj = {}
Object.defineProperty(obj,'name',{
get:function(){
console.log("获取了");
},
set:function(){
console.log('修改了');
}
})
obj.name= 'fei';
obj.name
Object.defineProperty( )是用来做什么的?它可以来控制一个对象属性的一些特有操作,比如读写权、是否可以枚举。
思路分析
实现mvvm主要包含两个方面,数据变化更新视图,视图变化更新数据:
关键点在于data如何更新view,因为view更新data其实可以通过事件监听即可,比如input标签监听 'input' 事件就可以实现了。所以我们着重来分析下,当数据改变,如何更新视图的。
数据更新视图的重点是如何知道数据变了,只要知道数据变了,那么接下去的事都好处理。如何知道数据变了,其实上文我们已经给出答案了,就是通过Object.defineProperty( )对属性设置一个set函数,当数据改变了就会来触发这个函数,所以我们只要将一些需要更新的方法放在这里面就可以实现data更新view了。
思路有了,接下去就是实现过程了。
实现过程
我们已经知道实现数据的双向绑定,首先要对数据进行劫持监听,所以我们需要设置一个监听器Observer,用来监听所有属性。如果属性发上变化了,就需要告诉订阅者Watcher看是否需要更新。因为订阅者是有很多个,所以我们需要有一个消息订阅器Dep来专门收集这些订阅者,然后在监听器Observer和订阅者Watcher之间进行统一管理的。接着,我们还需要有一个指令解析器Compile,对每个节点元素进行扫描和解析,将相关指令对应初始化成一个订阅者Watcher,并替换模板数据或者绑定相应的函数,此时当订阅者Watcher接收到相应属性的变化,就会执行对应的更新函数,从而更新视图。因此接下去我们执行以下3个步骤,实现数据的双向绑定:
1.实现一个监听器Observer,用来劫持并监听所有属性,如果有变动的,就通知订阅者。
2.实现一个订阅者Watcher,可以收到属性的变化通知并执行相应的函数,从而更新视图。
3.实现一个解析器Compile,可以扫描和解析每个节点的相关指令,并根据初始化模板数据以及初始化相应的订阅器。
流程图如下:
functionobjServer(obj){
let keys = Object.keys(obj);
keys.forEach((item)=>{
definedActive(obj,item,obj[item])
})
returnobj;
}
functiondefinedActive(obj,item,val){
Object.defineProperty(obj,item,{
get(){
console.log(`${item}获取了`)
},
set(newVlaue){
val = newVlaue;
console.log(`${item}修改了`)
}
})
}
let obj = objServer({
a:1,
b:2
})
obj.a
obj.b
obj.a = 2;
obj.b = 3;
27 vue和react的区别
=================相似处
1.都是用于创建UI的JS库
2.都是快速和轻量级的代码库
3.都是基于组件架构
4.都使用了虚拟DOM
5.都可以放在单独的html文件中
====================区别
1.vue通常使用HTML模板文件
2.react完全使用JS创建的虚拟DOM
3.vue的性能优于react,一般用于构建小而快的项目
4.react一般用于构建大型应用程序
5.都可以放在单独的html文件中
28 性能优化
1.减少HTTP请求次数
2.在前端尽量用变量保存Ajax的请求结果,减少请求次数
3.较少的使用全局变量
4.避免使用CSS表达式
5.避免在页面的主题布局中使用表
6.避免空的 src 和 href
7.精简 CSS 和 JS
8.对于多次http请求的数据,用localStorage或者sessionStorage进行缓存。
9.同步js请求编程异步js请求。
10.尽量减少 iframe 的个数
29 call和apply的区别与bind区别
相同的地方------------------》改变函数运行的上下文
1.都是在函数执行的时候,动态改变函数的运行环境
区别-----------------------》参数
1.call从第二个参数开始,每一个参数会依次传递给调用函数,
2.apply,只有两个参数,第二个参数是数组,数组中每一个成员会依次传递给调用函数
bind怎么传参呢?它可以像call那样传参。
xw.say.bind(xh,"实验小学","六年级")();
复制代码
但是由于bind返回的仍然是一个函数,所以我们还可以在调用的时候再进行传参。
30 项目经验
1.我这个项目包含几个模块
2.具体一个模块怎么做的
31 什么是闭包
闭包就是能够读取其他函数内部变量的函数
1.使用闭包主要是为了设计私有的方法和变量
2.闭包的优点是可以避免全局变量的污染
3.使用闭包的缺点,闭包会常驻内存,增加内存的使用量,使用不当容易造成内存泄漏
闭包有三种特性
1.函数嵌套调用
2.在函数内部可以引起外部的参数和变量
3.参数和变量不会以垃圾回收机制回收。