zoukankan      html  css  js  c++  java
  • JavaScript原型继承的陷阱

    JavaScript原型继承的陷阱

    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 = true
     
    console.log(child)
    console.log(parent)

    结果如下

    即修改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,结果如下

    可以看到,此时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)
  • 相关阅读:
    BZOJ 3506 机械排序臂 splay
    BZOJ 2843 LCT
    BZOJ 3669 魔法森林
    BZOJ 2049 LCT
    BZOJ 3223 文艺平衡树 splay
    BZOJ 1433 假期的宿舍 二分图匹配
    BZOJ 1051 受欢迎的牛 强连通块
    BZOJ 1503 郁闷的出纳员 treap
    BZOJ 1096 ZJOI2007 仓库设计 斜率优化dp
    BZOJ 1396: 识别子串( 后缀数组 + 线段树 )
  • 原文地址:https://www.cnblogs.com/rhinemetal/p/3946830.html
Copyright © 2011-2022 走看看