浅析JavaScript原型继承的陷阱


Posted in Javascript onDecember 03, 2013

JavaScript默认采用原型继承。虽然没有类(class)的概念,它的函数(function)可以充当构造器(constructor)。构造器结合this,new可以构建出类似Java的类。因此,JavaScript通过扩展自身能模拟类式(class-based)继承。

JavaScript和其它面向对象语言一样,对象类型采用引用方式。持有对象的变量只是一个地址,而基本类型数据是值。当原型上存储对象时,就可能有一些陷阱。

先看第一个例子

var create = function() { 
    function Fn() {} 
    return function(parent) { 
        Fn.prototype = parent 
        return new Fn 
    } 
}() var parent = { 
    name: 'jack', 
    age: 30, 
    isMarried: false
} 
var child = create(parent) 
console.log(child)

create工具函数实现了一个基本的原型继承,每次调用create都会根据parent对象去复制一个新对象,新对象全部的属性都来自于parent。这里parent有三个属性,都是基本数据类型:字符串,数字,布尔。

这时修改child看看会不会影响parent

child.name = 'lily'
child.age = 20, 
child.isMarried = trueconsole.log(child) 
console.log(parent)

结果如下

浅析JavaScript原型继承的陷阱

即修改child不会影响到parent。

再看看另外一个例子

var create = function() { 
    function Fn() {} 
    return function(parent) { 
        Fn.prototype = parent 
        return new Fn 
    } 
}() var parent = { 
    data: { 
        name: 'jack', 
        age: 30, 
        isMarried: false
    }, 
    language: ['Java'] 
} 
var child = create(parent) 
child.data.name = 'lily'
child.data.age = 20 
child.data.isMarried = true
child.language.push('javascript') 
console.dir(child) 
console.dir(parent)

注意这里的parent的两个属性data,language都是引用类型,一个是对象,一个是数组。child仍然继承与parent,随后修改了child,结果如下

浅析JavaScript原型继承的陷阱

可以看到,此时parent也被修改了,和child的name,age等都一样了。这是使用原型继承时需要注意的。

使用继承时比较好的方式是:

1,数据属性采用类式继承(挂在this上),这样new时也可以通过参数配置

2,方法采用原型继承,这样能节省内存,同时子类重写方法也不会影响父类

下面是一个满足以上2点的写类工具函数

/** 
 * @param {String} className 
 * @param {String/Function} superCls 
 * @param {Function} factory 
 */
function $class(name, superClass, factory) { 
    if (superClass === '') superClass = Object 
    function clazz() { 
        if (typeof this.init === 'function') { 
            this.init.apply(this, arguments) 
        } 
    } 
    var p = clazz.prototype = new superCls 
    clazz.prototype.constructor = clazz 
    clazz.prototype.className = className 
    var supr = superCls.prototype 
    window[className] = clazz 
    factory.call(p, supr) 
}

对象类型放在父类原型上时务必小心子类修改其,这时继承于该父类的所有子类的实例都将被修改。而这造成的bug很不容易发现。

ES5中加入了一个新API用来实现原型继承:Object.create。可以用它替代上面自实现的create函数,如下

var parent = { 
    name: 'jack', 
    age: 30, 
    isMarried: false
} 
var child = Object.create(parent) 
console.log(child)
Javascript 相关文章推荐
jquery打开直接跳到网页最下面、最低端实现代码
Apr 22 Javascript
JavaScript打印iframe内容示例代码
Aug 20 Javascript
Extjs 4.x 得到form CheckBox 复选框的值
May 04 Javascript
让javascript加载速度倍增的方法(解决JS加载速度慢的问题)
Dec 12 Javascript
javaScript中with函数用法实例分析
Jun 08 Javascript
javascript作用域、作用域链(菜鸟必看)
Jun 16 Javascript
AngularJS 避繁就简的路由
Jul 01 Javascript
js拖拽功能实现代码解析
Nov 28 Javascript
Angualrjs 表单验证的两种方式(失去焦点验证和点击提交验证)
May 09 Javascript
jQuery实现带3D切割效果的轮播图功能示例【附源码下载】
Apr 04 jQuery
微信小程序如何使用canvas二维码保存至手机相册
Jul 15 Javascript
layer 刷新某个页面的实现方法
Sep 05 Javascript
解析JavaScript中instanceof对于不同的构造器或许都返回true
Dec 03 #Javascript
探讨JavaScript中声明全局变量三种方式的异同
Dec 03 #Javascript
解析JavaScript中delete操作符不能删除的对象
Dec 03 #Javascript
解析Javascript小括号“()”的多义性
Dec 03 #Javascript
解析Javascript中中括号“[]”的多义性
Dec 03 #Javascript
jquery将一个表单序列化为一个对象的方法
Dec 02 #Javascript
jQuery获得内容和属性方法及示例
Dec 02 #Javascript
You might like
Discuz! Passport 通行证整合
2008/03/27 PHP
PHP中查询SQL Server或Sybase时TEXT字段被截断的解决方法
2009/03/10 PHP
php生成zip文件类实例
2015/04/07 PHP
PHP中用mysqli面向对象打开连接关闭mysql数据库的方法
2016/11/05 PHP
jQuery创建自己的插件(自定义插件)的方法
2010/06/10 Javascript
jQuery powerFloat万能浮动层下拉层插件使用介绍
2010/12/27 Javascript
另一个javascript小测验(代码集合)
2011/07/27 Javascript
Jquery中删除元素的实现代码
2011/12/29 Javascript
JavaScript调试技巧之console.log()详解
2014/03/19 Javascript
javascript:void(0)的问题使用探讨
2014/04/10 Javascript
PHP开发者必须掌握的6个关键字
2014/04/14 Javascript
jQuery实现鼠标划过添加和删除class的方法
2015/06/26 Javascript
基于jQuery实现点击列表加载更多效果
2016/05/31 Javascript
AngularJS 与Bootstrap实现表格分页实例代码
2016/10/14 Javascript
nodejs中向HTTP响应传送进程的输出
2017/03/19 NodeJs
vue Element-ui input 远程搜索与修改建议显示模版的示例代码
2017/10/19 Javascript
在一个页面实现两个zTree联动的方法
2017/12/20 Javascript
web前端vue实现插值文本和输出原始html
2018/01/19 Javascript
JavaScript在web自动化测试中的作用示例详解
2019/08/25 Javascript
[03:30]完美盛典趣味短片 CSGO2019年度名场面
2019/12/07 DOTA
Python中使用PIPE操作Linux管道
2015/02/04 Python
在Python中操作列表之List.pop()方法的使用
2015/05/21 Python
python抓取网页中图片并保存到本地
2015/12/01 Python
简要讲解Python编程中线程的创建与锁的使用
2016/02/28 Python
简单谈谈Python中的几种常见的数据类型
2017/02/10 Python
python脚本爬取字体文件的实现方法
2017/04/29 Python
Django contenttypes 框架详解(小结)
2018/08/13 Python
python将处理好的图像保存到指定目录下的方法
2019/01/10 Python
Python根据成绩分析系统浅析
2019/02/11 Python
在django中实现choices字段获取对应字段值
2020/07/12 Python
科颜氏法国官网:Kiehl’s法国
2019/08/20 全球购物
高中学生期末评语
2014/04/25 职场文书
乡镇平安建设汇报材料
2014/08/25 职场文书
2016年社区综治宣传月活动总结
2016/03/16 职场文书
查看nginx配置文件路径和资源文件路径的方法
2021/03/31 Servers
HTML中table表格拆分合并(colspan、rowspan)
2021/04/07 HTML / CSS