前端开发必须知道的JS(一) 原型和继承
原型和闭包是Js语言的难点,此文主要讲原型及原型实现的继承,在(二)中会讲下闭包,希望对大家有所帮助。若有疑问或不正之处,欢迎提出指正和讨论。
一. 原型与构造函数
Js所有的函数都有一个prototype属性,这个属性引用了一个对象,即原型对象,也简称原型。这个函数包括构造函数和普通函数,我们讲的更多是构造函数的原型,但是也不能否定普通函数也有原型。譬如普通函数:
function F(){
;
}
alert(F.prototype instanceof Object) //true
构造函数,也即构造对象。首先了解下通过构造函数实例化对象的过程。
function A(x){
this.x=x;
}
var obj=new A();
实例化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=;
for(o in A.prototype) {
alert(o);//alert出原型属性名字
num++;
}
alert("member: "+ num);//alert出原型所有成员个数。
但是,一旦定义了原型属性或原型方法,则所有通过该构造函数实例化出来的所有对象,都继承了这些原型属性和原型方法,这是通过内部的_proto_链来实现的。
譬如
A.prototype.say=function(){alert("Hi")};
那所有的A的对象都具有了say方法,这个原型对象的say方法是唯一的副本给大家共享的,而不是每一个对象都有关于say方法的一个副本。
二. 原型与继承
首先,看个简单的继承实现。
function A(x){
this.x=x;
}
function B(x,y){
this.tmpObj=A;
this.tmpObj(x);
deletethis.tmpObj;
this.y=y;
}
第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的原型对象的成员。而要实现这个目的,就是在此基础上再添加原型继承。
通过下面的例子,就能很深入地了解原型,以及原型参与实现的完美继承。(本文核心在此^_^)
function A(x){
this.x = x;
}
A.prototype.a ="a";
function B(x,y){
this.y = y;
A.call(this,x);
}
B.prototype.b1 =function(){
alert("b1");
}
B.prototype =new A();
B.prototype.b2 =function(){
alert("b2");
}
B.prototype.constructor = B;
var obj =new B(,);
这个例子讲的就是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之闭包及应用
本文讲的是函数闭包,不涉及对象闭包(如用with实现).如果你觉得我说的有偏差,欢迎拍砖,欢迎指教. 在前端开发必须知道的JS之原型和继承一文中说过下面写篇闭包,加之最近越来越发现需要加强我的闭包应用 ...
- 前端开发必须知道的JS(二) 闭包及应用
http://www.cnblogs.com/ljchow/archive/2010/07/06/1768749.html 在前端开发必须知道的JS(一) 原型和继承一文中说过下面写篇闭包,加之最近越 ...
- JS开发必须知道的41个技巧
JS是前端的核心,但有些使用技巧你还不一定知道:本文梳理了JS的41个技巧,帮助大家提高JS的使用技巧: Array 1.数组交集 普通数组 const arr1 = [, , , , , ,],ar ...
- 前端er必须知道的Git地址及常用工具地址
商城篇(找工作必练) 开源商城 推荐指数:5星,掌握了它,可以说,今后工作中的各种需求都不是问题,工作1~2年的也可以学习其中的思路(建议收藏). 这是一个集小程序/公众号/app为一体的商城系统,包 ...
- web开发必须知道的javascripat工具
1,JavaScript compressor and comparison tool 有许多工具可以帮助你压缩JavaScript代码,但是这个过程比较耗时,并且,对于某个特定的场景来说,很难分析出 ...
- 前端设计师必须知道的10个重要的CSS技巧
对于一个初入门的前端设计师,在设计修改网站前端的时候,我们需要编写一些CSS.JS的内容达到界面效果.今天分享10个对于前端设计师来说重要的CSS技巧,这也是我在给许多客户做网站的过程当中总结出来的. ...
- Webservice WCF WebApi 前端数据可视化 前端数据可视化 C# asp.net PhoneGap html5 C# Where 网站分布式开发简介 EntityFramework Core依赖注入上下文方式不同造成内存泄漏了解一下? SQL Server之深入理解STUFF 你必须知道的EntityFramework 6.x和EntityFramework Cor
Webservice WCF WebApi 注明:改编加组合 在.net平台下,有大量的技术让你创建一个HTTP服务,像Web Service,WCF,现在又出了Web API.在.net平台下, ...
- Node.js新手必须知道的4个JavaScript概念
如果只需要知道一种编程语言就可以构建一个全栈的应用程序,是不是特别了不起?Ryan Dahl为了把这个想法成为现实,创造了node.js.Node.js是建立在Chrome强劲的V8 JavaScri ...
- (转)【推荐】初级.NET程序员,你必须知道的EF知识和经验
转自:http://www.cnblogs.com/zhaopei/p/5721789.html [推荐]初级.NET程序员,你必须知道的EF知识和经验 阅读目录 [本文已下咒.先顶后看,会涨 ...
随机推荐
- eclipse安装Spring Tool Suite 各个版本的方法
有时候spring官网上找不到与我们当前eclipse对应的spring插件,用URL的方式安装费时又费力,那么如何下载对应的spring插件呢? 先到插件页面,然后点击“原先的插件版本” 显示出这个 ...
- dedecms 5.7 采集目标文章的发布时间 采集后变成当前本地时间
我已经解决了.现在把方法告诉你.都是我自己摸索出来的..虽然我不知道什么意思...下面是步骤: 1:找到 co_export.php然后找到这段代码: else if($itemName == 'pu ...
- js,mui,jq 操作基本的DOM
一.HTML <ul> <li>1111111</li> <li>22222222</li> </ul> 二.CSS li{ h ...
- pyglet -- 视频播放器 (简单实现,效果不是太好,切换资源会卡死)(三)
实现一个简单的视频播放器,效果不是很好.这里不多说,直接贴代码了. #-*- coding:gbk -*- import pyglet import os from pyglet.gl import ...
- ev的offsetX,pageX,clientX和screenX
event.offsetX.event.offsetY(相对事件发生的具体元素左上角的定位) 鼠标相对于事件源元素(srcElement)的X,Y坐标,只有IE事件有这2个属性,标准事件没有对应的属性 ...
- 到浏览器顶部的获取js和jquery
获取当前窗口到页面顶端高度: js: document.documentElement.scrollTop JQ:$(document).scrollTop()或者$(window).scrollTo ...
- 20190313 org.apache.commons.lang3.builder.EqualsBuilder的两种典型用法
org.apache.commons.lang3.builder.EqualsBuilder的两种典型用法 public boolean equals(Object obj) { if (obj == ...
- Java基础编程题——打印九九乘法表
package com.yangzl.basic; /** * 九九乘法表 * @author Administrator * */ public class Nine_Nine_Multiplica ...
- Java EE之 Hibernate 5.x版本中SchemaExport的用法
//hibernate 5.0.1 Final ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder().confi ...
- HNOI2018游记
第一次参加本省省选,结果又是一次划水 Day 0 喝了一个小时鸡汤 大家看看人家钱学森(sheng) 竞赛生要多发展些爱好 不要一考完就fake,那种下考说"大佬AC辣!太强啦!月莫月莫月莫 ...