JavaScript ES5中的继承
- 对象和函数的原型
- new 、constructor
- 原型链的查找顺序
- 原型链实现的继承
- 借用构造函数实现继承
- 寄生组合实现继承
1. 对象和函数的原型
- 获取对象的原型方式
- obj.__proto__:有兼容性问题
- Object.getPrototyprOf(obj)
当我们想获取一个属性的值时,如果在自己的对象中查找,找到直接返回,否则沿着原型链向上查找。
在JavaScript中,每一个对象都有一个特殊内置属性[[prototype]]
1.1函数的原型
所有的函数都有一个prototype属性,不是__proto__。
- 将函数看成是一个普通的对象时,它是具备__proto__(隐式原型)
1 | function foo() { } |
- 将函数看成一个函数的时候,它是具备prototype的
1 | function foo() { } |
对象是没有prototype原型的,这个是显式原型。
这个原型的作用是用来构建对象时,给对象设置隐式原型。
2. new、constructor
2.1 new操作符
- 创建空对象
- 将这个空对象赋值给this
- 将函数的显式原型赋值给对象,作为该对象的隐式原型
obj.__proto__ = Person.prototype
- 执行函数体代码
- 将这个对象默认返回
原型的作用:
- 多个对象拥有共同的值,我们可以将它放到构造函数对象的原型里面,有构造函数创建出来的对象,都可以共享这些属性。
2.2 constructor属性
默认情况下,原型对象上有一个constructor属性,这个constructor指向当前函数对象。
2.3 重写原型对象
当我们需要向原型对象添加过多的属性,我们可以选择重写原型。
1 | function Perso(){ |
但是上面这种方式添加constructor属性,会导致constructor可以被遍历。
更好的方式通过Object.defineProperty():Object.defineProperty(Person.prototype,”constructor”,{value:Person})
3. 面向对象的特征-继承
在ES5中实现继承。
使用原型链。
3.1 什么是原型链
我们从一个对象获取属性时,如果在当前对象中没有查找到,就会顺着原型向上查找,一层一层向上查找,这就原型链。
原型链的顶层为Object。
在Object中的原型中,值为null,该对象上有很多默认的属性和方法。
3.2 通过原型链实现方法继承
- 方式一: 父类的原型直接赋值给子类的原型(错误)
这样会导致子类修改原型对象的时候,将父类的原型对象也更改了。
- 方法二:创建一个父类的实例对象(new Person()),用这个实例作为子类的原型独享。
1 | // 定一个构造函数 |
原型链存在的弊端:某些属性保存在p对象上
- 直接打印对象,看不到我们想要的属性
- 会被多个对象共享
- 不能给Person传递参数
3.3借用构造函数实现属性继承
在子类的函数内部调用父类的函数。
1 | // 定一个构造函数 |
3.4 组合继承
组合继承是JavaScript最常用的继承方式之一。
3.5创造原型对象的方法
满足的条件
- 必须创建出一个对象
- 这个对象的隐式原型必须指向父类的显示原型
- 将这个对象赋值给予子类的显式原型
方案一:(不好)
1 | var p = new Person(); |
方案二:
1 | var obj = {}; |
方案三:
1 | function F(){}; |
方案四:
1 | var obj = Object.create(Person.prototype); |
使用Object.create()方法创建对象,并且将这个对象的原型指向传入的那个原型对象。
封装实践