JavaScript进阶:深入理解原型与原型链

来源:互联网 发布:电视的mac 编辑:程序博客网 时间:2024/05/16 04:43

JavaScript进阶:深入理解原型与原型链

一、普通对象和函数对象

对象可以分为普通对象和函数对象,ObjectFunctionECMAScript是自带函数对象。看如下代码:

function fun1 () {};var fun2 = new Function(); var ob1 = {};var ob2 = new Object();var ob3 = new fun1(); console.log (typeof Function);  //Functionconsole.log(typeof Object);     //Function console.log(typeof fun1);   //Functionconsole.log(typeof fun2);   //Functionconsole.log(typeof ob1);        //Objectconsole.log(typeof ob2);        //Objectconsole.log(typeof ob3);        //Object

在上面例子中ob123为普通对象。fun12为函数对象,因为fun1fun2归根结底是通过new Function()创建的。

 

二、原型对象

JavaScript中,每当定义一个对象的时候,对象中都包含一些预定义的属性。其中每个函数对象都会有一个prototype属性,这个属性指向函数的原型对象。这里有一个特别重要的特性:每个对象(普通对象和函数对象)都有_proto_属性,但是只有函数对象才有prototype属性

看如下代码:

function Person() {Person.prototype.name = "tom";Person.prototype.sayname = function () {alert (this.name);}}var person1 = new Person();var person2 = new Person();alert(person1.name == person2.name); //true,因为两个实例的name属性,都是同一属性,即原型对象中的name。

如果不好理解,可以把上面代码转换一下:

Person.propertype =  {name = "tom";sayname = function () {alert (this.name);}}

上面提到对象可分为普通对象和函数对象,这里原型对象便是普通对象了。可以这样认为原型对像就是Person.prototype

 

三、原型对象中的construtor属性

上述原型对象Person.prototype这里只给了他一个name属性,默认情况下所有的原型对象自动获得一个construtor属性。construtor属性是一个指针,指向它的构造函数,在例子中指向Person。基本构造函数有Number()、Boolean()、String()、Function()、Object()等等。如果通过构造函数创建新对象,这种创建方法都有一个共同点,那就是代码格式为:var 【对象】 = new 【构造函数】。

Person.prototype.constructor == Person;//true

看看如下代码:

console.log(Person.prototype.constructor === person1.constructor); //true

这里严格相等的原因是实例person1constructor属性和Person.prototypeconstructor属性是同一样东西,正确地说person1constructor属性继承于原型对象Person.prototype

 

四、_proto_

JS在创建对象(不论是普通对象还是函数对象)的时候,都有一个叫做_proto_的内置属性,用于指向创建它的构造函数的原型对象。

对象person1有一个_proto_属性,创建它的构造函数是Person,构造函数的原型对象是Person.prototype,所以

person1._proto_ = Person.prototype;

 

Person的原型对象为Person.prototype;Person.prototype.constructor = Person;person1._proto_ = Person.prototype;person1.constructor = Person;

五、检验对原型链的理解

1person1_proto_是什么?

解:意思是实例对象person1的构造函数的原型对象,首先person1的构造函数是Person,然后Person的原型对象是Person.prototype。所以person1_proto = Person.prototype

2、Person_proto_是什么?

解:因为Person在创建时是通过function Person(){}的形式创建的,所以Person的构造函数是Function,而Function的构造函数是Function.prototype。所以Person_proto = Function.prototype

3、Person.prototype._proto_是什么?

解:在前面提过Person.prototype是一个普通对象,所以他的构造函数是ObjectObject的原型对象是Object.prototype

4、Object._proto_是什么?

解:ObjectPerson一样是函数对象,所以两者答案一样。

5、Object.prototype._proto_是什么?

解:这个比较特殊,结果为null,因为null位于原型链的顶端。

 

六、函数对象

所有函数对象,包括:NumberBooleanStringFunctionObject_proto_都是Function.prototype

Number.__proto__ === Function.prototype  // trueNumber.constructor == Function //trueBoolean.__proto__ === Function.prototype // trueBoolean.constructor == Function //trueString.__proto__ === Function.prototype  // trueString.constructor == Function //trueObject.__proto__ === Function.prototype  // trueObject.constructor == Function // trueFunction.__proto__ === Function.prototype // trueFunction.constructor == Function //trueArray.__proto__ === Function.prototype   // trueArray.constructor == Function //trueRegExp.__proto__ === Function.prototype  // trueRegExp.constructor == Function //trueError.__proto__ === Function.prototype   // trueError.constructor == Function //trueDate.__proto__ === Function.prototype    // trueDate.constructor == Function //true

ECMAScript中内置构造器有12个,这里列举了8个构造器。剩下的如Global不可以访问,Arguments仅在函数调用时由JS引擎创建,MathJSON是以对象形式存在的。他们的构造函数是Object_proto_Object.prototype。如下代码:

Math.__proto__ === Object.prototype  // trueMath.construrctor == Object // trueJSON.__proto__ === Object.prototype  // trueJSON.construrctor == Object //true

所有的构造器都继承于Function.prototype,甚至包括根构造器ObjectFunction自身。

Function.prototype也是唯一一个类型为Function的原型对象。其他构造器的原型对象的类型都是Object。看如下代码:

console.log(typeof Function.prototype) // functionconsole.log(typeof Object.prototype)   // objectconsole.log(typeof Number.prototype)   // objectconsole.log(typeof Boolean.prototype)  // objectconsole.log(typeof String.prototype)   // objectconsole.log(typeof Array.prototype)    // objectconsole.log(typeof RegExp.prototype)   // objectconsole.log(typeof Error.prototype)    // objectconsole.log(typeof Date.prototype)     // objectconsole.log(typeof Object.prototype)   // object

这里可能有一个疑问,Function.prototype是一个Function,那么Function.prototype的构造函数应该是FunctionFunction.prototype._proto_岂不是Function.prototype,这样就进入一个死循环。其实ESMASriptFunction.prototype._protoObject.prototype,而Object.prototype._proto_则是原型链最顶端的null

 

六、构造器的继承

Object的原型对象里面有着如下图中的所有属性,

 

而当我们创建一个数组时:

var arr = new Array();

arr继承了Array.prototype的所有属性和方法,如下图:

 

但是却没有找到constructor,按照道理来说,每一个函数对象的原型对象都应该有constroctor才对。原因是,因为Array.prototype是一个对象,Array.prototype继承了Object.prototype的所有属性和方法,所以Array.prototype最终是拥有constructor属性的。