原型链与继承
系列文章
这是JavaScript
系列文章:
简述
- ES6虽然引入了
class
关键字,但也只是语法糖,JavaScript仍然是基于原型的。 - JavaScript对象都是
Object
的实例
- JavaScript 对象有一个指向一个原型对象的链。想要访问一个对象的属性,会顺着链搜寻,访问它的原型,以及原型的原型,直到找到要找到的属性,或者到达链尾的null。
Object.prototype
属性表示Object
的原型对象。- 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] MDN, 继承与原型链
[2] 阮一峰, Javascript继承机制的设计思想
[3] 阮一峰, Javascript 面向对象编程(一):封装
[4] 阮一峰, Javascript面向对象编程(二):构造函数的继承
[5] 阮一峰, Javascript面向对象编程(三):非构造函数的继承
感谢