说道这个继承, 了解 object-oriented 的朋友都知道, 大多 oo 语言都有两种, 一种是接口继承(只继承方法签名); 一种是实现继承(继承实际的方法)
奈何 js 中没有签名, 因而只有实现继承, 而且靠的是原型链实现的下面正式的说一说 js 中继承那点事儿
1 原型链
原型链: 实现继承的主要方法, 利用原型让一个引用类型继承另一个引用类型的属性和方法
回顾: 构造函数, 原型, 实例三者的关系
每一个构造函数都有一个原型对象(Person.prototype); 原型对象都包含指向构造函数的指针(constructor); 每个实例都包含指向原型对象的指针(看不见的_proto_指针)
原型链是怎么来的呢?
某个构造函数的原型对象是另一个构造函数的实例; 这个构造函数的原型对象就会有个 (看不见的_proto_指针) 指向另一个构造函数的原型对象;
那么另一个原型对象又是其他的构造函数实例又会怎么样, 就这样层层递进, 形成原型链; 来具体看一下吧
- // 第一个构造函数; 有一个属性和一个原型方法
- function SuperType() {
- this.property = true;
- }
- SuperType.prototype.getSuperValue = function() {
- return this.property
- }
- // 第二个构造函数; 目前有一个属性
- function SubType() {
- this.subproperty = false
- }
- // 继承了 SuperType;SubType 原型成了 SuperType 的实例; 实际就是重写 SubType 的原型对象; 给 SuperType 原型对象继承了
- SubType.prototype = new SuperType()
- // 现在这个构造函数有两个属性(一个本身的 subproperty, 一个继承的存在原型对象的 property); 两个方法(一个原型对象的 getSubValue, 一个原型对象的原型对象的 getSuperValue)
- SubType.prototype.getSubValue = function() {
- return this.subproperty
- }
- var instance = new SubType() // 创建第二个构造函数的实例
- console.log(instance.getSuperValue()) //true 先查找 instance 这个实例有没有此方法; 显然没有, 再查找 SubType 原型对象有没有此方法; 也没有, 再查找 SubType 原型对象的原型对象; 显然是存在的
注意: instance 的 constructor 现在指向的是 SuperType 这个构造函数; 因为原来的 SubType.prototype 被重写了, 其内部的 constructor 也就随着 SubType.prototype 的原型对象的 constructor 指向构造函数 SuperType; 至于原型搜索机制是怎么样运行的, 请仔细看上面的代码, 相信你是可以的
1.1 完整的原型
在原型那节已经提了些, 还是再说一下完整的原型包括 Object
所有函数的默认原型都是 Object 的实例; 每个默认原型都有个_proto_指针指向 Object.prototype; 因此自定义类型都继承如 toString,valueOf 的方法
而 Object.prototype 的_proto_指针指向 null 来结束原型链以 Person 构造函数为例, 看看完整的原型链图
1.2 原型和实例的关系判断
第一种使用 instanceof 操作符: 测试实例和原型链中出现的构造函数, 结果为 true
第二种使用 isPrototypeOf()方法: 只要是原型链中出现过的原型, 都可以说是该原型链所派生的实例的原型
- console.log(instance instanceof Object) // 都为 true
- console.log(instance instanceof SuperType)
- console.log(instance instanceof SubType)
- console.log(Object.prototype.isPrototypeOf(instance)) // 都为 true
- console.log(SuperType.prototype.isPrototypeOf(instance))
- console.log(SubType.prototype.isPrototypeOf(instance))
1.3 谨慎定义方法
注意: 给原型对象添加方法, 一定放在替换原型的后面, 因为放在替换原型之前是找不到了, 原型会被重写的;
注意: 在通过原型链继承时, 不能使用对象字面量创建原型方法, 因为也会重写原型链;
- function SuperType() {
- this.property = true;
- }
- SuperType.prototype.getSuperValue = function() {
- return this.property
- }
- function SubType() {
- this.subproperty = false
- }
- // 继承 SuperType
- SubType.prototype = new SuperType()
- // 使用字面量添加新方法, 导致上一行无效 因为现在的原型替换了 Object 实例而非 SuperType 的实例, 关系中断
- SubType.prototype = {
- getSubValue: function() {
- return this.subproperty;
- },
- somOtherMethod: function() {
- return false
- }
- };
- var instance = new SubType() console.log(instance.getSuperValue()) //error
1.4 原型链的问题
1 包含引用类型值的原型: 当实例是另一函数的原型时, 引用类型值就会变成原型上的属性, 就会被另一函数的实例所共享
- function SuperType(){
- this.colors=["yellow","red","olive"]
- }
- function SubType(){
- }
- SubType.prototype=new SuperType() //color 实际上就是原型上的了
- var instance1=new SubType()
- instance1.colors.push("purple")
- var instance2=new SubType()
- console.log(instance1.colors==instance2.colors) //true
2 创建子类型实例时, 不能向超类型的构造函数传递参数(没有办法在不影响所有对象实例的情况下, 给超类型的构造函数传递参数)
2 借助构造函数
为了解决原型中包含引用类型值带来的问题, 利用构造函数来解决
在子类型构造函数的内部调用超类型构造函数(函数是特定环境中执行代码的对象, 可以通过 apply 或 call 调用)
- function SuperType(){
- this.color=["yellow","red","olive"]
- }
- function SubType(){
- // 继承了 SuperType
- SuperType.call(this)
- }
- var instance1=new SubType()
- instance1.color.push("purple")
- var instance2=new SubType()
- console.log(instance1.color) //["yellow","red","olive","purple"]
- console.log(instance2.color) //["yellow","red","olive"]
- // 传递参数
- function SuperType(name){
- this.name=name
- }
- function SubType(){
- SuperType.call(this,"double")
- this.age=12
- }
- var instance1=new SubType()
- console.log(instance1.name) //double
- console.log(instance1.age) //12
问题: 仅仅借鉴构造函数, 那么避免不了构造函数的问题, 方法都在构造函数定义了, 函数无法复用
3 组合继承(常用的还是组合, 和原型与构造结合一样)
- function SuperType(name) {
- this.name = name;
- this.color = ["yellow", "red", "olive"];
- }
- SuperType.prototype.sayName = function() {
- console.log(this.name);
- }
- function SubType(name, age) {
- // 继承属性, 创建属性副本
- SuperType.call(this, name);
- this.age = age;
- }
- // 继承属性和方法, 只是原型中属性被后来的函数调用生成的属性副本遮盖
- SubType.prototype = new SuperType();
- alert(SubType.prototype.constructor) // 指向的是 SuperType
- SubType.prototype.constructor = SubType; // 将 constructor 回归到 SubType 构造函数身上
- SubType.prototype.sayAge = function() {
- console.log(this.age)
- }
- var instance1 = new SubType("double", 23) instance1.color.push("pink") console.log(instance1.color) //["yellow","red","olive","pink"]
- instance1.sayName() //double
- instance1.sayAge() //23
- var instance2 = new SubType("single", 34) console.log(instance2.color) //["yellow","red","olive"]
- instance2.sayName() //single
- instance2.sayAge() //34
还有其他的继承, 花点时间写一下
1 原型式继承
克罗克福德写的; 借助原型可以基于已有的对象创建新对象, 同时不必创建自定义类型
- function object(o){ // 本质上 object()函数对其中对象的浅复制
- function F(){} // 创建一个新的构造函数
- F.prototype=o // 构造函数原型为传入的对象
- return new F() // 返回构造函数的实例
- }
- var person={
- name:"double",
- friends:["tom","jack","mike"]
- }
- var person1=object(person) // 事实上为原型共享
- person1.name="grey"
- person1.friends.push("single")
- console.log(person1.friends) //["tom", "jack", "mike", "single"]
- var person2=object(person)
- person2.name="red"
- console.log(person2.friends) //["tom", "jack", "mike", "single"]
ES5 为了规范原型式的继承, 有个 Object.create()来方便, IE9 以上可以; 只是想一个对象和另一个对象保持类似的情况, 完全可以这种方法
- var person={
- name:"double",
- friends:["tom","jack","mike"]
- }
- var person1=Object.create(person)
- person1.name="single"
- person1.friends.push("singles")
- var person2=Object.create(person)
- console.log(person1.friends==person2.friends) //true
- //Object.create()接受两个参数, 一个为作为新对象原型的对象, 一个为新对象定义额外属性对象
- var person={
- name:"double",
- friends:["tom","jack","mike"]
- }
- var person1=Object.create(person,{
- name:{
- value:"single" // 每个属性都是通过自己描述符定义的
- }
- })
2 寄生式继承
思路和原型式继承一脉相承, 创建一个用于封装继承过程的函数, 内部通过方式增强对象, 返回对象; 主要考虑对象时使用
- function object(o) {
- function F() {}
- F.prototype = o
- return new F()
- }
- function createPerson(original) {
- var clone = object(original) // 继承原型
- clone.sayName = function() {
- alert("name")
- }
- return clone
- }
- var person = {
- name: "double",
- friends: ["single", "tom", "jack"]
- }
- var person1 = createPerson(person) person1.sayName() //name 引用类型值还是共享的
3 寄生组合继承
组合继承是继承中常常用到的, 但是会调用两次超类型构造函数; 寄生组合继承就是为了解决这个问题的
- function object(o){
- function F(){}
- F.prototype=o
- return new F()
- }
- function inheritPrototype(subType,superType){
- var prototype=object(superType) // 创建对象 (superType 实例)
- prototype.constructor=subType // 增强对象
- subType.prototype=prototype // 指定对象 (原型赋予实例)
- }
- function SuperType(name,sex){
- this.name=name
- this.sex=sex
- this.colors=["red"]
- }
- SuperType.prototype.sayName=function(){
- alert(this.name)
- }
- function SubType(name,sex,age){
- SuperType.call(this,name,sex)
- this.age=age
- }
- inheritPrototype(SubType,SuperType) // 目前 subType.prototype 什么都没有
- SubType.prototype.sayAge=function(){ // 为 subType.prototype 添加个方法
- alert(this.age)
- }
- var person1=new SubType("double","man",34)
- console.log(person1.name) //SuperType 这是个 Bug
- console.log(person1.sex) //man
- console.log(person1.colors) //["red"]
- person1.sayAge() //34
到此, 差不多结束啦, 感谢你对脚本之家的支持, 希望我们整理的内容能够帮助到你
您可能感兴趣的文章:
JS 面向对象之继承 --- 多种组合继承详解
JS 面向对象 (3) 之 Object 类, 静态属性, 闭包, 私有属性, call 和 apply 的使用, 继承的三种实现方法
学习 javascript 面向对象 javascript 实现继承的方式
详解 JavaScript 基于面向对象之继承实例
详解 JavaScript 基于面向对象之继承
Javascript 简单实现面向对象编程继承实例代码
javascript 面向对象封装与继承
javaScript 面向对象继承方法经典实现
JS Pro - 深入面向对象的程序设计之继承的详解
关于 JavaScript 的面向对象和继承有利新手学习
来源: http://www.jb51.net/article/134360.htm