🌟 原型与原型链
1. 原型prototype
每个函数都有一个prototype属性,它默认指向一个Object空对象 (即原型对象),里面没有我们的属性;原型中有一个属性constructor,它指向函数对象;构造函数和原型对象相互引用
function Fn(){
console.log("hello");
};
console.log(Fn.prototype); //Object
//验证constructor
console.log(Fn.prototype.constructor === fn); //true
//给原型对象添加属性(一般是方法)---> 实例对象可以访问
Fn.prototype.test = function(){
console.log("test()");
};
//创建实例来测试方法是否添加成功
var fun = new Fun();
fun.test();
2. 显示原型与隐藏原型
每个函数都有一个prototype,可称为显式原型(属性) ,默认指向一个空的Object对象;每个实例对象都有一个__proto__,可称为隐式原型(属性),也默认指向一个空的Object对象
对象的隐式原型的值为其对应构造函数的显式原型的值,在创建实例对象的同时把prototype的值复制给了__prototype__,内部语句:this.__proto__ = Fn.prototype;
function Fn(){ //内部语句:this.prototype = { }; 添加prototype属性
};
var fn = new Fn(); //内部语句:this.__proto = Fn.prototype;
console.log(Fn.prototype === fun.__proto__); //true
//给原型添加test方法
Fn.prototype.test=function(){
console.log("test");
};
fn.test();
//fn先寻找自己本身的__proto__里面是否含有test方法,如果没有,则去和prototype共同指向的空对象Object中找
3. 原型链
访问一个对象的属性时,先在自身属性中查找,找到返回;如果没有,再沿着__proto__这条链想上查找,直到Object构造函数的原型对象(原型链的尽头),如果仍然没有则返回undefined
Object本身是JS中内置的一个构造函数,函数也是一种特殊的对象,所以Object构造函数本身就含有prototype这个显式原型;Fn构造函数的prototype是指向一个Object空对象的,但这个Object空对象实际上是通过Object构造函数new出来的,所以其__proto__属性必然和Object构造函数对象中的prototype属性指向同一个对象。
1) Object构造函数/原型/实例对象的关系
var o1 = new Object();
var o2 = { };
2) 构造函数/原型对象/实例对象的关系
function Foo( ){ };
*其中Function = new Function( );,也就是说Function自己也是Function构造函数的实例对象。
4. 相关问题
(1)函数的显式原型prototype指向的对象默认是空Object,这个空的Object是Object构造函数的实例对象(但Object的prototype并不是Object的实例对象)
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.prototype === Function.__proto__); //true
(3) Object的原型对象是原型链的尽头 (Object的原型对象也是Object的实例对象,但是这个实例对象的__proto__没有值)
console.log(Object.prototype.__proto__); //null
5. 属性问题
查找属性时,先从自身找,没有的话会自动到原型链中查找。
function Fn(){
};
Fn.prototype.a = "xxx";
var fn1 = new Fn();
console.log(fn1.a); //xxx
//1. 读取实例对象的属性值时,会自动到原型链中查找
var fn2 = new Fn();
fn2.a = "yyy";
console.log(fn1.a, fn2.a); //xxx yyy
//2.设置实例对象的属性值时,如果当前对象没有此属性,直接添加此属性并设置其值
var fn2 = new Fn();
console.log(fn2.a); //xxx
// 3. 当实例对象中没有该属性时,会自动到原型链中查找,如果本身有就返回自身的
属性存自身,方法存原型。
function Person(name, age){
this.name = name;
this.age = age;
};
Person.prototype.setName = function(name){
this.name = name;
};
var p1 = new Person("jerry",23);
p1.setName("bob");
6. 探索instanceof
表达式: A(实例对象)instanceof B (构造函数)
如果B函数的prototype对象在A对象的原型链上则返回true,否则返回false
函数Foo的prototype是一个对象,由Object构造函数创建,所以也是Object的一个实例,而Object构造函数的原型对象就是原型链的终点。
console.log(Object instanceof Function); //true
//Object是Function的实例,也就是Object = new Function();
console.log(Object instanceof Object); //true
//Object作为实例对象的时候,其__proto__值和Function的prototype一致,而Function的prototype也是对象,所以它也是Object产生的实例对象
console.log(Function instanceof Function); //true
console.log(Function instanceof Object); //true
function Foo(){};
console.log(Object instanceof Foo); //false
7. 面试题
//测试题1
function A (){
};
A.prototype.n = 1;
var b = new A();
A.prototype = {
n:2,
m:3
};
var c = new A();
console.log(b.n, b.m, c.n, c.m);//1, undefined, 2,3
//测试题2
var F = function(){};
Object.prototype.a = function(){
console.log("a()");
};
Function.prototype.b = function(){
console.log("b()");
};
var f = new F();
f.a();
f.b(); //f.a is not a function
F.a();
F.b();