Javascript笔记:Object.create, new,原型以及Object.assign

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;

原型链示意动画:

Javascript笔记:Object.create, new,原型以及Object.assign

__proto__([[prototype]])和prototype

  • JavaScript的对象中都包含了一个" [[Prototype]]"内部属性,这个属性所对应的就是该对象的原型。"[[Prototype]]"作为对象的内部属性,是不能被直接访问的。所以为了方便查看一个对象的原型,Firefox和Chrome中提供了"__proto__"这个非标准(不是所有浏览器都支持)的访问器(ECMA引入了标准对象原型访问器"Object.getPrototype(object)")。
  • 对于函数对象,除了__proto__属性之外,还有prototype属性,当一个函数被用作构造函数来创建实例时,该函数的prototype属性值将被作为原型赋值给所有对象实例(也就是设置实例的__proto__属性)

new 与 instanceof

new运算符做4个操作:

  1. 创建空的对象
  2. 设置空对象的constructor(实际上是设置__proto__)
  3. 将空对象作为this调用constructor函数
  4. 如果函数不返回对象,返回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

田小计划 - 彻底理解JavaScript原型

上一篇:2、基本语法


下一篇:SOLIDS设计原则