二,3分钟快速理解js中的【原型 / 原型链】。
原型/原型链前端面试高频出现,极为重要!!!
相信大名鼎鼎的JS三座大山【原型/原型链】,【闭包/作用域】,【异步/单线程】在前端童鞋中是无人不知无人不晓了。曾经学习JS的时候看了N多篇相关的文章,根本记不住什么是原型/原型链,一大堆乱七八糟的东西扰乱视线。今天写一下自己对原型/原型链的理解,如有不对之处,还请各位童鞋指出。
要理解JS中的【原型/原型链】不能从字面意思去理解,不然很容易迷糊。咱们先不要管概念,直接讲原理;
回顾一下上一篇中写到了JS的【变量类型】,回顾一下js的变量类型分类【值类型】和【引用类型】
(1)值类型:字符串(String)、数字(Number)、布尔值(Boolean)、Null、Undefined
(2)引用类型:对象(Object)、数组(Array)、函数(Function)
值类型和引用类型有什么区别?
1).每一个值类型都有一个独立的内存区域保存自己的值,调用它的时候调用的是他的值,而引用类型调用的是内存中的地址。
下面看例子1:
var a = 1, b = a;
console.log(b) // 1
b = 2; console.log(a) // 1
改变b的值再打印a的值我们看看
这里不管怎么改变b的内容,a永远不会被b影响,a的值永远是1;因为a和b都有一个独立的内存区域保存自己的值,调用它的时候调用的是他的值。
再看例子2:
var a = {x:1,y:2}, b = a;
声明一个变量a,再声明一个变量b赋值为a
console.log(b); // {x:1,y:2}
打印b,发现b和a的内容一样
console.log(a===b) // true
确认一下,a绝对等于b
b.x = 3, b.y = 4; console.log(a);
改变b的值,再打印a,奇迹出现了
console.log(a); // {x:3,y:4}
我们改变b的值这时候a竟然也改变了?什么情况?
之前提到了引用类型调用的是内存中的地址而不是调用它的值,所以你第一次声明一个引用类型的时候(声明a)会给a开辟一块内存空间用来存放a,再声明b=a的时候不会再次开辟一块内存空间,而是直接把变量b指向到了存放a的内存空间中,此时a和b公用一块内存空间;没错,JS因为一些设计上的原因它就是这样的机制。
这里把a换成数组或者函数,只要属于引用类型都是这种机制。
当然还有一个误区:
var a = {x:1,y:2}, b = a;
b = {x:3,y:4};
console.log(a) // {x:1,y:2}
为什么这里a的值就不会改变了呢?因为你没有去改变b的值,而是给b重新赋值。这时候相当于给b重新开辟了一块内存空间而不是改变b和a公用的内存空间里的内容。这就是值类型和引用类型的区别之一
那么他们还有什么区别呢?有!
所有的引用类型都有一个__proto__
属性(proto前后各两个下划线),我们称之为【隐式原型】
从上图可以看出,我们声明一个变量a为一个对象(引用类型)的时候它会多出一个属性__proto__
再看第二张图
我们声明了对象,数组和函数,发现函数有一个prototype
属性而对象和数组没有。因为所有的函数都有一个prototype
属性,我们称之为【显式原型】;这里函数也是引用类型,所以函数既有显式原型又有隐式原型。这就叫做原型;那么什么是原型链呢?看第三张图
我们写一个构造函数P,然后在P的显式原型上设置一个属性name,值为字符串’czj’
然后new一个构造函数P的实例赋值给变量a,再打印一个a的name属性,结果是’czj’。很奇怪对不对,我们明明没有设置a的name属性。这是为什么呢?这里涉及到两个JS的设计规则
一.)就是一个函数的隐式原型__proto__
指向它构造函数的显式原型prototype
。
所以a.__proto__ === P.prototype // true
二.)当JS在一个函数中找一个属性或者方法没有找到的时候就会去那个函数的原型中去寻找。
当我们打印a.name
的时候a本身没有name属性所以会去a.__proto__
属性中去寻找,又因为函数的隐式原型__proto__
绝对等于它构造函数的显式原型prototype
所以a.__proto__ === P.prototype
,而我们之前在P.prototype
中设置过name属性,所以a.name === 'czj'
。就这样函数a有原型P,那么P呢?P.__proto__ === Function.prototype // true
P的构造函数其实是Function。就这样函数a的构造函数是P,P的构造函数是Function对象new出来的,而他们又各自都有各自的原型,那么就形成了一条原型链。这就叫做原型链!
另外提一点,值得注意的是原型链并不是无限存在下去的,原型链的顶端是Object.prototype。
其实还有很多如instanceof, constructor跟原型链的关系没有写,这个留到以后的文章中再说吧。
下一篇写一下【闭包/作用域】