原型链
这是第三次修改这个篇文章了
我想我需要将自己的思路说出来
下面的1-4只是为了告诉明确两件件事情。(原型链是会配合着属性的查找使用的。要不然我们一般也用不到原型链。),第二 原型链的查找,仅仅只会沿着_proto_去查找
我们需要知道的基础
我们建立出来三个Object,看下初始化会是什么样子,带那些属性
var apple = function () {};//我们建立了一个函数 我们看下 这个函数有哪些属性 apple(初始化自带属性) ______________ | protoType | | _proto_ | -------------- var obj = {};//onject 属性 obj (初始化自带属性) ______________ | | | _proto_ | -------------- var apple_1 = new apple();//new 实例 apple_1 (初始化自带属性) ______________ | | | _proto_ | --------------
1,只有function(函数)才具有 protoType +_proto_ 属性
2,除了函数之外的object只有 _proto_
3,new 出来的实例 只有_proto_ (虽然他是函数,但是只有_proto_)-------我跟你一样,想知道为什么。继续往下看。
_proto_中属性的查找
要说到原型链,我们前面提到了,会伴随的属性的查找。
看下面的代码
var apple = function () {};//我们建立了一个函数 我们看下 这个函数有哪些属性 apple(初始化自带属性) ______________ | protoType | | _proto_ | -------------- apple.prototype.price = 10; apple.__proto__.price = 20 ;
此时我们打印一下 apple.price你可以猜测一下。是undefind , null ,10 ,20 ,中的哪一个
最后 js 给出的答案是20,也就是虽然函数 apple() 有 prototype 与 _proto_的属性。但是查找属性只会去_proto_ 里面去查找。
过程是:编译器先去 apple第一层去查找有没有price 结果他查出来的是没有price(因为只有 prototype 与 _proto_属性);
apple(初始化自带属性) ______________ | protoType | | _proto_ | --------------
然后他会越过protoType(<1>)直接去 _proto_ 里面查找,发现有price,就给你,没有的话,继续找_protop_里面找找看看有没有_proto_ 属性。没有的话就返回undefind,有的话再去找这一层级中的_proto_上有没有price属性。接下来就是(<1>)。
为什么会跳过去protoType (或者说不去查找protoType中有没有prices属性),看下面的代码
var orange = function(){}; orange (初始化自带属性) ______________ | protoType | | _proto_ | -------------- orange.protoType.price = 10; console.log(orange.price);
可以猜测一下 orange.price 打印出来的是什么
返回来的是 undefind,---在寻找Object的属性的时候,编译器,只去_proto_上去寻找属性,其他的一概不理。只认_proto_
所以我们知道了,原型链就是又_proto_组成的,让并沿着只_proto_寻找下去。找不到就返回undefind。
哪什么是protoType
我还只建议你去看这一片的文章 https://www.jianshu.com/p/686b61c4a43d。不要看我下面关于 _proto_ 与 protoType了
protoType 就是_proto_ 指向的地方,也就是 这两个的内容是一模一样的。
我们有些东西需要作为开胃菜!
因为在js(ES6之前,后来才出现class,但是这个也是个语法唐)中一开始没有calss的概念。到那时c# java中是有的。有了calss更有利于面向对象编程。就是编写出来的代码更容易维护。
js中没有calss那可怎么面向对象编程,我们使用 new ,js给了我们这样的一个方式去面向对象编程,其实你用的Function Object,你可以理解为都是 new 出来的,只不过我们编译器让我们写的更加方便。看下面的代码
var Obj_a = new Object(); var Obj_b = {}; console.log( typeof obj_a) console.log( typeof obj_b)
再来一个代码
var fun = new Function(); var fun_1 = function(){} console,log(typeof fun); console,log(typeof fun_1);
然后我们再来一个代码
var apple = function(){}; var apple_a = new apple();
对比上面的你有没有很熟悉的感觉,你是不是也常常这样使用。
然后 apple_a 与 apple 之间是什么关系。
apple_a 是apple的构造函数, apple是apple_a的构造函数。由apple实例化出来了一个apple_a的对象。
既然由上面这一层关系,这跟prototype由又有什么关系
我们检查了一个 apple_a._proto_ 与 apple.protoType 之间的关系。结果显示他们是相等关系。
还记得前面我们说过什么吗 ---- protoType 就是_proto_ 指向的地方,也就是 这两个的内容是一模一样的。
而Objct需要寻找一个属性,它就会层层的寻找 _proto_ 这个属性。也只认_proto_ ,包括protoType都不认。
我们打印一下 apple.prototype,它里面也有一个_proto_
这样你大概清楚了吧。他们之间是什么关系。给你看下面的图
var animal = function() {}; var dog = function() {}; animal.prototype.price = 20; animal.price = 1000; dog.prototype = animal; var cat = new animal(); var tidy = new dog(); // 下面两行分别输出什么? console.log(cat.price); console.log(tidy.price);
prototype的指向规则是什么(更新中)
看到这里就好了。下面的内容是我之前写的
那就是。原型链就是 _proto_ 这个属性串起来的。只会在_proto_上去查找你需要的属性。(如果你并不清楚就记住这一点) 而 protoType 是 _proto_ 所指向的内容 。
1> 是骡子是马拉出来走两步。
我们知道js分为8中类型 分别是
undefined
、null
、布尔值(Boolean)、字符串(String)、数值(Number)、对象(Object)。smbol我们分别在js走一下,看看有什么不同(可以按下F12然后点开控制台(Console)输入下面的代码,会看到一样的结果)
number :
string:
bool:
Object:(有个箭头,然后我们打开箭头)
smybol:
underfind:
null
通过上面的打印,我感觉你应该看到了有什么不同,就是 Object 与其他的打印出来的不同,不同的地方在于Object打印出来的竟然可以打开。里面有个_proto_东西。然后我们接着打开我们会看到下面这幅图
为什么Object类型会这样。可以打开,下面还有这个这么多东西。
看到了这个东西。我们就离原型链更近了一步。
2>呀哈,怪妖艳的哈。一串一串的
看到上面一串一串的。为什么会这样挂的一串一串的。
我们呢,再来试试
我先创建一个Object
我们给这个 a 添加一个值 a(stirng 字符串),然后再打印一下,显示出来。这个 a 添加了一个参数后,会变成何方神圣
看到这里,我想你大概能看出来。难道是a(Object)自己身的属相都会挂在上面。
我们再来加多一些
确实是这样,原来Object被打印出来之后,他里面的属性都会这样的形式显示出来。(那上面的变量我们都可以调用 a.b a.c a.d a.e a.f 给他打印出来。)
3>原来js8中基本类型中 只有Object类型的才有 _proto_
我们上面做的实验发现只有Object才能打开,并且属性都会挂在上面,而且这些属性可以被调用。
我们还发现有一个 _proto_ ,这是个什么东西,按照方才的推理,Object的属相都会被挂在身上,并且可以调用。那么这个_ptoro_ 也可以调用.。 我们来试试 看看能不能,像调取属相的方法样调取这个 _proto_。
我擦嘞,打印出来。那就是说 _proto_,就是Object类型的一个属性,不过是系统在我们创建Object类型的时候帮助我们默认的添加了折磨一个属性。
4> _proto_ 里面又挂了一串东西
我们看到上面的图片看到了,当我们打开_proto_之后,又是一串的东西。按照刚刚的推理。这里面是_proto_的属性,自然也就是Object的属性。我们应该可以通过Object调取这里面的属性。
我们单独给_proto_增加一个属性 我们输入 a.__proto__.g = 1 看看能不能给_proto_一个属性g
成功了。我们再打印一下,看到了上面这幅图,g的属性被放在_proto_这个属性之下了。
那我们就打印一下 g ,使用两种方式去调用。我们发现了一个问题,都可以使用,并且打印数是一个东西。
所以实验出来的是,当Object,第一级属性中,找不到相关参数的时候,他会去_proto_里面去找。找到了就打印出来。
但是找不到呢?我们实验一下。
js编译器给我们反馈是----undefind。
所以通过上面我们可以的出来一个结论。
当Object类型的变量,在查找一个属性的时候。挡在第一级的属性里面找不到,就回去_proto_里面去寻找。不管下面会有多少个_proto_他都会去寻找。直到没有_proto_的时候。它就不会再去寻找了,返回给你undefind。告诉你,嘿,我爬山涉水呀翻山越岭啊,我能翻的都翻过了,我实在是找不到。
上面这样图 ,我们发现打开第一个_proto_里面就没有了。它就会返回undefind。
结论来了 这个寻找需要的属性的过程,所以依赖的串串就是原型链。
懂了这个你就有了 js 中的继承是怎么实现的基础了
- 当调取一个对象的属性时,会先在本身查找,若无,就根据 proto 找到构造原型,若无,继续往上找。最后会到达顶层Object prototype,它的 proto 指向null,均无结果则返回undefined,结束。
- 由 proto 串起的路径就是『原型链』
官方一点的话语
所有的JS对象都有一个prototype属性,指向它的原型对象。当试图访问一个对象的属性时,如果没有在该对象上找到,它还会搜寻该对象的原型,以及该对象的原型的原型,依次层层向上搜索,直到找到一个名字匹配的属性或到达原型链的末尾。
看到这里或许你会有疑问,什么是prototype什么是原型对象。不急 这是我们下面需要处理的问题
下面的内容是我之前写的,但是写了一些,我感觉还是写的不好,所以我打算换一种方式。下面的先保留
参照了三个文章
之前我也不太懂 原型链 似懂非懂的样子 其中的prototype proto constructor 原型链之间到底是什么关系!
今天就帮你了解上面的这些概念是什么 以及与原型链之间的关系
prototype里面有个constructor(指向构造函数 构造函数与实例(实例对象)相对应)
proto 里面有个constructor(指向构造函数 构造函数与实例(实例对象)相对应)
构造函数与实例(实例对象)
function A (){};//大家都默认 构造函数 第一个字母,大写开头 当然你写成小写也没有问题 var b = new A();
其中函数 A() 我们叫构造函数
通过 new A() ,而得到的变量 b 。就是实例(实例对象) 。
A 作为模板 new出来一个b的实例。所以b是A的实例,A是b的构造函数。
为什么通过 new ,我们就能得到一个 实例对象 b
目的: 为了解决从原型对象生成实例的问题,Javascript提供了一个构造函数(Constructor)模式。
所谓"构造函数",事实上就是一个普通函数,可是内部使用了this变量。对构造函数使用new运算符,就能生成实例。而且this变量会绑定在实例对象上。
话外:上面中A()函数中的 this 指向的是 window ,而b中的this指向的是指向的是 b (如果你对作用域并不太清除的话,记住一句话,并不是函数在哪里创建就决定了作用域指向哪里,而是函数在哪里被调用才决定作用域指向哪里)
然后我们看一张图
function Position(){ var name = 'jiang' var say = function () { console.log('你们好,我的名字叫:',name) } }; var postion1 = new Position(); var postion2 = new Position();
我们首先把 positon1 与 position2打印一下
prototype
第一我们需要知道
prototype 都存在哪些地方:只有一个地方 那就是 function 函数 但是 new 出来的实例并没有
proto(_proto) 都存在哪些地方:Objcet (Objcet 类型 arry,function,null ,{} )
出现的目的 : 为了实现数据共享和抽象出通用的属性,加了一个原型prototype