1. js继承的7种方式
回顾:
- 每个构造函数都有一个原型对象;
- 原型对象都包含一个指向构造函数的指针;
- 实例都包含一个指向原型对象的内部指针;
- 一切皆为对象,只要是对象,就会有 proto 属性,该属性存储了指向其构造的指针。
1.1确定原型和实例的关系:
1.1.1instanceOf
使用 instanceof
操作符来检测实例与原型链中出现过的构造函数,结果就会返回true
;
因此可以说instance是Object、SuperType或SubType中任何一个类型的原型。
1.1.2 isPrototypeOf
只要是原型链中出现过的原型,都可以说是该原型链派生的实例的原型,因此isPrototypeOf()
方法也会返回true
。
Object.prototype.isPrototypeOf(instance)
SuperType.prototype.isPrototypeOf(instance)
SubType.prototype.isPrototypeOf(instance)
1.2 7中方式
1.2.1 原型链:子类原型对象等于超类的实例
Object.prototype.isPrototypeOf(instance)
SuperType.prototype.isPrototypeOf(instance)
SubType.prototype.isPrototypeOf(instance)
function SuperType() {
this.colors = ['red','yellow','blue']
}
function SubType() {
}
SubType.prototype = new SuperType()
var instance1 = new SubType()
instance1.colors.push('green')
var instance2 = new SubType()
instance2.colors // ['red','yellow','blue', 'green']
2个问题(引用类型、不能向超类型的构造函数传递参数)
1.2.2 借用构造函数: 在SubType内部SuperType.call(this, 'Lee')
(能解决原型链的2个问题)
2个问题
- 构造函数模式的通病
每个方法都要在每个实例上重新创建一遍!不同实例上的同名函数是不相等的,然而,创建2个完成同样任务的Function实例的确没有必要。
- 超类型的原型中定义的方法,对子类型而言是不可见的
1.2.3 组合继承
借用构造函数方式,解决传参和引用 + 原型链方式,使用原型链上的方法
无论什么情况下,都会调用两次超类型构造函数:一次是在创建子类型原型的时候,另一次在子类型构造函数内部。 子类型最终会包含超类型对象的全部实例属性,但我们不得不在调用子类型构造函数时重写这些属性。
function SuperType(name) {
this.name = name
this.colors = ['red','yellow','blue']
}
SuperType.prototype.sayName = function() {
alert(this.name)
}
function SubType(name, age) {
SuperType.call(this, name) // 第二次调用超类型构造函数,在新对象上创建了实例属性name、colors,将屏蔽原型中的两个同名属性
this.age = age
}
SubType.prototype = new SuperType() //第一次调用超类型构造函数,SubType.prototype 获得两个属性:name、colors
var instance1 = new SubType('Lee', 26)
instance1.colors.push('green')
var instance2 = new SubType()
instance2.colors // ['red','yellow','blue']
1.2.4 原型式继承-和原型链对应
在没有必要兴师动众地创建构造函数,而只是想让一个对象与另一个对象保持类似的情况下,原型式继承时完全可以胜任的
相同问题:引用类型值的属性会共享
从本质上讲,object()对传入其中的对象执行了一次浅拷贝
function object(o) {
function F() {}
F.prototype = o
return new F()
}
Object.create()
两个参数:
- 用来作为新对象原型的对象
- 对新对象定义额外属性的对象,与
Object.defineProperties()
方法的第二个参数格式相同,每个属性都是通过自己的描述符定义的。
var person = {
name: 'Lee',
friends: ['a', 'b']
}
var p1 = Object.create(person)
p1.name = 'Yoona'
p1.friends = ['c']
var p2 = Object.create(person)
p2.name = 'Jessica'
p2.friends.push('d')
console.log(person.friends) // a, b, c, d
1.2.5 寄生式继承-和构造函数对应
只能解决方法复用,没有解决引用
function createPerson(original) {
// var clone = object(original)
var clone = Object.create(original)
clone.sayHi = function() {
alert('Hi')
}
return clone
}
var p = {
name: 'Lee',
friends: ['a', 'b']
}
var p1 = createPerson(p)
p1.sayHi()
p1.friends.push('cs')
console.dir(p.friends)// a b cs
1.2.6 寄生组合式继承-解决组合继承问题
通过借用构造函数:继承属性
通过原型链的混合形成:继承方法
目的:不必为了指定子类型的原型而调用超类型的构造函数,可以使用寄生式继承来继承超类型的原型。
call
借用构造函数继承属性和方法Object.create
来指定原型- 添加
constructor
从而弥补重写原型而失去的默认的属性
1.2.7 ES6 Class的继承 extends关键字
子类必须在constructor方法中调用super方法
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
}
class ColorPoint extends Point {
constructor(x, y, color) {
super(x, y);
this.color = color;
}
}
let cp = new ColorPoint(25, 8, 'green');
cp instanceof ColorPoint // true
cp instanceof Point // true
父类的静态方法,也会被子类继承。
class A {
static hello() {
console.log('hello world');
}
}
class B extends A {
}
B.hello() // hello world
2. js作用域的类型
全局 局部 块级
3. 块级作用域和全局、局部作用域的区别
4. 闭包
描述用词一定要准确
闭包读取内部嵌套函数的变量
闭包的优点、缺点:
闭包:能够读取其他函数内部变量的函数。(应用场景:要获取某函数内部的局部变量)
闭包的优点:1.能够读取函数内部的变量 2.让这些变量一直存在于内存中,不会在调用结束后,被垃圾回收机制回收
闭包的缺点:正所谓物极必反,由于闭包会使函数中的变量保存在内存中,内存消耗很大,所以不能滥用闭包,解决办法是,退出函数之前,将不使用的局部变量删除。
5. vue的api – 读vue文档
vue-router是vue的生态
6. computed和watch的区别
一切关于vue的,参考文档最合理
计算属性和侦听器
6.1 计算属性
- 对于任何复杂逻辑,你都应当使用计算属性;
- 我们提供的函数将用作计算属性的 getter 函数;
- 计算属性缓存vs 方法(计算属性是基于它们的响应式依赖进行缓存的。只在相关响应式依赖发生改变时它们才会重新求值。这就意味着只要 message 还没有发生改变,多次访问 reversedMessage 计算属性会立即返回之前的计算结果,而不必再次执行函数)每当触发重新渲染时,调用方法将总会再次执行函数。【假设我们有一个性能开销比较大的计算属性 A,它需要遍历一个巨大的数组并做大量的计算。】;
Date.now()
不是响应式依赖;- 计算属性默认只有 getter,不过在需要时你也可以提供一个 setter;
6.2 侦听属性
- 当需要在数据变化时执行异步或开销较大的操作时,这个方式是最有用的。question-answer,限制我们执行该操作的频率,并在我们得到最终结果前,设置中间状态。
- 无缓存性,页面重新渲染时值不变化也会执行。
7. 对vue的对象进行深度监听
7.1 deep:true
watch:{
obj:{ //监听的对象
deep:true, //深度监听设置为 true
handler:function(newV,oldV){
console.log('watch中:',newV)
}
}
}
data () {
return {
obj:{
name:'夜空中最亮的星星',
age:18
}
}
},
watch:{
'obj.name':{
deep:true,
handler:function(newV,oldV){
console.log('watch中:',newV)
}
}
}
7.2 计算属性+侦听器
data () {
return {
obj:{
name:'夜空中最亮的星星',
age:18
}
}
},
computed:{
name(){
return this.obj.name;
}
},
watch:{
name(newV){
console.log('watch中name为:',newV)
}
}
8. 节流和防抖
9. vue的生命周期
创建 挂载 更新 销毁
VUE-生命周期/请求数据
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id="app">
<button @click="des">销毁</button>
<button @click="add">修改状态</button>{{ count }}
</div>
</body>
<script src="vue.js"></script>
<script>
new Vue({
el: '#app',
data: {
count: 0
},
methods: {
add () {
this.count += 1
},
des () {
this.$destroy() // 触发销毁
}
},
beforeCreate () {
console.log('创建实例之前', this.$el) // undefined
console.log('创建实例之前', this.$data) // undefined
console.log('创建实例之前', this.count) // undefined
},
created () {
console.log('创建实例成功', this.$el) // undefined
console.log('创建实例成功', this.$data) // {count: 0}
console.log('创建实例成功', this.count) // 0
},
beforeMount () {
console.log('装载之前', this.$el) // <div id="app"></div>
console.log('装载之前', this.$data) // {count: 0}
console.log('装载之前', this.count) // 0
},
mounted () {
console.log('装载之后', this.$el) // <div id="app"></div>
console.log('装载之后', this.$data) // {count: 0}
console.log('装载之后', this.count) // 0
},
beforeUpdate () {
console.log('更新之前', this.$el) // <div id="app"></div>
console.log('更新之前', this.$data) // {count: 0}
console.log('更新之前', this.count) // 1
},
updated () {
console.log('更新之后', this.$el) // <div id="app"></div>
console.log('更新之后', this.$data) // {count: 0}
console.log('更新之后', this.count) // 1
},
beforeDestroy () {
console.log('销毁之前', this.$el) // <div id="app"></div>
console.log('销毁之前', this.$data) // {count: 0}
console.log('销毁之前', this.count) // 1
},
destroyed () {
console.log('销毁之后', this.$el) // <div id="app"></div>
console.log('销毁之后', this.$data) // {count: 0}
console.log('销毁之后', this.count) // 1
}
})
</script>
</html>
10. 在beforeCreated和created之间能请求数据吗
无论在哪个生命周期都是能请求数据的,只是不一定能渲染
在生命周期的什么阶段进行请求:看需求
一般在 created 里面就可以,如果涉及到需要页面加载完成之后的操作话就用 mounted;
- created 阶段的优势是:请求时间比较早,页面 loading 时间相对较短;
- mounted 阶段的优势是:页面已经渲染完成,如果想请求之后进行 DOM 操作的话,必须在 mounted 阶段发起请求;
11. 什么是虚拟DOM
12. 什么是MVVM框架
13. 解释响应式原理
在源代码中是怎么实现的
14. 项目中的难点
代表性、成就感最高的——决定了做到什么程度
15. 大数据渲染
16. 循环数组的方式
forEach没有返回值,返回值undefined?斟酌
17. vue的双向数据绑定
Vue内部通过Object.defineProperty方法属性拦截的方式,把data对象里每个数据的读写转化成getter/setter,当数据变化时通知视图更新。
vue的双向绑定原理及实现
代码实现:细读!!!
- 【数据层】【视图层】的数据同步
- 数据劫持+发布者-订阅者模式
Object.defineProperty()
方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。
17. 1 对象属性
ECMAScript中有两种属性: 数据属性和访问器属性, 数据属性一般用于存储数据数值, 访问器属性对应的是set/get操作, 不能直接存储数据值, 每种属性下面又都含有四个特性.下面介绍一下:
17.1.1 数据属性(数值)
1.[[Configurable]]: 表示能否通过delete将属性删除,能否把属性修改为访问器属性, 默认为false。当把属性Configurable设置为false后,该属性不能通过delete删除,并且也无法再将该属性的Configurable设置回true
2.[[Enumerable]]: 表示属性可否被枚举(即是否可以通过for in循环返回),默认false
3.[[Writable]]: 表示属性是否可写(即是否可以修改属性的值),默认false
4.[[Value]]: 该属性的数据值, 默认是undefined
17.1.1 访问器属性(set/get操作)
1.[[Configurable]]: 表示能否通过delete将属性删除,能否把属性修改为数据属性, 默认为false。当把属性Configurable设置为false后,该属性不能通过delete删除,并且也无法再将该属性的Configurable设置回true
2.[[Enumerable]]: 表示属性可否被枚举(即是否可以通过for in循环返回),默认false
3.[[Get]]: 读取属性时调用的函数, 默认为undefined
4.[[Set]]: 写入属性时调用的函数, 默认是undefined