zoukankan      html  css  js  c++  java
  • 破解 JS(原型)继承

    总体分为四大类:利用空对象作为中介继承、Object.create 继承、setPrototypeOf 继承、拷贝继承

    function Animal(name, age) {
      this.name = name;
      this.age = age;
    }
    
    Animal.prototype = {
      speak: function() {
        console.log('my name is ' + this.name);
      }
    }
    
    function Cat() {
      Animal.apply(this, arguments);
      this.food = 'mouse';
    }


    一、利用空对象作为中介继承

    function extend(child, parent) {
      var F = function() {};
      F.prototype = parent.prototype;
      child.prototype = new F();
      child.prototype.constructor = child;
    }

    F是空对象,所以几乎不占内存。这其实就是 YUI 实现继承的方法。

    试一试

    二、Object.create 继承

    Object.create 会使用指定的原型对象和属性去创建一个新对象。

    function extend(child, parent) {
      // 任何一个prototype对象都有一个constructor属性,指向它的构造函数。
      // 使 Cat.prototype 指向 Animal.prototype, 但他有一个副作用:Cat.prototype.constructor指向Animal
      child.prototype = Object.create(parent.prototype);
      // 修正 constructor
      child.prototype.constructor = child;
    }

    试一试

    疑问一:为什么不直接  child.prototype = parent.prototype; ?

      如果这样的话,child.prototype 会直接引用 parent.prototype 对象,那么当你对 child.prototype.constructor 进行赋值操作时,就把 parent.prototype.constructor 也给修改了

    疑问二:为什么不用child.prototype = new parent(); 

       new parent() 确实会创建一个关联到 child.prototype 的新对象。但如果函数 parent 有一些副作用(比如修改状态、注册到其它对象、给 this 添加属性等等)的话,会影响到 child() 的后代,后果不堪设想!

    综上所诉,Object.create 是最好的选择,虽然它是创建了一个新对象替换掉了默认的对象。那有没有直接修改默认对象的方法呢?答案就是 setPrototypeOf

    三、setPrototypeOf 继承

    setPrototypeOf 是 ES6新增的辅助函数。下面来做一下对比

    // 抛弃默认的 child.prototype
    child.prototype = Object.create(parent.prototype);
    
    // 直接修改默认的 child.prototype
    Object.setPrototypeOf(child.prototype, parent.prototype);

    经过对比发现:如果忽略Object.create() 带来的轻微的损失(抛弃的对象需要进行垃圾回收),它比 ES6 的方法有更好的可读性。

     四、拷贝继承

    也是 jQuery 实现继承的方法

    // 拷贝继承
    function extend() {
        var options, src, copy, copyIsArray, clone,
            target = arguments[0] || {},
            deep = false,
            i = 1;
        
        if ( typeof target === 'boolean') {
            deep = target;
    
            target = arguments[i] || {};
            i++;
        }
    
        if ( typeof target !== 'object' && !isFun(target)) {
            target = {};
        }
    
        // 循环一个或多个要拷贝的对象
        for( ; i<arguments.length; i++ ) {
            if ( (options = arguments[i]) != null ) {
                for ( name in options ) {
                    src = target[name];
                    copy = options[name];
    
                    // 防止死循环
                    if ( target === copy ) {
                        continue;
                    }
    
                    copyIsArray = isArray(copy);
                    if ( deep && copy && ( isPlainObject(copy) || copyIsArray ) ) {
                        // 深拷贝
                        if ( copyIsArray ) {
                            copyIsArray = false;
                            clone = src && isArray(src) ? src : [];
                        } else {
                            clone = src && isPlainObject(src) ? src : {};
                        }
    
                        target[name] = extend( deep, clone, copy );
                    // 防止拷贝 undefined    
                    } else if ( copy !== undefined ) {
                        target[name] = copy;
                    }
                }
            }
        }
    
        return target;
    }
    
    function isFun(obj) {
        return type(obj) === '[object Function]';
    }
    
    function isPlainObject(obj) {
        return type(obj) === '[object Object]';
    }
    
    function isArray(obj) {
        // IE8不支持
        if (Array.isArray) {
            return Array.isArray(obj);
        } else {
            return type(obj) === '[object Array]';
        }
    }
    
    function type(obj) {
        if ( obj == null ) {
            // obj + '' = 'null'/'undefined'
            return false;
        }
    
        return Object.prototype.toString.call(obj);
    }
    
    
    var object1 = {
      apple: 0,
      banana: { weight: 52, price: 100 }
    };
    var object2 = {
      banana: { price: 200 },
      cherry: 97
    };
    
    extend(true, object1, object2);

     试一试

  • 相关阅读:
    运行自动安装apk代码,报错: Original error: Could not find aapt Please set the ANDROID_HOME environment variable with the Android SDK root directory path.
    已安装Appium-Python-Client,但appium无法导入WebDriver
    打开uiautomatorviewer.bat闪退
    常见的python面试题【杭州多测师】【python笔试题】
    支付功能怎么测试?
    自动化运维工具——ansible详解
    服务端编程——异常+校验器+环境变量
    服务端编程——初始koa
    用postman发送请求,在koa中获取的请求body为undefined
    jQuery入口函数测试
  • 原文地址:https://www.cnblogs.com/kuangliu/p/7392141.html
Copyright © 2011-2022 走看看