继承
本来想先说说原型链,但是感觉先说一下继承会更好的理解原型链,那好,废话不多说,开始!!
这里的继承是面向对象里面的范畴;
继承就是让一个类型(可以通过构造函数或者class来定义)的对象能够访问另外一个类型的属性和方法,它是类和类之间的一种关系,通常说子类继承父类。但这里容易出现一个误区:认为实例继承了某个类,某人有响应的属性和方法是因为他继承人类,这种说法是错误。
注明:以下举例比较随意,看个人理解
比如,现在有一个"动物"构造函数
function Animal(){
this.type="动物";
}
要求:怎么才能让"猫"继承"动物"呢?
- 继承的第一种方法:
使用call和apply方法,将父元素的构造函数绑定到子对象上,即在第一行加一个function函数名.call(this);
<script>
//当Animal没有参数的情况
function Animal(){
this.type="动物";
}
//当Animal有参数的情况
function Animal(type){
this.type = type;
}
// 先创建一个Cat的构造函数,会被它的实例对象 cat1 继承
function Cat(){
// 加下面这句的话,cat1就可以访问Animal了
Animal.call(this);
Animal.apply(this);
// 当Animal里面有参数时:
Animal.call(this,"动物");
Animal.apply(this,["动物"]);
//apply和call区别:
// 在Animal没有参数的情况下和上面call一样
// 在Animal有参数的情况下,apply需要把参数放在一个数组里面
this.color="blue"
}
var cat1=new Cat();
console.log(cat1.color); //blue
console.log(cat1.type); // 动物
// 缺点:如果Animal里面有很多属性和方法,而cat1并不想全部要,这样实现起来就出现一些问题
</script>
注意点:
apply和call区别:
在Animal没有参数的情况下,apply和上面call一样
在Animal有参数的情况下,apply需要把参数放在一个数组里面
- 继承的第二种方法:
使用prototype属性,如果“猫”的prototype对象,指向一个Animal的实例,那么所有“猫”的实例就能继承Animal;
function Animal(type){
this.type = type;
}
function Cat(){
this.color = "blue";
}
Cat.prototype = new Animal("动物");
// 注意此时,Cat已经把Animal的属性和方法继承过来
// Cat.prototype.constructor = Cat;
var cat1 = new Cat();
console.log(cat1.type); //动物
console.log(cat1.constructor); //在不手动纠正的情况下,cat1.constructor指向的是Animal
// 问题:Cat 的实例对象 就是cat1,cat1.constructor不是指向的Cat,而是Animal,需要手动矫正一下属性
Cat.prototype.constructor = Cat;
console.log(cat1.constructor); //在手动矫正一下属性之后,在打印的话就是指向Cat
注意点:
将Cat的prototype对象指向一个Animal的实例,会删除原来的prototype的值;
prototype对象都有一个constructor属性,指向它的构造函数;
每一个实例也有一个constructor属性,默认调用prototype对象的constructor属性
最后注意:要重新去定义一下Cat的constructor属性
- 继承的第三种方法:
第三种方法是对第二种的改进,由于Animal对象中,不变的属性都可以写入Animal.prototype。所以,也可以让Cat()跳过Animal(),直接继承Animal.ptototype。
function Animal(type){
this.type = type;
}
Animal.prototype.say=function(){
alert("hello");
}
function Cat(){
this.color = "blue";
}
Cat.prototype = Animal.prototype;
Cat.prototype.constructor = Cat;
var cat1 = new Cat();
cat1.say();
console.log(cat1.type); //undefined
// console.log(cat1.constructor);
// 创建一个 Animal 的实例对象 ani1
var ani1=new Animal("动物");
console.log(ani1.constructor);
注意点:
与前一种比较,这样做的优点是效率比较高(不用执行和建立Animal的实例了),比较省内存。
缺点:Cat.prototype和Animal.prototype现在指向同一个对象,那么任何对Cat.prototype的修改,都会反映到Animal.prototype。
- 继承的第四种方法:
利用一个空对象作为中介(常用方法)
function Animal(type){
this.type = type;
}
Animal.prototype.say=function(){
alert("hello");
}
function Cat(){
this.color = "blue";
}
// Cat.prototype = Animal.prototype;
// Cat.prototype.constructor = Cat;
var F=function(){};
F.prototype=Animal.prototype;
// F的原型对象里面的方法是animal的全部的方法
// 让Cat的原型对象直接指向 F 的实例对象,会把F里面的方法拿过来
Cat.prototype=new F();
Cat.prototype.constructor = Cat;
var cat1 = new Cat();
cat1.say();
console.log(cat1.constructor);
注意点:
F是空对象,所以几乎不占内存。这时,修改了Cat的prototype对象,就不会影响Animal的prototype对象
- 继承的第五种方法:
拷贝继承
function Animal(type){
this.type = type;
}
Animal.prototype.say=function(){
alert("hello");
}
function Cat(){
this.color = "blue";
}
function extend(par,child) {
var par_prototype=par.prototype;
var child_prototype=child.prototype;
for(x in par_prototype){
child_prototype[x]=par_prototype[x];
}
}
extend(Animal,Cat);
var cat1 = new Cat();
cat1.say();
console.log(cat1.constructor);
是什么原因导致通过改变prototype的指向能够完成这样一个继承呢?
所以下面说说原型链吧
原型链
1、普通函数和函数对象
在JavaScript中,一切皆对象,但是对象也有区别的,分为普通对象和函数对象,Function是js自带的函数对象
function f1(){};
var f2=function(){};
var f3=new Function('str','console.log(str)');
var o3=new f1();
var o1={};
var o2=new Object();
console.log(typeof Object);//function
console.log(typeof Function);//function
console.log(typeof o1);//object
console.log(typeof o2);//object
console.log(typeof o3);//object
console.log(typeof f1);//function
console.log(typeof f2);//function
console.log(typeof f3);//function
o1、o2、o3 为普通对象
f1、f2、f3 为函数对象
怎么区分呢?
凡是通过new function()创建的对象都是函数对象,其他的都是普通对象。
f1/f2/f3归根结底都是通过new Function()的方式进行创建的
Function Object 也都是通过new Function()创建的
在JavaScript中,每当定义一个对象(函数)的时候,对象中都会包含一些预定义的属性。其中函数对象的一个属性就是原型对象 prototype。注意:普通对象没有prototype,但是有 _ proto _ 属性 。
2、原型对象
原型对象其实就是普通对象(function、object除外,它是函数对象,但是它很特殊,它没有prototype属性(前面说到函数对象都有prototype属性))。看下面这个案例:
function f1() {};
console.log(f1.prototype);
console.log(typeof f1.prototype);
console.log(typeof Object.prototype);
console.log(typeof Function.prototype.prototype);
从这句console.log(f1.prototype);的输出结果来看,f1.prototype就是f1的实例对象。就是在f1创建的时候,创建一个它的实例对象并赋值给它的prototype,基本过程如下:
var temp=new f1();
f1.prototype=temp;
// 那么Function也就迎刃而解:
var temp1=new Function();
Function.prototype=temp1;
那原型对象是用来作甚的呢?主要是用于继承。举例说明:
var person=function(name){
this.name=name;
}
person.prototype.getName=function(){
return this.name;
}
var veb=new person('fqniu');
veb.getName();
从这个例子中可以看出,通过给person.prototype设置了一个函数对象的属性,那有person实例(例子中的:veb)出来的普通对象就继承了这个属性。具体怎么实现的继承,就要看下面的原型链了。
3、原型链
js在创建对象(不论是普通对象还是函数对象)的时候,都有一个叫做_proto_ 的内置属性,用于指向创建它的函数对象的原型对象prototype
console.log(veb.__proto__ === person.prototype);//true
// 同样,person.prototype对象也有_proto_属性,它指向创建它的函数对象(object)的prototype
console.log(person.prototype.__proto__ === Object.prototype);//true
// 继续,object.prototype对象也有_proto_属性,但它比较特殊,为null
console.log(Object.prototype.__proto__);//null
然后把这个有_proto_ 串起来的直到Object.prototype. _ proto _ 为null的链叫做原型链。它会一直往上找
(_ proto _),知道为null为止,如下:
veb(_ proto _)——> person.prototype( _ proto _)——>Object.prototype( _ proto _)——>null
例如如下图:
那先说到这里,还需要进一步整理和消化才行!!!