javascript原型的那些事儿

来源:互联网 发布:eviews for mac 编辑:程序博客网 时间:2024/04/29 02:14

声明:

        发帖是对自己学过知识的一个总结,也是分享知识成果的一种有效途径。由于个人经验有限,难免会有一些错误的地方,还请大家批评,指正。

        也可以致电  tianzhen.chen0509@gmail.com 告诉我错误,在此表示感激。

        若需转载,请注明出处。

一.有感而发

        Javascript(以下简称js)是每个web开发人员都会接触到的一门动态脚本语言,我想很多人最先会用到它就是利用它来做web页面上的表单检验,而其实,js的用处远不止如此。为了提供更加强大的处理,现在流行的组件库,如Jquery,Extjs,Dojo等封装了很多的特性,使得我们利用js可以比较轻松做很多复杂的事情。这些组件库大多都是很庞大和复杂的,特别是每个库都有自己的设计理念,但不管怎样,都会用到js的prototype这个特性,它可是js中非常非常重要的特性之一。所以今天单独来聊聊这一个特性。我相信每一个想要学好js的人都应该好好掌握js中原型这个概念,特别是在看别人的代码时,如果不懂原型,你可能会常常觉得一头雾水。


二.闲聊

        我们知道,js中两个大的概念就是函数和对象,用Function创建出来的就是函数,用Object创建出来的就是对象,但其实函数也是对象来的。因为对象其实就是键值对的一个集合,是一种封装和聚集信息的载体,而函数因为也可以有方法和属性,所以称它为对象也是很合理的。

        要创建一个对象,非常简单,var obj= {}或者var obj=new Object()这样就可以了,咋看这时候obj好像空空如也,但其实它有一个隐藏的属性叫__proto__,有何用呢?它其实是一个对象来的,指向的是创建该对象的构造函数的prototype属性。以var obj=new Object()为例,obj有一个属性叫__proto__,由于它的构造函数是Object这个函数,而函数会有一个属性叫prototype,所以最终的结果就是  obj.__proto__ ===Object.prototype。另外一个例子如下:

var  add = function(p , q){    return p + q;};var instance = new add();alert(instance.__proto__ === add.prototype);//打印出true

        如果你接触js比较久了,可能会听说过原型链这个东西,而秘密就在于此,后边会慢慢展开讨论。

        现在我们再来看一个函数,拿我们上面的add函数来讲,其实,每个函数创建出来之后,会有两个隐藏的属性,一个之前说过了,就是prototype,但同时函数也会有__proto__这个属性的哦。那prototype和__proto__对于函数来讲,又分别指向什么呢?对于prototype属性来说,它指向一个对象,而这个对象有一个constructor属性,指向了函数自己,晕吗?看下边

var sayHello= function(){    alert("hello");};alert(sayHello.prototype.constructor === sayHello);//打印出true

        所以想要以sayHello为构造函数创建一个对象可以这样 var ins = new sayHello.prototype.constructor(),嫌麻烦(我也是),一般我们都用var ins = new sayHello()来创建。而对于__proto__这个属性,它跟对象的__proto__的作用是差不多的。在这里,我们还要明白,我们上边的sayHello函数是有另外一种写法的  var sayHello = new Function("alert('hello')");那么现在你知道函数的__proto__是什么了吗?它就是构造了sayHello这个函数的构造函数Function的prototype属性,也就是sayHello.__proto__===Function.prototype。在这里,我想你不难猜,还有Function.prototype.constructor === Function,所以想要创建一个函数还可以

var sayHello =new Function.prototype.constructor("alert('sayHello.')");。说了这么多,会不会更晕了,画个图看看吧。

        这里对上图作一些说明:

        注意,通过var hello = new sayHello()方式创建出来的对象实例是没有直接constructor属性的,但是如果我们执行alert(hello.constructor);却是有值的,这关系到了原型链,后边再说明。

       看看上图的三个注意点,先看1,Function.__proto__===Function.prototype,如果应用我上边的规则,那么则可以看出其实Function函数是由Function构造函数构造出来的,这有点自构造的味道,但从结果来看确实有这种效果。注意点2,Object.__proto__===Function.prototype,说明了Object()这个函数也是由Function()构造出来的。最后一点,Object.prototype.__proto__这个是比较特殊的,它的值默认为null。

       好了,如果到了这里,你对上面的东西还有些疑惑,我建议你再多看几遍。下面正式讲原型链相关的东西。

 

三.原型链(prototype)

        经过第二部分的闲聊,我想对于对象和函数一些内幕大家都有所了解了,那么__proto__和prototype这些属性,到底是用来作什么的,有什么指导意义呢?

        学过面向对象的朋友都知道在面向对象世界中,类有封装,继承和多态等主要特性,但在js中是没有class的概念的,有的只是object,所以js中没有显示的这些特性,但原型特性使得我们也可以在js中做到面向对象的一些主要特性,如继承。

         我们先从代码切入:

var sayHello =function(){    alert("hello");};var hello = new sayHello();alert(hello.constructor);//非nullhello.constructor===sayHello.prototype.constructor;//true

        从第二部分的图中我们知道,hello这个对象是没有constructor这个属性的,但如果执行alert(hello.constructor);却发现它又是有值的。原来,在执行hello.constructor的过程当中,js运行时先查看hello这个对象有没有constructor这个属性,结果发现没有,然后它就去hello.__proto__指向的sayHello函数的prototype去查看,结果发现它有constructor这个属性,所以就返回了,结果就有了hello.constructor===sayHello.prototype.constructor。

        再看另外一个例子:

function Car(){  };function Bus(){};Car.prototype.name ="Car";Bus.prototype = new Car();var bus = new Bus();//bus.run undefinedalert(bus.name);//”Car”

         首先查找bus的name属性,结果找不到,去找bus的构造函数Bus.prototype对象,发现为new Car()这个对象,它也没有name属性,结果再去找new Car()对象的构造函数Car的prototype对象,结果发现有name属性了,所以打印出”Car”来。

        最后说明一个注意事项,原型链只有在查找属性时才会用到,在更新时是不会有影响的。如下所示:

function Animal(){};Animal.prototype.name = "animal";var dog = new Animal();alert(dog.name);//打印出"animal"dog.name = "dog";alert(Animal.prototype.name);//打印出"animal",不会受dog.name= "dog";影响alert(dog.name)//打印出"dog"delete dog.name;alert(dog.name)//打印出"animal"//因为dog本身的name属性删除掉了,所以从原型链中找

 

        其实,js的原型链要说难也不是很难,但如果不够全面地了解,你很容易就陷进去,并很难说出个所以然来,希望到这里大家对原型链有一个更好的了解。


四.跟原型相关的一些东西

        1.  hasOwnProperty 判断一个属性是否为一个对象的原有属性(即不要从构造函数的prototype中去找)

        看如下代码:

var sayHello = function(){};sayHello.prototype.message = "hello world!";var hello = new sayHello();hello.name = "hello";alert(hello.hasOwnProperty("message"));//false  message属性是在原型链中才有的alert(hello.hasOwnProperty("name"));//true  name属性在hello对象本身中能找到

        2. isPrototypeOf 判断一个对象是否在某个对象的原型链上(就好像判断一个对象是否为另外一个对象的父类一样)

        看如下代码:

function Book(){};function Novel(){};var book = new Book();Novel.prototype = book;var novel = new Novel();book.isPrototypeOf(novel);//true    

 

        3.其它

        如果我们想对原有的js类进行一些扩展,比如给所有的String添加一个trim方法,则可以这样写

String.prototype.trim = function(){//实现}; 
那么对于任何一个String ,如"test".trim();将是合法的。

 

五.结束语

        其实,js原型这个东西,我也是一步一步了解的,当你刚开始看的时候,也许会很晕,甚至会疑惑这个东西是不是真正的有用处,但当你代码看得多了,多实践,多思考,你会发现,原型这个东西真的是js的一大特色来的。好吧,大家加油吧。



原创粉丝点击