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