原文地址:https://dev.to/bhagatparwinder/prototypical-inheritance-3b0p
在这一系列文章之前,我们谈到 JavaScript 中的一切皆对象。当我们创建对象的时候,本身就是需要重用属性和方法。大多数现代语言都以这样或那样的方式支持继承,JavaScript 通过原型链或继承来实现。
JavaScript 中的每个对象都有一个隐藏的属性[[Prototype]]
,它有两个值:要么是 null (指向原型链的最末端) 或是指向另一个对象。
原型对象自身也有原型,直到对象到达 null 为它的原型。
从对象继承属性
// declare an initial object animal with height and weight property
let animal = {
height: 5,
weight: 50
};
console.log(animal.__proto__); // null or {}
// since animal is not inherited from anything, it doesn't have a prototypical chain
// create an object fish from animal
let fish = Object.create(animal);
console.log(fish.height); // 5, inherited from animal
console.log(fish.weight); // 50, inherited from animal
console.log(fish.__proto__); // { height: 5, weight: 50 }
// ^ chain points to animal, that is how we got fish height and weight
fish.canSwim = true; // adding a property to fish object
console.log(animal.canSwim); // undefined, it does not exist on animal. It is fish's own property
console.log(fish.canSwim); // true
let octopus = Object.create(fish); // create an object from fish
console.log(octopus.height); // 5, traversing the prototype chain octopus => fish => animal
console.log(octopus.weight); // 50, inherited all the way from animal
console.log(octopus.__proto__); // { canSwim: true }, points to fish but only shows properties that fish "owns"
octopus.legs = 8; // adding a property to octopus object
console.log(octopus.legs); // 8
console.log(animal.legs); // neither animal or fish has the legs property
console.log(fish.legs);
// hasOwnProperty method is true when an Object owns a property and did not inherit it
console.log(octopus.hasOwnProperty("legs")); // true
console.log(octopus.hasOwnProperty("height")); // false
console.log(fish.hasOwnProperty("weight")); // false
__ proto __
在上面的例子中,我们使用 __proto__
来获取对象的 prototype,__proto__
是用来读写 [[Prototype]] 的。我们现在有更新的方法来做同样的事(getProtypeOf
和 setProtypeOf
),但 __proto__
依旧被大多数浏览器或服务端环境支持。
__proto__
有两条规则:
- 在任何时候,__proto__都不能创建一个循环引用或依赖关系。如果我们在一个循环引用中分配proto,JavaScript会抛出一个错误。
- 就像我们上面提到的
proto
的值要么是一个对象要么是 null。
通过构造函数来继承属性
let foo = function() {
this.name = "Parwinder";
this.age = 57;
}
let bar = new foo(); // create an object bar using function foo
console.log(bar); // foo { name: 'Parwinder', age: 57 }, due to inheritance
console.log(bar.name); // Parwinder, inherited from foo
foo.prototype.car = "Volvo"; // adding a new property "car" to original function foo
console.log(bar.car); // Volvo
// check bar if it has a property car, if not follow up the prototype chain.
// get to foo following the chain
// does foo have car on its prototype? Yes. Log the value "Volvo"
console.log(bar.gender); // undefined
// check bar if it has a property gender, if not follow up the prototype chain.
// get to foo following the chain
// does foo have gender on its prototype? no.
// go up the prototypical chain.
// we have reached the end of the chain with null. Log undefined.
this 关键字的行为和继承
无论方法是在对象或者原型链中发现的,this
永远指向点前面的对象。让我们来以一个例子来理解下:
const checkVotingRights = {
age: 24,
legalToVote: function() {
return this.age > 18;
}
};
console.log(checkVotingRights.age); // 24
// When calling checkVotingRights.age in this case, "this" refers to checkVotingRights
console.log(checkVotingRights.legalToVote());
const teenagerEligible = Object.create(checkVotingRights);
// teenagerEligible is an object that inherits checkVotingRights
teenagerEligible.age = 13; // set age on the newly created object
console.log(teenagerEligible.legalToVote()); // false
// when teenagerEligible.legalToVote is called, "this" refers to teenagerEligible
delete 操作符与 Object.create 一起使用
无论何时我们从对象删除一个继承的属性时,它将会打印继承的值。
var a = {
a: 1
};
var b = Object.create(a);
console.log(a.a); // 1
console.log(b.a); // 1
b.a = 10;
console.log(a.a); // 1
console.log(b.a); // 10
delete b.a;
console.log(a.a); // 1
console.log(b.a); // 1, value deleted but showing value from a
delete a.a;
console.log(a.a); // undefined
console.log(b.a); // undefined
掌握 for…in
for...in
循环对象上的属性包括继承来的属性。
let animal = {
height: 5,
weight: 50
};
let fish = Object.create(animal);
fish.canSwim = true;
for (let item in fish) {
console.log(item); // canSwim, height, weight
}
for (let item in fish) {
console.log(fish[item]); // true, 5, 50
}