原型链初步学习
- 这篇博客只是我初步理解原型链的一个个人学习笔记,写的比较粗略,且有的地方可能理解错误.
- 更多更专业的关于原型链的解释请看JavaScript深入之从原型到原型链和阮一峰的博客:Javascript继承机制的设计思想
JS标准库里几个构造函数之间的关系
每个对象都有toString()
和valueOf()
函数,如果每个对象里都存这样的相同的函数,那会浪费内存.
问题解决方法:原型链
把toString()
和valueOf()
等函数单独存放在一个对象里,原来对象里的toString()
和valueOf()
只存地址,指向这个地址
当声明对象是,出来自己定义的属性,还有一个隐藏的属性__proto__
里面存的是toString()
和valueOf()
等的地址
他的值就是公用属性的值
共有的属性和方法
在执行o1.toString()
是发生的事:
首先看o1
是不是真正的对象,如果不是,就包装成临时对象,用完再回收垃圾,然后进入对象,看他有没有toString
方法,如果有特有的toString()
,就用,如果没有,就进入__proto__
属性去找toString()
方法.
说明所有的toString()
都是同一个地址
__proto__
进阶
如果这个对象不是普通对象拥有特有的方法,会一层一层去找
代码:
第一层的toString()
是Number()
特有的,可以转换成16进制toString(16)
,第二层里面的的toString()
是所有对象都有的公共属性.比如找valueOf()
如果在第一层里 __proto__
找不到,就去第二层找.
数字对象普通对象
结构:
原型链
所有的共有属性,,如果没有东西引用他,就会被垃圾回收,那么谁在引用他呢?
答案是 prototype ,prototype
的意思是 原型.
所有函数都有protoype
属性,包括构造函数.即String()
,Number()
,Boolean()
,Object()
有protoype
在最开始JS初始化的时候,函数的protoype
属性会加载到内存当中
原型===共有属性Object.prototype
意思还是Object
的共有属性
Object
是构造函数(JS里函数也是一种对象,也可以有方法,可以 有属性)
函数名字加()
就是执行.
Number
的共有属性
共有属性的共有属性
其他String
,Boolean
的原型也是一样
new
做的事情,一创造一个哈希,二绑定原型(共有属性),即把__proto__
指向该有的共有属性,也就是原型
__proto__
与prototype
的关系
[__proto__
指向prototype
],
在无代码情况下window
里面有Number
属性(函数也是对象,也可以有属性)
实际上为Number:function Number(){}
这个
无代码的时候,即为下面这样,浏览器已经将其初始化好了.
可以看到prototype
是用来指向这些共有属性的,不然这些共有属性就被垃圾回收了,所以要用一个线来牵引着.
如图
写了代码之后
所以:
-
prototype
是浏览器开始就准备好了的,用来防止共有属性被垃圾回收的, -
__proto__
是在开始写代码的时候用来引用共有函数的. -
String.prototype
是String
的公用属性的引用,是JS在初始化的时候就已经存在的,用他是因为如果不用他,那么公用属性就跑了,被垃圾回收了 -
's'.__proto__
是String
的公用属性的引用,是在声明新对象的时候存在的,有他是因为我要用他,用公用属性
共同点就是都是公共属性的引用.
var o1 = {};
o1.__proto__ === Object.prototype//true
Number.prototype.__proto__===Object.prototype//true
var s1 = new String('s1');
s1.__proto__ === String.prototype//true
String.prototype.__proto__ ===Object.prototype//true
function DOG(name){
this.name = name;
}
DOG.prototype.__proto__ === Object.prototype//true
对象与构造函数
形式
总结的式子
只有函数才能有prototype
两个属性对比:
共同点:存的地址相同,都指向同一个对象
不同点:一个是对象的属性,一个是函数的属性
面试题:
1'1'.__proto__
'1'
会创建一个临时String对象,然后指向String.prototype
2函数.prototype
是一个对象,那么
var obj = 函数.prototype;
obj.__proto__ === Object.prototype;//true
函数.prototype.__proto__ === Object.prototype;//true
成立(可以看上图无代码的时候)
Number.prototype.__proto__ === Object.prototype
//true
String.prototype.__proto__ === Object.prototype
//true
Boolean.prototype.__proto__ === Object.prototype
//true
成立
原型链面试:
怎么回答
JS 原型是什么?
举例
var a = [1,2,3]
- 只有0、1、2、length 4 个key
- 为什么可以
a.push(4)
,push
是哪来的? -
a.__proto__ === Array.prototype
(a是实例数组对象,Array是构造函数) -
push
函数 就是沿着a.__proto__
找到的,也就是Array.prototype.push
-
Array.prototype
还有很多方法,如join
、pop
、slice
、splice
、concat
-
Array.prototype
就是 a 的原型(proto)
聚完例子后用new对象举例,说给面试官听:
比若说
-
我们新创建一个构造函数
function Person() {}
-
然后根据构造函数构造一个新对象
var person1 = new Person();
- 每个函数都有一个
prototype
属性,这个构造函数的 prototype 属性指向了一个对象,这个对象正是调用该构造函数而创建的实例的原型。 -
当我们给
Person
的prototype
的name
属性赋值为'Kevin'
Person.prototype.name = 'Kevin'; var person1 = new Person(); var person2 = new Person(); console.log(person1.name) // Kevin console.log(person2.name) // Kevin
每一个新的实例对象对象都会从原型"继承"属性,实例对象拥有该原型的所有属性。
- 说白了,原型就是 构造函数 用来 构造 新实例 的 模板对象。
- 这就是原型。
开始解释原型链
那么我们该怎么表示实例与实例原型,也就是 person1 和 Person.prototype 之间的关系呢,这时候我们就要讲到第二个属性__proto__
。
什么是原型链?
先回答什么是原型。在上面,然后继续从__proto__
开始往下说。
说:
- JavaScript对象除了 null 都具有的一个属性,叫
__proto__
,这个属性会指向该对象的原型对象。 - 当读取实例的属性时,如果找不到,就会通过
__proto__
查找原型中的属性,如果还查不到,就去找原型的原型。 - 例如
Person.prototype
这个原型的原型就是Object
这个构造函数的prototype
,既Object.prototype
这个原型对象。然后,Person.prototype.__proto__
就指向Object.prototype
这个原型。然后Object.prototype
原型是null
。 - 这些原型对象通过
__proto__
像链子一样连起来,就叫做原型链。
然后给面试官画:
链子上都画上__proto__
person1----->Person.prototype----->Object.prototype----->null
Array.prototype----->Object.prototype----->null