JavaScript面向对象编程(一)原型与继承
2013-08-03 17:25
681 查看
原型(prototype)
JavaScript是通过原型(prototype)进行对象之间的继承。当一个对象A继承自另外一个对象B后,A就拥有了B中定义的属性,而B就成为了A的原型。JavaScript中定义了Object.create()方法。该方法创建一个对象,将方法的第一个参数作为所创建对象的原型(这个方法支持可选的第二个参数,用于对对象的属性进行描述,详细可以参考《JavaScript:The Definitive Guide》)。如下面代码所示:
其中obj1是obj2的原型。所以obj2就可访问obj1中定义的name属性。在代码的第三行给obj2添加了一个自己的属性id并且赋值为1。这样obj2的值就是{id: 1, name: "obj"}。同理,obj2又作为obj3的原型,这样obj3就可访问obj2中定义的属性id以及obj2从obj1中继承的属性name。代码的第五行给obj3添加了自己的属性type并赋值为"type"。所以obj3的值就是{type: "type", id: 1, name: "obj"}。
原型链(prototype chain)
原型链是访问对象属性的机制。当我们访问一个对象的属性时,先从对象本身定义的属性找,如果找不到,再从对象的原型找,如果再找不到就从原型的原型找,依次类推,直到在Object。prototype都找不到的话,就返回undefined。这就是一个原型链。仍旧以上面的代码为例,
当要访问obj3的name属性时,先从obj3自己定义的属性中寻找。而obj3只定义了type属性,那么就从原型链的下一个节点,即obj2中寻找。而obj2只定义了id属性,所以再向下一个节点,从obj2的原型寻找,即obj1。obj1中有定义了name属性,那么就返回obj1的name属性值,即"obj"。这个搜索的原型链就是obj3->obj2->obj1->Object.prototype。而整个原型链都没有定义othername这个属性,所以访问它的结果就是undefined。
必须注意的是,这个name属性的值是保存在obj1中的,因此当obj1的name值改变之后,从所有继承于它的对象中name的值都要改变(前提是这些对象没有自己定义name属性)。而且这个是动态的,在对象生成之后改变其原型的属性值也会产生效果。原因是name属性是从其原型中访问的。看下面代码,我们更新了obj1.name的值,然后发现从继承于obj1的对象访问name值的结果都发生了变化。
对象可以通过同名自有属性覆盖所继承的非只读属性。(属性的只读性在此文中暂不讨论,会在后续文章中讨论)。看下面代码:
因为obj3自己定义了name属性,那么从obj3访问name属性时,就不必再从原型链的后续节点中寻找。所以obj1的name值改变对obj3.name没有影响。反观obj2,因为它没有自定义name属性,所以访问obj2.name时会继续搜寻原型链。当搜寻到obj1时找到了name属性,所以就访问到了obj1的name属性的值。
hasOwnProperty()函数
hasOwnProperty()可以判断对象是否定义了某个属性,而不是从其原型继承过来或者其原型链根本没有这一属性。该函数的参数是属性名,返回true代表定义了某个属性。下面的代码对三个对象检测了三个属性。
isPrototypeOf() 函数
通过isPrototypeOf() 函数可以判断一个对象是否处于另外一个对象的原型链之中。需要注意的是,并不一定是直接继承的,在原型链中即可,看代码的第2行。
Object.prototype
前文有提到,在搜索原型链的过程中,如果在Object.prototype都搜索不到的话,那么就会返回undefined。因为所有的对象都从Object.prototype中继承属性和方法(当然可以用自定义的同名属性或方法覆盖)。所以,给Object.prototype添加属性或方法时,可从所有的对象中访问。
代码中新定义了一个对象直接量x,它不定义任何自有的属性。但是可以从x访问myProperty原因是Object.prototype中有定义。从MDN中摘录一句话”All objects in JavaScript are descended from Object Object; all objects inherit methods and properties from Object.prototypeObject.prototype”。Object是构造函数。使用new Object()构造的对象都以Object.prototype为原型。
new关键字的作用初步总结下来就是:new Func()生成一个对象,用跟在new后面的函数Func对所生成的对象进行初始化,并且将Func.prototype作为所生成对象的原型。所以,所有的对象不论是用构造函数 new Object()构造的,或者是使用直接量初始化的,都从Object.prototype中继承属性和方法。同理,数组以Array.prototype为原型。函数以Function.prototype为原型。日期对象以Date.prototype为原型。字符串以String.prototype为原型。其中,Array、Function、Date和String都是构造函数。
在Object、Array、Function、Date和String中可以添加方法和属性,以达到扩展功能的效果。比如我们给字符串添加一个sayHello方法。
后续的文章JavaScript面向对象编程(二)构造函数和类会对new 关键字和构造函数继续讨论。
参考文章:
1)《JavaScript:The Definitive Guide》第六章
2)MDN https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/prototype
JavaScript是通过原型(prototype)进行对象之间的继承。当一个对象A继承自另外一个对象B后,A就拥有了B中定义的属性,而B就成为了A的原型。JavaScript中定义了Object.create()方法。该方法创建一个对象,将方法的第一个参数作为所创建对象的原型(这个方法支持可选的第二个参数,用于对对象的属性进行描述,详细可以参考《JavaScript:The Definitive Guide》)。如下面代码所示:
var obj1 = {name:"obj"}; var obj2 = Object.create(obj1); obj2.id = 1;//{id: 1, name: "obj"} var obj3 = Object.create(obj2); obj3.type = "type"; //{type: "type", id: 1, name: "obj"}
其中obj1是obj2的原型。所以obj2就可访问obj1中定义的name属性。在代码的第三行给obj2添加了一个自己的属性id并且赋值为1。这样obj2的值就是{id: 1, name: "obj"}。同理,obj2又作为obj3的原型,这样obj3就可访问obj2中定义的属性id以及obj2从obj1中继承的属性name。代码的第五行给obj3添加了自己的属性type并赋值为"type"。所以obj3的值就是{type: "type", id: 1, name: "obj"}。
原型链(prototype chain)
原型链是访问对象属性的机制。当我们访问一个对象的属性时,先从对象本身定义的属性找,如果找不到,再从对象的原型找,如果再找不到就从原型的原型找,依次类推,直到在Object。prototype都找不到的话,就返回undefined。这就是一个原型链。仍旧以上面的代码为例,
console.log(obj3.name);//obj console.log(obj3.othername);//undefinded
当要访问obj3的name属性时,先从obj3自己定义的属性中寻找。而obj3只定义了type属性,那么就从原型链的下一个节点,即obj2中寻找。而obj2只定义了id属性,所以再向下一个节点,从obj2的原型寻找,即obj1。obj1中有定义了name属性,那么就返回obj1的name属性值,即"obj"。这个搜索的原型链就是obj3->obj2->obj1->Object.prototype。而整个原型链都没有定义othername这个属性,所以访问它的结果就是undefined。
必须注意的是,这个name属性的值是保存在obj1中的,因此当obj1的name值改变之后,从所有继承于它的对象中name的值都要改变(前提是这些对象没有自己定义name属性)。而且这个是动态的,在对象生成之后改变其原型的属性值也会产生效果。原因是name属性是从其原型中访问的。看下面代码,我们更新了obj1.name的值,然后发现从继承于obj1的对象访问name值的结果都发生了变化。
obj1.name="new name"; console.log(obj2.name);//new name console.log(obj3.name);//new name
对象可以通过同名自有属性覆盖所继承的非只读属性。(属性的只读性在此文中暂不讨论,会在后续文章中讨论)。看下面代码:
obj3.name ="obj3";//override the name console.log(obj3.name);//obj3 obj1.name = "another name"; console.log(obj3.name);//obj3 console.log(obj2.name);//another name
因为obj3自己定义了name属性,那么从obj3访问name属性时,就不必再从原型链的后续节点中寻找。所以obj1的name值改变对obj3.name没有影响。反观obj2,因为它没有自定义name属性,所以访问obj2.name时会继续搜寻原型链。当搜寻到obj1时找到了name属性,所以就访问到了obj1的name属性的值。
hasOwnProperty()函数
hasOwnProperty()可以判断对象是否定义了某个属性,而不是从其原型继承过来或者其原型链根本没有这一属性。该函数的参数是属性名,返回true代表定义了某个属性。下面的代码对三个对象检测了三个属性。
obj1.hasOwnProperty("name");//true obj1.hasOwnProperty("id");//false obj1.hasOwnProperty("type");//false obj2.hasOwnProperty("name");//false obj2.hasOwnProperty("id");//true obj2.hasOwnProperty("type");//false obj3.hasOwnProperty("name");//true obj3.hasOwnProperty("id");//false obj3.hasOwnProperty("type");//true
isPrototypeOf() 函数
通过isPrototypeOf() 函数可以判断一个对象是否处于另外一个对象的原型链之中。需要注意的是,并不一定是直接继承的,在原型链中即可,看代码的第2行。
obj1.isPrototypeOf(obj2)//true obj1.isPrototypeOf(obj3)//true obj2.isPrototypeOf(obj3)//true obj3.isPrototypeOf(obj3)//false
Object.prototype
前文有提到,在搜索原型链的过程中,如果在Object.prototype都搜索不到的话,那么就会返回undefined。因为所有的对象都从Object.prototype中继承属性和方法(当然可以用自定义的同名属性或方法覆盖)。所以,给Object.prototype添加属性或方法时,可从所有的对象中访问。
Object.prototype.myProperty = "myProperty"; var x = {}; console.log(x.myProperty);//myProperty console.log(obj3.myProperty);//myProperty
代码中新定义了一个对象直接量x,它不定义任何自有的属性。但是可以从x访问myProperty原因是Object.prototype中有定义。从MDN中摘录一句话”All objects in JavaScript are descended from Object Object; all objects inherit methods and properties from Object.prototypeObject.prototype”。Object是构造函数。使用new Object()构造的对象都以Object.prototype为原型。
new关键字的作用初步总结下来就是:new Func()生成一个对象,用跟在new后面的函数Func对所生成的对象进行初始化,并且将Func.prototype作为所生成对象的原型。所以,所有的对象不论是用构造函数 new Object()构造的,或者是使用直接量初始化的,都从Object.prototype中继承属性和方法。同理,数组以Array.prototype为原型。函数以Function.prototype为原型。日期对象以Date.prototype为原型。字符串以String.prototype为原型。其中,Array、Function、Date和String都是构造函数。
Date.prototype.showToday = "it is today"; Array.prototype.showLength = "my length is..."; Function.prototype.showParameters = "parameters..."; String.prototype.showContext = "context"; var a = new Date(); console.log(a.showToday);//it is today var b = []; console.log(b.showLength);//my length is... var c = function(){}; console.log(c.showParameters);//parameters... var d = ""; console.log(d.showContext);//context
在Object、Array、Function、Date和String中可以添加方法和属性,以达到扩展功能的效果。比如我们给字符串添加一个sayHello方法。
String.prototype.sayHello = function(){ alert("Hello, I am a string!"); } var s = ""; s.sayHello();
后续的文章JavaScript面向对象编程(二)构造函数和类会对new 关键字和构造函数继续讨论。
参考文章:
1)《JavaScript:The Definitive Guide》第六章
2)MDN https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/prototype
相关文章推荐
- JavaScript面向对象编程之prototype原型与继承
- JavaScript面向对象编程(9)高速构建继承关系之整合原型链
- Javascript对象和原型继承的理解
- JavaScript之继承(原型链)
- 【面试必备】javascript的原型和继承
- JavaScript面向对象编程(继承实现方式)
- javascript原型链和继承
- javascript之对象学习笔记(二)--对象原型,继承
- JavaScript-原型继承工作原理
- Javascript面向对象编程(三):非构造函数的继承
- Javascript面向对象编程(三):非构造函数的继承
- Javascript面向对象编程-原型式继承和复制继承
- JavaScript 面向对象思想以及原型、继承
- Javascript面向对象编程(二):构造函数的继承
- Javascript面向对象编程(二):构造函数的继承
- javascript 原型链继承
- 理解JavaScript中的原型继承(2)
- JavaScript原型的工作原理(以及如何利用它来实现类的继承)
- javascript自执行函数与原型继承