一、原型
先看一段代码
function Fn () {} // 1
console.log(Fn.prototype)
运行完第一行代码之后,会生成一个叫Fn的构造函数,而这个构造函数中有一个prototype属性,指向的就是这个函数的原型对象。
如下图所示:
函数Fn有一个prototype属性,这个属性就指向它的原型对象即Fn.prototype,原型对象又有一个constructor构造器,这个构造器指向Fn函数本身。
原型对象里面存放着所有实例对象需要共享的属性和方法!
Fn函数的实例化:
let fn = new Fn()
通过new操作符就可以实例化一个fn对象,来看一下他们之间的对应关系。
使用new这个关键字的时候,JS编译器会做四件事情:
1、创建一个新的空的对象
2、把这个对象链接到原型对象上,将构造函数的作用域赋给新对象(因此this就指向了这个新对象)
3、执行构造函数中的代码(为这个新对象添加属性)
4、如果这个函数有返回值,则返回;否则,就会默认返回新对象
实例对象fn会有一个__proto__属性,它指向构造函数Fn的原型。
上面我们看到了一个Fn.protottype里面有一个__proto__属性,那我们来看看它的指向吧
从图可以看到,Fn.prototype.__proto__指向Object.prototype,这就说明每个函数的原型都是Object的实例,也就是说每个函数的原型都是通过 new Object() 产生的。
上面的构造函数和原型对象之间的关系汇成的了一条链。
二、原型链
1、原型链(图解)
* 访问一个对象的属性时,
* 先在自身的属性中查找,找到返回
* 如果没有,再沿着__proto__这条链向上查找,找到返回
* 如果最终没找到,返回 undefined
* 别名:隐式原型链
* 作用:查找对象的属性(方法)
2、构造函数/原型/试题对象的关系(图解)
3、构造函数/原型/试题对象的关系2(图解)
原型链 实际上通过隐式原型链来查找 原型链是来查找对象的属性的,非变量属性。
在上面的查找过程,我们提到了最顶层的原型对象,这个对象就是Object.prototype,这个对象中保存了最常用的方法,如toString、valueOf、hasOwnProperty等,因此我们才能在任何对象中使用这些方法。
1、函数的显式原型指向的对象默认是空 Object实例对象(但 Object 不满足)
function Fn() {}
console.log(Fn.prototype instanceof Object) // true
console.log(Object.prototype instanceof Object) // false
console.log(Function.prototype instanceof Object) // true
2、所有的函数都是 function 的实例包含(Function)
console.log(Function.__proto__ === Function.prototype) // true
3、Object的原型对象是原型链的尽头
console.log(Object.prototype.__proto__) // null
三、原型链的属性问题
1、读取对象的属性值时,会自动到原型链中查找
2、设置对象的属性值时,不会查找原型链,若当前对象中无此属性,直接添加该属性并设置值。
3、方法一般定义在原型中,属性一般通过构造函数定义在对象本身上。
function Fn(){}
Fn.prototype.a = 'xxx'
var fn1 = new Fn()
console.log(fn1.a,fn1)
var fn2 = new Fn()
fn2.a = 'yyy'
console.log(fn1.a,fn2.a,fn2)
四、探索 instanceOf
function Foo(){}
var f1 = new Foo()
console.log(f1 instanceof Foo) // true
console.log(f1 instanceof Object) // true
1、instanceof是如何判断的?
* 表达式 A instanceof B
* 如果函数 B的显式原型对象在 A 的隐式原型链上,返回 true,否则返回 false。
2、Function是通过 new自己产生的实例
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
五、原型链相关面试题
测试题1:
Foo.prototype.n = 1
var f1 = new Foo()
Foo.prototype = {
n:2,
m:3
}
var f2 = new Foo()
console.log(f1.n,f1.m)
console.log(f2.n,f2.m)
测试题2:
var F = function F(){}
Object.prototype.a = function(){
console.info('a()')
}
Function.prototype.b = function(){
console.info('b()')
}
var f = new F()
f.a()
f.b()
F.a()
F.b()
三、总结
(一)原型函数
1、函数的 prototype 属性
* 每个函数都有一个 prototype 属性,默认指向一个 Object 空对象(即:原型对象)。
* 原型对象中有一个 constructor 属性,它指向函数对象。
2、给原型对象添加属性(一般都是方法)
* 作用:函数的所有实例对象自动拥有原型中的属性(方法)
console.log(Date.prototype, typeof Date.prototype)
function fun(){}
fun.prototype.test = function (){
console.log('test()')
}
console.log(fun.prototype)
console.log(Date.prototype.constructor === Date)
console.log(fun.prototype.constructor === fun)
(二)显式原型和隐式原型
1、每个函数都有一个 prototype 属性,即显式原型,指向其函数的原型对象。
2、每个实例对象会有一个 __proto__ 属性, 即隐式原型,指向对象的原型对象
3、对象的隐式原型的值与其对应的构造函数的显式原型值相等。
function Fn(){ // 内部语句
// this.prototype = {}
}
console.log(Fn.prototype)
var fn1 = new Fn()
console.log(fn1.__proto__)
console.log(Fn.prototype === fn1.__proto__)
1、Object是作为众多new出来的实例的基类 function Object(){ [ native code ] }
2、Function是作为众多function出来的函数的基类 function Function(){ [ native code ] }
3、构造函数的proto(包括Function.prototype和Object.prototype)都指向Function.prototype
4、原型对象的proto都指向Object.prototype (每个函数的原型都是 Object 的实例)
5、Object.prototype.proto指向null