(转)【javascript基础】原型与原型链

原文地址:http://www.cnblogs.com/allenxing/p/3527654.htmljavascript

前言

荒废了好几天,在宿舍闷了几天了,一直想着回家放松,什么也没搞,论文就让老师催吧。不过,闲的没事干的感受真是很差,仍是看看书,写写博客吧,今天和你们说说函数的原型。html

原型是什么

第一次看到这个的时候,没太理解这个概念,其实也就是一个概念呗,没啥神秘的。书上说每一个函数都有一个prototype属性(原型属性),这个属性是一个指针,指向一个对象(原型对象),这个对象包含这个函数建立的实例的共享属性和方法。也就是说原型对象中的属性和方法是全部实例共享的,打住,那咱们就先建立一个函数看看,原型是什么东东java

var test = function(){}
console.log(test.prototype);//Object {}

看来,是这的有这么一个属性,能够看出是一个对象,可是默认的是一个空的对象,既然是一个对象,那咱们就能够给它添加属性和方法喽,试试看数组

var test = function (){}
test.prototype.name = "hainan";
test.prototype.age = "25";
test.prototype.getName = function(){console.log(this.name);}
console.log(test.prototype);//Object {}
View Code

咱们成功的添加了属性和方法,骄傲吧,对象中的属性一会咱们在解释,如今看看咱们修改以后的原型对象,有啥用呢?刚才咱们说过,原型对象中的属性和方法是这个函数new出来实例所共享的,那咱们就new实例出来试试ide

复制代码
var test = function test(){}
test.prototype.name = "hainan";
test.prototype.age = "25";
test.prototype.getName = function(){console.log(this.name);}
console.log(test.prototype);//Object {}
var o1 = new test();
console.log(o1.getName());
console.log(o1.name);
var o2 = new test();
console.log(o2.getName());
console.log(o2.name);
复制代码
View Code

看上面的图,咱们new出来两个实例,这两个实例中并无name对象和getName()方法,可是咱们却使用了该属性和方法,就是由于函数的原型对象中存在这个属性和方法,而且是每一个实例均可以使用的,这就是原型的神秘之处。也就是说之后咱们想在每个实例中添加属性和方法,那咱们就把这个共有的属性或方法直接添加到原型对象上就能够了。函数

理解原型对象

原型对象

如今咱们知道了,函数有一个prototype属性,这个属性是一个指针,指向一个原型对象,这个原型对象中的属性和方法是这个函数的实例所共有的。如今咱们看看这个原型对象中的属性,看这幅图post

咱们给一个原型属性添加属性以后,这个对象就不是空得了,看第一个图,上面打印出来的Object是空的,其实这个对象不是空的,只是有些属性没有被枚举出来,看下图this

嗯,这样就对了,不管啥时候,只要建立一个新函数,就为给这个函数建立一个prototype属性,在默认的状况下,全部的原型对象会自动添加一个constructor属性,从名字就能够看出来应该指向构造函数,看图spa

指向了构造函数,也就是test.prototype.constructor === test。prototype

属性中还有一个__prototype__属性,咱们先放一下,咱们仍是看new出来那个实例的图,此次我把属性展开你们看看

var test = function test(){}
test.prototype.name = "hainan";
test.prototype.age = "25";
test.prototype.getName = function(){console.log(this.name);}
console.log(test.prototype);//Object {}
var o1 = new test();
console.log(o1);
View Code

咱们分别打印了test的原型对象和一个实例对象,咱们能够看到原型对象和实例对象中都有一个__proto__对象,可是指向不一样,原型对象中的__proto__指向Object,而实例中的__proto__指向内部明叫test的对象,展开这个对象能够看到就是原型对象。就是说每个实例中有一个__proto__属性指向原型对象。是否是有点晕呢,画个图看看先

 

是否是清楚点了呢,每个实例的内部都包含一个内部属性__proto__,指向了构造函数的原型,就是这个属性链接了实例和原型对象之间的关系,而且咱们知道实例中不包含name属性和getName方法,可是咱们却使用了getName(),就是经过这个__proto__属性查找的。

你们应该发现了test的原型对象中也有一个__proto__属性,这个属性指向谁呢,咱们来分析一下。咱们知道了__proto__属性存在一个实例中并指向的是一个原型对象,如今就是说test的原型对象时某一个对象的实例喽,由于它有一个__proto__属性嘛。那test的原型对象是哪一个对象的实例呢?咱们知道javascript全部的对象都是基于Object这个对象的,都是Object的实例,咱们大胆的猜想就是Oject的实例,咱们展开这个属性看看就知道咱们猜的对不对了,看图

 

 嗯,咱们看到了__proto__属性指向的对象中存在一些方法,这些方法就是咱们前面介绍的Object对象的方法,好牛,咱们猜对了。咱们刚才说了一下,实例能够共享原型的方法和属性,也就是test的原型可使用Object原型中方法,而test的实例可使用test的原型中的方法,也就是说test的实例可使用Object原型中的方法,嗯,就是这样,咱们试一下,看代码,使用一个简单的函数试一下就知道了

var test = function test(){}
test.prototype.name = "hainan";
test.prototype.age = "25";
test.prototype.getName = function(){console.log(this.name);}
var o1 = new test();
console.log(o1.toString());
View Code

咱们使用了Object原型中的方法,咱们再一次猜对了。如今咱们把上面的图补充完整

如今就完整了,这就是这个例子的完整原型图形。咱们之前说过,全部的应用类型都是继承Object,全部函数的默认原型都是Object的实例,所以默认原型都包含一个默认指针指向Object.prototype。这就是咱们所说的原型链,继承就是经过原型链实现的,这就是全部的自定义类型都会继承toString()和valueOf()等默认方法的根本缘由。Object是全部引用类型的父类,能够这么理解。

isPrototypeOf

使用方法a.isprototypeOf(b),判断对象a是不是实例b__proto__指向的原型对象,若是是返回true,不然返回false。看个例子

复制代码
var test = function test(){}
test.prototype.name = "hainan";
test.prototype.age = "25";
test.prototype.getName = function(){console.log(this.name);}
var o1 = new test();
console.log(test.prototype.isPrototypeOf(o1));//true
复制代码

hasOwnProperty

这个方法是检测一个属性是否存在实例中,存在原型中会返回false,看例子

复制代码
var test = function test(){}
test.prototype.name = "hainan";
test.prototype.age = "25";
test.prototype.getName = function(){console.log(this.name);}
var o = new test();
console.log(o.hasOwnProperty("name"));//false
o.name = "xing";//给实例添加属性
console.log(o.hasOwnProperty("name"));//true
复制代码

属性查找

对象实例能够访问保存在原型中的值,可是不能重写原型中的值。每次要读取某一个属性时,都会执行一次搜索:首先在对象自己开始查找,若是查找到了就返回这个属性,若是没有找到,则继续搜索__proto__指向的原型对象,若是尚未找到,则继续搜索原型对象中__proto__指向的原型对象,这样一直迭代下去,这就是原型链的做用。看例子

复制代码
var test = function test(){}
test.prototype.name = "hainan";
test.prototype.age = "25";
test.prototype.getName = function(){console.log(this.name);}
var o = new test();
console.log(o.name);//hainan
o.name = "xing";//给实例添加属性
console.log(o.name);//xing
delete o.name;//删除实例的name属性
console.log(o.name);//hainan
复制代码

重写原型对象

咱们已经知道怎么给一个原型对象添加属性和方法,可是你们都会想到一个简单地方法来一块儿添加,就像这样

复制代码
var test = function test(){}
test.prototype = {
  name : "hainan",
  age : "25",
  getName : function(){return this.name}    
};
var o = new test(); o.getName();//"hainan"
console.log(o.constructor == test);//false
复制代码

咱们思考一下,上面的代码test.prorotype如今已经指向了一个新的对象,已经不是原来那个默认的原型对象了,原来的默认原型对象咱们以前只是添加属性并无重写它,全部他的内部属性仍是存在了,如今咱们重写了这个对象,即prototype指向了一个新的对象了,那他原来的属性constructor就没有了,若是咱们之后会使用这个属性,那咱们应该人为的设置,例如

复制代码
var test = function test(){}
test.prototype = {
  constructor : test,
  name : "hainan",
  age : "25",
  getName : function(){return this.name}    
};
var o = new test(); o.getName();//"hainan"
console.log(o.constructor == test);//true
复制代码

原型动态性

看个例子先

var test = function test(){}
var o = new test();
test.prototype.name = "hainan";
console.log(o.name);

咱们通常的时候确定是先设置原型在建立实例,这在任何状况下都是没有问题的。咱们建立实例是在给原型添加属性以后,即便这样咱们也能够在实例中使用这个属性,这就是原型的动态性。这是因为在原型中查找值的过程是一次搜索,全部你修改的属性能够当即在实例中获得体现。可是重写一个函数的原型就不是这样了,看例子

复制代码
var test = function test(){}
var o = new test(); 
o.getName();//TypeError: Object #<test> has no method 'getName' test.prototype = { constructor : test, name : "hainan", age : "25", getName : function(){return this.name} };
复制代码

出现了错误,也就是我先建立了一个实例,在重写函数的原型对象这是不行的。缘由是这样的,因为实例中的__proto__指针只是指向原型,而不是构造函数,上面的这段代码中,咱们建立实例的时候,这个实例中的__proto__指向的是函数的默认原型对象,当咱们重写了这个函数的原型对象时,虽然函数的prototype属性指向了新的对象,可是实例中的已经建立好了,它__proto__并无改变,这个属性仍是指向的是默认的原型对象,全部它的内部没有这个方法。可是若是在重写原型以后建立一个实例的话,这个新的实例的__proto__指向的就是新的原型对象了,像这样

复制代码
var test = function test(){}
test.prototype = {
  constructor : test,
  name : "hainan",
  age : "25",
  getName : function(){return this.name}    
};
var o = new test(); 
o.getName();//"hainan"
复制代码

原生对象的原型

原生对象和咱们自定义对象同样,都存在原型,原生对象的方法都存在原型中,这样咱们建立一个新的对象实例时,这些对象就会拥有这些方法,固然咱们可扩展原型对象,这样咱们之后new出来的原型对象的实例就会共享这个方法或属性了。

console.log(String.prototype.slice);//function

咱们扩展一个Array类型,增长一个函数contains(),判断数组中是否包含一个值

复制代码
Array.prototype.contains = function(){
   var length = arguments.length;
   if(length != 1) return;
   for(var i=0;l=this.length,i<l;++i ){
      if(arguments[0] === this[i]) return true;
   }
   return false;
}
var arr = [1,2,3];
console.log(arr.contains(1));//true
console.log(arr.contains(4));//false
复制代码

这就成了。

PS:给你们截一个图你们看看,不懂的能够在下面讨论下

 

小结

就先写到这吧,你们有不懂的能够在下面讨论吧,我不懂的话我再去问大神,大伙要是以为写得乱的话推荐去看看《javascript高级程序设计》,那上面写得比较好。小伙伴们都要回家了吧,提早祝你们春节快乐,立刻有钱,立马变土豪。

相关文章
相关标签/搜索