说说JS中的原型对象和原型链

栏目: JavaScript · 发布时间: 5年前

内容简介:理解原型对象(有些文章简称为原型)和原型链,是理解JS的重要一环。下面是笔者对JS中原型的理解,俗话说,JS中万物皆对象。函数也是一个对象,什么是函数对象?每声明一个函数,此函数在JS执行解释时都会被当作一个对象来维护,这就是函数对象。JS中声明函数的方式有:

理解原型对象(有些文章简称为原型)和原型链,是理解JS的重要一环。下面是笔者对JS中原型的理解,

函数对象

俗话说,JS中万物皆对象。函数也是一个对象, 只不过函数是在特定环境中执行代码的对象。

什么是函数对象?每声明一个函数,此函数在JS执行解释时都会被当作一个对象来维护,这就是函数对象。JS中声明函数的方式有:

function fn1(){}
var fn2 = function(){}
var fn3 = new Function()
复制代码

所以可以理解为fn1、fn2、fn3都是函数对象。JS中还包括一些系统内置的函数对象,比如:

Function  Object  Array  String  Number  RegExp
复制代码

函数对象之外的对象都是普通对象。函数对象能创建普通的对象,反之则不行。

理解原型对象(其实就一普通对象)

1、只有函数对象才拥有原型对象

也即无论什么时候以什么方式创建一个函数(函数对象),都会根据特定的规则为该函数创建一个prototype属性(原型对象的地址的引用),这个属性就是指向该函数的原型对象。比如:

function Person () {};
console.log(Person.prototype) // Person.prototype就是Person的原型对象,实际是原型对象的内存地址的引用
复制代码
说说JS中的原型对象和原型链
看到没有,原型对象并不神秘,就是一个普通的对象,只不过其默认有了 constructor__proto__ (下一节会讲)属性而已(其中 __proto__

不建议在实际中应用,因为在有些浏览器可能并没有实现该属性)。

由上图看出,函数 Person 的原型对象( Person.prototype )默认拥有一个属性 constructor ,此属性就是用来重新指向函数 Person

function Person () {};
Person.prototype.constructor === Person // true
复制代码
说说JS中的原型对象和原型链

2、普通对象与原型对象的关系

一般我们定义一个构造函数(构造函数其实就是普通的函数,只不过目的是创建对象),然后通过 new 操作符来创建一个普通对象。

function Person (name) {
    this.name = name;
    this.age = 18;
}
var xiaoming = new Person('小明'); // {name: '小明', age: 18}
var xiaohong = new Person('小红'); // {name: '小红', age: 18}
复制代码

在上述代码中,变量 xiaomingxiaohong 是构造函数 Person 的实例。我们通过上一节知道了 Person 与其原型对象的关系,但实例与构造函数的原型对象有什么关系呢?

每当调用构造函数创建一个实例即普通对象后,该实例将包含一个内部的指针 [[Prototype]] ,这个指针指向的就是构造函数的原型对象。

目前ECMAScript的标准中并没有实现标准的访问该指针的方式,但像Firefox、Chrome和Safari等浏览器实现了 __proto__ 属性,此属性就是用来访问指针 [[Prototype]] ,所以可以借用 __proto__ 属性展示实例和原型对象的关系。

xiaoming.__proto__ === Person.prototype // true
xiaohong.__proto__ === Person.prototype // true
复制代码

3、总结上述两小节

每创建一个函数,就会为相应的函数创建一个 prototype 的属性,这个属性指向了函数的原型对象,这个函数的原型对象会默认拥有一个 constructor 属性,此属性指向了对应的函数。而使用 new 操作符调用函数创建出来的实例,会拥有一个内部的指针 [[Prototype]] ,此指针指向函数的原型对象。

千言万语不如一幅图:

说说JS中的原型对象和原型链

原型链

由上节我们可以知道,原型对象上的属性和方法被所有实例所共享的。每当访问一个对象的属性或者方法时,会首先搜索对象自身,如果找到了此属性或者方法,则直接返回,否则向对应的原型对象上面搜索,如果找到则直接返回,否则继续向原型对象的原型对象上查找,直到搜索到null,抛出错误或返回 undefined

function Person (name) {
    this.name = name;
    this.age = 18;
}
Person.prototype.sayName () { // 在Person的原型对象上添加的方法,被所有实例共享
    console.log(this.name);
}
var xiaoming = new Person('小明'); // {name: '小明', age: 18}
xiaoming.sayName(); // 小明
复制代码

上面代码中,实例 xiaoming 本身并没有 sayName 方法,但却成功调用了。 其实就是通过实例内部的 [[Prototype]] 指针去原型对象 Person.prototype 上找对应的方法,然后调用。

如果我调用一个实例本身和原型对象都没有的方法,其过程是怎么样的呢?

xiaoming.sayAge() // 实例本身和原型对象都不存在的方法
复制代码

(1)首先搜索 xiaoming 这个对象,并没有 sayAge 方法,

说说JS中的原型对象和原型链
(2)继续向原型对象搜索(通过内部的 [[Prototype]] 指针)。没有找到 sayAge

方法

说说JS中的原型对象和原型链
(3)继续向原型对象的原型对象上搜索,即 xiaoming.__proto__.__proto__ 。也没有找到 sayAge

方法。

说说JS中的原型对象和原型链
(4)继续向原型对象的原型对象的原型对象上搜索,即 xiaoming.__proto__.__proto__.__proto__ ,但发现 xiaoming.__proto__.__proto__.__proto__null ,停止搜索,抛出错误或返回 undefined

如果原型对象和实例上具有同名的属性或方法,则搜索时取最近的。

如上述的原型链的搜索机制,你通过阅读本文知道 xiaoming.__proto__Person.prototype ,但 xiaoming.__proto__.__proto__ 呢? 不说话看图:

说说JS中的原型对象和原型链

由此,可得到下面的关系图:

说说JS中的原型对象和原型链

思考

原型链中的关系图其实还缺少一环,就是内置函数 FunctionFunction 比较特殊,有兴趣的可以去研究下 FunctionObject 的关系。

本文是笔者对原型对象和原型链的理解,如有错误或不足的地方,欢迎指正。


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Iterative Methods for Sparse Linear Systems, Second Edition

Iterative Methods for Sparse Linear Systems, Second Edition

Yousef Saad / Society for Industrial and Applied Mathematics / 2003-04-30 / USD 102.00

Tremendous progress has been made in the scientific and engineering disciplines regarding the use of iterative methods for linear systems. The size and complexity of linear and nonlinear systems arisi......一起来看看 《Iterative Methods for Sparse Linear Systems, Second Edition》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码