破解 JS(原型)继承

来源:互联网 发布:sqlserver rds客户端 编辑:程序博客网 时间:2024/05/18 15:25

总体分为四大类:利用空对象作为中介继承、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.prototypechild.prototype = Object.create(parent.prototype);// 直接修改默认的 child.prototypeObject.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);

 

 试一试