浅析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 相关文章推荐
关于javascript 回调函数中变量作用域的讨论
Sep 11 Javascript
javascript学习笔记(四) Number 数字类型
Jun 19 Javascript
Javascript中对象继承的实现小例
May 12 Javascript
jQuery插件datepicker 日期连续选择
Jun 12 Javascript
JavaScript使用RegExp进行正则匹配的方法
Jul 11 Javascript
jQuery遮罩层效果实例分析
Jan 14 Javascript
基于vue打包后字体和图片资源失效问题的解决方法
Mar 06 Javascript
javascript验证form表单数据的案例详解
Mar 25 Javascript
vue-cli3 配置开发与测试环境详解
May 17 Javascript
VUE脚手架具体使用方法
May 20 Javascript
利用js canvas实现五子棋游戏
Oct 11 Javascript
详解如何解决使用JSON.stringify时遇到的循环引用问题
Mar 23 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
window+nginx+php环境配置 附配置搭配说明
2010/12/29 PHP
PHP获取MSN好友列表类的实现代码
2013/06/23 PHP
PHP中文竖排转换实现方法
2015/10/23 PHP
PHP房贷计算器实例代码,等额本息,等额本金
2017/04/01 PHP
PHP判断json格式是否正确的实现代码
2017/09/20 PHP
javascript使用中为什么10..toString()正常而10.toString()出错呢
2013/01/11 Javascript
Java/JS获取flash高宽的具体方法
2013/12/27 Javascript
JavaScript继承模式粗探
2016/01/12 Javascript
jQuery日历插件datepicker用法详解
2016/03/03 Javascript
分析js闭包引起的事件注册问题
2016/03/29 Javascript
JS操作COOKIE实现备忘记录的方法
2016/04/01 Javascript
angular实现商品筛选功能
2017/02/01 Javascript
JavaScript实现瀑布流以及加载效果
2017/02/11 Javascript
详解如何将angular-ui的图片轮播组件封装成一个指令
2017/05/09 Javascript
详解微信小程序Radio选中样式切换
2017/07/06 Javascript
浅谈ajax在jquery中的请求和servlet中的响应
2018/01/22 jQuery
js和jQuery以及easyui实现对下拉框的指定赋值方法
2018/01/23 jQuery
Vue 去除路径中的#号
2018/04/19 Javascript
nodejs检测因特网是否断开的解决方案
2019/04/17 NodeJs
微信小程序事件对象中e.target和e.currentTarget的区别详解
2019/05/08 Javascript
Vue 动态添加路由及生成菜单的方法示例
2019/06/20 Javascript
Python 可爱的大小写
2008/09/06 Python
如何运行Python程序的方法
2013/04/21 Python
OpenCV2从摄像头获取帧并写入视频文件的方法
2018/08/03 Python
解析Python3中的Import
2019/10/13 Python
Python内置数据类型list各方法的性能测试过程解析
2020/01/07 Python
Pycharm小白级简单使用教程
2020/01/08 Python
python使用re模块爬取豆瓣Top250电影
2020/10/20 Python
python中pyqtgraph知识点总结
2021/01/26 Python
西班牙灯具网上商店:Lampara.es
2018/06/05 全球购物
成龙霸王洗发水广告词
2014/03/14 职场文书
共青团员自我评价
2015/03/10 职场文书
市直属机关2016年主题党日活动总结
2016/04/05 职场文书
使用feign服务调用添加Header参数
2021/06/23 Java/Android
24年收藏2000多部退役军用电台
2022/02/18 无线电
Ruby使用Mysql2连接操作MySQL
2022/04/19 Ruby