如题,筹备面试一次就看一次,索性本人好好总结一下吧,一劳永逸。
本文从以下几个方面着手
- 0 怎么了解面向对象
- 1 创建对象的形式
- 2 记住原型链的小窍门
- 3instanceof 模仿实现
- 4new 关键字 模仿实现
- 5 继承的实现(逐渐实现)
0 怎么了解面向对象
其实我也不晓得咋答复这问题,我只晓得,面试官问这个后,就示意他要问一堆继承的问题了。上面是援用周老师的一段说辞。
“ 面向对象是一种编程思维 与面向过程是对应的 个别的语言都是面向对象的 js 自身也是基于面向对象构建进去的,例如 js 自身就有很多内置类,Promise 就是,能够 new Promise 来创立一个实例,来治理异步编程。还有 vue 也是,平时都是创立 vue 的实例啊。”
1 创建对象的形式
1. 对象字面量
var o1 = {name: 'o1'}
var o2 = new Object({name: 'o2'})
2. 通过构造函数
var M = function(name){this.name = name}
var o3 = new M('o3')
3.Object.create
var o4 = Object.create(p)
2 记住原型链的小窍门
记忆总是有法则的,如高中期间学的三角函数,须要背公式很多,强行去背全副的公式是容易凌乱的。不过如果把外围的几点背牢,其余的公式只须要稍加推导即可。对于原型链也是一样,有几点在最开始就记住的话,前面就不会乱了。原型链中要害概念:构造函数 , 实例,constructor, proto,prototype, 首先要记住他们的关系
- 实例(对象)有__proto__ , 实例(对象)没有 prototype
- 构造函数有 prototype,同时 prototype 又是对象,那么 prototype 即满足下面一条,除了领有__proto__外,还含有 constructor
- 构造函数的 prototype 的 constructor 就是指向构造函数自身,即上例子中 M.prototype.constructor === M
下面 3 点请先牢记,前面所总结的残缺继承和这有严密的关联
其实 构造函数 , 实例,constructor, proto,prototype 的关系曾经在下面的例子和 3 点介绍中介绍完了。无妨再回顾一下
- 构造函数即一般函数,只不过前边有 new 关键字
- 通过 new 加 构造函数,生成的对象即为实例。
-
以下面生成 o3 实例为例子
o3.__proto__ === M.prototype //true o3.prototype //undefined o3.__proto__ === M.prototype //true
-
o3 实例自身并无 constructor,不过会借助原型链向上查找,即,
o3.constructor === M.prototype.constructor // true o3.constructor === M //true
小结 理清这几个关键词的关系后,原型链就清朗很多了
3 instanceof 模仿实现
instanceof 的原理是什么呢?先来看一下应用
[] instanceof Array // true
即右边是对象,左边是类型,instanceof 就是要判断左边类型的 prototype,是否在右边实例的原型链上, 如下例子所示
[].__proto__ === Array.prototype //true
Array.prototype.__proto__ === Object.prototype //true
Object.prototype__proto__ //null
那么根据这个思维来实现一下 instanceof 吧,肯定会印象更加粗浅
function myInstanceof2(left, right){if(left === null || left === undefined){return false}
if(right.prototype === left.__proto__) {return true}
left = left.__proto__
return myInstanceof2(left, right)
}
console.log(myInstanceof2([], Array))
4 new 模仿实现(简要版)
new 的过程产生了什么?
- 生成空对象
- 这个空对象的__proto__赋值为构造函数的 prototype
- 绑定 this 指向
-
返回这个对象
// 构造函数 function M(name){this.name = name} // 原生 new var obj = new M('123') // 模仿实现 function create() { // 生成空对象 let obj = {} // 拿到传进来参数的第一项,并扭转参数类数组 let Con = [].shift.call(arguments) // 对空对象的原型指向赋值 obj.__proto__ = Con.prototype // 绑定 this //(对应上面应用来阐明:Con 是参数第一项 M,// arguments 是参数['123'], // 就是 M 办法执行,参数是 '123',执行这个函数的 this 是 obj)let result = Con.apply(obj, arguments) return result instanceof Object ? result : obj } var testObj = create(M, '123') console.log('testObj', testObj)
5 继承的实现(逐渐实现)
一步一步来,从简到繁,更能直观发现继承的原理与毛病
-
构造方法形式 外围 Parent1.call(this)
// 构造方法形式 function Parent1(){this.name = 'Parent1'} Parent1.prototype.say = function () {alert('say') } function Child1(){Parent1.call(this) this.type = 'type' } var c1 = new Child1() c1.say() // 报错
毛病:只能继承父类构造函数外部属性,无奈继承父类构造函数原型对象上属性
思考:为什么 call 实现了继承,call 实质是什么?
-
只借助原型继承 外围 Child2.prototype = new Parent2()
// 原型 function Parent2(){ this.name = 'Parent2' this.arr = [1,2] } Parent2.prototype.say = function () {alert('say') } function Child2(){// Parent2.call(this) this.type = 'type' } Child2.prototype = new Parent2() var c21 = new Child2() var c22 = new Child2() c21.say() c21.arr.push('9') console.log('c21.arr :', c21.arr) console.log('c22.arr :', c22.arr)
毛病:c21.arr 与 c22.arr 对应的是同一个援用
思考:为什么这么写是同一个援用?
- 组合继承 1
把下面两个继承形式的长处合并起来,毛病都摈弃掉
function Parent3(){
this.name = 'Parent3'
this.arr = [1,2]
}
Parent3.prototype.say = function () {alert('say')
}
function Child3(){Parent3.call(this)
this.type = 'type'
}
Child3.prototype = new Parent3()
var c31 = new Child3()
var c32 = new Child3()
c31.say()
c31.arr.push('9')
console.log('c31.arr :', c31.arr)
console.log('c31.arr :', c32.arr)
思考:这么写就没有问题了吗?
答:生成一个实例要执行 Parent3.call(this),new Child3(),也就是 Parent3 执行了两遍。
- 组合继承 2
扭转上例子 的
Child3.prototype = new Parent3()
为
Child3.prototype = Parent3.prototype
毛病:很显著,无奈定义子类构造函数原型公有的办法
-
组合继承优化 3 再次扭转上例子 的
Child3.prototype = Parent3.prototype
为
Child3.prototype = Object.create(Parent3.prototype)
问题就都解决了。因为 Object.create 的原理是:生成一个对象,这个对象的__proto__, 指向所传的参数。
思考:是否还有疏漏?一时想不起来的话,能够看下这几个后果
console.log(c31 instanceof Child3) // true
console.log(c31 instanceof Parent3) // true
console.log(c31.constructor === Child3) // false
console.log(c31.constructor === Parent3) // true
所以回想起文章结尾所说的那几个须要牢记的点,就须要从新赋值一下子类构造函数的 constructor:Child3.prototype.constructor = Child3,完整版如下
function Parent3(){
this.name = 'Parent3'
this.arr = [1,2]
}
Parent3.prototype.say = function () {alert('say')
}
function Child3(){Parent3.call(this)
this.type = 'type'
}
Child3.prototype = Object.create(Parent3.prototype)
Child3.prototype.constructor = Child3
var c31 = new Child3()
var c32 = new Child3()
c31.say()
c31.arr.push('9')
console.log('c31.arr :', c31.arr)
console.log('c31.arr :', c32.arr)
console.log('c31 instanceof Child3 :', c31 instanceof Child3)
console.log('c31 instanceof Parent3 :', c31 instanceof Parent3)
console.log('c31.constructor === Child3 :', c31.constructor === Child3)
console.log('c31.constructor === Parent3 :', c31.constructor === Parent3)
5 es6 的继承
class Parent{constructor(name) {this.name = name}
getName(){return this.name}
}
class Child{constructor(age) {this.age = age}
getAge(){return this.age}
}
es6 继承记住几个注意事项吧
- 1 构造函数不能当一般函数一样执行 Parent() 是会报错的
- 2 不容许重定向原型 Child.prototype = Object.create(Parent.prototype) 无用
- 3 继承写法如下,下面的 Child 类想继承父类,改成如下写法就好
<figcaption></figcaption>
留神写了 extends 关键字,constructor 中就必须写 super(), 打印后果如下: