想理解更深可参考: 深入javascript之原型和原型链 https://blog.youkuaiyun.com/yucihent/article/details/79424506
这篇文章有空需要更加深去看一下:详谈JavaScript原型链 https://www.cnblogs.com/chengzp/p/prototype.html
重点:
- 原型链最重要的作用就是继承,实例来继承上一级的属性或方法。此外,如果有多个实例,而多个实例存在共同方法,或共同属性,我们不想每一个实例都创建一份这些属性或方法,就可以将这些属性存在原型对象上,实例一样可以使用这些属性或方法;
- 类似于作用域链,实例在调用方法时,如果在本身没有找到,就会在原型对象上查找,如果也没有找到,就会再向上一级原型对象查找,一直找到Object.prototype ;如果中间找到会停止查找返回该方法。如果一直没找到会返回未定义;
要理解原型链就要明白原型对象、构造函数、实例,三者之间的关系。
我们先来梳理三者的关系:
每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,实力也有一个指向原型对象的内部指针。
当这个原型对象是另一个类型的实例,那么这个原型对象就有一个内部指针指向另一个原型,以此类推就构成了一条原型链。原型链的根就是Object.prototype。
文字描述往往没有图像描述来的直观,那么我们用一个三者之间的关系图来直观的了解。
如上图所展示的,原型对象指向构造函数的指针是 constructor ,构造函数的 prototype 指向原型对象,构造函数通过 new 操作符来创建一个实例 , 实例又可以通过内部指针 proto 指向原型对象。
proto 连接的这一条原型对象就构成了原型链。
如上面列举的创建对象方式的第二种方式,M 就是一个构造函数,那么 M 就应该有一个 原型对象,可以通过 prototype 找到!而 M 的原型对象存在 constructor,按照上图所示, M 的 原型对象的 constructor 指向的就是 M 本身;实践看结果:
上图就可以明了的证明构造函数和原型对象之间的关系;
那么我们再来看实例和原型对象的关系。依照上面的关系图,实例的内部指针 proto 指向原型对象,构造函数 M 的原型也指向同一个原型对象,究竟指向的是不是同一个原型对象呢?我们再来看:
这样两个例子就很明白的说明了实例、构造函数、原型对象三者之间的关系。
那么明白了三者之间的关系我们就来说一下原型链,从一个实例对象向上找有一个构造实例的原型对象,这个原型对象又有构造它的上一级原型对象,如此一级一级的关系链,就构成了原型链。原型链的最顶端就是Object.prototype ;
原型链最重要的作用就是继承,实例来继承上一级的属性或方法。此外,如果有多个实例,而多个实例存在共同方法,或共同属性,我们不想每一个实例都创建一份这些属性或方法,就可以将这些属性存在原型对象上,实例一样可以使用这些属性或方法;
类似于作用域链,实例在调用方法时,如果在本身没有找到,就会在原型对象上查找,如果也没有找到,就会再向上一级原型对象查找,一直找到Object.prototype ;如果中间找到会停止查找返回该方法。如果一直没找到会返回未定义;
class person{
constructor(age,sex){
this.age=age
this.sex=sex
this.hobby="running"
this.name="ling"
}
say(word){
alert(word)
}
}
var ling=new person()//用new 方法创建一个 名为 ling 的 实例
class baby extends person{ //baby 继承父类 person 的属性和方法
constructor(){
super("5months","female") //这里和上面person 类在 constructor 传入的两个参数
this.name="xinxin"
this.food="milk" //constructor 中,可以添加新属性,或者其他代码
}
}
let xinxin =new baby()
根据上文的关系分析,结合例子,
原型对象 对应 person的实例 (这里的person的实例,指的是 person 类中,用constructor创建的实例,而不是特指某个已经创建出来的具名实例,比如用 new 方法创建出来的对象 ling)
构造函数 对应 baby
实例 对应 xinxin
console.log(xinxin.__proto__===baby.prototype)
检测第一级原型对象 是否指向同一个,执行结果为true
console.log(xinxin.__proto__.__proto__===person.prototype)
上去第二级原型对象, 执行结果也为true
prototype 和 _proto_
只有函数有prototype,对象是没有的。
但是函数也是有__proto__的,因为函数也是对象。函数的__proto__指向的是Function.prototype。
也就是说普通函数是Function这个构造函数的一个实例。
instanceof原理
instanceof是判断实例对象的__proto__和生成该实例的构造函数的prototype是不是引用的同一个地址。
是返回true,否返回false。
注意:实例的instanceof在比较的时候,与原型链上想上找的的构造函数相比都是true。
继续上面的代码
那怎么判断实例是由哪个构造函数生成的呢?这时候就要用到constructor了。
实例的原型的构造函数, obj.__proto__.constructor
原型对象和实例之间有什么作用呢?
通过一个构造函数创建出来的多个实例,如果都要添加一个方法,给每个实例去添加并不是一个明智的选择。这时就该用上原型了。
在实例的原型上添加一个方法,这个原型的所有实例便都有了这个方法。
var M = function (name) { this.name = name; }
var o3 = new M('o3')
var o5 = new M()
o3.__proto__.say=furnction(){
console.log('hello world')
}
o3.say()
o5.say()
打印结果