写给Android/Java开发者的JavaScript精解(2)
apple5962
8年前
<p><strong>Java和JavaScript最不一样的地方是什么?我觉得是函数!在Java中,函数(也称为方法)是对象的一部分,一般是通过对象调用函数。在JavaScript中,函数已经和对象平起平坐,函数是独立于对象的,甚至可以用来创建对象。因此,从Java转学JavaScript时,应时刻注意不要将Java中对象和函数的概念生搬硬套到JavaScript中,这是学好JavaScript的关键。</strong></p> <h2><strong>五、在JavaScript中,对象能继承吗?</strong></h2> <p>在Java中,继承通常指的是类之间、接口之间的一种关系,所谓A类继承B类,是指A类中可以直接使用B类中的非private的字段和方法,就像A类中本来就有这些字段和方法一样。</p> <p>在JavaScript中,没有对应于Java的类、接口的概念,但是却有继承的概念。只是这里的继承指的是对象之间的一种关系。对象A继承对象B,指的是对象A可以直接使用对象B中的属性,就想对象A本身就有这些属性一样。</p> <h3><strong>1、对象继承的实现原理</strong>。</h3> <p>由于JavaScript中的对象只有键值对,所以实现对象之间的继承很自然地也要使用这一特性。</p> <p>在任意一个JavaScript对象a中,都有一个属性叫做"__proto__"(注意:这里proto两侧各是<strong>两条</strong>短下划线),这个属性的值是另一个对象b,它被称为对象A的原型(即proto一词的含义)。此时,对象a可以将对象b中的属性当作自己的来使用。举例如下:<br> <code>var b ={ prop_of_b : "property of b" }; var a = {__proto__ : b } ;</code><br> 上面代码中,我们将对象a的原型设置为对象b,此时我们可以这样使用:</p> <p>a.prop_of_b //值为 property of b</p> <p>对象a的原型为对象b,也可以称为对象a继承对象b,只是这种继承不再是Java中的继承了,它叫做基于原型的继承(我们把它简称为<strong>原型继承</strong>)。</p> <h3><strong>2、原型继承中的属性屏蔽</strong></h3> <p>好的,现在问题来了,假如我们在a中定义一个与对象b中同名的属性会怎样呢?如下所示:</p> <p>a.prop_of_b = ' own property of a' ;</p> <p>此时,如果在程序中再访问a.prop_of_b,它的值是什么呢?答案是:<strong>own property of a</strong>。<br> 这里,要引入一个新的概念,叫做对象的自有属性(own property),简单讲,它指的是那些对象自己拥有的,不是继承自原型的属性。</p> <p>当我们使用圆点语法(object.prop)去获取一个对象的属性值时,JavaScript引擎首先在对象的自有属性中查找,一旦找到,就直接返回该自有属性的值,只有当自有属性中没有要访问的属性时,才会去对象的原型中查找它的自有属性。</p> <p>回到上面的例子,a已经有了一个自有属性prop_of_b,所以访问a.prop_of_b时就直接返回了该自有属性的值 ' own property of a',而没有去它的原型b中查找属性prop_of_b。</p> <p>所以,当对象的自有属性与原型对象中的属性同名时,会屏蔽原型对象中的同名属性。这种特性很像Java中的覆写(override)。</p> <h3><strong>3、原型继承中的原型链</strong></h3> <p>进一步思考,我们可能会问,假如我们访问的对象属性既不是对象的自有属性,也不是原型对象的自有属性,JavaScript会怎么处理我们的访问请求呢?比如我这样写:</p> <p>a.notExistProperty 。</p> <p>这里,又要引入一个新的概念:<strong>原型链</strong>!</p> <p>很简单,对象a的原型是b,对象b也会有一个原型c,对象c还会有一个原型d,对象d还会有......<br> 这很自然地形成了一个原型链,链的尽头是什么?答案是一个很特殊很重要的对象:<strong>Object.prototype</strong>。</p> <p>原型链尽头的对象,就是Object的属性prototype所指向的对象。我们在上篇文章开头已经用到了Object对象,我们用它来创建了一个新的对象(new Object(),记得吧?),现在,我们对它有了新的认识,它的prototype属性所指向的对象是<strong>所有对象</strong>原型链的最后一站。</p> <p>如果你非要问对象Object.prototype难道没有__proto__属性?如果有,这个属性值是什么?<br> 答案是:<strong>有!</strong>但它的值是<strong>null</strong>。好了,你好奇心是不是得到了极大满足?</p> <p>明白了原型链,我们再来看JavaScript引擎如何处理<br> a.notExistProperty<br> 首先,引擎查找a的自有属性,没找到notExistProperty,<br> 然后查找其原型b的自有属性,也没有找到notExistProperty,<br> 然后查找b的原型的自有属性......<br> 就这样一级一级往上找,<br> 最后找到了Object.prototype,发现它也没有属性notExistProperty,于是JavaScript返回值 <strong>undefined</strong>,表示这个属性未定义。</p> <h3><strong>4、使用原型继承的一些便利方法</strong></h3> <p>对象Object和对象Object.prototype中有一些方法,为大家使用原型带来了很大便利。如Object.prototype有一个方法hasOwnProperty可以用来判断一个属性是不是对象的自有属性。上例中,我们给b增加一个新的属性:<br> b.prop1 = 'prop1' ;</p> <p>虽然,我们可以通过a.prop1来获取'prop1',但我们可以判断出来prop1并不是a的自有属性,方法如下:<br> a.hasOwnProperty('prop1') ; //值为 false</p> <p><strong>注:</strong>使用这个方法时,属性字符串必须加上单引号或双引号</p> <p>这里有一个问题,为什么我们可以直接在对象a上调用方法hasOwnProperty呢?这个方法不是Object.prototype所有的吗?</p> <p><strong>原因是这样的:</strong>a的原型是对象b,b的原型是Object.prototype,a继承了这条原型链上游<strong>所有</strong>对象的<strong>所有</strong>属性和方法,自然也包括hasOwnProperty方法。</p> <p>可是,我们并没有指定对象b的原型为Object.prototype啊?</p> <p>答案是:当我们用大括号语法({})创建对象时,如果没有明确指定该对象的原型,那么JavaScript会自动把它的原型设为Object.prototype。</p> <p>Object为我们使用原型提供了什么好的方法?</p> <ul> <li> <p>Object.create( ):方便我们创建对象的同时指定它的原型<br> 如 var b = { propB : 'propB' } ;<br> var a = Object.create(b);<br> 以上,对象a的原型将是对象b。</p> </li> <li> <p>Object.getPrototypeOf():方便我们获取一个对象的原型对象。<br> Object.getPrototypeOf(a) //值为对象b</p> </li> <li> <p>Object.setPrototypeOf():方便我们为一个对象设置原型,使用与上类似。</p> </li> </ul> <p>实际上,便利方法还有很多,ES6中甚至引入了class、extends、super、static、constructor等关键字,将原型继承封装得很像面向对像语言中的类继承。这个内容在下一个问题:创建对象的方法有多少中会详细讲,敬请期待。</p> <p><br> </p> <p>来自:http://www.jianshu.com/p/1a0ae94fd592<br> </p> <p> </p>