面试总结
# 面试总结
# 事件冒泡
阻止事件冒泡的两种方式:
event.stopPropagation()
$("#button").click( function(event){ alert("button-click"); // 阻止事件冒泡到DOM树上 event.stopPropagation(); } );
1
2
3
4
5event.target
$('#switcher').click(function(event){ if(event.target==this){//判断是否是当前绑定事件的元素元素触发的该事件 $('#switcher .button').toggleClass('hidden'); } })
1
2
3
4
5写入
return false
,虽然阻止了冒泡,但是也阻止了默认行为
event.preventDefault()
是阻止元素事件的默认行为,不能阻止冒泡
# 节点
# 克隆节点
使用
cloneNode(bool deep)
方法,deep为true的时候会将节点的子节点都一起克隆window.onload = function () { var sourceNode = document.getElementById("div-0"); // 获得被克隆的节点对象 for (var i = 1; i < 5; i++) { var clonedNode = sourceNode.cloneNode(true); // 克隆节点 clonedNode.setAttribute("id", "div-" + i); // 修改一下id 值,避免id 重复 sourceNode.parentNode.appendChild(clonedNode); // 在父节点插入克隆的节点 } }
1
2
3
4
5
6
7
8
# 移动节点
使用jQuery的
before()
方法或after()
方法:$(function(){ $('.move-up').click(function(){ var cur_li = $('.move'); var prev_li = cur_li.prev(); // prev()方法:获取当前节点的上一个节点,返回数组 if(prev_li.length != 0){ prev_li.before(cur_li); // 调用before()方法向上移动节点 } else { alert("元素已经到顶部!"); } }); });
1
2
3
4
5
6
7
8
9
10
11
12
13$(function(){ $('.move-down').click(function(){ var cur_li = $('.move'); var next_li = cur_li.next(); // next()方法:获取当前节点的下一个节点,返回数组 if(next_li.length != 0){ next_li.after(cur_li); // 调用after()方法向下移动节点 } else { alert("元素节点已经到底部!"); } });
1
2
3
4
5
6
7
8
9
10
11参考网址
# IE兼容
参考网址
方法:
明确你要兼容的浏览器范围
检查伪类和伪元素:IE9是不支持伪类的,所以不能用伪类
:after
的方法去清除浮动,只能用clear:both;
判断浏览器类型:
// navigator对象是浏览器对象,包含浏览器的相关信息 if(navigator.userAgent.indexOf('Opera') != -1) { alert('Opera'); } else if(navigator.userAgent.indexOf('MSIE') != -1) { alert('Internet Explorer'); } else if(navigator.userAgent.indexOf('Firefox') != -1) { alert('Firefox'); } else if(navigator.userAgent.indexOf('Netscape') != -1) { alert('Netscape'); } else if(navigator.userAgent.indexOf('Safari') != -1) { alert('Safari'); } else{ alert('无法识别的浏览器。'); }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# HTML相关
# HTML5新增标签
# SEO优化
- 三大标签:title(百度不超过28个中文)、description(不超过120个字)、Keywords(6-8个关键词)
# css 问题
# CSS基础
轮廓
outline
- 和border的用法一样,不过设置outline不会影响盒子大小和布局
阴影效果
box-shadow(x, y, 模糊半径(可省), color)
# 定位
相对定位
relative
- 相对定位是相对于元素在文档流中的位置决定的
- 相对定位会提升元素的层级
- 相对定位元素不会使元素脱离文档流
- 相对定位不会改变元素的性质,块还是块,行内还是行内
绝对定位
absolute
元素从文档流脱离
元素性质改变,行内变成块,块的高度被内容撑开
会提升层级
是相对于最近的、已定位的父元素进行定位
注意点:
left | right | top | bottom 的默认值是auto
水平布局公式变为:
left/right + margin-left/right + border-left/right + padding-left/right + width = 包含块的宽度
垂直布局公式变为:
top + margin-top/bottom + border-top/bottom + padding-top/bottom + height = 包含块的高度
利用以上几点,可以实现水平垂直居中
.parent { background-color: red; position: relative; width: 500px; height: 500px; margin: 100px auto; } .child { background-color: green; width: 100px; height: 100px; position: absolute; margin: auto; top: 0; bottom: 0; left: 0; right: 0; }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
固定定位
fixed
- 固定定位也是一种绝对定位,大部分特点和绝对定位一样
- 不同的是固定定位是参考于浏览器的视口进行定位
粘性定位
sticky
- 使元素到达某个位置的时候固定
# 溢出省略号
- 单行文本
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
2
3
- 多行文本
overflow : hidden;
text-overflow: ellipsis表示用省略号; /*ellipsis表示用省略号表示被裁剪的文本*/
display: -webkit-box; /*伸缩盒子*/
-webkit-line-clamp: 2; /*限制一个块元素显示的文本行数*/
-webkit-box-orient: vertical;/*伸缩盒子子元素的排列方式vertical-垂直*/
2
3
4
5
# 隐藏元素
- 参考网址
{display: none;}
不占据空间,无法点击{visibility: hidden;}
占据空间,无法点击{visibility: hidden; position: absolute;}
不占据空间,无法点击- 将该元素改为透明,用
opacity:0;
display: none
和visibility: hidden
的区别:- display不占据空间,visibility占据空间
- display在隐藏的时候会重绘
# 水平垂直居中的方法
单行文本可以使用
height
和line-height
方法来实现position
+margin
:需要知道子元素的宽高.parent { position: relatiive; } .child { position: absolute; height: 100px; top: 50%; left: 50%; margin: -50px 0 0 -50px; }
1
2
3
4
5
6
7
8
9
10
11position
+transform
:不需要知道子元素的宽高.parent { position: relatiive; } .child { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); }
1
2
3
4
5
6
7
8
9
10使用flex布局:只能在IE10+使用
.parent { display: flex; justify-content: center; align-items: center; } .child { }
1
2
3
4
5
6
7
8
# CSS3新增的属性
- 边框、背景、渐变、阴影、2D转换、3D转换
# 选择器
# 常用选择器
- id、class、标签、通配符、属性
# 组合选择器
- 后代选择器
A B
- 子选择器
A > B
- 相邻兄弟选择器
A + B
- 普通兄弟选择器
A ~ B
# 伪类选择器
伪类用 :
- active、checked、focus、hover
- first-child/last-child/nth-child
- first-of-type/last-of-type/nth-of-type
# 伪元素选择器
伪元素用 ::
first-letter
/*实现首字母下沉*/ p::first-letter { font-size: 50px; } <p>Lorem is dash</p>
1
2
3
4
5
6first-line:选中第一行
first-selection:鼠标选中的时候
before和after
# 选择器权重
选择器 | 权重 |
---|---|
!important | 无穷大 |
内联样式 | 1000 |
id选择器 | 100 |
class | 属性 | 伪类 | 10 |
标签选择器 | 伪元素 | 1 |
通配符选择器 | 0 |
# 清除浮动
作用:
- 不让块级元素被浮动元素覆盖,而是在浮动元素下单独一行
- 不需要文字环绕效果
- 解决浮动导致的高度坍塌问题
方法(解决高度塌陷):
设置父元素为固定高度
向父元素的末尾插入一个div,并设置
clear:both
伪元素清除(类似方法2)
#parent::after { content: ""; display: block | table; clear: both; }
1
2
3
4
5
6将父元素变为BFC,如设置父元素
overflow: hidden
方法(解决父子元素外边距重叠)
将父元素变为BFC,如设置父元素
overflow: hidden
伪元素清除法
.con { width: 200px; height: 200px; background-color: #ff0000; } .con::before { content: ""; display: table; } .box1 { width: 100px; height: 100px; background-color: #0000ff; margin-top: 100px; }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# BFC
- 概念:页面中的一块独立的渲染区域,有自己的渲染规则,决定了内部元素的排列方式
- 规则:
- 内部box会在垂直方向一个一个放置
- 子元素和父元素的外边框不会合并
- 开启BFC的元素不会被浮动元素所覆盖
- 计算BFC高度的时候,浮动元素也参与计算(用来解决高度塌陷问题)
- 是页面上一个独立的容器,容器里的子元素和外面的元素不会互相影响
- 产生BFC的方式:
- 根元素
- float不为none
- position为absolute或fixed
- display为inline-block
- overflow不为visible
# 移动端适配
# 几个概念
视口viewport:浏览器可显示区域的大小
<meta name="viewport" content="width=device-width,initial-scale=1"/>
1
2物理像素:手机屏幕的最小单元,如分辨率为
1980*1080
表示1980个物理像素点*1080个像素点
设备像素比(dpr):
dpr = 物理像素/设备独立像素(视口宽度)
,放大网页会变大,缩小网页会减小px:设备独立像素,css像素
em:1em = 当前对象的font-size
rem:1rem = 根元素的font-size
vw:100vw = 视口宽度,1vw = 1%视口宽度,在iphone里面视口宽度是375,所有drp=2
# JS题目
# 反转字符串
let str = "adbfka ded";
let reStr = [...str].reverse().join("");
2
3
# 数组去重
Set结构去重
// 不能用于数组对象 let unique = [...new Set(array)];
1
2
3遍历:不能用于数组对象,而且不能辨别NaN
let arr1 = [1,1,'1','1',NaN,NaN,undefined,undefined,null,null]; let unique = arr => { let newArr = []; arr1.foreach(item => { if(newArr.indexOf(item) < 0){ aNnewArr.push(item); } }) return newArr; } console.log(unique); //输出:[1,'1',NaN,NaN,undefined,null] //NaN没有去重
1
2
3
4
5
6
7
8
9
10
11
12
# 判断变量类型
function getType(value){
if(Object.prototype.toString.call(value) === "[Object Object]"){
console.log("value是对象");
}else if(Object.prototype.toString.call(value) === "[Object Array]"){
console.log("value是数组");
}
}
2
3
4
5
6
7
8
# 深拷贝和浅拷贝
区别:js的数据类型分为基础类型(Number、String、Boolean等)和引用类型(Object、Array),浅拷贝只复制了对象的引用地址,修改拷贝之后对象的值,被拷贝的对象的值也会改变,深拷贝把对象和值复制过来,修改其中一个对象的值,另一个对象不会受到影响
深拷贝实现
function deepCopy(obj) { let newObj = {}; let flag = false; if (Array.isArray(obj)) { newObj = []; flag = true; } for (let key in obj) { if (flag) { newObj.push(typeof obj[key] == "object" && obj[key] != null ? deepCopy(obj[key]) : obj[key]); } else { newObj[key] = typeof obj[key] == "object" && obj[key] != null ? deepCopy(obj[key]) : obj[key]; } } return newObj; }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 取出url参数
function getQuery(url) {
url = url.replace(/((.*)\?)|(#(.*))/g, "");
let obj = {};
let arr = url.split("&")
if(arr[0] == ""){
return obj;
}
arr.forEach(item => {
let kv = item.split("=");
obj[kv[0]] = kv[1];
});
return obj;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
# 防抖和节流
# 概念
- 防抖:就是指触发事件后在 n 秒内函数只能执行一次,如果在 n 秒内又触发了事件,则会重新计算函数执行时间。
- 节流:指连续触发事件但是在 n 秒中只执行一次函数
# 实现
防抖(debounce)
/** * @desc 函数防抖 * @param func 函数 * @param wait 延迟执行毫秒数 * @param immediate true 表立即执行,false 表非立即执行 */ function debounce(func,wait,immediate) { let timeout; return function () { let context = this; let args = arguments; if (timeout) clearTimeout(timeout); if (immediate) { // 第一次触发事件之后立即执行,在延迟执行毫秒数内触发不会重复执行 var callNow = !timeout; timeout = setTimeout(() => { timeout = null; }, wait) if (callNow) func.apply(context, args) } else { // 最后一次触发事件的延迟执行毫秒数后再执行 timeout = setTimeout(function(){ func.apply(context, args) }, wait); } } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29节流(throttle):实现方式有事件戳或定时器
// 时间戳版 function throttle(func, wait) { let prev = 0; return function() { let now = new Date(); let context = this; let args = arguments; if (now - prev > wait) { func.apply(context, args); prev = now; } } } // 定时器版 function throttle(func, wait) { let timeout; return function() { let context = this; let args = arguments; if (!timeout) { timeout = setTimeout(() => { timeout = null; func.apply(context, args); }, wait) } } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 设计模式
# 设计模式的基本原则
- 单一职责原则
- 开放封闭原则
- 接口隔离原则
- 里式替换原则
- 依赖倒置原则
- 迪米特法原则
# 单例模式
保证一个类只有一个实例,并且提供一个全局的访问点。
适用场景:弹窗,无论点多少次,弹窗只应该被创建一次
class CreateUser {
constructor(name) {
this.name = name;
}
getName() {
return this.name;
}
}
// 代理实现单例模式
var ProxyMode = (function() {
var instance = null;
return function(name) {
if(!instance) {
instance = new CreateUser(name);
}
return instance;
}
})();
// 测试
var a = new ProxyMode("aaaa");
var b = new ProxyMode("bbb");
console.log(a === b); // 输出true
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 工厂模式
- 是一种创建对象的设计模式,不对外暴露创建对象的逻辑,而是把这些逻辑封装在一个函数里
- 使用场景:请求的封装
function PersonFactory(name) { // 工厂函数
let obj = new Object();
obj.name = name;
obj.sayName = function(){
return this.name;
}
return obj;
}
let person1 = new PersonFactory("张三");
let person2 = new PersonFactory("李四");
console.log(person1.name); // 张三
console.log(person1.sayName()); // 张三
console.log(person2.name); // 李四
console.log(person2.sayName()); // 李四
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 创建者模式
类似工厂模式,不过更加注重创建对象的细节,可以创建出复杂对象或者复合对象
使用场景:对接口返回的数据进行处理
// 原数据 var data = { name: 'li si' } // 创建者模式函数 function FnData (data) { var p = new Person(data) p.nameObj = new NameObj(data.name) return p } function Person (data) { this.name = data.name } function NameObj (name) { this.fullname = name this.firstname = name.split(' ')[0] this.secondname = name.split(' ')[1] } // 测试 var _data = FnData(data) console.log(_data)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 装饰器模式
希望在不改变原对象的基础上,通过对其扩展功能和属性来实现更复杂的逻辑
使用场景:扩展某个对象的功能
function Person (money) { this.money = money } // 添加收入 function addMoney (person) { person.money += 2; } // 减少收入 function reduceMoney (person) { person.money -= 10; } // 测试 var p = new Person(100) addMoney(p) reduceMoney(p) console.log(p)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 组合模式
- 用于将多个部分组合成一个整体
- 适用场景:创建表单,表单的输入框、下拉框等可以自由组合
# 策略模式
对不同的输入采取不同的策略,如果有需求变更,只需要添加或修改策略
使用场景:使用object代替重复的if...else代码
// if...else function showStar (level) { if (level === 'A') { return '5颗星' } else if (level === 'B') { return '4颗星' } else if (level === 'C') { return '3颗星' } else { return '' } } // 使用策略模式 function showStarFn (level) { const resultMap = { A: '5颗星', B: '4颗星', C: '3颗星', de } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 观察者模式
观察者模式也称发布订阅模式、消息模式,消息中心是主体,当满足触发条件时,会通知订阅的个体
使用场景:onClick事件绑定方法、vue里的watch
var msgCenter = (function () { var registerList = {} return { // 订阅消息 register: function (key, fn) { if (typeof fn !== 'function') { console.log('请添加函数'); return; } if (!registerList[key]) { registerList[key] = []; } registerList[key].push(fn); }, // 发布消息 trigger: function (key, ...rest) { const funList = registerList[key]; if (!(funList && funList.length)) { return false; } funList.forEach(fn => { fn.apply(this, rest); }); } } })() // 测试 msgCenter.register('click', () => { console.log('我订阅了') }); msgCenter.register('click', () => { console.log('我也订阅了') }); msgCenter.trigger('click'); // 输出:我订阅了 我也订阅了
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# 原型和原型链
# 创建对象
- new Object
var p = new Obejct();
p.name = 'hh';
p.age = 18;
p.eat = function () {
console.log('吃饭');
}
2
3
4
5
6
7
- 字面量
var p = {
name: 'hh',
age: 18,
eat: function () {
console.log('吃饭');
}
}
2
3
4
5
6
7
8
- 工厂模式
function createP (name, age) {
var obj = new Obejct();
obj.name = name;
obj.age = age;
obj.eat = function () {
console.log('吃饭');
}
}
var pp = createP('hehe', 19);
var ppp = createP('pp', 18);
2
3
4
5
6
7
8
9
10
11
缺点:不能判断类型,所有的对象都是Object类型
typeof pp
输出Objectpp instanceof Object
输出true
- 构造函数
function Person (name, age) {
this.name = name;
this.age = age;
this.eat = function () {
console.log('吃饭');
}
}
var p = new Person('hehe', 18);
var p2 = new Person('hehe', 18);
2
3
4
5
6
7
8
9
10
- 构造函数创建对象的过程(new 的时候发生了什么)
- 在内存中创建一个空对象
- 对象的
__proto__
属性指向构造函数的prototype
- 设置构造函数的this,让this指向刚刚创建好的空对象
- 按顺序执行构造函数中的代码(可以执行构造函数中的函数)
- 返回这个对象(把this返回)
获取对象的具体类型用
instanceof
,不能用typeof
typeof p
输出"object"p instanceof Object
输出truep instanceof Person
输出truep.constructor === Person
输出true,但不建议使用,因为构造器可以被改变
p 和 p2 的 eat() 方法是不一样的,在不同的内存中存储
p.eat() === p2.eat()
输出false
# 原型链
# 概念
构造函数、实例对象、构造函数的原型对象、Object构造函数、Object原型对象、null
构造函数和实例对象
- 通过构造函数创建实例对象
构造函数和构造函数的原型对象
- 构造函数里面具有该构造函数的原型对象
prototype
- 构造函数的原型对象的
constructor
指向了构造函数
- 构造函数里面具有该构造函数的原型对象
实例对象和构造函数的原型对象
- 实例对象中的
__proto__
指向了它的构造函数的原型对象prototype
,即:p.__proto__ === Person.prototype
- 实例对象中的
构造函数的原型对象和Object原型对象
- 构造函数的原型对象
Person.prototype
里面有__proto__
属性,说明原型对象也是一个对象,说明是被某个构造函数实例化出来的,这个构造函数就是Object构造函数,所以构造函数的原型对象的__proto__
(注意,不是构造函数的__proto__
) 指向Object构造函数的原型对象prototype
,即:Person.prototype.__proto__ === Object.prototype
- 构造函数的原型对象
Object原型对象和null
- Object原型对象也是个对象,但是里面没有
__proto__
属性,所以Object.prototypr.__proto__ === null
,即Object原型对象的__proto__
指向了null
- Object原型对象也是个对象,但是里面没有
总结:
实例对象的原型对象等于其构造函数的原型对象,即:
p.__proto__ === Person.prototype
构造函数的原型对象的原型对象等于Object构造函数的原型对象,即:
Person.prototype.__proto__ === Object.prototype
实例对象的原型对象的原型对象等于Object构造函数的原型对象,即:
p.__proto__.__proto__ === Object.prototype
Object原型对象的原型对象为null,即:
Object.prototype.__proto__ === null
# 属性查找规则
读取属性
- 在实例对象本身先查找,如果没有找到,会在原型链上查找,一直找到Object原型
设置属性:
- 使用
p.test = 'hehe'
如果没有这个属性,不会在原型链上查找,直接在这个对象本身设置
- 使用
例:
Function.prototype.a = function () { console.log(4); } function Foo () { Foo.a = function () { console.log(1); } this.a = function () { console.log(2);2 } } Foo.prototype.a = function () { console.log(3); } Foo.a(); // 输出4 var foo = new Foo(); // 会执行构造函数里的代码,给Foo添加实例属性 Foo.a(); // 输出1 foo.a(); // 输出
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 继承
# 概念
类型和类型之间的关系
# 目的
把子类中的成员提取到父类中,实现代码重用
# 方式
- 原型继承
function Person (name, age) {
this.name = name;
this.age = age;
this.eat = function () {
console.log('吃饭');
}
}
Person.prototype.sleep = function () {
console.log('睡觉');
}
function Child (sex) {
this.sex = sex;
}
// 将原型对象设为父类的实例
Child.prototype = new Person();
// 将构造器指向自身的构造函数
Child.prototype.constructor = Child;
var c = new Child('男');
var p = new Person('人类总的', 10);
console.dir(c);
console.dir(p);
console.log(c instanceof Child);
console.log(c instanceof Person);
console.log(c instanceof Object);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
- 输出:
- 缺点:
- 无法设置父类属性的值
- 注意:
- 子类实例对象的原型对象的原型对象 === 父类构造函数的原型对象,所以
子类实例对象 instanceof 父类 === true
- 子类实例对象的原型对象的原型对象 === 父类构造函数的原型对象,所以
借用构造函数
function Person(name, age) { this.name = name; this.age = age; this.eat = function () { console.log('吃饭'); } } Person.prototype.sleep = function () { console.log('睡觉'); } // name和age是父类的属性,sex是子类的属性 function Child(name, age, sex) { // 借用构造函数,使用call方法 Person.call(this, name, age); this.sex = sex; } var c = new Child('我是子类', 18, '男'); var p = new Person('我是父类', 10); console.dir(c); console.dir(p); console.log(c instanceof Child); console.log(c instanceof Person); console.log(c instanceof Object);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24输出:
- 缺点:
- 父类的原型对象的属性和方法无法继承
- 注意:
- 父类的非原型对象的属性和方法会直接添加到子类的实例对象上
- 子类实例对象的原型对象的原型对象 === Object的原型对象,非父类的原型对象,即子类与父类无原型链上的关联,所以
子类实例对象 instanceof 父类 === false
组合继承 (借用构造函数继承 + 原型继承)
function Person(name, age) { this.name = name; this.age = age; this.eat = function () { console.log('吃饭'); } } Person.prototype.sleep = function () { console.log('睡觉'); } function Child(name, age, sex) { // 借用构造函数继承 Person.call(this, name, age); this.sex = sex; } // 原型继承,不要等于Person.prototype Child.prototype = new Person(); // 添加构造器 Child.prototype.constructor = Child; // 子类的原型对象私有的方法 Child.prototype.drinknainai = function () { console.log('喝奶'); } var c = new Child('我是子类', 18, '男'); var p = new Person('我是父类', 10); console.dir(c); console.dir(p); console.log(c instanceof Child); console.log(c instanceof Person); console.log(c instanceof Object);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
输出:
- 缺点:
__proto__
的属性没有用- 执行了两次父类构造函数
注意:
- 在使用原型继承的时候,不用使用
子类.prototype = 父类.prototype
,如Child.prototype = Person.prototype
,因为会和父类的原型对象共用一个地址,导致子类无法在自己的原型对象上添加自己私有的属性和方法,一旦添加,父类的原型对象也会改变,导致混乱
- 在使用原型继承的时候,不用使用
寄生组合继承
function Person(name, age) { this.name = name; this.age = age; this.eat = function () { console.log('吃饭'); } } Person.prototype.sleep = function () { console.log('睡觉'); } function Child(name, age, sex) { // 借用构造函数继承 Person.call(this, name, age); this.sex = sex; } // 创造一个无自身属性方法新函数,原型指向父类的原型 function Fn() {}; Fn.prototype = Person.prototype; // 将子类的原型指向新函数的实例对象 Child.prototype = new Fn(); // 添加构造器 Child.prototype.constructor = Child; // 子类的原型对象私有的方法 Child.prototype.drinknainai = function () { console.log('喝奶'); } var c = new Child('我是子类', 18, '男'); var p = new Person('我是父类', 10); console.dir(c); console.dir(p); console.log(c instanceof Child); console.log(c instanceof Person); console.log(c instanceof Object);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34输出:
# 网络相关
# HTTP状态码
1xx:1开头是信息状态码
2xx: 2开头是请求成功,如200
3xx: 3开头是重定向:301-永久移动,302-临时移动,307-临时重定向
4xx: 4开头是客户端错误:400-请求语法错误,401-未授权,403-禁止访问,404-找不到,
5xx: 5开头是服务器错误:500-服务端内部错误,502-错误网关
# 跨域
- 为什么跨域:因为浏览器的同源策略,不能在一个域下访问另一个域的资源,同源是指:协议+域名+端口号相同
- 解决方法:
- jsonp:利用html的script标签可以访问外域的原理,通过动态创建script标签,设置标签的src属性,再通过回调获取到数据,缺点是只能使用get请求
- cors:在后端有一个cors属性,设置成*可以运行所有域访问
- proxy代理:在webpack设置proxy本地代理,但是只能在开发阶段用,部署到服务器上会失效
- nginx反向代理:这是最好的一种方式,可以在部署时用,一般在开发阶段用proxy代理,正式生产环境用nginx反向代理
# Vue题目
# MVVM
- 理解:
- Model:数据模型,也可以定义数据的修改和操作的业务逻辑
- View:用户操作界面,当ViewModel对Model进行更新的时候,会通过数据绑定更新到View
- ViewModel:业务逻辑层,主要功能是给View提供数据响应View的操作,Model for View
- 总结:MVVM模式简化了界面和业务的依赖,解决了数据频繁更新,MVVM在使用中,利用双向绑定技术,使得Model变化,ViewModel变化,View跟着变化
# 常用指令
- 渲染
- 插值语法
- v-text,更新innerText
- v-html,更新innerHTML
- 条件渲染
- v-if / v-else-if / v-else
- v-show
- 循环语句
- v-for
- 属性和事件
- v-bind
- v-on
- 双向绑定
- v-model
- 其他
- v-slot
- v-pre
- v-once: 只渲染该元素或组件一次
- v-clock:用了设置样式,编译结束之后样式会自动移除, 当网络较慢,网页还在加载 Vue.js ,而导致 Vue 来不及渲染,这时页面就会显示出 Vue 源代码。我们可以使用 v-cloak 指令来解决这一问题
# 生命周期
# 完整生命周期
- beforeCreate:组件实例创建之初,组件属性集合data生效之前
- created:组件实例已创建,data已生效,但是真实的dom还没生成,$el不可用
- beforeMount:$el和data都初始化了,相关的render函数首次被调用,编译模板,把data和模板生成html,但是html没有挂载到页面上
- mounted:完成html的挂载和渲染
- beforeUpdate:数据更新前
- updated:数据更新后
- activited:keep-alive专属,组件被激活时使用
- deactivited:keep-alive专属,组件被销毁时使用
- beforeDestory:组件销毁前
- destoryed:组件销毁后
# 父子组件生命周期执行顺序
- 先执行父组件的beforeCreate、created、beforeMount,再执行子组件的beforeCreate、created、beforeMount、mounted,最后再执行父组件的mounted,所以如果要把一些常用的数据传递给子组件,最好在父组件的created和beforeMount传递完成
# vue-router
# 路由传参
使用
:参数名
传参,页面刷新数据不会丢失// 在路由中配置(:参数名) { path: '/particulars/:id', name: 'particulars', component: particulars } // 跳转 this.$router.push({ path: `/particulars/${id}`,}) // 获取参数 this.$route.params.id
1
2
3
4
5
6
7
8
9
10
11使用params传参,页面刷新数据会丢失
// 在跳转的时候添加params属性 this.$router.push({ name: 'particulars', params: { id: id } }) // 获取参数 this.$route.params.id
1
2
3
4
5
6
7
8
9
10使用query传递参数,这样的话传递的参数会显示在url后面,?id=xxx
// 在跳转的时候添加params属性 this.$router.push({ name: 'particulars', query: { id: id } }) // 获取参数 this.$route.query.id
1
2
3
4
5
6
7
8
9
10
# history和hash模式的区别
history模式:地址栏URL中没有#,访问二级页面的时候,在刷新操作,会出现404错误,需要后端做配合重定向到首页路由
hash模式:地址栏URL中带有#,#和#后面的内容不会被包含在http请求中,所以在页面刷新的时候不会跳转,不出现404错误
# 导航守卫
- 全局守卫
- beforeEach
- beforeResolve
- afterEach
- 路由独享守卫
- beforeEnter
- 组件内守卫
- beforeRouteEnter
- beforeRouteUpdate
- beforeRouteLeave
# router和route
- router是vue-router的一个实例对象,提供一些方法,比如push、replace、go、back等
- route是跳转的路由对象,每个路由都有一个route对象,里面有一些name、path、params、query属性
# keep-alive
- 理解:keep-alive是对组件进行缓存,第一次组件执行初始化之后如果再回到这个组件,这个组件不会再执行created、mounted等生命周期,组件的数据和第一次初始化的数据相同,常用的应用场景比如商品的详情页或者tab页面这些频繁点击且数据不常更新的页面。keep-alive有include和exclude两个属性通过正则匹配去判断是否缓存
# 组件通信
- 父子组件通信
- 父组件向子组件传递数据通过props,子组件向父组件传递数据通过$emit
- 通过
children父链、子链的方式通信 - 通过$refs获取到组件实例
- 兄弟组件通信
- evenBus、vuex,一般是用vuex
- 跨级通信
- evenBus、vuex,一般是用vuex
# vuex
- vuex用作全局数据的状态管理,实现了一个单向数据流
- state:页面的状态管理对象,全局唯一
- action:在组件里通过dispatch调用action的异步方法获取到数据
- mutation:如果要修改数据,可以在action里通过commit调用mutation里的方法来修改state上的数据
- getters:如果要获取state对象里的数据,可以通过getters读取
- module:如果使用单一状态树,所以数据都集中在一个对象里,就会显得比较臃肿,所以可以使用module进行模块划分,每个模块都有自己的state、action、mutation、getters
# 自定义指令
vue允许注册自定义指令
// 注册一个全局自定义指令 `v-focus` Vue.directive('focus', { // 只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置 bind: function (el) {}, // 被绑定元素插入父节点时调用 inserted: function (el) {}, // 所在组件的 VNode 更新时调用 update: function (el) {}, // 指令所在组件的 VNode 及其子 VNode 全部更新后调用 componentUpdated: function (el) {}, // 只调用一次,指令与元素解绑时调用 unbind: function (el) {} })
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 过滤器
- filter不会修改原始数据,用于过滤数据,应用场景比如日期格式的美化
- 过滤器的功能函数:第一个参数是原数据,第二个参数是传入的参数
# watch和computed的区别
- watch是监听一个数据的变化,是一个数据变化之后的回调,其函数参数有oldValue和newValue,如果要监听复杂数据类型比如对象的话,需要添加
deep: true
属性 - computed是一个计算属性,对数据有一个缓存的效果,只有依赖的数据发生改变的时候才会重新求值,里面提供了get和set方法
# vue和angular以及react的区别
# vue与angular的区别
- angular采用ts开发,vue可以用js和ts
- angular依赖对数据做脏检查,vue使用基于依赖追踪的观察并且使用异步队列更新,所有数据都是独立触发
- angular社区完善,vue的学习成本较小
# vue与react的区别
- vue的组件分为全局注册和局部注册,react都是通过import引用
- vue有多指令系统,让模板可以实现更丰富的功能,react只能用jsx语法
- react的设计思想是all in js,通过js生成html,通过js操作css,而vue是把html,css,js组合到一起,用各自的处理方式
- react是函数式编程,推崇组件化、数据不可变、单向数据流,vue是声明式写法,数据可变,双向绑定