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