经常浏览各种文章,学习各种技术,看了,不用,忘了.
好记性,不如烂笔头!
继承概念:
继承机制实例
说明继承机制最简单的方式是,利用一个经典的例子 - 几何形状。实际上,几何形状只有两种,即椭圆形和多边形。圆是椭圆的一种,它只有一个焦点。三角形、矩形和五边形都是多边形的一种,具有不同数量的边。这就构成了一种完美的继承关系。
在这个例子中,形状 是椭圆形和多边形的基类(base class)(所有类都由它继承而来)。圆形继承了椭圆形,因此圆形是椭圆形的子类(subclass),椭圆形是圆形的超类(superclass)。同样,三角形、矩形和五边形都是多边形的子类,多边形是它们的超类。最后,正方形(Square)继承了矩形。
下面的图示是解释 Shape 和它的子类之间关系的 图示:
在讲解继承之前先了解一下JavaScript中创建一个对象的过程
function Person(name){
//调用new的过程相当于 var this=new Object();
//给this对象赋值
//最后return this;
this.name=name;
};
Person.prototype.getName=function(){
return this.name;
};
var a=new Person("seven");
console.log(a.name); //输出:seven
console.log(a.getName); //输出:seven
console.log(Object.getPrototypeOf(a)===Person.prototype); //输出:true
javaScript的函数可以被当做普通函数调用,也可以作为构造器调用.
javaScript中的原型继承
原型编程的规则:
- 1.所有数据都是对象
- 2.要得到一个对象不是实例化类,而是找到一个对象作为原型并克隆ta
- 3.对象会记住它的原型(__proto__)
- 4.如果对象无法响应某个请求,ta会把这个请求委托给自动的构造函数的原型
继承的方式
ECMAScript 实现继承的方式不止一种。这是因为 JavaScript 中的继承机制并不是明确规定的,而是通过模仿实现的。这意味着所有的继承细节并非完全由解释程序处理。作为开发者,你有权决定最适用的继承方式。
对象冒充
因为构造函数只是一个函数,所以可使 ClassA 构造函数成为 ClassB 的方法,然后调用它。ClassB 就会收到 ClassA 的构造函数中定义的属性和方法。例如,用下面的方式定义 ClassA 和 ClassB:
function ClassA(sColor) {
this.color = sColor;
this.sayColor = function () {
alert(this.color);
};
}
function ClassB(sColor) {
this.newMethod = ClassA;
this.newMethod(sColor);
delete this.newMethod;
}
在这段代码中,为 ClassA 赋予了方法 newMethod(请记住,函数名只是指向它的指针)。然后调用该方法,传递给它的是 ClassB 构造函数的参数 sColor。最后一行代码删除了对 ClassA 的引用,这样以后就不能再调用它。
所有新属性和新方法都必须在删除了新方法的代码行后定义。否则,可能会覆盖超类的相关属性和方法:
function ClassB(sColor, sName) {
this.newMethod = ClassA;
this.newMethod(sColor);
delete this.newMethod;
this.name = sName;
this.sayName = function () {
alert(this.name);
};
}
为证明前面的代码有效,可以运行下面的例子:
var objA = new ClassA("blue");
var objB = new ClassB("red", "John");
objA.sayColor(); //输出 "blue"
objB.sayColor(); //输出 "red"
objB.sayName(); //输出 "John"
可以使用Call方法优化一下
function ClassB(sColor, sName) {
//this.newMethod = ClassA;
//this.newMethod(color);
//delete this.newMethod;
ClassA.call(this, sColor);
this.name = sName;
this.sayName = function () {
alert(this.name);
};
}
这种方式只是ClassB调用了ClassA,给ClassB的this赋值
原型链(prototype chaining)
function ClassA() {
}
ClassA.prototype.color = "blue";
ClassA.prototype.sayColor = function () {
alert(this.color);
};
function ClassB() {
}
ClassB.prototype = new ClassA();
原型方式的神奇之处在于最后一行代码。这里,把 ClassB 的 prototype 属性设置成 ClassA 的实例。因为想要 ClassA 的所有属性和方法,但又不想逐个将它们 ClassB 的 prototype 属性。还有比把 ClassA 的实例赋予 prototype 属性更好的方法吗?
调用 ClassA 的构造函数,没有给它传递参数。这在原型链中是标准做法。要确保构造函数没有任何参数。
与对象冒充相似,子类的所有属性和方法都必须出现在 prototype 属性被赋值后,因为在它之前赋值的所有方法都会被删除。为什么?因为 prototype 属性被替换成了新对象,添加了新方法的原始对象将被销毁。所以,为 ClassB 类添加 name 属性和 sayName() 方法的代码如下:
function ClassB() {
}
ClassB.prototype = new ClassA();
ClassB.prototype.name = "";
ClassB.prototype.sayName = function () {
alert(this.name);
};
可通过运行下面的例子测试这段代码:
var objA = new ClassA();
var objB = new ClassB();
objA.color = "blue";
objB.color = "red";
objB.name = "John";
objA.sayColor();
objB.sayColor();
objB.sayName()
混合方式
创建类的最好方式是用构造函数定义属性,用原型定义方法。这种方式同样适用于继承机制,用对象冒充继承构造函数的属性,用原型链继承 prototype 对象的方法。用这两种方式重写前面的例子,代码如下:
function ClassA(sColor) {
this.color = sColor;
}
ClassA.prototype.sayColor = function () {
alert(this.color);
};
function ClassB(sColor, sName) {
ClassA.call(this, sColor);//对象冒充
this.name = sName;
}
ClassB.prototype = new ClassA();//原型
ClassB.prototype.sayName = function () {
alert(this.name);
};
在 ClassB 构造函数中,用对象冒充继承 ClassA 类的 sColor 属性。用原型链继承 ClassA 类的方法。
ES6 实现了Class,可以通过关键字extends实现继承
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
}
class ColorPoint extends Point {
constructor(x, y, color) {
super(x, y); this.color = color; // 正确
}
}
转换成es5
"use strict";
function _possibleConstructorReturn(self, call) {
return call && (typeof call === "object" || typeof call === "function") ? call : self;
}
function _inherits(subClass, superClass) {
subClass.prototype = Object.create(
superClass && superClass.prototype,
{
constructor: {
value: subClass,
enumerable: false,
writable: true,
configurable: true
}
}
);
if (superClass) {
Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
}
}
var Point = function Point(x, y) {
this.x = x;
this.y = y;
};
var ColorPoint = function (_Point) {
_inherits(ColorPoint, _Point);
function ColorPoint(x, y, color) {
var _this = _possibleConstructorReturn(this, (ColorPoint.__proto__ || Object.getPrototypeOf(ColorPoint)).call(this, x, y));
_this.color = color; // 正确
return _this;
}
return ColorPoint;l
}(Point);
这里重点讲解一下ES5中的继承
1. _inherits,首先使用Object.create将父类Point原型拷贝到子类ColorPoint上
2.将父类构造函数挂载到子类的__proto__ 上
非构造函数的继承
比如,现在有一个对象,叫做"中国人"。
var Chinese = {
nation:'中国'
};
还有一个对象,叫做"医生"。
var Doctor ={ career:'医生' }
请问怎样才能让"医生"去继承"中国人",也就是说,我怎样才能生成一个"中国医生"的对象?
可以利用空构造函数:
function extends(o) {
function F() {}
F.prototype = o;
return new F();
}
var Doctor = object(Chinese);
Doctor.career = '医生';
也可以使用Object.create()
var Doctor = Object.create(Chinese);
Doctor.career = '医生';
到此结束!