Javascript 中实现对象原型继承的三种方式



Javascript 中有一个思想:万物皆对象,几个基础类型(String, Number, Boolean, Null, Undefined, Symbol),几个引用类型(Array, Date, Function, Object, Regexp)本质上都是对象。
那么对象之间如何实现关联(也就是OO里面的继承)呢?js中通过prototype(原型)来实现。每个对象都有一个proto指针,指向上一个原型,这就像是一个链表。最顶端的object的原型指向null, 代表终结。 所以,当我们定义一个变量:
let name = new String('allen')
//let name = 'allen'
console.log('name:', name)
console.log('name.__proto__:', name.__proto__)
console.log('name.__proto__.__proto__:',name.__proto__.__proto__)
console.log('name.__proto__.__proto__.__proto__:', name.__proto__.__proto__.__proto__)
输出:
name: [String: 'allen']
name.__proto__: [String: '']
name.__proto__.__proto__: {}
name.__proto__.__proto__.__proto__: null
[Finished in 0.1s]
我们要实现继承(至少看上去是继承的样子)有三种方法:
- Object.create(),
- Function.prototype={},
- class extend
1. Object.create()
看例子:
let dog = {
name: 'dog'
}
let mardDog = Object.create(dog)
这里 mardDog
是一个新的空对象,存有一个指针__proto__
,指向的是 dog
对象, dog
对象中也有一个__proto__
,指向的是Object
,Object
中也有一个__proto__
,指向的是null
。
这就是原型链。利用Object.create(),我们可以创建多个对象,dog
对象都是他们的原型,那么他们可不可以改变dog里面的属性呢。答案是可以 的,但不建议。代码如下:
mardDog.name = 'mardDog'
/*
dog ====> {name: 'dog'}
mardDog ====> {name: 'mardDog'}
*/
可以看到,我们并没有能改变原型中的属性。而下面这种方式:
mardDog.__proto__.name = 'xxxDog'
/*
dog ====> {name: 'xxxDog'}
mardDog ====> {name: 'mardDog'}
*/
我们成功改变了上一层的属性。 那么,为什么我们不推荐使用proto去改变原型中的共有属性呢?因为这种方法非常慢,并且会严重影响进程。事实上,proto从来没有被写进规范,但是浏览器厂商都实现了它。
2. Function.prototype = {}
这个方法其实是利用构造函数来实现 先看例子:
function dog (){
this.name = 'dog'
this.age = 1
}
let dog1 = new dog()
function mardDog (){
this.yiel = function (){
console.log(this.name)
}
}
mardDog.prototype = new dog()
let mardDog1 = new mardDog()
mardDog1.yiel()
输出:
dog
[Finished in 0.1s]
可以看到,我们成功实现了mardDog 对 dog的继承
3. class extend
es6中实现了 class
这个关键字,虽然只是语法糖,本质上是方法二的封装,但这种思路对熟悉OO的开发者是很友好的,并且把js中令人迷惑的原型封装了起来,使它变得更容易开发。
例子:
class dog {
constructor () {
this.name = 'dog'
this.age = 1
}
}
class mardDog extends dog {
constructor () {
super()
}
yiel () {
console.log(this.name)
}
}
let mardDog1 = new mardDog()
mardDog1.yiel()
输出:
dog
这样我们成功地实现了继承
总结
由于es6 是大势所趋,建议在工程环境中使用clas来实现对象的继承。当然原型以及原型链的原理是必须掌握的。es6中还有一些方法也是十分有用。以下:
Object.getPrototypeOf(childobj) 顾名思义,该方法得到childobj的prototype,也可以理解为父类。
Object.setPrototypeOf(childObj, obj)这个方法是将childObj设为obj的继承类。由于前面提到过,对prototype的操作十分微妙,所以这个方法还是能不用就不用,可以用Object.create(obj)来代替
其他方法参照MDN上的解释