版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_36091581/article/details/79128047
浅谈JS中原型链和继承
最近有很多面试官提到JS中原型链和继承的关系,面试者答得云里雾里有点含糊。那我们来好好理一下什么是原型链,什么是原型链继承。
原型链是什么?所谓的原型链是一个有限的实例对象和原型之间组成的有限链,就是用来实现共享属性和继承的。简单的一句话就是构造函数一层一层往父级找直到找到null为止的一条链。
其实,所有的函数都是 Function 的实例。在构造函数上都有一个原型属性 prototype,该属性也是一个对象;那么在原型对象上有一个 constructor 属性,该属性指向的就是构造函数;而每个实例对象上有一个 proto 属性,该属性也指向原型对象,并且该属性不是标准属性,不可以用在编程中,该属性用于浏览器内部使用。
我们举个简单的例子来理解一下:
var obj = new Object();
// 对象是有原型对象的
// 原型对象也有原型对象
obj._proto_._proto_._proto_
// 原型对象也有原型对象,对象的原型对象一直往上找,会找到一个null
// 原型链示例
var arr = [];
arr -> Array.prototype ->Object.prototype -> null
var o = new Object();
o -> Object.prototype -> null;
构造函数、原型和实例之间的关系
①+Object
上图可见:foo是一个构造函数(每个构造函数上都有一个原型属性 prototype),它有两个实例对象,分别是foo2和foo1,在每个实例对象上都有_proto_
属性,info的是实例对象上自定义的属性。foo的原型对象是foo.prototype,它有一个constructor属性(constructor指向的是它的构造函数)。以此类推可知object的原型对象和构造函数。
②+Function+Object+Array
再举个例子:
function Person(){
this.name="bob" //这是一个实例属性
}
Person.prototype.eat=function(){ //给对象的原型对象添加一个eat的方法,接下来,new的实例会共享这个方法
return "food";
}
var p1=new Person(); //这里究竟发生生了什么?
p1.eat()//->food
var p2=new Person();
p2.eat()//->food,所以只要是Person的对象,他们都会共享原型对象的方法,当然,p1.name也会共享Person的实例属性,因为p1是Person的一个实例
function Person(){
this.name="bob" ;
}
Person.prototype.eat=function(){
return "food";
}
function Student(){}
Student.prototype=new Person();
Student.prototype={
run:function(){
return "run";
}
};
var one=new Student();
console.log(one.eat());//Uncaught TypeError: undefined is not a function
在上面的代码中,把Person的实例赋给Student的原型,接下来又把原型改写成另一个对象字面量,现在原型包含的是Object实例,不再是Person实例,因此原型链已经被切断了,也就是说Student和Person没关系了。
继承
1.原型继承
function Animal(name){
this.name = name;
}
function Tiger(color){
this.color = color;
}
// var tiger = new Tiger('yellow');
// console.log(tiger.color);
// console.log(tiger.name); //undefined
// Tiger.prototype = new Animal('老虎'); //一种方式
Object.prototype.name = '大老虎'; //第二种方式
var tiger = new Tiger('yellow');
console.log(tiger.color); // log 'yellow'
console.log(tiger.name); // log '大老虎'
2.拷贝继承
function Animal(name){
this.name = name;
}
function Mouse(nickname){
Animal.call(this,'老鼠');
this.nickname = nickname;
}
var m = new Mouse('杰瑞');
console.log(m.name); // log '老鼠'
console.log(m.nickname);// log '杰瑞'
总结
1、原型链
1)构造函数、原型和实例的关系
①构造函数都有一个属性prototype,这个属性是一个对象(Object的实例)
②每个原型对象prototype里面有一个constructor属性,该属性指向原型对象所属的构造函数
③实例对象都有一个_proto_属性,该属性也指向构造函数的原型对象,它是一个非标准属性,不可以用于编程,它是用于浏览器自己使用的
2)prototype与_proto_的关系
①prototype是构造函数的属性
②_proto_是实例对象的属性
——这两者都指向同一个对象
【总结】i)函数也是对象,对象不一定是函数;
ii)对象的本质:无序的键值对集合;键值对当中的值可以是任意数据类型的值
iii)对象就是一个容器,这个容器当中放的是(属性和方法)
3)属性搜索
①在访问对象的某个成员的时候会先在对象中找是否存在
②如果当前对象中没有就在构造函数的原型对象中找
③如果原型对象中没有找到就到原型对象的原型上找
④知道Object的原型对象的原型是null为止
2、Function
——所有函数都是Function的实例
①本地对象:独立于宿主环境(浏览器)的对象——包括Object、Array、Date、RegExp、Function、Error、Number、String、Boolean
②内置对象——包括Math、Global(window,在js中就是全局变量),使用的时候不需要new
③宿主对象——包括自定义对象、DOM、BOM