JavaScript 指南 - 继承与原型链

  1. MDN 
  2. Web技术文档 
  3. JavaScript 
  4. JavaScript 指南 
  5. 继承与原型链

继承与原型链

4 名贡献者: 

  •  
  •  
  •  

在本文章中

  1. 基于原型链的继承
    1. 继承属性
    2. 继承方法
  2. 使用不同的方法来创建对象和生成原型链
    1. 使用普通语法创建对象
    2. 使用构造方法创建对象
    3. 使用Object.create创建对象
    4. 性能
    5. 不好的实践:扩展原生对象的原型
    6. 结论

对于那些熟悉基于类的面向对象语言(java或者c++)的开发者来说,JavaScript的语法是比较怪异的, 这是由于javascript是一门动态语言,而且它没有类的概念(虽然class是个保留字,不能作为变量名来使用).

继承方面,javascript中的每个对象都有一个内部私有的链接指向另一个对象 ,这个对象就是原对象的原型. 这个原型对象也有自己的原型, 直到对象的原型为null为止(也就是没有原型). 这种一级一级的链结构就称为原型链.

虽然这通常会被称作JavaScript的弱点之一,实际上这种原型继承的模型要比经典的继承模型还要强大。虽然在原型模型上构建一个经典模型是相当琐碎的,但如果采取其他方式实现则会更加困难。

基于原型链的继承

继承属性

javascript对象有两种不同的属性,一种是对象自身的属性,另外一种是继承于原型链上的属性.下面的代码则演示了当访问一个对象的属性时,发生的行为。

// 假定我们有个对象o,并且o所在的原型链如下:
// {a:1, b:2} ---> {b:3, c:4} ---> null
// 'a'和'b'是o自身的属性.

// 该例中,用"对象.[[Prototype]]"来表示这个对象的原型.
// 这只是一个纯粹的符号表示(ECMAScript标准中也这样使用),不能在实际代码中使用.

console.log(o.a); // 1
// a是o的自身属性吗?是的,该属性的值为1

console.log(o.b); // 2
// b是o的自身属性吗?是的,该属性的值为2
// o.[[Prototype]]上还有一个'b'属性,但是它不会被访问到.这种情况称为"属性遮蔽".

console.log(o.c); // 4
// c是o的自身属性吗?不是,那看看o.[[Prototype]]上有没有.
// c是o.[[Prototype]]的自身属性吗?是的,该属性的值为4
console.log(o.d); // undefined
// d是o的自身属性吗?不是,那看看o.[[Prototype]]上有没有.
// d是o.[[Prototype]]的自身属性吗?不是,那看看o.[[Prototype]].[[Prototype]]上有没有.
// o.[[Prototype]].[[Prototype]]为null,原型链已到顶端,没有d属性,返回undefined

继承方法

JavaScript并没有真正的"方法". JavaScript只有函数,而且任何函数都可以添加到对象上作为对象的属性。继承的函数与其他的属性是基本没有差别的, 包括"属性遮蔽" (这种情况相当于其他语言的方法重写).

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

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

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

var p = Object.create(o);
// p是一个对象, p.[[Prototype]]是o.

p.a = 12; // 创建p的自身属性a.
console.log(p.m()); // 13
// 调用p.m时, 'this'指向 p. 'this.a'则是12.

使用不同的方法来创建对象和生成原型链

使用普通语法创建对象

var o = {a: 1};

// o这个对象继承了Object.prototype上面的所有属性
// 所以可以这样使用 o.hasOwnProperty('a').
// hasOwnProperty 是Object.prototype的自身属性.
// Object.prototype的原型为null,如下:
// o ---> Object.prototype ---> null

var a = ["yo", "whadup", "?"];

// 数组都继承于Array.prototype (indexOf, forEach,等方法都是从它继承而来).
// 原型链如下:
// a ---> Array.prototype ---> Object.prototype ---> null

function f(){
  return 2;
}

// 函数都继承于Function.prototype(call, bind,等方法都是从它继承而来):
// f ---> Function.prototype ---> Object.prototype ---> null

使用构造方法创建对象

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

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

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

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

使用Object.create创建对象

ECMAScript 5中引入了一个新方法: 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方法,该方法由所有对象继承自Object.proptotype

hasOwnProperty是JavaScript中唯一一个只涉及对象自身属性而不会遍历原型链的方法。

注意:仅仅通过判断值是否为undefined还不足以检测一个属性是否存在,一个属性可能存在而其值恰好为undefined

不好的实践:扩展原生对象的原型

一个经常使用的不好实践是扩展Object.prototype或者其他内置对象的原型。

该技术被称为monkey patching,它破坏了对象的封装性。虽然一些流行的框架(如Prototype.js)在使用该技术,但是该技术依然不是好的实践,附加的非标准的方法使得内置的类型混乱。

扩展内置对象原型的唯一正当理由是移植较新JavaScript引擎的特性,如Array.forEach

结论

在编写使用到原型继承模型的复杂代码前理解原型继承模型十分重要。同时,还要清楚代码中原型链的长度,并在必要时结束原型链,以避免可能存在的性能问题。更进一步,除非为了兼容新JavaScript特性,否则永远不要扩展原生对象的原型。

文档标签和贡献者

对本页有贡献的人: ziyunfeihutuxuReyCGteoli

最后编辑者: ReyCG, May 20, 2014 11:42:11 PM

2005-2014 Mozilla 开发者网络和独立贡献者 
内容置于 这些许可证下 · g关于 MDN · 贡献代码 · 隐私政策

时间: 2024-11-01 23:58:16

JavaScript 指南 - 继承与原型链的相关文章

Javascript:继承和原型链

JavaScript:继承和原型链(译) 原文:Inheritance and the prototype chain 译者:youngsterxyf 对于具备基于类的编程语言(如Java或C++)经验的程序员来说,JavaScript有点混乱,因为它是一种动态语言,并且不提供class的实现(虽然关键字class是保留的,不可用作变量名). 说到继承,JavaScript只有一种结构:对象.每个对象都有一个内部链接指向另一个对象,这个对象称为原型 (prototype).那个原型对象也有自己的

深入理解JS继承和原型链的问题_javascript技巧

对于那些熟悉基于类的面向对象语言(Java 或者 C++)的开发者来说,JavaScript 的语法是比较怪异的,这是由于 JavaScript 是一门动态语言,而且它没有类的概念( ES6 新增了class 关键字,但只是语法糖,JavaScript 仍旧是基于原型). 涉及到继承这一块,Javascript 只有一种结构,那就是:对象.在 javaScript 中,每个对象都有一个指向它的原型(prototype)对象的内部链接.这个原型对象又有自己的原型,直到某个对象的原型为null 为止

js对象继承之原型链继承实例_javascript技巧

本文实例讲述了js对象继承之原型链继承的用法.分享给大家供大家参考.具体分析如下: 复制代码 代码如下: <script type="text/javascript"> //定义猫的对象 var kitty  = {color:'yellow',bark:function(){alert('喵喵');},climb:function(){alert('我会爬树')}}; //老虎对象的构造函数 function tiger(){  this.color = "ye

Javascript中的继承与原型链理解

在编程语言中,对象并不像真实世界中那样随处可见,随口可以指代.通常我们只有少数的原生对象,剩下的,需要我们自己去创建.在Java语言中,创建一只会"咯咯咯"叫的鸡时,我们是这么做的: public class Chicken{  public void makeSound(){  System.out.println("咯咯咯");  }} Chicken littleChicken = new Chicken(); 先定义一个Chicken类,在Chicken中对

javascript教程之不完整的继承(js原型链)_基础知识

Javascript的继承和标准的oop继承有很大的区别,Javascript的继承是采用原型链的技术,每个类都会将"成员变量"和"成员函数"放到 prototype 上,Js++都过superclass将其链接起来,即 C.prototype.superclass = C.superclass = P.prototype;当 var c = new C()时,c.__proto__ = C.prototype ;当 c访问"成员变量"时,如果在

js对象继承之原型链继承实例

 代码如下: <script type="text/javascript"> //定义猫的对象 var kitty  = {color:'yellow',bark:function(){alert('喵喵');},climb:function(){alert('我会爬树')}};   //老虎对象的构造函数 function tiger(){  this.color = "yellow and black";  this.back = function(

Javascript学习笔记7 原型链的原理_基础知识

我们先看看这样一段代码: 复制代码 代码如下: <script type="text/javascript"> var Person = function () { }; var p = new Person(); </script> 很简单的一段代码,我们来看看这个new究竟做了什么?我们可以把new的过程拆分成以下三步: <1> var p={}; 也就是说,初始化一个对象p. <2> p.__proto__=Person.proto

js原型链与继承解析(初体验)_javascript技巧

首先定义一个对象obj,该对象的原型为obj._proto_,我们可以用ES5中的getPrototypeOf这一方法来查询obj的原型,我们通过判断obj的原型是否与Object.prototype相等来证明是否存在obj的原型,答案返回true,所以存在.然后我们定义一个函数foo(),任何一个函数都有它的prototype对象,即函数的原型,我们可以在函数的原型上添加任意属性,之后通过new一个实例化的对象可以共享其属性(下面的两个例子会详细介绍). function foo(){} fo

JavaScript面向对象继承基础讲解

说好的讲解JavaScript继承,可是迟迟到现在讲解.废话不多说,直接进入正题.   既然你想了解继承,证明你对JavaScript面向对象已经有一定的了解,如还有什么不理解的可以参考详解JavaScript中面向对象基础知识,接下来讲一般通过那些方法完成JavaScript的继承.   原型链   JavaScript中实现继承最简单的方式就是使用原型链,将子类型的原型指向父类型的实例即可,即"子类型.prototype = new 父类型();",实现方法如下:  // 为父类型