原型链和基于原型链的继承
原型
要了解原型链,我们得先来了解原型
每个函数(构造)被创建时,系统会自动为这个构造函数创建并关联一个空的对象,这个空的对象就是原型;
原型的作用
原型的属性和方法可以被与它关联的构造函数创建出来的所有对象共享;
原型的访问方式
构造函数名.prototype可以直接访问到原型,也可以利用对象.proto属性访问
function Person(name, age){
this.name = name;
this.age = age;
}
Person.prototype.gender = "male";
Person.prototype.sayHello = function(){
console.log("Sad to meet you!");
};
// console.log(Person.prototype);
var p = new Person("黎明", 18);
console.log(p.gender);
p.sayHello();
var p1 = new Person("周杰伦", 9);
console.log(p1.gender);
p1.sayHello();
console.log(p.sayHello === p1.sayHello);
原型的使用方式
可以利用对象的动态特性,给原型对象添加成员
Person.prototype.sayHello = function(){
console.log("How old are you!");
}
var p = new Person();
p.sayHello();
也可以直接给构造函数.prototype赋值一个新的对象
Person.prototype = {
sayHello: function(){
console.log("How old are you!");
}
}
var p = new Person();
p.sayHello();
proto属性
和构造函数.prototype一样可以访问到原型(低版本IE不支持,看情况使用)
var p = new Person();
console.log(Person.prototype);
console.log(p.__proto__);
console.log(Person.prototype === p.__proto__);
p.__proto__.gender = "male"; //只限调试的时候使用这个属性
console.log(p.gender);
constructor属性
系统自己创建的原型当中,会有一个constructor属性,指向和这个原型相关联的构造函数,当使用 Person.prototype 直接赋值一个新的对象的时候,系统不会自动为这个对象新增constructor属性指向构造函数,所以,一般情况下我们会手动为这个对象新增一个constructor属性指向构造函数, 以维护原型三角关系的合理性
function Person() {}
console.log(Person.prototype.constructor);
// 会指向Person
由上面的例子,我们可以得出一个三角关系图
原型链
什么是原型链?
当谈到继承时,Javascript 只有一种结构:对象。每个对象都有一个内部链接到另一个对象,称为它的原型 prototype。该原型对象有自己的原型,等等,直到达到一个以null为原型的对象。根据定义,null没有原型,并且作为这个原型链 prototype chain中的最终链接。其实说的通俗一点,对象都有原型,原型也是对象,所以原型也有原型,这样就形成了原型链!
复杂的原型链设计
function Human(){
this.name = "";
this.age = 18;
}
var h = new Human();
function Man(){
this.girlFriend = "";
}
Man.prototype = h;
Man.prototype.constructor = Man;
var m = new Man();
function Student(){
this.stuNo = 9527;
}
Student.prototype = m;
Student.prototype.constructor = Student;
var s = new Student();
function BadStudent(){
this.sleep = function(){
}
}
BadStudent.prototype = s;
BadStudent.prototype.constructor = BadStudent;
var badS = new BadStudent();
函数的原型链
完整的原型链 (原型链的顶层父级是null)
继承
1. 混入式继承
var obj = {};
var obj1 = {
name: "",
age: 18
}
for(var k in obj1){
obj[k] = obj1[k];
}
2. 原型继承 (通过原型实现的继承就是原型)
直接将要继承的对象作为构造函数的原型存在
function Person(){
}
var human = {
name: "",
age: 18
}
Person.prototype = human;
var p = new Person();
var p1 = new Person();
// 通过混入的方式将要继承的对象中的所有的成员加到原本的原型上去
function Person(){
}
var human = {
name: "",
age: 18
}
for(var k in human){
Person.prototype[k] = human[k];
}
var p = new Person();
var p1 = new Person();
function extend(son, parent){
for(var k in parent){
son[k] = parent[k];
}
}
3. 经典继承(对原型继承的优化)
function Person(){
}
Person.prototype = {}; //原型继承
var p = new Person();
//需求: 创建一个对象,使用原型继承的方式继承自另外一个对象
var human = {
name: "",
age: 18
}
//创建一个p对象,p对象的原型就是参数中的human对象
var p = Object.create(human);
// Object.create 有兼容性问题的
4. 构造函数伪装实现继承
function Person(name,age){
this.name = name;
this.age = age;
}
function Student (){
Person.call(this,name,age);
//这里的this是new出来的Student类型对象
//通过欺骗构造函数实现继承
}
Object.prototype中的成员介绍
- constructor属性 指向Object构造函数
- hasOwnProperty方法 判断当前对象本身是否拥有某个属性
注意和in关键字进行区分
var obj = {
name: ""
};
console.log(obj.hasOwnProperty("name"));
- isPrototypeOf 判断当前对象是不是另外一个对象的原型
var son = {};
son.__proto__ = obj;
console.log(obj.isPrototypeOf(son));
propertyIsEnumerable 首先判断指定的属性是否属于对象本身,然后再判断当前对象指定的属性是否可以被for-in遍历,只有两个条件同时满足才返回true
toLocaleString toString 将对象转换成字符串
valueOf 获取对象的值
值类型和引用类型参与运算的时候,会先调用valueOf方法,如果返回值可以参与运算,则直接得出结果,如果返回值不能参与运算,则继续调用toString方法,获取返回值进行运算