浅析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 相关文章推荐
js刷新框架子页面的七种方法代码
Nov 20 Javascript
jquery.validate使用攻略 第三部
Jul 01 Javascript
js函数中onmousedown和onclick的区别和联系探讨
May 19 Javascript
jQuery实现鼠标滑过Div层背景变颜色的方法
Feb 17 Javascript
JS中检测数据类型的几种方式及优缺点小结
Dec 12 Javascript
详谈jQuery.load()和Jsp的include的区别
Apr 12 jQuery
详解vue 模拟后台数据(加载本地json文件)调试
Aug 25 Javascript
layui问题之模拟select点击事件的实例讲解
Aug 15 Javascript
微信小程序wx.request的简单封装
Nov 13 Javascript
JS实现音量控制拖动
Jan 15 Javascript
Vue中实现回车键切换焦点的方法
Feb 19 Javascript
vue祖孙组件之间的数据传递案例
Dec 07 Vue.js
解析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
FleaPHP的安全设置方法
2008/09/15 PHP
PHP在字符串中查找指定字符串并删除的代码
2008/10/02 PHP
PHP持久连接mysql_pconnect()函数使用介绍
2012/02/05 PHP
PHP过滤黑名单关键字的方法
2014/12/01 PHP
PHP批量获取网页中所有固定种子链接的方法
2016/11/18 PHP
php检查函数必传参数是否存在的实例详解
2017/08/28 PHP
Javascript YUI 读码日记之 YAHOO.util.Dom - Part.3
2008/03/22 Javascript
js wmp操作代码小结(音乐连播功能)
2008/11/08 Javascript
IE6下JS动态设置图片src地址问题
2010/01/08 Javascript
Javascript 面向对象 命名空间
2010/05/13 Javascript
js监听鼠标事件控制textarea输入字符串的个数
2014/09/29 Javascript
jquery简单实现外部链接用新窗口打开的方法
2015/05/30 Javascript
基于JavaScript Array数组方法(新手必看篇)
2016/08/20 Javascript
js css自定义分页效果
2017/02/24 Javascript
浏览器调试动态js脚本的方法(图解)
2018/01/19 Javascript
JS实现关键词高亮显示正则匹配
2018/06/22 Javascript
浅谈ElementUI中switch回调函数change的参数问题
2018/08/24 Javascript
element vue validate验证名称重复 输入框与后台重复验证 特殊字符 字符长度 及注意事项小结【实例代码】
2018/11/20 Javascript
简述vue路由打开一个新的窗口的方法
2018/11/29 Javascript
ionic+html5+API实现双击返回键退出应用
2019/09/17 Javascript
详解vue中在父组件点击按钮触发子组件的事件
2020/11/13 Javascript
Python入门篇之字典
2014/10/17 Python
python绘制圆柱体的方法
2018/07/02 Python
Pycharm取消py脚本中SQL识别的方法
2018/11/29 Python
python使用pipeline批量读写redis的方法
2019/02/18 Python
Python字典遍历操作实例小结
2019/03/05 Python
python递归下载文件夹下所有文件
2019/08/31 Python
Django模板语言 Tags使用详解
2019/09/09 Python
PHP统计代码行数的小代码
2019/09/19 Python
Python 支持向量机分类器的实现
2020/01/15 Python
Python3加密解密库Crypto的RSA加解密和签名/验签实现方法实例
2020/02/11 Python
纯CSS3实现自定义Tooltip边框涂鸦风格的教程
2014/11/05 HTML / CSS
深入理解css中vertical-align属性
2017/04/18 HTML / CSS
优秀毕业生推荐信范文
2014/03/07 职场文书
民族团结好少年事迹材料
2014/08/19 职场文书
服务器间如何实现文件共享
2022/05/20 Servers