JavaScript的继承和原型链

171 阅读3分钟

「这是我参与2022首次更文挑战的第10天,活动详情查看:2022首次更文挑战」。

最早接触编程时,我学习的C++,后来又用PHP做了几个项目。后来工作就一直是Java开发,早期学习web时,没有太认真学习JavaScript,因为觉得比较简单。这几年深入接触了在以后才发现并不简单。

在ES6中引入了class关键字,不过只是语法糖,还是和Java不同,JavaScript还是基于原型的。并且JavaScript完全是动态的,都是运行时,不存在类,所有的都是实例。即使模拟出来一个类,也只是一个函数写法。

示例:

class People {
  constructor(name, sex) {
    this.name = name
    this.sex = sex
  }
  eat() {
    console.log(`${this.name} 正在吃东西。`)
  }
  sleep() {
    console.log(`${this.name} 正在睡觉。`)
  }
}

const lee = new People('小王', '男')

当我们执行const lee = new People('小王', '男')时,实际执行的是:

var lee = new Object();
lee.__proto__ = People.prototype;
People.call(lee);

JavaScript只有一种结构:对象。每个实例对象都有一个私有属性_proto_指向它的构造函数原型对象。这个原型对象也有一个自己的原型对象,层层向上,直到一个对象的原型对象为null。null没有原型,作为原型链的最后一个环节。

几乎所有的JavaScript对象都是位于原型链顶端的Object的示例。

继承方法

JavaScript 并没有其他基于类的语言所定义的“方法”。在 JavaScript 里,任何函数都可以添加到对象上作为对象的属性,函数的继承与其他的属性继承没有差别。

当继承的函数被调用时,this指向的是当前继承的对象,而不是继承的函数所在的原型对象。

示例:

var o = {
  a: 2,
  m: function(){
    return this.a + 1;
  }
};

console.log(o.m()); // 3
// 当调用 o.m 时,'this' 指向了 o.

var p = Object.create(o);
// p是一个继承自 o 的对象

p.a = 4; // 创建 p 的自身属性 'a'
console.log(p.m()); // 5
// 调用 p.m 时,'this' 指向了 p
// 又因为 p 继承了 o 的 m 函数
// 所以,此时的 'this.a' 即 p.a,就是 p 的自身属性 'a' 

原型对象

绝⼤部分的函数(少数内建函数除外)都有⼀个 prototype 属性,这个属性是原型对象⽤来创建新对象实例,⽽所有被创建的

对象都会共享原型对象,因此这些对象便可以访问原型对象的属性。

例如 hasOwnProperty() ⽅法存在于Obejct原型对象中,它便可以被任何对象当做⾃⼰的⽅法使⽤.

object.hasOwnProperty(propertyName)用法

hasOwnProperty() 函数的返回值为 Boolean 类型。如果对象 object 具有名称为 propertyName 的属性,则返回true,否则返回false

var person = {
    name: "Messi",
    age: 29,
};
console.log(person.hasOwnProperty("name")); //true
console.log(person.hasOwnProperty("hasOwnProperty")); //false
console.log(Object.prototype.hasOwnProperty("hasOwnProperty")); //true

上面的示例代码, hasOwnProperty() 并不存在于 person 对象中,但是 person 依然可以拥有此⽅法.

person对象是通过原型链找到了Object对象中的⽅法。

原型链

每个对象都有 __proto__ 属性,此属性指向该对象的构造函数的原型。

对象可以通过 __proto__ 与上游的构造函数的原型对象连接起来,⽽上游的原型对象也有⼀个 __proto__ ,这样就形成了原型链。

无标题-2022-01-26-1919.png