1.原型和原型链:
- 每一个构造函数都有一个原型对象 (prototype)
- 每一个原型对象都包含一个指向构造函数的指针 ( constructor)
- 每一个实例也都包含一个指向原型对象的指针_ proto _
-
所有函数的默认原型都是Object的实例,都包含一个内部指针,指向Object.prototype, (这正是所有自定义类型都会继承toString() valueOf()等默认方法的根本原因)
-
根据这样一种关系,形成了链条关系,这就是所谓的原型链的基本概念
根据这种原型链的关系,可以用来实现继承,本质是重写原型对象,使原型对象的prototype指向一个新类型的实例
- 对一个构造函数实例化后,它的原型链指向什么?
参考答案:
指向该构造函数实例化出来对象的原型对象。
对于构造函数来讲,可以通过 prototype 访问到该对象。
对于实例对象来讲,可以通过隐式属性 __proto__ 来访问到。
2.继承:(六大方法)
-
使用原型链实现继承:
原理是重写原型对象,将实例对象指向的原型对象重写为另一个类型的实例对象
function SuperType() { this.prototype = true; } SuperType.prototype.getSuperValue = function(){ return this.prototype; }; function SubType() { this.subprototype = false; } //继承了SuperType SubType.prototype = new SuperType(); SubType.prototype.getSubValue = function () { return this.subprototype; }; var instance = new SubType(); console.log(instance.getSuperValue());//true console.log(instance.getSubValue());//false
缺点:
-
在原型对象上的属性,会被所有实例对象共享 (一个实例对象对原型上的属性修改了,所有实例对象的原型上的属性都会被修改)
-
无法传递参数给你继承过来的属性方法
-
-
借用构造函数实现继承:
在构造函数中通过call(),apply()去调用另一个类型的构造函数,实现继承另一个类型的实例对象身上的方法和属性
使用call( ) 和 apply( )传递参数
-
**优点:**相对第一种方法来说,借用构造函数的方法,可以向继承过来的属性方法传递参数,
-
**缺点:**只使用借用构造函数的方法实现继承,那么所需要继承的函数都在构造函数里定义,就没有函数复用可言
-
-
组合继承:(上述两种方法组合在一起使用)
使用原型链方法继承原型上的属性和方法,使用构造函数来继承实例里的属性
-
优点: 融合了上述两种方法的优点,可以使用构造函数来继承实例里的属性传入参数 ,然后使用原型链方法继承原型上的属性和方法实现函数复用
-
**缺点:**每次都要调用两次超类型的构造函数
-
第一次在子类型的原型对象中调用 (new一个超类型的实例对象),
-
第二次在子类型的构造函数中调用 (通过apply,call调用超类型的构造函数)
-
-
-
原型式继承:
因为可以使用原型对象的constructor构造函数来生成实例对象的一个特点, 所以道格拉斯提出可以声明一个类似原型对象的函数给它传一个object参数,然后在函数内部创建一个临时的构造函数,让构造对象的prototype指向传进来的object对象,最后返回这个构造函数构造的对象实例. (模拟使用原型对象创建实例对象的过程)
原理上讲,是对传进来的object对象进行了一次浅克隆,
function object(o){ function F() {} F.prototype = o; return new F(); }
- ECMAScript新增Object.create()这个方法规范了原型式继承
- 所有通过这样的方式构造的实例对象,都会共享你传进去的object对象上的基本类型值属性和引用类型值属性
-
寄生式继承:
原理和寄生构造函数和工厂模式有点类似,就是封装一个继承的函数,这个函数给人感觉是他自己干了所有的工作最后返回一个对象
实际上函数里面使用原型式继承的那个函数object(),传进去一个对象,得到一个返回来的对象后,在对象身上添加一些方法来强调这个对象,然后再返回这个对象.
function object(o){ function F() {} F.prototype = o; return new F(); } function createAnother(original){ var clone = object(original);//通过调用函数创建一个新对象 clone.sayHi = function () {//以某种方式来增强这个对象 console.log("Hi"); }; return clone; //返回这个对象 }
相当于对原型式继承进行了封装,在它的基础上在封装函数的内部给返回来的新的对象添加了你所要定义的方法,最后还是返回这个对象
- **优点:**如果你继承的东西主要是一个对象,而不是什么自定义类型等别的东西的时候,寄生式继承就很有用,因为它针对的是对象的一个继承
- **缺点:**跟借用构造函数实现继承一样,不能实现函数复用,每创造一个对象,对象的身上就已经有你自定义的属性里了,
-
寄生组合式继承:
通过借用构造函数来继承属性,通过原型链的混成形式来继承方法**(实现函数复用)**
原型链的混成形式是指,用原型式继承方式的object函数传进去超类型的构造函数的原型对象,返回了一个对象,将子类型的原型对象重写成这个返回来的对象,
(使用超类型的构造函数的原型对象的副本)
然后将重写了的原型对象的constructor指向子类型的构造函数,用来增强对象
整个过程可以这样描述:
(子类型是你要用的,超类型是你要继承的)
- 封装了一个函数,传进去两个参数,一个是子类型的构造函数,一个是超类型的构造函数
- 使用原型式继承方式的object,传进去超类型的构造函数的原型对象,返回来一个新的对象,(对原型对象进行了浅克隆)
- 将新出的对象的constrcutor重写为子类型的构造函数,用来增强对象
- 将子类型的原型对象重改为这个新的对象
弥补了因为重写原型对象,而失去原有的constructor属性