如何理解JavaScript中的原型和原型链
首先是一张关系图,避免抽象化理解时产生的困难
Function对象
函数对象是JavaScript学习中不可避免的一部分,而且这一部分相对重要且抽象 函数的创建方式有2种:
- 字面量创建
var foo = function(){ console.log("test"); }
- new 关键字创建实例对象
//let 函数名 = new Function(“参数列表”,”函数体”); let sum = new Function("num1,num2","return num1+num2");
我们多数在使用new关键字的时候,是用于创建实例对象
那么我们首先来看一下,在使用new关键字创建实例对象的时候,都经历了什么:
- 创建一个新对象
- 链接到原型对象,继承属性和方法
- 将构造函数的作用域赋给新对象(this指向改变到实例对象中)
- 返回新对象
这个时候我们的关注点 原型对象 就出现了:
原型对象prototype
prototype
是一个显式原型属性(也可以叫它原型对象),只有函数才有该属性,通常我们叫这个时候的函数为"构造函数"prototype
的伴随构造函数的声明就会被自动创建- 原型对象
prototype
只有一个属性:constructor
代码举例:
function Student(name,age){ this.name = name; this.age = age; } let s1 = new Student("Tom",17);
首先我们创建了一个构造函数Student
,此时Student
的结构中会出现一个prototype
属性,即原型对象,这是引擎自动给它的,我们可以直接进行使用
- 实例对象
prototype
中的constructor
属性:
此时可以看出constructor
对应的是构造函数,也就是Student
并且这是一个公有不可枚举属性,一旦改变了prototype
,这个属性就会不见,当然可以再手动添加回去
而当我们再使用new关键字创建实例对象s1
之后,我们来看一下s1
的结构:
实例对象s1
中除了在Student
获得的age
,name
属性之外,还有一个__proto__
属性,所以它又是什么东西呢?
__proto__
是什么
__proto__
是每个对象都有的隐式原型属性,指向了创建该对象的构造函数的原型对象prototype
,但是 prototype
是内部私有属性,我们并不能访问到,所以使用__proto__
进行访问
至于__proto__
是如何产生的,上面的new关键字创建函数的时候的第三部"链接到原型,继承属性和方法"的时候就让实例对象,例如s1
拥有了__proto__
属性
从实例对象s1
的__proto__
指向构造函数Student
的prototype
,构成了原型链
通过原型链的概念,我们就不难理解实例对象是如何继承构造函数中原型对象的属性和方法了
function Student(name,age){ this.name = name; this.age = age; } Student.prototype.method = function(){ console.log("我的名字是"+this.name+",我的年龄是"+this.age); } let s1 = new Student("Tom",17); s1.method(); //我的名字是Tom,我的年龄是17
函数的原型链结构
任意的一个函数, 都是相当于 Function 的实例. 类似于 {} 与 new Object() 的关系
function foo () {}; // 告诉解释器, 有一个对象叫 foo, 它是一个函数 // 相当于 new Function() 得到一个 函数对象
- 函数有
__proto__
属性 - 函数的构造函数是 Function
- 函数应该继承自
Function.prototype
Fucntion.prototype
继承自Object.protoype
- 构造函数有prototype, 实例对象才有__proto__指向原型, 构造函数的原型才有 constructor 指向构造函数
intanceof
array instanceof Array
判断 构造函数 Array 的原型 是否在 实例对象 array 的原型链存在