Using the Object.create() method
Objects can also be created using the Object.create() method. This method can be very useful, because it allows you to choose the prototype object for the object you want to create, without having to define a constructor function.
o = {};
// Is equivalent to:
o = Object.create(Object.prototype);
o = Object.create(Object.prototype, {
// foo is a regular data property
foo: {
writable: true,
configurable: true,
value: "hello",
},
// bar is an accessor property
bar: {
configurable: false,
get() {
return 10;
},
set(value) {
console.log("Setting `o.bar` to", value);
},
},
});
// Create a new object whose prototype is a new, empty
// object and add a single property 'p', with value 42.
o = Object.create({}, { p: { value: 42 } });
With Object.create(), we can create an object with null as prototype. The equivalent syntax in object initializers would be the __proto__ key.
o = Object.create(null);
// Is equivalent to:
o = { __proto__: null };
By default properties are not writable, enumerable or configurable.
o.p = 24; // throws in strict mode
o.p; // 42
o.q = 12;
for (const prop in o) {
console.log(prop);
}
// 'q'
delete o.p;
// false; throws in strict mode
To specify a property with the same attributes as in an initializer, explicitly specify writable, enumerable and configurable.
o2 = Object.create(
{},
{
p: {
value: 42,
writable: true,
enumerable: true,
configurable: true,
},
},
);
// This is not equivalent to:
// o2 = Object.create({ p: 42 })
// which will create an object with prototype { p: 42 }
https://www.freecodecamp.org/news/a-beginners-guide-to-javascripts-prototype/
Object.create
Let's improve our example once again by using Object.create. Simply put, Object.create allows you to create an object which will delegate to another object on failed lookups. Put differently, Object.create allows you to create an object and whenever there's a failed property lookup on that object, it can consult another object to see if that other object has the property. That was a lot of words. Let's see some code.
const parent={name:'Stacey',age:35,heritage:'Irish'}
const child=Object.create(parent)
child.name='Ryan'
child.age=7
console.log(child.name)// Ryan
console.log(child.age)// 7
console.log(child.heritage)// Irish
So in the example above, because child was created with Object.create(parent), whenever there's a failed property lookup on child, JavaScript will delegate that lookup to the parent object. What that means is that even though child doesn't have a heritage property, parent does so when you log child.heritage you'll get the parent's heritage which was Irish.
Now with Object.create in our tool shed, how can we use it in order to simplify our Animal code from earlier? Well, instead of adding all the shared methods to the animal one by one like we're doing now, we can use Object.create to delegate to the animalMethods object instead. To sound really smart, let's call this one Functional Instantiation with Shared Methods and Object.create ?
Functional Instantiation with Shared Methods and Object.create
? So now when we call leo.eat, JavaScript will look for the eat method on the leo object. That lookup will fail, then, because of Object.create, it'll delegate to the animalMethods object which is where it'll find eat.
So far, so good. There are still some improvements we can make though. It seems just a tad "hacky" to have to manage a separate object (animalMethods) in order to share methods across instances. That seems like a common feature that you'd want to be implemented into the language itself. Turns out it is and it's the whole reason you're here - prototype.
So what exactly is prototype in JavaScript? Well, simply put, every function in JavaScript has a prototype property that references an object. Anticlimactic, right? Test it out for yourself.
function doThing(){}
console.log(doThing.prototype)// {}
What if instead of creating a separate object to manage our methods (like we're doing with animalMethods), we just put each of those methods on the Animal function's prototype? Then all we would have to do is instead of using Object.create to delegate to animalMethods, we could use it to delegate to Animal.prototype. We'll call this pattern Prototypal Instantiation.
Prototypal Instantiation
??? Hopefully you just had a big "aha" moment. Again, prototype is just a property that every function in JavaScript has and, as we saw above, it allows us to share methods across all instances of a function. All our functionality is still the same but now instead of having to manage a separate object for all the methods, we can just use another object that comes built into the Animal function itself, Animal.prototype.
At this point we know three things:
-How to create a constructor function.
-How to add methods to the constructor function's prototype.
-How to use Object.create to delegate failed lookups to the function's prototype.
Object.create(proto):创建一个新对象,这个对象继承(关联)了proto的属性,改变新对象的同名属性并不会影响原对象proto。如果直接用=来赋值,则只是一个对象的引用。
Object.create(protoObj):返回一个新对象,这个对象的构造函数的原型(prototype)指向protoObj。所以当访问新对象b.a的时候实际上是通过原型链访问protoObj.a。
Object.create(protoObj)生成了一个实例,这个实例的原型由protoObj来指定,但是它的构造函数F被隐藏了。
if(typeof Object.create!=="function"){
Object.create=function(proto, propertiesObject){
function Constructor(){} //实现一个隐藏构造函数
Constructor.prototype = proto //函数的原型设置为参数传进来的原型
var o = new Constructor() // 返回一个构造函数的实例,此实例的__proto__指向参数proto
Object.defineProperties(o, propertiesObject);
return o;
}
or
return({__proto__:proto});
真正的Object.create()还可以传入第二个参数,这个参数与Object.defineProperties(,)的第二个参数格式相同, 通过第二个参数在新对象中重新创建一个属性,然后通过属性遮蔽原理避免修改原对象。
Object.create(null) 创建一个真正的空对象,并没有继承Object原型链上的方法。
// create an object with null as prototype
var o = Object.create(null);
var a = {} 并不是一个纯粹的空对象,它会继承原型链上的很多方法。
o={};
// is equivalent to:
o=Object.create(Object.prototype);
// Shape - superclass
function Shape(){
this.x=0;
this.y=0;
}
// superclass method
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); // subclass extends superclass
Rectangle.prototype.constructor=Rectangle;
//If you don't set Rectangle.prototype.constructor to Rectangle, it will take the prototype.constructor of Shape (parent).To avoid that, we set the prototype.constructor to Rectangle (child).
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.'