原型、原型链、继承
# 1、原型、原型链
什么是原型(prototype)?
# 1. 先了解构造函数!
function Person(name){//constructor
this.name=name;
this.getName=function(){
console.log(this.name);
}
}
Person.prototype.type="人类"
var dongfang=new Person("dongfang");//实例化
var xiaobao=new Person("xiaobao");//实例化
2
3
4
5
6
7
8
9
- 首字母大写,区分普通函数
- 内部使用this对象,指向即将实例化的对象
- 使用new来实例对象
试想一下,如果每执行一次构造函数,就要创建一个新的对象,所有新的对象的方法还都是相同的,造成了大量的空间占用。
所以要把这个方法放在一个单独的地方,让所有实例都可以访问。这就是原型(prototype)
# 2. 原型
在javascript中,每当定义一个函数数据类型(函数对象,函数),都会天生带一个prototype属性,这个属性指向函数的原型对象,并且这个属性是一个对象数据类型(对象)。
原型对象就相当于一个公共区域,所有实例都访问这个原型对象
function Person(){
this.name="dongfang";
}
Person.prototype.age="18"; //通过prototype设置构造函数属性
Person.prototype.getName=function(){
console.log(this.name);
}
Person.prototype.getAge=function(){
console.log(this.age);
}
var dongfang= new Person();
dongfang.getName();
dongfang.getAge();
2
3
4
5
6
7
8
9
10
11
12
13
在构造函数内声明属性与用prototype声明属性有什么不同。
//构造函数内声明属性
function DOG(name){
this.name = name;
this.sex = "公狗";
}
var dogA= new DOG("端午");
var dogB= new DOG("糯米");
dogB.sex="母狗";
console.log(dogA.sex); //公狗
console.log(dogB.sex); //母狗
//使用prototype声明属性
function DOG(name){
this.name = name;
}
DOG.prototype.sex="公狗";
var dogA=new DOG("端午");
var dogB=new DOG("糯米");
console.log(dogA.sex); //公狗
console.log(dogA.sex); //公狗
DOG.prototype.sex="母狗";
console.log(dogA.sex); //母狗
console.log(dogA.sex); //母狗
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
总结:
所有实例对象都需要共享的属性和方法,都放在这个prototype里,不需要共享的属性和方法,放在构造函数里面
放在prototype里的属性和方法节省了大量内存的开支。
# 3. 原型链
__proto__和constructor
每一个对象数据类型(),还天生自带一个__proto__属性,该值指向当前实例所属类的原型(prototype)。原型对象中有一个属性constructor(构造器)指向函数对象
每个对象都有 __proto__ 属性,但只有函数对象才有 prototype 属性
function Person(){};
var person = new Person();
console.log(person.__proto__ === Person.prototype); //true
console.log(Person.prototype.constructor === Person); //true
//es5获得对象原型的方法
console.log(Object.getPrototypeOf(person) == Person.prototype) //true
2
3
4
5
6
7
8
# 那么到底什么是原型链?
js中万物都是对象,对象与对象间也有关系。js中是通过prototype对象指向父类对象,直到指向Object为止,形成了一条原型的指向链条,就叫做原型链
打个比方:person->Person->Object 普通人继承人类,人类继承对象
当我们访问对象的一个属性或方法时,他会先在对象自身中寻找,如果有直接使用,如果没有就去原型中寻找,如果还找不到去原型的原型中寻找,最后找到Object对象的原型。但是Object对象的原型没有原型!!!如果Object的原型依然找不到,就返回null
Object是JS所有对象数据类型的基类,在Object.prototype上没有__proto__这个属性
console.log(Object.prototype.__proto__ === null); //true
var A = function(){};
var a = new A();
console.log(a.__proto__); //A {}(即构造器function A 的原型对象)
console.log(a.__proto__.__proto__); //Object {}(即构造器function Object 的原型对象)
console.log( Object.prototype); //Object的原型对象
console.log(a.__proto__.__proto__.__proto__); //null
var arr=new Array();
console.log(arr.__proto__); //所有数组方法
console.log(Array.prototype);
var str=new String();
console.log(str.__proto__); //所有字符串方法
console.log(String.prototype);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 思考题:
person1.__proto__
是什么?Person.__proto__
是什么?Person.prototype.__proto__
是什么?Object.__proto__
是什么?Object.prototype.__proto__
是什么?
第一题:
因为 person1.__proto__ === person1 的构造函数.prototype
因为 person1的构造函数 === Person
所以 person1.__proto__ === Person.prototype
第二题:
因为 Person.__proto__ === Person的构造函数.prototype
因为 Person的构造函数 === Function
所以 Person.__proto__ === Function.prototype
第三题:
Person.prototype
是一个普通对象,我们无需关注它有哪些属性,只要记住它是一个普通对象。
因为一个普通对象的构造函数 === Object
所以 Person.prototype.__proto__ === Object.prototype
第四题,参照第二题,因为 Person 和 Object 一样都是构造函数
第五题:
Object.prototype
对象也有proto属性,但它比较特殊,为 null 。因为 null 处于原型链的顶端,这个只能记住。
Object.prototype.__proto__ === null
总结:
- 原型prototype,是函数对象独有的,存储实例对象共享的属性和方法的对象(节省内存开支)
- _proto_,是所有对象都有的,指向当前实例所属类的原型对象的属性
- constructor,是构造函数中的属性与方法
- js中是通过原型prototype对象指向父类对象,直到指向Object为止,形成了一条原型的指向链条就是原型链
# 2、继承
js中继承就是获取父类中已有属性和方法的方式
function Person(name,age){ //父类constructor
this.name=name;
this.age=age;
this.sex="man";
this.getName=function(){
console.log(this.name);
}
}
Person.prototype.message="原型";
var dongfang=new Person();
var person=new Person("dongfang");
2
3
4
5
6
7
8
9
10
11
12
构造函数继承
function Student(name){ //利用call,apply方法在创建对象时使用父类构造函数创建实例 Person.call(this,name,age); //使用call方法继承 Person.apply(this,[name]); //使用apply方法继承 } var stu=new Student("dongfang"); stu.getName(); console.log(stu.sex); //缺点,获取不到构造函数原型上的属性与方法 console.log(stu.message); //undefined console.log(person.message); //原型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16原型链继承
function Student(name){ this.name=name; //需要把实参与即将实例化的实例属性对应 }; var dongfang=new Person(); Student.prototype = dongfang; //把父类实例化(父类实例)传递给新构造函数(子类)的原型,因为实例化中包含constructor里的全部属性方法,和prototype里面的全部属性方法 var stu=new Student("dongfang"); console.log(stu.name); undefined stu.getName(); //可以获取构造函数原型上的属性与方法 console.log(stu.message); //原型 console.log(person.message); //原型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15