JavaScript 原型与原型链深度解析(速通版)
名称和从属关系
对应名称
-
prototype: 原型
:是函数上的一个属性,它定义了构造函数制造的对象的公共祖先构造函数new出来的对象,都
隐式继承
了构造函数的原型上的属性。Car.prototype.name = 'su7' Car.prototype.lang = 5000 Car.prototype.height = 1400 function Car(color, owner) { // this.name = 'su7' // this.lang = 5000 // this.height = 1400 this.color = color this.owner = owner } let tian = new Car('black', 'tiantian') console.log(tian); console.log(tian.name); //隐式具有,也就是继承了
减少了每次new出来一个新对象都要执行那三行代码(固有属性,车名,车长,车重)的资源浪费。
可以看见,因为这几个属性是隐式具有
的,因此我们输出car时,看不到这几个属性,因此思考一个问题,在我们的实例对象tian上,我们能不能修改这几个隐式继承来的属性(原型上的属性)?
答案
实例对象可以修改显示继承到的属性,但是无法修改隐式继承到的属性(原型上的)
tian.name = 'xiaomi'
console.log(tian.name);
console.log(tian);
可以看见,我们只是往实例对象上新增了一个name属性,这个name属性(显示)和原型上的name属性是两个东西。
实例对象无法给原型新增属性
实例对象无法删除原型上的属性
__proto__:原型链
:(原型的链接点),每一个对象都有自己的隐式原型,除了Object.create(null)出来的对象。
从属关系
prototype -> 从属于函数,函数的原型属性,而他自己其实就是一个对象{}
__proto__ ->Object的一个属性 也是一个对象{}
- 对象的
__proto__
保存着该对象的构造函数的prototype
怎么理解呢?
Car.prototype.name = 'su7'
Car.prototype.lang = 5000
Car.prototype.height = 1400
function Car(color, owner) {
// this.name = 'su7'
// this.lang = 5000
// this.height = 1400
this.color = color
this.owner = owner
}
let tian = new Car('black', 'tiantian')
console.log(tian);
console.log(tian.name); //隐式具有,也就是继承了
这个实例对象是不是承接了构造它的构造函数上显式写着的属性,打印出来这个实例对象,也只能看见这个color和owner,但是他其实也是具有隐式的属性的,只不过是隐式具有,因此,实例对象自己本身承接了显示属性,而实例对象的隐式原型是不是也继承了构造这个实例对象的构造函数的显示原型?
从属关系与原型链的基本认知
简单理解,原型链是什么? 原型链就是以一个对象为基准,以__proto__
为连接的一个链条,一直到Object.prototype为止。js引擎在查找属性时,会沿着原型链查找。即对象的隐式原型向上查找,找不到就沿着隐式原型的隐式原型继续查找,直到找到Object.prototype,如果没找到就返回undefined。null为止。
深入认识原型链、原型与原型链继承
举个例子 :创建一个Test函数,里面有a属性,值为1
Test.prototype.b=2
结果:Test.prototype,原型是函数身上的一个属性,但是它本身也是一个对象。既然是对象,那就应该有自己的隐式原型(前面说的一种情况除外Object.create(null))新增了一个b属性,值为2
在这个图中如果大家理解了,大家会发现对象的隐式原型是不是应该全等于构造函数的显式原型?即:
obj.__proto__ === obj.constructor.prototype
// constructor就是构造器,对象的构造器属性返回值为这个对象的构造函数,也就是谁构造了我。
//例如obj是通过 new Obejct()构造出来的那刚刚的代码就等于
obj.__proto__ === Object.prototype
这个链条长这样,顶层一直到Object.prototype
既然这样我们是否可以通过Object.prototype.c = 3增加一个c?
我们可以通过test对象之间访问到a,b,c。也就是说,我们能否访问到这些值,与是不是构造他的函数是没有关系的,也就是说并不是说我构造了你,你就只能继承我,我们可以通过原型继承,__proto__
上保存了Test.prototype这个对象,这个对象里面有b...当我们在test里面找不到c的时候就会通过Test.prototype上去找,这个上面也找不到,就接着谁构造了我去这个构造函数的显式原型上找,最终登顶到Object.prototype
上找,即:原型链可以从底层到高层一步步继承,当底层能够找到时,就不会再接着往上找了(沿着__proto__
去找链上的任意一个原型属性,里面只要有我们要的值,就拿下来,没有就接着往上找)(原型链继承)
Function与Object的特殊性
Test
(实例对象)底层是由Function构造来的,那么Test.__proto__也就全等于Function.prototype
(构造这个实例对象的构造函数的显式原型),Test的proto对应了他的原型
这里的Function的proto 全等于他的prototype是底层指定好了的
Object本身就是一个函数,因此这里他等于Function的prototype是合情合理的,据此我们就能推出
判断属性是否存在方法
hasOwnproperty方法看对象上是否有这个属性,ab本身有,c是通过继承来的
如果想看看链条上有没有呢?---->in
小结
<script>
function Person() {
}
let p = new Person();
console.log(p);
if (p.__proto__ === Person.prototype) {
console.log(true);
}
if (Person.prototype.__proto__ === Object.prototype) {
console.log(true);
}
if (Object.prototype.__proto__ === null) {
console.log(true);
}
</script>
大家可以回头自行理解一下这份代码,理解什么是隐式原型,什么是显式原型,而原型链又是如何形成的。
注意这里的谷歌将原来的__proto__
改成了两个中括号,事实上是一个东西,大家能够看到,打印这个实例对象,这个对象就会有自己的隐式原型__proto__
属性,本身也是一个对象,这个属性保存着构造器和自己这个对象拥有的__proto__
属性,那么顺着这个链接点一直找,能够看到这个实例对象是由谁构造出来的,上一级又是又谁构造出来的,一直登顶。
题外话 大家都知道学习一门语言需要我们逐步掌握每一种数据结构,既然要掌握数据结构,就得了解里面本身固有的方法和属性,现在你知道了原型和原型链,你有办法将你想了解的数据结构拥有的属性和方法列出来了吗?
大家是不是逐渐理解了,底层帮你写好的方法是通过什么方式去写的?,这里我们只是var arr = [],这个数组只是一个普通的对象,事实上js引擎就是arr = new Array(),通过这个构造器帮我们创建了一个对象,也就是他在这个构造器中封装好了挂了这么多的属性和方法供我们使用,然后我们通过原型就能够逐层拿到我们需要的属性和方法了
转载自:https://juejin.cn/post/7366177335882973195