破解 JS(原型)继承
Posted 烟火
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了破解 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);
以上是关于破解 JS(原型)继承的主要内容,如果未能解决你的问题,请参考以下文章