Javascript学习---原型继承

原型

在Javascript中每一个对象都有一个隐藏的属性--prototype,prototype的值要么为null要么指向一个叫做原型的对象,当我们要调用一个对象不存在的属性时,Javascript会默认从对象的原型获取该属性,这也叫做原型继承。


对象的prototype属性是内置且隐藏的,这里有多种方法去设置/获取它,其中的一种方法是使用__proto__,例如:

let animal = {
  eats: true
};
let rabbit = {
  jumps: true
};

rabbit.__proto__ = animal;

要注意的是,__proto__和prototype是不同的,__proto__是prototype的getter和setter方法


当我们需要读取rabbit对象中不存在的属性时,Javascript会自动到rabbit的原型中寻找,因为我们设置了rabbit.__proto__ = animal,所以就到animal对象中寻找属性,若animal对象中没有该属性,则又到animal的原型中查找,沿着原型链寻找下去,例如:

let animal = {
  eats: true
};
let rabbit = {
  jumps: true
};

rabbit.__proto__ = animal; // (*)

// we can find both properties in rabbit now:
alert( rabbit.eats ); // true (**)
alert( rabbit.jumps ); // true

这里,rabbit.eats就是从animal对象中获取的


同理,对象方法属性也是一样:

let animal = {
  eats: true,
  walk() {
    alert("Animal walk");
  }
};

let rabbit = {
  jumps: true,
  __proto__: animal
};

// walk is taken from the prototype
rabbit.walk(); // Animal walk


下面是一个原型链的例子:

let animal = {
  eats: true,
  walk() {
    alert("Animal walk");
  }
};

let rabbit = {
  jumps: true,
  __proto__: animal
};

let longEar = {
  earLength: 10,
  __proto__: rabbit
}

// walk is taken from the prototype chain
longEar.walk(); // Animal walk
alert(longEar.jumps); // true (from rabbit)

要注意的是,原型链有以下两个限制:

(1)原型链不能绕成一个圈,否则会出现循环引用的问题,编译器就会报错;

(2)__proto__的值要么是null,要么是原型对象的引用,其他的值都会被忽略;


读写规则

我们可以手动对原型进行读和写操作。

我们已经知道对象属性有数据属性和访问器属性这两种,对于数据属性,我们可以在该对象直接进行读写操作,例如:

let animal = {
  eats: true,
  walk() {
    /* this method won't be used by rabbit */
  }
};

let rabbit = {
  __proto__: animal
}

rabbit.walk = function() {
  alert("Rabbit! Bounce-bounce!");
};

rabbit.walk(); // Rabbit! Bounce-bounce!

这里,walk()方法就直接添加在rabbit对象里,并没有添加在rabbit的原型里


对于访问器属性也是一样:

let user = {
  name: "John",
  surname: "Smith",

  set fullName(value) {
    [this.name, this.surname] = value.split(" ");
  },

  get fullName() {
    return `${this.name} ${this.surname}`;
  }
};

let admin = {
  __proto__: user,
  isAdmin: true
};

alert(admin.fullName); // John Smith (*)

// setter triggers!
admin.fullName = "Alice Cooper"; // (**)

admin由于没有getter和setter方法,故它会在原型链进行寻找,这里admin调用的是user的getter和setter方法


this的值

原型继承还有一个问题就是,this所指的对象到底是哪一个?是admin?还是user?

问题的答案很简单,this跟原型继承没有关系,只要是谁调用属性方法,this的值就指向谁。也就是说点号“.”前的对象是哪个,this就指向哪个。例如:

// animal has methods
let animal = {
  walk() {
    if (!this.isSleeping) {
      alert(`I walk`);
    }
  },
  sleep() {
    this.isSleeping = true;
  }
};

let rabbit = {
  name: "White Rabbit",
  __proto__: animal
};

// modifies rabbit.isSleeping
rabbit.sleep();

alert(rabbit.isSleeping); // true
alert(animal.isSleeping); // undefined (no such property in the prototype)
这里,rabbit.sleep()虽然调用的是animal的方法,但是点号“.”前的对象是rabbit,所以this的值为rabbit对象,isSleeping这个属性就属于rabbit的了



猜你喜欢

转载自blog.csdn.net/Blue_Sky2015/article/details/79325804