JavaScript 原型、原型链与继承

时间:2021-05-06 14:59:31

JavaScript 原型、原型链与继承

原型 prototype 与 原型链 prototype chain

JavaScript only has one construct: objects. Each object has an internal link to another object called its prototype.
That prototype object has a prototype of its own, and so on until an object is reached with null as its prototype.
null, by definition, has no prototype, and acts as the final link in this prototype chain.

利用原型实现继承

var myObj = {};

function myFunc() {
}


console.log(myObj.prototype);
// undefined
console.log(myFunc.prototype); // myFunc {}

通过 prototype 我们可以动态的向对象添加属性和方法,并且是可以继承的。

function myFunc(val) {
this.val = val;
}

myFunc.prototype.getVal = function() {
return this.val;
};

var myInstance = new myFunc(1);

// myInstance 调用原型属性
console.log(myInstance.getVal()); // 1

// 访问 myInstance 的原型
console.log(Object.getPrototypeOf(myInstance)); // myFunc { getVal: [Function] }

// 为 myInstance 添加属性 val
myInstance.val = 100;
// getVal 中的 this 指向 myInstance 实例
console.log(myInstance.getVal()); // 100

当继承的函数 getVal 被调用时, this 指向 myInstance 实例, 而不是原型 myFunc。

原型链

根据 ECMAScript 标准,someObject.[[Prototype]] 符号是用于指派 someObject 的原型。
这个等同于 JavaScript 的 proto 属性(现已弃用)。
从 ECMAScript 6 开始, [[Prototype]] 可以用 Object.getPrototypeOf() 和 Object.setPrototypeOf() 访问器来访问。

当一个对象访问一个属性时,查找的顺序是:

该对象自身属性 =》 该对象的原型的属性 =》 该对象的原型的原型属性 … 直到找到匹配的属性或到达原型链末端(prototype 为 undefined)

var objA = function () {
this.a = 'a';
this.b = 'b';
};

var myInstance = new objA();
myInstance.a = 'a in myInstance';

// myInstance 自身的属性有 a, 访问并停止查找属性
console.log(myInstance.a); // a in myInstance

// myInstance 自身的属性没有 b,访问 myInstance 的原型 objA, objA 有属性 b, 访问并停止查找属性
console.log(myInstance.b); // b

// myInstance 自身和原型链上每个原型都没有属性 c,结果为 undefined
console.log(myInstance.c); // undefined

创建对象和原型链的几种方法

通过 JS 语法创建

var obj = {
a: 1
};
// 继承于 Object.prototype
// 原型链:obj => Object.prototype => undefined
console.log(Object.getPrototypeOf(obj)); // {}

var arr = [1];
// 继承于 Array.prototype
// 原型链:arr => Array.prototype => undefined
console.log(Object.getPrototypeOf(arr)); // []

function func() {
}
// 继承于 Function.prototype
// 原型链:func => Function.prototype => undefined
console.log(Object.getPrototypeOf(func)); // [Function]

通过构造函数创建

在 JavaScript 中, 用 new 操作符来作用与一个函数时,这个函数就是构造函数 constructor。

var func = function() {
this.val = 1;
};

func.prototype.getVal = function() {
return this.val;
};

var instance = new func();

// instance 是构造函数 func 的一个实例对象
// instance 自身具有 val 属性
// instance.[[Prototype]] 指向 func.prototype

通过 Object.create 创建

var a = {
a: 'a'
};

var b = Object.create(a);
// b 继承于 a.prototype
// 原型链:b => a => Object.prototype => undefined

console.log(b.a) // a

通过 class 关键字 [ES6]

ES6 中的 class 只是基于原型继承的一种语法糖:

'use strict';

class Person {
constructor(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
}

class Student extends Person {
constructor(firstName, lastName) {
super(firstName, lastName);
}
get name() {
return this.firstName + ' ' + this.lastName;
}
set name(newFirstName) {
this.firstName = newFirstName;
}
}

var me = new Student('Dog', 'Dong');

console.log(me.name); // Dog Dong
me.name = 'Dog2';
console.log(me.name); // Dog2 Dong

其他

hasOwnProperty()

检测某个对象自身(不含原型链)是否含有某属性。

var objA = {
valA: 'val a'
};

var objB = Object.create(objA);
objB.valB = 'val b';

console.log(objA.hasOwnProperty('valA')); // true
console.log(objB.hasOwnProperty('valA')); // false
console.log(objA.hasOwnProperty('valB')); // false
console.log(objB.hasOwnProperty('valB')); // true

在原型链上查找属性是很耗时的,尤其是访问一个不存在的属性时,整个原型链会被遍历。
当我们只想访问对象自身属性时,可以通过 hasOwnProperty 来判断对象是否有该属性,避免遍历原型链。

Object.getPrototypeOf() [ES6]

返回对象的原型。

var objA = {
valA: 'val a'
};

var objB = Object.create(objA);

console.log(Object.getPrototypeOf(objB)); // { valA: 'val a' }

Object.setPrototypeOf() [ES6]

设置对象的原型。

var objA = {
valA: 'val a'
};

var objB = {};
Object.setPrototypeOf(objB, objA);

console.log(Object.getPrototypeOf(objB)); // { valA: 'val a' }

原文链接