上篇末尾遗留的解释:
js中函数也是对象,因此上图一共有六个对象
其中Person 和 Coder 是函数,他们有prototype 属性
Person的prototype属性是原生的,是一个内部生成的obj,有toString方法
Coder的portotype属性认为的指向 Person的一个实例person
其他的不赘述。
(一)把对象共享的属性放在构造函数的prototype属性中
前一篇文章介绍原型链的过程,我们会发现,没有必要给所有的子对象都添加相同的方法,只要构造函数的prototype属性中有这个方法即可。
/* function Person(name){ this.name = name; this.sayHello = function(){alert(this.name);}; } */ function Person(name){ this.name = name; } Person.prototype.sayHello = function(){ alert(this.name); }; var p = new Person("jim"); p.sayHello();
这样,就不需要为每个对象添加sayHello方法了。
function Coder(language,name){ this.language = language; this.name = name; } Coder.prototype = new Person("无名"); Coder.prototype.code = function(){ alert("i am a "+this.language+" coder"); }; var c1 = new Coder("js","jim"); c1.sayHello(); c1.code();
这样,Coder的每个对象也都可以访问到 Person中的方法了
c1本身没有sayHello方法,会到Coder的prototype属性中找。而这个属性指向了Person的一个实例,这个实例当然有办法执行sayHello方法咯
好处:
1.不用为每个对象实定义各自的方法
2.“子类”能够继承父类的方法
坏处:
1.Corder.prototype属性中含有一些不必要的私有属性,如name
(二)单继承 prototype
出于提高效率的考虑,可不可以Coder.prototype 指向 Person.prototype 呢?
/* function Person(name){ this.name = name; this.sayHello = function(){alert(this.name);}; } */ function Person(name){ this.name = name; } Person.prototype.sayHello = function(){ alert(this.name); }; function Coder(language,name){ this.language = language; this.name = name; } Coder.prototype = Person.prototype; Coder.prototype.code = function(){ alert("i am a "+this.language+" coder"); }; var c1 = new Coder("js","jim"); c1.sayHello(); c1.code();
这样做的好处:
1.少实例化一个对象
2.找sayHello方法时,原型链更短
坏处:破坏了“父类”Person的接口
(三)使用临时构造函数,只继承公共方法,不继承私有属性
function Person(name){ this.name = name; } Person.prototype.sayHello = function(){ alert(this.name); }; function Coder(language,name){ this.language = language; this.name = name; } var F = function(){}; F.prototype = Person.prototype; Coder.prototype = new F(); Coder.prototype.code = function(){ alert("i am a "+this.language+" coder"); }; var c1 = new Coder("js","jim"); c1.sayHello(); c1.code();
好处:Coder.prototype 不含有 name等私有属性
(四)封装上面的过程
function extend(Child,Parent){ var F = function(){}; F.prototype = Parent.prototype; Child.prototype = new F(); Child.prototype.constructor = Child; }
通过这个方法,我们就可以很方便地继承一个“类”
最后一行代码需要解释一下
原始的构造函数的prototype属性中的constructor指向构造函数,这个属性,是的实例化对象obj可以通过 obj.constructor调用构造函数,再次构造新的对象。
开发实践中在一定的情形下会出现这种情况,因此当我们把prototype属性指向其他对象时,我需要人工的声明下constructor属性。
现在我们看看 Coder 通过 extend函数如何 继承
function extend(Child,Parent){ var F = function(){}; F.prototype = Parent.prototype; Child.prototype = new F(); Child.prototype.constructor = Child; } function Person(name){ this.name = name; } Person.prototype.sayHello = function(){ alert(this.name); }; function Coder(language,name){ this.language = language; this.name = name; } extend(Person,Coder); Coder.prototype.code = function(){ alert("i am a "+this.language+" coder"); }; var c1 = new Coder("js","jim"); c1.sayHello(); c1.code(); var c2 = new c1.constructor("php","lily"); c2.code();
好处:封装了extend方法,方便。YUI的extend方法就是这么实现的
我为了突出重点,故意疏漏了 子类如何方法父类的方法属性( 如果有需要的话)。
到目前为止,我们都是使用原型链的方式实现继承,除了原型链还有别的途径吗??有的,通过复制prototype属性。
原型链继承的实现列表
1.把对象共享的属性放在构造函数的prototype属性中
2.单继承 prototype(复制prototype)
3.使用临时构造函数,只继承公共方法,不继承私有属性
4.封装3的过程
(五)通过复制prototype属性实现继承
我们除了通过原型链 实现 继承,还可以让Child 复制 Parent的 的prototype属性,这样做的好处:
Child的prototype没有链,有2.单继承的优点,也不会修改Parent的prototype,也不会继承私有方法
看似聚集了上面四种方法的所有优点
如何复制prototype属性呢?我们实现一个extend2的方法:
/* function extend(Child,Parent){ var F = function(){}; F.prototype = Parent.prototype; Child.prototype = new F(); Child.prototype.constructor = Child; } */ function extend2(Child,Parent){ var p = Parent.prototype; var c = Child.prototype; for(var i in p){ c[i] = p[i]; } }
我们没有把Child的原型对象指向新的实例,只是复制了Parent.prototype中的所有属性,一次不需要再人工赋值constructor属性
function Person(name){ this.name = name; } Person.prototype.sayHello = function(){ alert(this.name); }; function Coder(language,name){ this.language = language; this.name = name; } extend2(Person,Coder); Coder.prototype.code = function(){ alert("i am a "+this.language+" coder"); }; var c1 = new Coder("js","jim"); c1.sayHello(); c1.code(); var c2 = new c1.constructor("php","lily"); c2.code();
和之前通过extend实现继承的代码基本一致,只是多了一个2.
这样做的好处:
没有链了,貌似可以提高效率
坏处:
复制属性,会带来一些不必要的内存开销。
但是,prototype属性大部分是方法,这种情况下,只是多了一个指针指向方法而已,并没有真正重新实现一次方法。
这五种方式实现继承都基于一种假设:我们的对象实例都是通过构造函数生成的。
如果我们不用构造函数呢,还可以做些别的事情吗??详看后文