一步一步掌握Javascript中的原型与原型链

2017-01-14 08:48:27来源:CSDN作者:u013940159人点击

如果大家想深入学习Javascript编程语言,Javascript中的原型及原型链是必须掌握的。当初我在学习原型及原型链的时候,就遇到过不少阻碍,希望通过我的这篇文章,能够让你真正的掌握JavaScript中的原型及原型链。好啦,开始我们的原型及原型链的旅途吧~

在介绍Javascript原型之前,我们先来了解一段历史。


Javascript继承机制的设计思想

1994年,网景公司发布了Navigator浏览器0.9版,当时这个版本的浏览器只能用来浏览,并不具有与用户用户进行互动的功能,比如说,要判断用户是否填写了表单数据,只能通过服务器来进行判断,这样会带来一个弊端:极大的浪费了带宽以及服务器资源。在这种情况下,就需要一种脚本语言,这种语言能够与浏览器进行交互,Brendan Eich负责开发这种脚本语言(也就是Javascript),当时,这位工程师认为这种脚本语言不需要设计的太复杂,只需完成简单操作即可,比如判断用户是否填写了表单数据。此时,我们还要了解下当时的编程语言背景,在1994年的时候,C++是最兴盛的面向对象的编程语言,Java1.0也将于第二年推出,Brendan Eich也将Javascript设计为面向对象的语言,在Javascript中一切皆对象。当时,他遇到了一个难题,到底需不需要将继承机制引入Javascript中?最终的结果是,也许他受到了C++和Java的影响,继承机制最终被引入到Javascript编程语言中。


下面我们来看看在C++中生成一个对象的方法:

A *a=new A(param);

而在Java中生成一个对象的方法:

Foo foo=new Foo();
我们再来看看Javascript生成一个对象的方法:

function Dog(name){	this.name=name;}var dogA = new Dog("旺旺");alert(dogA.name);//旺旺

我们再来看看Javascript中另外一种写法:

function Dog(name){	this.name=name;	this.species ="犬科";}var dogA = new Dog("旺旺");dogA.species="猫科";var dogB = new Dog("旺旺2");alert(dogB.species);//犬科
在这个例子中,生成了两个对象dogA与dogB,dogA修改了species,但是我们访问dogB的species,还是原来的值。于是,我们可以看出通过这种方法生成的实例对象,每个对象都有自己的属性和方法的副本,实例对象间不能做到属性的方法的共享,这样带来的一个缺点就是极大的浪费了系统的资源。有没有改进的方法呢?有,肯定有!


Javascript中原型(prototype)的引入

考虑到上面的不足,这位工程师决定给每个构造函数添加一个prototype属性,这个属性指向一个对象,称为prototype对象。在Javascript中,一切皆对象,对象可以分为三类:实例对象(通过new和构造函数创建出来的对象)、函数对象(一般也称为函数)、原型对象(函数对象的prototype属性所指向的对象)。我们首先来看下实例对象中的属性和方法,如下图:


实例一旦创建,将自动引用prototype对象的方法和属性,也就是说,针对实例对象而言,它的属性和方法可以分为两种:一种是本地的,另外一种是引用的。


prototype、_proto_和constructor三角关系

我将用下面的一副图来描述三者的关系:



我将上幅图总结为下面几点内容:

  • 任何函数对象都有prototype属性,它指向对应的原型对象,表示其实例对象的原型对象;
  • 任何原型对象都有一个constructor属性,它指向对应的函数对象;
  • 任何对象都有一个隐藏的_proto属性,它是对原型对象的引用;
  • _proto_属性不是一个规范的属性,只是部分浏览器实现了此属性(如chrome和Firefox),如果想访问对象的原型,可以使用Object.getPrototype(object)访问。

一定要牢记上面的几点内容,它将对后面的内容非常重要。


原型实例分析

说明:后面的实例,如果看不懂,可以再看看前面的内容,好好理解下前面的内容,一定可以理解后面的例子的。同时,下面的例子运行结果我会有一定的解释。

step1:查看对象的原型

function Person(name, age){    this.name = name;    this.age = age;     this.getInfo = function(){    	console.log(this.name + " is " + this.age + " years old");    }}var will = new Person("Will", 28);console.log(will.__proto__);console.log(will.constructor);
运行结果:



解释will对象本身并没有"constructor"这个属性,但是通过原型链查找,找到了will原型(will.__proto__)的"constructor"属性,并得到了Person函数

对象之间的关系:




step2:查看对象will原型的原型

function Person(name, age){    this.name = name;    this.age = age;     this.getInfo = function(){    	console.log(this.name + " is " + this.age + " years old");    }}console.log(will.__proto__ === Person.prototype);console.log(Person.prototype.__proto__);console.log(Person.prototype.constructor);console.log(Person.prototype.constructor === Person);
运行结果:


对象之间的关系



step3:查看对象Object的原型

function Person(name, age){    this.name = name;    this.age = age;     this.getInfo = function(){    	console.log(this.name + " is " + this.age + " years old");    }}console.log(Person.prototype.__proto__ === Object.prototype);console.log(typeof Object);console.log(Object);console.log(Object.prototype);console.log(Object.prototype.__proto__);console.log(Object.prototype.constructor);
运行结果:


对象之间的关系:




step4:查看函数对象的原型

function Person(name, age){    this.name = name;    this.age = age;     this.getInfo = function(){    	console.log(this.name + " is " + this.age + " years old");    }}console.log(Person.__proto__ === Function.prototype);console.log(Person.constructor === Function)console.log(typeof Function);console.log(Function);console.log(Function.prototype);console.log(Function.prototype.__proto__);console.log(Function.prototype.constructor);

运行结果:



对象之间的关系:



通过原型改进实例-实现继承

step1:最老的方式

function Person(name, age){    this.name = name;    this.age = age;     this.getInfo = function(){    	console.log(this.name + " is " + this.age + " years old");    }}var will = new Person('Will',28);var wilber = new Person("Will", 20);

对象之间的关系:



step2:通过原型prototype实现继承

function Person(name, age){    this.name = name;    this.age = age;    }Person.prototype.getInfo = function(){	console.log(this.name + " is " + this.age + " years old");}
对象之间的关系:



原型链

什么是原型链?

由于_proto_是任何对象都有的属性,而Javascript中万物皆对象,所以会形成一条_proto_连接起来的链条,递归访问_proto_必须最终到头,并且值为null。
原型链有什么作用?
属性查找与隐藏:当Javascript引擎查找对象的属性时,先查找对象本身是否存在该属性,如果不存在,再沿着_proto_这条链向上查找,但不会查找自身的prototype。

var A = function(){};var a = new A();


以上面的这幅图为例,查找某个属性的时候,会沿着这条原型链进行查找,直到为null。

原型链之属性查找

function Person(name, age){    this.name = name;    this.age = age; }Person.prototype.MaxNumber = 9999;Person.__proto__.MinNumber = -9999;var will = new Person("Will", 28);console.log(will.MaxNumber);// 9999console.log(will.MinNumber);//undefined

原型链之属性隐藏

function Person(name, age){    this.name = name;    this.age = age; }Person.prototype.getInfo = function(){	console.log(this.name + " is " + this.age + " years old");	}var will = new Person("Will", 28);will.getInfo = function(){    console.log("getInfo method from will instead of prototype");//};will.getInfo();


对象创建方式影响原型链的构成

var July = {    name: "July",    age: 28,    getInfo: function(){        console.log(this.name + " is " + this.age + " years old");    },}console.log(July.getInfo());



hasOwnProperty
var will = new Person('Will',28);	var wilber = new Person("Will", 20);	for(var attr in will){	    console.log(attr);	}		console.log('---------------');	for(var attr in wilber){	    if(will.hasOwnProperty(attr)){	        console.log(attr);	    }	}


总结

  1. 在Javascript中,通过原型(prototype)实现了对象的继承;
  2. 在Javascript中,一切皆对象,prototype、_proto_与constructorJavascript中所有的对象关联起来;
  3. 原型链可以实现对象属性的查找和隐藏;
  4. hasOwnProterty函数可以用来判断属性是对象本地属性还是原型链上的属性;

相信到这里,你已经掌握了Javascript中的原型和原型链的知识点了。



最新文章

123

最新摄影

微信扫一扫

第七城市微信公众平台