前言
文章只作为本人的学习笔记和分享,有借鉴有补充,有错误,也会有进步,待更。
Ⅰ、需要理解的
- 每个
构造函数
都有一个prototype(显示原型)
属性,他是一个指针
,指向一个对象,这个对象的用途是包含所有实例
共享的方法和属性
我们把这个对象叫做原型对象
)。 - 每个
引用类型
都有一个__proto__(隐式原型)
属性。 对象
的__proto__
属性,指向它的构造函数
的原型对象(Function.prototype
)。- 每个
构造函数
的prototype
原型对象里的constructor
指向构造函数
本身。 - 特殊:
Object.create()
创建的对象没有__proto__
属性
注意:在现在的浏览器中你看不到__proto__,而是[[prototype]]。在MDN文档中你可以看到他的提示是:虽然Object.prototype.__proto__
现在大多数浏览器都支持它,但它的存在和确切行为仅在 ECMAScript 2015 规范中标准化为遗留功能,以确保与 Web 浏览器的兼容性。为了获得更好的支持,请Object.getPrototypeOf()
改用。
let a = {};
console.log(Object.getPrototypeOf(a) === a.__proto__); //true
Ⅱ、通过代码理解
① 代码示例:理解prototype:
function Person() {
console.log('这是a函数输出');
}
console.log(Person.prototype); //输出空对象
Person.prototype.showName = function(){
console.log('这是Person的 showName 的输出');
}
Person.prototype.city = '深圳'
var watson = new Person()
var jack = new Person()
watson.showName() //这是Person的 showName 的输出
console.log(jack.city); //深圳
上述代码实例中,构建函数Person
中的prototype
一个指针,他默认指向一个空对象原型对象
,我们在Person上面添加方法showName和属性city,继而通过Person创建的实例对象watson和jack,就可以都能获取到共showName
方法和city属性,这就是第一点所说的,我们也可以拿其他的函数查看
var a = new Object()
console.log(Object.toString()) //function Object() { [native code] } 表示他是程序自带的一个函数
console.log(a.toString()) //function Object() { [native code] } 表示他也是程序自带的一个
- 我们知道对象是有toString方法的,这是在我们使用js的时候,已经被添加到其原型对象上了,所以我们是使用 new Object() 创造出来的对象,其都带有一些一定的公共方法如
toString()
,hasOwnProperty()...等
。
② 代码示例:理解 __proto__
:
var info = {
name:'watson',
age:100
}
console.log(info);
console.log(info.__proto__);
- 我们在对象info里面可以看到
[[prototype]]
其实就是__proto__
,称为对象的隐式原型
,即info.__proto__
- 每一个
对象
都有__proto__
属性
③ 代码示例:理解 prototype
和__proto__
关系:
var obj = {}
var arr = []
var fn = function () {}
obj.__proto__ === Object.prototype // true
arr.__proto__ === Array.prototype // true
fn.__proto__ === Function.prototype // true
- 在之间定义变量为
{}
的时候其实也是一个变量的创建过程,是通过new Object()
创建的, 所以对象的隐式原型
指向它的构造函数
的显示原型
,即obj.__proto__ === Object.prototype
会得到true
- 数组和函数是特俗的对象,也是存在相同的指向关系的。
④ 代码示例:理解 constructor
和构造函数
关系:
function Person() {
console.log('这是a函数输出');
}
console.log(Person.prototype);
console.log(Person.prototype.constructor === Person);
- 在理解数据类型判断的时候会用到
constructor
,也存在这个每个构造函数
的prototype
原型对象里的constructor
指向构造函数
本身。
⑤ 在第五点需要理解的点中,可以查阅上诉链接的文档,这里不做详解。
Ⅲ、原型基本关系
上诉的基本代码示例,我们可以理解一个基本的几者的关系。
Ⅳ、原型链
1. 先上一个总结图:
- 访问一个对象的属性时,先在自身属性中查找,找到返回,如果没有, 再沿着
__ proto __
这条链向上查找, 找到返回,如果最终没找到, 返回undefined。
function Foo() {
this.test1 = function(){
console.log('test1()');
}
}
Foo.prototype.test2 = function(){
console.log('test2()');
}
var f1 = new Foo()
f1.test1() //test1()
f1.test2() //test2()
console.log(f1.toString()) //[object Object]
console.log(f1.test3) //undefined,他一直寻找到Object.prototype上都没有,再继续通过__proto__寻找test3属性都没有找到
f1.test3() //f1.test3为undefined,所以被当作函数执行的时候会报错,Uncaught TypeError: f1.test3 is not a function
- 首先通过
构造Foo函数
创建的实例对象f1
,他是一个对象。 对象f1
的对象属性自身上有test1属性时候,便使用自身的test1。对象f1
的对象属性自身上没有test2属性时候,便会在构造函数Foo
的原型对象上面寻找test2属性,发现有则会使用Foo.prototype.test2
对象f1
的对象属性自身上和向上的构造函数Foo
的原型对象上都没有test3属性时候,便通过__proto__
继续向上一直寻找(此时的继续寻找是以Foo.prototype为起点,因为构造函数的原型对象也是一个对象,所以Foo.prototype也是有他的__proto__属性的,而他是怎么来的呢? 对象---> 通过new Objecte() 而来的,所以他会一直找到函数Object的显示原型上去
),形成一条隐式原型链
,如果最终都没有找到则返回undefined,防止无限循环。- 可以理解为
原型链
的作用:查找对象的属性(方法) - 结合上图理解上面 4 点。
2. 通过instanceof
来理解剩余部分
instance
是如何判断的?
- 表达式:
A instanceof B
。 - 表达式理解(关键): 如果
B函数的显式原型在A的原型链
上的话(原型链是一个隐式原型,__proto__寻找的过程
),则返回true,否则返回false。 - 通俗理解:
instanceof
字面意思便是: 关键字,实例
的意思,那么表达式就看看作来判断:1.A 是B的实例?
---->2.A是 B 构造函数创建的实例对象
--->3. B.prototype === A.__proto__ , A.__proto__ 非最终,不为null的情况下
。
function Foo() { } f
var f1 = new Foo()
console.log(f1 instanceof Foo) // true f1是由构建函数创建的实例对象
console.log(f1 instanceof Object) // true f1---> Foo --->Object (继续向上寻找的话是,便是Object)
Function
和Object
console.log(Object instanceof Function) // true
console.log(Object instanceof Object) // true
console.log(Function instanceof Function) // true
console.log(Function instanceof Object) // true
function Foo() {}
console.log(Object instanceof Foo) // false
- 在图中我们可以看到
Object created by Function,构造函数 Object 是由Function 创建的
, Object原型链是一个隐式原型(__proto__)
寻找的过程,即Object的显示原型 Object.ptototype会寻找到 Function.prototype
。 - 由上一点在
Object的原型链中 Object.prototype 指向其构造函数的原型对象Object.prototype,那么这个对象(引用类型)也有其隐式原型--->Object.prototype.__proto__是存在的,即使最终为null
。 构造函数Function
的原型对象为显式原型
的指向Function.prototype
,原型对象
是一个对象属性 ,也有其隐式原型
属性,所以存在Function.prototype.__proto__
,返回 true。函数式通过 new 他本身产生的实例
,那么便存在Function.__proto__指向其构造函数Function的 显式原型 Function.prototype,所以返回 true
。- 函数是特殊的对象,而对象不一定是函数,所以Object instanceof Object ---> false。
需要注意的点帮助理解的是:
- 显示原型指向的该 x.prototype 也是一个对象,那么他便是存在
.__proto__
属性的。- 函数是特殊的对象。
- Object created by Function 构造函数 Object() 也是 Function() 创建出来的。
- Function是通过new自己产生的实例,即 Function.proto 指向---> Function.prototype。
Ⅴ、测试题
/* 测试题1 */
function A () {} A.prototype.n = 1
let b = new A()
A.prototype = { n: 2, m: 3}
let c = new A()
console.log(b.n, b.m, c.n, c.m)
// 1 undefined 2 3
function F (){}
Object.prototype.a = function(){
console.log('a()')
}
Function.prototype.b = function(){
console.log('b()')
}
let f = new F()
f.a() //a()
f.b() //f.b is not a function -->找不到
F.a() //a()
F.b() //b()
console.log(f)
console.log(Object.prototype)
console.log(Function.prototype)