Javacript 中有一个思想: 万物皆对象, 几个基础类型 (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 上的解释
来源: https://juejin.im/post/5a9933c36fb9a028c8124fde