Object.create
和new
先看一个例子:
function Animal(name,sex) {
this.name = name;
this.sex = sex;
}
Animal.prototype.getName = function (){
return this.name;
}
let obj1 = new Animal("zyy","male");
let obj2 = Object.create(Animal.prototype);
console.log(obj1)
console.log(obj2)
console.log(obj1 instanceof Animal)//true
console.log(obj2 instanceof Animal)//true
console.log(obj1.__proto__ === Animal.prototype)//true
console.log(obj2.__proto__ === Animal.prototype)//true
分析:obj1
和obj2
都为Animal
的对象。实际上obj1
就是Animal
的对象,obj2
为F
的对象(怎么出来了一个F
???),具体Object.create
和new
怎么实现的?接下来再看
1. new
操作符做的事情
- 创建一个空对象
- 给该对象的
__proto__
赋值为fn.prototype
,即设置原型链 - 执行
fn
,并将obj
作为内部this
- 如果
fn
有返回值,则将其作为new
操作返回内容,否则返回obj
function test(a,b) {
this.a = a;
this.b = b;
}
//new操作符做的事情
function myNew(fn,args){
let obj = new Object();
obj.__proto__ = fn.prototype;
let result = fn.apply(obj,args);
if(typeof(result) === "object") return result;//如果fn执行返回的为一个对象时返回该对象
return obj;
}
console.log(myNew(test,[1,2]))
console.log(new test(1,2))
2. Object.create
做的事情
- 各种出错判断
- 创建一个在外部被隐藏的
F
函数 - 创建并返回该
F
函数的对象
搬来官网的Polyfill
方法
if (typeof Object.create !== "function") {
Object.create = function(proto, propertiesObject) {
if (typeof proto !== "function" || typeof proto !== "object") {
throw new TypeError('Object prototype may only be an Object: ' + proto);
} else if (proto === null) {
throw new TypeError('Object prototype may only be an Object: ' + proto);
}
if (typeof propertiesObject != 'undefined') throw new Error("This browser's implementation of Object.create is a shim and doesn't support a second argument.");
function F() {}
F.__proto__ = proto;
return new F();
}
}
搬来官网的例子(曾经让我无法理解的例子
// Shape - 父类(superclass)
function Shape() {
this.x = 0;
this.y = 0;
}
// 父类的方法
Shape.prototype.move = function(x, y) {
this.x += x;
this.y += y;
console.info('Shape moved.');
};
// Rectangle - 子类(subclass)
function Rectangle() {
Shape.call(this); // call super constructor.
}
// 子类续承父类
Rectangle.prototype = Object.create(Shape.prototype);
Rectangle.prototype.constructor = Rectangle;
var rect = new Rectangle();
console.log('Is rect an instance of Rectangle?',
rect instanceof Rectangle); // true
console.log('Is rect an instance of Shape?',
rect instanceof Shape); // true
rect.move(1, 1); // Outputs, 'Shape moved.'
输出:
理解:Rectangle.prototype
实际上为Object.create
返回的F
函数的一个实例,而该实例的__proto__
为Shape.prototype
,所以该实例实际上也为Shape
的一个实例,从而rect.__proto__
为Shape
的一个实例且rect
继承了Shape
类。
下图为它们之间的关系:
然后我们又回到第一个例子:
我们可以很容易发现,使用Object.create
方式创建的对象并没有继承其父类Animal
内部的的属性,这是因为,使用Object.create
创建出来的对象的构造函数为F
,且其被隐藏,在创建时并没有将其创建的实例与Animal
函数进行绑定,故无法访问到Animal
函数中的内部属性,但是可以访问到其原型链上的属性与方法。
既然已经知道了其原理,那么难道通过Object.create
方法不能创建自己的内部属性吗?当然不是
Object.create
创建自己的属性
该方法的第二个参数便是可以传入创建对象的属性配置集合。其中的具体配置与
Object.defineProperties
中属性的配置相同,可参考 Object.defineProperties
let o = Object.create(Object.prototype, {
// foo会成为所创建对象的数据属性
foo: {
writable:true,
configurable:true,
value: "hello"
},
// bar会成为所创建对象的访问器属性
bar: {
configurable: false,
get: function() { return 10 },
set: function(value) {
console.log("Setting `o.bar` to", value);
}
}
});
打印输出:
Object.create
多继承
由于Object.create
方法无法将父元素的属性/方法继承,只能继承其原型上的属性和方法。故我们需要先采用构造继承的方式将父元素的属性/方法继承,然后再通过Object.create
的方式继承父元素原型上的属性和方法。由于一个对象不能从属于两个父类,故如果要实现多继承,只能将另一个父类的原型方法以及原型属性添加到My
的原型上,另一个父类的方法和属性可以通过构造继承的方式将其继承。
function Person(name, age) {
this.name = name;
this.age = age;
}
function Animal(height, weight) {
this.height = height;
this.weight = weight;
}
function My() {
Person.call(this, "zyy", 21);
Animal.call(this, 160, 110);
}
My.prototype = Object.create(Animal.prototype);
Object.assign(My.prototype, Person.prototype);
My.prototype.constructor = My;
console.log("myObject=====", new My())