全部 / 前端 / 技术 · 2022年6月26日 0

44 – 原型继承

广告位招租 (vx: ghostcode, 备注:网站广告)

原文地址: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]] 的。我们现在有更新的方法来做同样的事(getProtypeOfsetProtypeOf),但 __proto__ 依旧被大多数浏览器或服务端环境支持。

__proto__ 有两条规则:

  1. 在任何时候,__proto__都不能创建一个循环引用或依赖关系。如果我们在一个循环引用中分配proto,JavaScript会抛出一个错误。
  2. 就像我们上面提到的 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
}