JavaScript 原型链与继承

原型链与继承


系列文章

这是JavaScript系列文章:

  • promise
  • async & await
  • 原型链与继承
  • 闭包
  • this
  • CommonJS
  • ES6 Module

文章目录

  • 原型链与继承
    • 系列文章
    • 简述
    • 原型链的继承
      • 属性继承
      • 方法继承
    • 生成原型链
      • 语法
      • 构造方法
      • class
      • Object.create()
      • 性能
      • 错误示例
    • 问题
    • 参考链接


简述

  1. ES6虽然引入了class关键字,但也只是语法糖,JavaScript仍然是基于原型的。
  2. JavaScript对象都是Object的实例
    JavaScript 原型链与继承_第1张图片
  3. JavaScript 对象有一个指向一个原型对象的链。想要访问一个对象的属性,会顺着链搜寻,访问它的原型,以及原型的原型,直到找到要找到的属性,或者到达链尾的null。
  4. Object.prototype 属性表示 Object 的原型对象。
  5. proto 是一个隐含的私有属性,prototype 更像是一个公共属性,可以将一些共同的属性设置在这个属性下。

原型链的继承

属性继承

/** 写法一 */
let parent = function () {
	this.a = 1;
	this.b = 2;
}

/** 写法二,此写法效果同写法一
function parent() {
	this.a = 1;
	this.b = 2;
}
*/

let child = new parent();  // { a: 1, b: 2 }

// 在parent原型上定义属性
parent.prototype.b = 3;
parent.prototype.c = 4;

// 不要直接使用 parent.prototype = { b: 3, c: 4 }; 这样会破坏原型链
// child.[[prototype]] / child.__proto__ / child.constructor.prototype 有属性b和c

// child.[[prototype]].[[prototype]] 和 child.prototype. 效果相同
// child.[[prototype]].[[prototype]].[[prototype]] 是 null
// null 没有 [[prototype]]
// 原型链: child({ a: 1, b: 2 }) --> parent({ b: 3, c: 4 }) --> Object.prototype --> null

// 'a'是child的自身属性
// 'b'是child的自身属性,虽然原型上也有一个'b'属性,但是不会被访问到,这个就叫做“属性遮蔽”
// 'c'不是child的自身属性,查找原型上是否有此属性,'c'是child.[[prototype]]的属性么?答案是肯定的。
// 'd'不是child的自身属性,查找原型上是否有此属性,仍然没有,再查找原型的原型,child.[[prototype]].[[prototype]]为null,所以停止搜索,返回undefined

方法继承

任何函数都能添加到对象上作为对象的属性。并且具备属性继承的各种特点,包含“属性遮蔽”。

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

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

// 调用parent.m时,‘this’指向parent.
console.log(parent.m());  // 3

// child是一个继承自parent的对象
let child = Object.create(parent);

child.a = 4;  // 创建child的自身属性a
console.log(parent.m());  // 5
// 此时‘this’指向child对象,‘this.a’即p.a,

生成原型链

语法

// o 继承了 Object.prototype 上面的所有属性
// o 没有 hasOwnProperty 属性,但是 Object.prototype 有 hasOwnProperty 属性
// 原型链:o --> Object.prototype --> null
let o = {a: 1};

// 数组都继承于 Array.prototype 
// 原型链:a ---> Array.prototype ---> Object.prototype ---> null
let a = ["yo", "whadup", "?"];

// 函数都继承于Function.prototype
// 原型链: f ---> Function.prototype ---> Object.prototype ---> null
function f () {}

构造方法

构造器其实就是一个普通的函数。当使用 new 操作符 来作用这个函数时,它就可以被称为构造方法(构造函数)。

function Graph() {
  this.vertices = [];
  this.edges = [];
}

Graph.prototype = {
  addVertex: function(v){
    this.vertices.push(v);
  }
};

// g是生成的对象,他的自身属性有'vertices'和'edges'.
// 在g被实例化时,g.[[Prototype]]指向了Graph.prototype.
var g = new Graph();

class

"use strict";

class Polygon {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
}

class Square extends Polygon {
  constructor(sideLength) {
    super(sideLength, sideLength);
  }
  get area() {
    return this.height * this.width;
  }
  set sideLength(newLength) {
    this.height = newLength;
    this.width = newLength;
  }
}

var square = new Square(2);

Object.create()

新对象的原型就是调用 create 方法时传入的第一个参数

var a = {a: 1};   // a ---> Object.prototype ---> null

var b = Object.create(a);  // b ---> a ---> Object.prototype ---> null
console.log(b.a); // 1 (继承而来)

var c = Object.create(b);  // c ---> b ---> a ---> Object.prototype ---> null

var d = Object.create(null);  // d ---> null
console.log(d.hasOwnProperty); // undefined, 因为d没有继承Object.prototype

性能

  • hasOwnProperty 是 JavaScript 中处理属性并且不会遍历原型链的方法之一。(另一种方法: Object.keys())

  • 注意:检查属性是否undefined还不够。该属性可能存在,但其值恰好设置为undefined

错误示例

扩展内置原型的唯一理由是支持JavaScript 引擎的新特性

问题

  1. 各种专属名词解释:

    原型
    原型对象
    实例

参考链接

[1] MDN, 继承与原型链
[2] 阮一峰, Javascript继承机制的设计思想
[3] 阮一峰, Javascript 面向对象编程(一):封装
[4] 阮一峰, Javascript面向对象编程(二):构造函数的继承
[5] 阮一峰, Javascript面向对象编程(三):非构造函数的继承

感谢

你可能感兴趣的:(JavaScript,前端)