Javascript原型继承容易忽略的错误
编写Javascript的开发者都知道,JS虽然没有类(ES6添加了class语法),但是可以模拟出OOP语言的类和面向对象的概念,比如我们都知道的一句话,Javascript中处处是对象,而面向对象语言的特性是继承,封装,多态,抽象,而本文讨论的是Javascript的继承,Javascript的继承方式有原型继承,组合继承,寄生继承等等,在日常开发中,哪种继承方式更好用在于开发者对于程序的结果以及性能的考虑。笔者在下面列举出原型继承中经常容易被忽略的错误。
常见错误一:
function Fa(name){
this.name=name;
}
Fa.prototype.myname=function(){ //设置Fa的原型方法
console.log(this.name);
}
function Son(name){
Fa.call(this,name); //将Fa的this显示绑定给Son,这里的this显示绑定机制后续笔者会更新
}
Son.prototype=Fa.prototype; //将son的原型对象引用到fa的原型对象,其实就是c++里面的指针概念,直接指向fa的原型
//注意!!!!!:当你修改Son.prototype的constructor时
// Son.prototype.constructor=Son;
// 这样会导致Fa创建出来的对象的constructor也指向了Son,会使对象的类型变的很混乱
Son.prototype.sayhello=function(){ //设置son的原型方法
console.log("this is son");
}
//创建对象
var son=new Son("son");
son.sayhello(); //"this is son"
console.log(son);
var fa=new Fa("fa");
fa.sayhello(); //"this is son"
console.log(fa);
//这里发现在由Fa创建的对象中也存在sayhello方法,这是因为son.prototype直接引用了Fa.prototype
// Son.prototype=Fa.prototype; 并不会创建一个关联到 Son.prototype 的新对象,它只
//是让 Son.prototype 直接引用 Fa.prototype 对象。因此当你执行类似 Son.prototype.
//sayhello = ... 的赋值语句时也会直接修改 Fa.prototype 对象本身。显然这不是你想要的结
//果,否则你根本不需要 Son 对象,直接使用 Fa就可以了,这样一来代码也会更简单一些。
常见错误二:
function Fa(name){
this.name=name;
}
Fa.prototype.myname=function(){
console.log(this.name);
}
function Son(name){
Fa.call(this,name);
}
Son.prototype=new Fa("fa"); //调用Fa的构造函数new一个新的对象关联给Son.prototype
Son.prototype.sayhello=function(){
console.log("this is son");
}
var son=new Son("son");
console.log(son);
son.myname(); //son
var fa=new Fa("fa");
console.log(fa);
fa.sayhello(); //这里会报错。Uncaught TypeError: fa.sayhello is not a function
//我们发现在son的原型创建的方法并没有影响到Fa的原型。但是在Son.prototype = new Fa()后,
// var son=new Son("son");我们输出son.name的值为son,在原型上有一个Fa实例对象,这个实例对象也有name属性
//而输出son是因为原型链上的隐式屏蔽,这一层的属性会屏蔽上一层相同属性的值。
// Son.prototype = new Fa() 的确会创建一个关联到 Son.prototype 的新对象。但是它使用
//了 Fa(..) 的“构造函数调用”,如果函数 Foo 有一些副作用(比如写日志、修改状态、注
//册到其他对象、给 this 添加数据属性,等等)的话,就会影响到 Son() 的“后代”,后果
//不堪设想 也会让原型变的臃肿
正确做法:
function Fa(name){
this.name=name;
}
Fa.prototype.myname=function(){
console.log(this.name);
}
function Son(name){
Fa.call(this,name);
}
Son.prototype=Object.create(Fa.prototype);
//注意这一句
//当修改son的原型时。son的constructor也会指向fa,这里需要手动修改constructor
//ES6方法 属性描述符 //IE8以下不兼容
Object.defineProperty(Son.prototype,"constructor",{
writable:true, //读写属性 ,为false时为只读,外界无法修改
configurable:true,
//配置属性,为false时,外界无法删除该属性,比如delete Son.prototype.constructor会失效 在严格模式下会报错
enumerable:false,
//枚举属性,此时为false,在外界的for in访问方法不会列举出该属性,为true时反之;
value:Son //将constructor关联到Son
});
var son=new Son("son");
console.log(son);
son.myname();
console.log(son instanceof Son); //true
// 这段代码的核心部分就是语句 son.prototype = Object.create( fa.prototype ) 。调用
// Object.create(..) 会凭空创建一个“新”对象并把新对象内部的 Prototype 关联到你
// 指定的对象(本例中是 fa.prototype )。
// 换句话说,这条语句的意思是:“创建一个新的 son.prototype 对象并把它关联到 fa.
// prototype ”。
这里我们对比一下三个方法:
和你想要的机制不一样!
Son.prototype = Fa.prototype;
基本上满足你的需求,但是可能会产生一些副作用 :(
Son.prototype = new Fa();
使用 Object.create(..) 而不是使用具有副
作用的 Fa(..) 。这样做唯一的缺点就是需要创建一个新对象然后把旧对象抛弃掉,不能
直接修改已有的默认对象。
如果能有一个标准并且可靠的方法来修改对象的 Prototype关联就好了。在 ES6 之前,
我们只能通过设置 .__proto__ 属性来实现,但是这个方法并不是标准并且无法兼容所有浏
览器。ES6 添加了辅助函数 Object.setPrototypeOf(..) ,可以用标准并且可靠的方法来修
改关联。
ES6 之前需要抛弃默认的 Son.prototype
Son.ptototype = Object.create( Fa.prototype );
ES6 开始可以直接修改现有的 Son.prototype
Object.setPrototypeOf( Son.prototype,Fa.prototype );
如果忽略掉 Object.create(..) 方法带来的轻微性能损失(抛弃的对象需要进行垃圾回收)
它实际上比 ES6 及其之后的方法更短而且可读性更高。不过无论如何,这是两种完
全不同的语法。
以上是笔者对于原型继承中常常忽略的错误的总结以及更好的解决方法,至于寄生继承之类的方法并不是本文讨论的范围,后续笔者也会更新寄生继承的方法,如果忽视这些细节上的错误,对后续程序运行的结果也会产生一些不可预知的结果,细节决定成败。
Javascript原型继承容易忽略的错误的更多相关文章
- 再谈javascript原型继承
Javascript原型继承是一个被说烂掉了的话题,但是自己对于这个问题一直没有彻底理解,今天花了点时间又看了一遍<Javascript模式>中关于原型实现继承的几种方法,下面来一一说明下 ...
- 彻底理解Javascript原型继承
彻底理解Javascript原型继承 之前写过一篇Javascript继承主题的文章,这篇文章作为一篇读书笔记,分析的不够深入. 本文试图进一步思考,争取彻底理解Javascript继承原理 实例成员 ...
- [转]Javascript原型继承
真正意义上来说Javascript并不是一门面向对象的语言,没有提供传统的继承方式,但是它提供了一种原型继承的方式,利用自身提供的原型属性来实现继承.Javascript原型继承是一个被说烂掉了的话题 ...
- JavaScript原型继承工作原理
原型继承的定义 当你阅读关于JS原型继承的解释时,你时常会看到以下这段文字: 当查找一个对象的属性时,JavaScript 会向上遍历原型链,直到找到给定名称的属性为止.——出自JavaScript秘 ...
- 【读书笔记】读《编写高质量代码—Web前端开发修炼之道》 - JavaScript原型继承与面向对象
JavaScript是基于原型的语言,通过new实例化出来的对象,其属性和行为来自于两部分,一部分来自于构造函数,另一部分是来自于原型.构造函数中定义的属性和行为的优先级比原型中定义的属性和优先级高, ...
- JavaScript 原型继承开端
1.原型继承本质 就javascript对象系统的实现来讲,对象并没有原型,而构造器有原型(构造器.prototype指向其原型).对象只有构造自某个原型的说法,并没有持有某个原型的说法. ...
- 浅析Javascript原型继承(转)
引自: http://blog.csdn.net/kittyjie/article/details/4380918 原作者解释的浅显易懂,非常不错的JavaScript prototype总结 JS没 ...
- javascript原型继承圣杯模式
javascript纯面向对象开发需要使用到的一个模式,来对对象之间原型继承做中间层代理避免重复继承与代码杂乱 <!DOCTYPE html> <html lang="en ...
- javascript原型继承
在传统的基于Class的语言如Java.C++中,继承的本质是扩展一个已有的Class,并生成新的Subclass. 由于这类语言严格区分类和实例,继承实际上是类型的扩展.但是,JavaScript由 ...
随机推荐
- JEECG 3.7.2版本发布,企业级JAVA快速开发平台
JEECG 3.7.2版本发布 - 微云快速开发平台 JEECG是一款基于代码生成器的J2EE快速开发平台,开源界"小普元"超越传统商业企业级开发平台.引领新的开发模式(Onli ...
- [国嵌攻略][163][linux-usb软件系统架构]
软件系统架构 1.主机端软件架构 USB设备驱动->USB核心->USB主控制器驱动->USB主控制器 2.设备端软件架构 Gadget驱动->Gadget API->U ...
- [国嵌笔记][004][Linux快速体验]
Linux文件系统 bin目录:可执行的程序 boot目录:与Linux启动相关的文件 dev目录:设备以文件的方式存放 etc目录:配置文件 home目录:用户文件 lib目录:与库相关的文件 ro ...
- visual studio 打开微软MVC3示例MvcMusicStore的详细修改方法
1.官方下载地址:http://mvcmusicstore.codeplex.com/ 2.直接打开项目后,引用中会有三个dll文件报错,分别是System.Web.MVC;System.Web.He ...
- 急!!ftp登录错误,提示 530 not logged in,连接失败 ,,是怎么回事啊
愤怒地青鸟 | 浏览 68533 次 发布于2017-04-11 00:44 最佳答案 核心提示:Ftp登陆不了是很经常碰到的事,很多人常常是不加分析就发贴询问.老实说,这样既浪费自己时间,又浪费别人 ...
- Struts2与Ajax数据交互
写在前面: ajax请求在项目中常常使用,今天就平时掌握的总结一下,关于使用ajax请求到Struts2中的action时,前台页面与后台action之间的数据传递交互问题. 这里我主要记录下自己所掌 ...
- log4j:ERROR Category option " 1 " not a decimal integer.错误解决
log4j.properties 的配置文件中: log4j.appender.stdout.layout.ConversionPattern = %d{ABSOLUTE} %5p %c{ 1 }: ...
- hive(II)--sql考查的高频问题
在了解别人hive能力水平的时候,不管是别人问我还是我了解别人,有一些都是必然会问的东西.问的问题也大都大同小异.这里总结一下我遇到的那些hive方面面试可能涉及的问题 1.行转列(列转行) 当我们建 ...
- (2-2)SpringCloud-服务注册到Eureka Server集群并消费
服务注册到Eureka Server集群 在(2-1)SpringCloue-Eureka实现高可用注册中心中我们搭建好了高可用的Eureka注册中心,下面我们要把服务注册到Eureka Server ...
- 【高并发简单解决方案】redis队列缓存 + mysql 批量入库 + php离线整合
需求背景:有个调用统计日志存储和统计需求,要求存储到mysql中:存储数据高峰能达到日均千万,瓶颈在于直接入库并发太高,可能会把mysql干垮. 问题分析 思考:应用网站架构的衍化过程中,应用最新的框 ...