`
talentluke
  • 浏览: 592484 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

前端开发必须知道的JS(一) 原型和继承

 
阅读更多

源自www.cnblogs.com/ljchow/archive/2010/06/08/1753526.html

 

原型和闭包是Js语言的难点,此文主要讲原型及原型实现的继承,在(二)中会讲下闭包,希望对大家有所帮助。若有疑问或不正之处,欢迎提出指正和讨论。

一. 原型与构造函数

  Js所有的函数都有一个prototype属性,这个属性引用了一个对象,即原型对象,也简称原型。这个函数包括构造函数和普通函数,我们讲的更多是构造函数的原型,但是也不能否定普通函数也有原型。譬如普通函数:

function F(){
  ;
}
alert(F.prototype
instanceof Object) //true

 

  构造函数,也即构造对象。首先了解下通过构造函数实例化对象的过程。

function A(x){
  
this.x=x;
}
var obj=new A(1);

实例化obj对象有三步:

  1. 创建obj对象:obj=new Object();

  2. 将obj的内部__proto__指向构造他的函数A的prototype,同时,obj.constructor===A.prototype.constructor(这个是永远成立的,即使A.prototype不再指向原来的A原型,也就是说:类的实例对象的constructor属性永远指向"构造函数"的prototype.constructor), 从而使得obj.constructor.prototype指向 A.prototype(obj.constructor.prototype===A.prototype,当A.prototype改变时则不成立, 下文有遇到)。obj.constructor.prototype与的内部_proto_是两码事,实例化对象时用的是_proto_,obj是没有 prototype属性的,但是有内部的__proto__,通过__proto__来取得原型链上的原型属性和原型方法,FireFox公开了 __proto__,可以在FireFox中alert(obj.__proto__);

  3. 将obj作为this去调用构造函数A,从而设置成员(即对象属性和对象方法)并初始化。

  当这3步完成,这个obj对象就与构造函数A再无联系,这个时候即使构造函数A再加任何成员,都不再影响已经实例化的obj对象了。此时,obj对象具有了x属性,同时具有了构造函数A的原型对象的所有成员,当然,此时该原型对象是没有成员的。

  原型对象初始是空的,也就是没有一个成员(即原型属性和原型方法)。可以通过如下方法验证原型对象具有多少成员。

 

var num=0;
for(o in A.prototype) {
  alert(o);
//alert出原型属性名字
  num++;
}
alert(
"member: "+ num);//alert出原型所有成员个数。

 

  但是,一旦定义了原型属性或原型方法,则所有通过该构造函数实例化出来的所有对象,都继承了这些原型属性和原型方法,这是通过内部的_proto_链来实现的。

  譬如

  A.prototype.say=function(){alert("Hi")};

  那所有的A的对象都具有了say方法,这个原型对象的say方法是唯一的副本给大家共享的,而不是每一个对象都有关于say方法的一个副本。

 

二. 原型与继承

  首先,看个简单的继承实现。

复制代码
1 function A(x){
2   this.x=x;
3 }
4  function B(x,y){
5   this.tmpObj=A;
6   this.tmpObj(x);
7   deletethis.tmpObj;
8   this.y=y;
9 }
复制代码

 

  第5、6、7行:创建临时属性tmpObj引用构造函数A,然后在B内部执行,执行完后删除。当在B内部执行了this.x=x后(这里的 this是B的对象),B当然就拥有了x属性,当然B的x属性和A的x属性两者是独立,所以并不能算严格的继承。第5、6、7行有更简单的实现,就是通过 call(apply)方法:A.call(this,x);

这两种方法都有将this传递到A的执行里,this指向的是B的对象,这就是为什么不直接A(x)的原因。这种继承方式即是类继承(js没有类, 这里只是指构造函数),虽然继承了A构造对象的所有属性方法,但是不能继承A的原型对象的成员。而要实现这个目的,就是在此基础上再添加原型继承。

 

  通过下面的例子,就能很深入地了解原型,以及原型参与实现的完美继承。(本文核心在此^_^)

复制代码
1 function A(x){
2   this.x = x;
3 }
4 A.prototype.a ="a";
5 function B(x,y){
6   this.y = y;
7   A.call(this,x);
8 }
9 B.prototype.b1 =function(){
10   alert("b1");
11 }
12 B.prototype =new A();
13 B.prototype.b2 =function(){
14   alert("b2");
15 }
16 B.prototype.constructor = B;
17 var obj =new B(1,3);
复制代码

  这个例子讲的就是B继承A。第7行类继承:A.call(this.x);上面已讲过。实现原型继承的是第12行:B.prototype =new A();

  就是说把B的原型指向 了A的1个实例对象,这个实例对象具有x属性,为undefined,还具有a属性,值为"a"。所以B原型也具有了这2个属性(或者说,B和A建立了原 型链,B是A的下级)。而因为方才的类继承,B的实例对象也具有了x属性,也就是说obj对象有2个同名的x属性,此时原型属性x要让位于实例对象属性 x,所以obj.x是1,而非undefined。第13行又定义了原型方法b2,所以B原型也具有了b2。虽然第9~11行设置了原型方法b1,但是你 会发现第12行执行后,B原型不再具有b1方法,也就是obj.b1是undefined。因为第12行使得B原型指向改变,原来具有b1的原型对象被抛 弃,自然就没有b1了。

 

  第12行执行完后,B原型(B.prototype)指向了A的实例对象,而A的实例对象的构造器是构造函数A,所以B.prototype.constructor就是构造对象A了(换句话说,A构造了B的原型)。

alert(B.prototype.constructor) 出来后就是"function A(x){...}" 。同样地,obj.constructor也是A构造对象,alert(obj.constructor)出来后就是"function A(x){...}" ,也就是说B.prototype.constructor===obj.constructor(true),但是 B.prototype===obj.constructor.prototype(false),因为前者是B的原型,具有成员:x,a,b2,后者是 A的原型,具有成员:a。如何修正这个问题呢,就在第16行,将B原型的构造器重新指向了B构造函数,那么 B.prototype===obj.constructor.prototype(true),都具有成员:x,a,b2。

 

  如果没有第16行,那 是不是obj = new B(1,3)会去调用A构造函数实例化呢?答案是否定的,你会发现obj.y=3,所以仍然是调用的B构造函数实例化的。虽然 obj.constructor===A(true),但是对于new B()的行为来说,执行了上面所说的通过构造函数创建实例对象的3个步骤,第一步,创建空对象;第二步,obj.__proto__ === B.prototype,B.prototype是具有x,a,b2成员的,obj.constructor指向了 B.prototype.constructor,即构造函数A;第三步,调用的构造函数B去设置和初始化成员,具有了属性x,y。虽然不加16行不影响 obj的属性,但如上一段说,却影响obj.constructor和obj.constructor.prototype。所以在使用了原型继承后,要 进行修正的操作。

  关于第12、16行,总言之,第12行使得B原型继承了A的原型对象的所有成员,但是也使得B的实例对象的构造器的原型指向了A原型,所以要通过第16行修正这个缺陷。

分享到:
评论

相关推荐

    前端开发必须知道的JS之原型和继承

    原型与构造函数 Js所有的函数都有一个prototype属性,这个属性引用了一个对象,即原型对象,也简称原型。这个函数包括构造函数和普通函数,我们讲的更多是构造函数的原型,但是也不能否定普通函数也有原型。譬如...

    前端初学者写的原型链继承代码.html

    简单的原型链继承代码,适合初学者看

    前端开发必须知道的JS之闭包及应用

    在前端开发必须知道的JS之原型和继承一文中说过下面写篇闭包,加之最近越来越发现需要加强我的闭包应用能力,所以此文不能再拖了。本文讲的是函数闭包,不涉及对象闭包(如用with实现)。如果你觉得我说的有偏差,...

    前端开源库-generate-js

    前端开源库-generate-js生成JS,一个易于使用的原型继承模型和生成器。

    2022前端企业高频问答题

    JavaScript: 数据类型、面向对象、继承、闭包、插件、作用域、跨域、原型链、模块化、自定义事件、内存泄漏、事件机制、异步装载回调、模板引擎、Nodejs、JSON、ajax等。 其他: HTTP、安全、正则、优化、重构、...

    大厂Web全栈工程师 Promise+ES6+Vue高级+React高级+前端工程化+Node.js+AST+Webpack

    完成课程的学习课程可以帮助同学们从普通的Web工程师蜕变为高级Web全栈开发工程师,课程可以同学们全程的了解以Promise、ES6、Vue高级、React高级、前端工程化、Node.js、AST、Webpack技术为主的Web全栈技术,非常的...

    web前端面试指南和高频考题解析,大厂员工整理,pdf和md版本都

    - JS 的数据类型分类和判断 - 值类型和引用类型 - 原型与原型链(继承) - 原型和原型链定义 - 继承写法 - 作用域和闭包 - 执行上下文 - this - 闭包是什么 - 异步 - 同步 vs 异步 - 异步和单线程 - 前端...

    前端 二阶段 js 复习总纲规划

    JavaScript一种直译式脚本语言,是一种动态类型、弱类型、基于原型的语言,内置支持类型。它的解释器被称为JavaScript引擎,为浏览器的一部分,广泛用于客户端的脚本语言,最早是在HTML(标准通用标记语言下的一个...

    前端面试题之JS篇

    ES5和ES6继承方式区别2. Generator了解3. 手写Promise实现4. Promise优缺点5. 观察者模式6. 手写实现bind7. 手写实现4种继承8. css菊花图9. http状态码10. Object.create实现(原型式继承,特点:实例的proto指向...

    player-prototypes:JavaScript原型简介

    初学者学习javascript或具有另一种面向对象语言的经验的开发人员,学习javascript并习惯于使用“类”来实现继承希望将一些javascript添加到他们的脚本库中的初学者前端设计师先决条件对javascript对象的理解基本熟悉...

    前端面试宝典,收集整理了全网最新、最全面的面试资料,帮助各位求职面试者斩获理想Offer

    前端面试宝典,收集整理了全网最新、最全面的面试资料,...涉及到原型链和继承、作用域和闭包、异步和单线程、ES6高级知识和一些简单的算法设计实现等考点)、面经系列(前端工程师在BAT等一线互联网公司的技术面试题)

    前端Javascript相关面试基础问答整理md

    从“原始值和引用值类型及区别”到“EventLoop事件循环&宏任务和微任务 ”,整理了Javascript学习和面试中遇到的一些基础和常见的问题。 总共包含33个问答,部分问题带有代码解答。 1. 原始值和引用值类型及区别 2. ...

    网站前端设计资料

    js基础教程:Javascript[1] 是一种由Netscape的LiveScript发展而来的原型化继承的基于对象的动态类型的区分大小写的客户端脚本语言,主要目的是为了解决服务器端语言,比如Perl,遗留的速度问题,为客户提供更流畅的...

    作为前端,你需要懂得javascript实现继承的方法

    在ES6之前,javascript不跟其他语言一样,有直接继承的方法,它需要借助于构造函数+原型对象模拟实现继承。现在我们可以利用ES6的extends方法实现继承,如果想了解更多有关ES6实现的继承请查看《ES6学习笔记(二):...

    JavaScript面向对象编程指南(第2版)

    依次介绍了JavaScript的发展历史、基础性话题(变量、数据类型、数组、循环以及条件表达式)、函数、对象、原型、继承的实现、BOM和DOM等。附录部分包括了学习JavaScript编程常用的参考资源。尤其值得一提的是,本书...

    最新的前端面试指南

    1、JavaScript相关:闭包、跨域、继承、原型链、设计模式、正则 2、CSS相关:选择器权重 3、HTML相关:盒模型、viewport、块级元素、行内元素 4、构建工具相关:gulp、webpack了解一个 5、Node.js相关:HTTP模块、...

    前端面试的热门问题之 JavaScript

    缺点:只能继承父类实例的属性和方法,不能继承原型上的属性和方法。 实例继承:为父类实例添加新特性,作为子类实例返回 拷贝继承:拷贝父类元素上的属性和方法 组合继承:相当于构造继承和原型链继承的组合体。...

    javascript从入门到高级教程,PPT2007格式.rar

    需要office2007才能打开,或者使用office2003兼容2007包,本课程分了两段,第一部分是javascript入门基础和中级教程,后面是高级,讲到了javascript的作用域链和原型链,以及javascript面向对象的继承写法,...

Global site tag (gtag.js) - Google Analytics