Object.create()
Object.create:以传入对象为原型,创建一个新对象(新对象的原型[[prototype]]是传入对象)。
例子:继承
//父类Shape构造方法 function Shape() { this.x = 0; this.y = 0; } // 通过prototype为Shape添加方法 Shape.prototype.move = function(x, y) { this.x += x; this.y += y; console.info('Shape moved.'); }; // 构建子类Rectangle function Rectangle() { Shape.call(this); // 调用Shape的构造方法 } // 子类的prototype指向父类的prototype Rectangle.prototype = Object.create(Shape.prototype); //这里 Rectangle.prototype.constructor 会丢失(会沿着原型链找到Shape). 需要重新设置为 Rectangle. Rectangle.prototype.constructor = Rectangle;
原型链示意动画:
__proto__([[prototype]])和prototype
- JavaScript的对象中都包含了一个" [[Prototype]]"内部属性,这个属性所对应的就是该对象的原型。"[[Prototype]]"作为对象的内部属性,是不能被直接访问的。所以为了方便查看一个对象的原型,Firefox和Chrome中提供了"__proto__"这个非标准(不是所有浏览器都支持)的访问器(ECMA引入了标准对象原型访问器"Object.getPrototype(object)")。
- 对于函数对象,除了__proto__属性之外,还有prototype属性,当一个函数被用作构造函数来创建实例时,该函数的prototype属性值将被作为原型赋值给所有对象实例(也就是设置实例的__proto__属性)
new 与 instanceof
new运算符做4个操作:
- 创建空的对象
- 设置空对象的constructor(实际上是设置__proto__)
- 将空对象作为this调用constructor函数
- 如果函数不返回对象,返回this
instanceof运算符判断对象是否是由某个constructor构建的,通过检验对象的原型链中是否出现constructor.prototype
例子:new与instanceof
var rect = new Rectangle(); rect.__proto__ === Rectangle.prototype; //true console.log(rect instanceof Rectangle); // true console.log(rect instanceof Shape); // true rect.move(1, 1); // 'Shape moved.'
Object.assign(ES6)
Object.assign(target, ...sources)复制来源对象里所有可枚举的属性到目标对象,并返回目标对象。
例子:mixin
function Shape() { this.x = 0; this.y = 0; } Shape.prototype.move = function(x, y) { this.x += x; this.y += y; console.info('Shape moved.'); }; function Rectangle() { this.shape = "Rectangle"; // call super constructor. } Rectangle.prototype.size = function(w, h) { return w * h; }; function Mixin() { Shape.call(this); Rectangle.call(this); } // Mixin.prototype链到Shape.prototype Mixin.prototype = Object.create(Shape.prototype); // constructor指向Mixin Mixin.prototype.constructor = Mixin; // Rectangle.prototype复制到Mixin.prototype Object.assign(Mixin.prototype, Rectangle.prototype); // 注意这里不会覆盖constructor因为Rectangle.prototype.propertyIsEnumerable(constructor);//false console.log(Mixin.prototype.constructor === Mixin); //true var mixin = new Mixin(); console.log('mixin instanceof Rectangle); // false console.log('mixin instanceof Shape); // true //这里把move指向另一个函数 Rectangle.prototype.move = function(x, y) { console.info('move changed'); }; //并且给Rectangle加一个新方法 Rectangle.prototype.moveTwice = function(x, y) { this.x += x * 2; this.y += y * 2; console.info('Shape moved twice.'); }; mixin.move(); //Shape moved mixin.moveTwice(); //TypeError,mixin没有moveTwice方法
这是mdn举了利用Object.assign进行mixin的例子,进行了一些改编。实际上会有问题:assign只是复制并没有修改原型链,后续新增的方法,Mixin是不会同步改动的。另外,虽然Object.assign拷贝属性值不是深拷贝,但这里和指针是不一样的,Mixin.prototype的move仍指向旧函数。(可参考上一篇:非基本类型值的传递)
参考资料:
mdn - Object.create(), Object.assign(), instanceof, new