JavaScript中的原型(链)&继承(一)

Zee有话说^_^,下面的内容大部分摘自《Professional JavaScript for Web Developers (3rd edition
)》和《JavaScript语言精粹与编程实践》。结合书中一些概念,总结了对书中知识的理解。为了方便自己回顾以及帮助更多朋友。可能有不准确的地方,希望交流探讨。

博文相关内容解析:构造器(constructor)–> 构造函数——(构造器实际上指向构造函数)(在一般情况下二者含义相同)

原型对象

JavaScript中的面向对象

通常我们所说的“面向对象”有三个基本特性,即封装、继承和多态。如果三个特性都满足,我们称为“面向对象语言”,而满足部分特性的语言为“基于对象语言“。
ECMA-262把对象定义为:”无序属性的集合,其属性可以包含基本值、对象或者函数。“相当于说对象是一组没有特定顺序的值。对象的每个属性和方法都有一个名字,而每个名字都映射到一个值。可以把ECMAScript的对象理解为散列表:无非就是一组名值对,值可以是数据和函数。每个对象都是基于一个引用类型创建的,可以是原生类型,也可以是开发人员定义的类型。

理解对象

创建自定义对象的最简单方式就是创建一个Object的实例,然后再为它添加属性和方法。

var person = new Object();
person.name = "Zee";
person.age = 20;
person.job = "beginner";

person.sayName = function(){
    alert(this.name);
}

对象字面量创建对象

var person = {
    name:"Zee",
    age:20,
    job:"beginner",
    sayName:function(){
        alert(this.name);
    }
};

创建了一个名为person的对象,添加了三个属性和一个方法。

提出原型(对象)

无论什么时候,只要创建一个新函数,就会根据一组特定的规则为该函数创建一个prototype属性,这个属性指向函数的原型对象。在默认情况下,所有原型对象都会自动获得一个constructor(构造函数)属性,这个属性是一个指向prototype属性所在函数的指针。Person.prototype.constructor指向Person。通过这个构造函数,还可以继续为原型对象添加其他属性和方法。

Person 2
prototype 1

————————————————————————————————————————

Person Prototype 1
constructor 2
name “Zee”
age 20
job “beginner”
sayName() (function)

person1
[[Prototype]] 1

表格中的对应数字为指针连接,展示了Person构造函数、Person的原型属性以及Person现有的两个实例之间的关系。在此,Person.prototype指向了原型对象,而Person.prototype.constructor又指回了Person。原型对象中除了包含constructor属性之外,还包括后来添加的属性。Person的每个实例person1和person2都包含一个内部属性,该属性仅仅指向了Person.prototype;换句话说,它们与构造函数没有直接的关系。

另外原型最初只包含constructor属性,而该属性也是共享的,因此可以通过对象实例访问。

_proto_和prototype属性的区别

_proto_属性在Firefox、Safari和Chrome中得到了支持。(除IE)

protptype属性是一个静态属性,_proto_属性则是一个实例属性。prototype属性表示构造函数的原型对象,_proto_属性表示原型对象中定义的内部属性[[Prototype]]的值。构造函数的每个新实例都有一个_proto_属性,用于引用创建它的构造函数的prototype属性,也就是该构造函数的原型对象。
[[Prototype]] = _proto_;
person1._proto_ = Person.prototype;
Person.prototype.constructor = Person;

以上代码帮助理解

原型继承

“对象系统”的继承特性,有三种实现方案,包括基于类,基于原型,基于元类。
JavaScript使用原型继承来实现对象系统。因此JavaScript没有类,采用一种名为“构造器Constructor”的机制来实现类的某些功用。对象的特性,由构造器或由构造机制带来的特性。

空对象(null)与空的对象

空对象

  • 作为一个保留字
  • 属于对象类型
  • 对象是空值的
  • 可以for…in列举

由于它并不是自object()构造器(或其子类)实例而来,因此instanceof运算会返回false

空的对象

是一个标准的、通过object()构造的对象实例。
例如

obj = new Object();

来得到的obj实例。此外对象直接量也会隐式地调用object()来构造实例,因此下面的代码也可以得到一个“空的对象”;

obj = { };

空的对象具有“对象”的一切特性。因此可以存取与定义属性和方法(tostring、valueof等),而instanceof运算也会返回true

原型继承的基本性质

对象并没有原型,而构造器(构造函数)有原型,属性“<构造器>.prototype”指向原型。
原型其实也是一个对象实例。原型的含义是指:如果构造器(构造函数)指向一个原型对象A,则由该构造器创建的实例都必然复制自A。由于实例复制自对象A,所以实例必然继承了A的所有属性、方法和其他性质。

空的对象是所有对象的基础

//取原型对象
proto = Object.prototype;
//列举对象成员并计数
var num = 0;
for(var n in proto){
    num++;
}
//显示计数
alert(num);

Object()构造器的原型就是一个空的对象

原型继承的实质是“复制”

obj1 = new Object();
obj2 = { };

上面都无非是自Object.prototype上复制出一个“对象”的映像来——它们也是空的对象;(这里的概念不绝对,具体在原型继承的实现内容中涉及)
因此对象的“构建过程”可以被简单地隐射为“复制”。

Object.prototype复制==> obj1 = clone(prototype)

原型继承的实现

JavaScript使用读遍历机制实现的原型继承。
把写复制的粒度从原型变成了成员,即我们需要写对象(例如obj2)的属性时,不必把一个原型的映像复制出来,使以后的操作指向该映像。而是仅当写某个实例成员时,将成员的信息复制到实例映像中。
Object.prototype=>obj1=>|名 |值 | 类型 |
|value|10|number |

我们发现obj1仍然是一个指向原型的引用,在操作过程中也没有与原型相同大小的对象实例创建出来。写操作并不导致大量的内存分配,经济的使用了内存。但obj1(及所有的对象实例)需要维护一张成员表。这个成员列表指向在obj1中发生了修改的成员名、值与类型。这张表与原型是否一致并不重要,但要遵循两条规则:
-保证在读取时被首先访问到
-如果在对象中没有指定属性,则尝试遍历对象的整个原型链(后面细谈),直到原型为空(null)或找到该属性。

总结原型继承实现规则发现,其实与“对象是什么”没有关系的。它描述的是一种对象的成员的存取规则,以及存储这些成员时的数据结构约定。
存取实例中的属性,比存取原型中的属性效率要高,因为少一个指针访问。

构造器(构造函数)与一般函数

//设定_proto_是函数内置的成员,get_prototype()是它的读方法。
var _proto_ = null;
function get_prototype(){
    if(!_proto_){
        _proto_ = new Object();
        _proto_.constructor = this;
    }
    return _proto_;
}

上例中函数只有在需要引用到原型时,才具有构造器的特性。也就是一般函数到构造函数的转化需要进行原型的引用。

函数的原型总是一个标准的、系统内置的object()构造器的实例,不过该实例创建后constructor属性总被赋值为当前(构造)函数。因为delete运算符总是可以删去当前属性,而让成员存取到父类的属性值。所以

function MyObject(){
    //1、显示true,表明原型的构造器(原型对象属性constructor)总是指向(构造)函数自身
    alert(MyObject.prototype.constructor == MyObject);
    //2、删除该成员
    delete MyObject.prototype.constructor;
    //3、删除操作将使该成员指向父类原型中的值
    //(显示值true)
    alert(MyObject.prototype.constructor == Object);
    alert(MyObject.prototype.constructor == new Object().constructor);
}

对象实例的基本结构

对构造函数特点和原型继承实现的了解之后,我们继续讨论它们之间的内在关系。
JavaScript中的对象实例本质上只是“一个指向其原型的,并持有一个属性列表的结构”

对象实例本身没有什么特别的性质,像一个结构体一样。
它所有的对象性质,来源于系统对原型的,以及对成员列表的理解。
现在理解“空的对象是所有对象的基础”可能更加深刻。也就是Object.prototype是所有对象的最顶层的原型。“空的对象”只不过是满足以下条件的一个结构。

  • 对象实例指向的原型对象为object.prototype。
  • 对象实例的成员列表指向一个空表

猜你喜欢

转载自blog.csdn.net/hcjs_zee/article/details/71173097