JavaScript 原型、原型链与继承

简介: JavaScript 原型、原型链与继承

JavaScript 不包含传统的类继承模型,而是使用 prototypal 原型模型。

虽然这经常被当作是 JavaScript 的缺点被提及,其实基于原型的继承模型比传统的类继承还要强大。实现传统的类继承模型是很简单,但是实现 JavaScript 中的原型继承则要困难的多。

由于 JavaScript 是唯一一个被广泛使用的基于原型继承的语言,所以理解两种继承模式的差异是需要一定时间的,今天我们就来了解一下原型和原型链。

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' }

目录
相关文章
|
25天前
|
JavaScript 前端开发 开发者
理解JavaScript中的原型链:基础与实践
【10月更文挑战第8天】理解JavaScript中的原型链:基础与实践
|
8天前
|
JavaScript 前端开发
Javascript如何实现继承?
【10月更文挑战第24天】JavaScript 中实现继承的方式有很多种,每种方式都有其优缺点和适用场景。在实际开发中,我们需要根据具体的需求和情况选择合适的继承方式,以实现代码的复用和扩展。
|
2天前
|
JavaScript 前端开发
如何使用原型链继承实现 JavaScript 继承?
【10月更文挑战第22天】使用原型链继承可以实现JavaScript中的继承关系,但需要注意其共享性、查找效率以及参数传递等问题,根据具体的应用场景合理地选择和使用继承方式,以满足代码的复用性和可维护性要求。
|
2天前
|
JavaScript 前端开发 开发者
js实现继承怎么实现
【10月更文挑战第26天】每种方式都有其优缺点和适用场景,开发者可以根据具体的需求和项目情况选择合适的继承方式来实现代码的复用和扩展。
14 1
|
12天前
|
JavaScript 前端开发 开发者
探索JavaScript原型链:深入理解与实战应用
【10月更文挑战第21天】探索JavaScript原型链:深入理解与实战应用
21 1
|
2月前
|
自然语言处理 JavaScript 前端开发
一文梳理JavaScript中常见的七大继承方案
该文章系统地概述了JavaScript中七种常见的继承模式,包括原型链继承、构造函数继承、组合继承、原型式继承、寄生式继承、寄生组合继承等,并探讨了每种模式的实现方式及其优缺点。
一文梳理JavaScript中常见的七大继承方案
|
20天前
|
JavaScript 前端开发 开发者
深入理解JavaScript原型链:从基础到进阶
【10月更文挑战第13天】深入理解JavaScript原型链:从基础到进阶
19 0
|
20天前
|
JavaScript 前端开发 开发者
原型链深入解析:JavaScript中的核心机制
【10月更文挑战第13天】原型链深入解析:JavaScript中的核心机制
24 0
|
20天前
|
JavaScript 前端开发 安全
深入理解JavaScript原型链:从基础到进阶
【10月更文挑战第13天】深入理解JavaScript原型链:从基础到进阶
26 0
|
2月前
|
JavaScript 前端开发
js之class继承|27
js之class继承|27