JS中关于原型对象与原型链的理解!

时间:2023-01-07 01:02:52

1、首先我们先来看一张图

 

 

JS中关于原型对象与原型链的理解!

prototype

每个函数都有一个 prototype 属性
每一个JavaScript对象(null除外)在创建的时候就会与之关联另一个对象,这个对象就是我们所说的原型,每一个对象都会从原型"继承"属性。

demo:

function Person() {

}
// 虽然写在注释里,但是你要注意:
// prototype是函数才会有的属性
Person.prototype.name = 'Kevin';
var person1 = new Person();
var person2 = new Person();
console.log(person1.name) // Kevin
console.log(person2.name) // Kevin

构造函数Person可以通过prototype属性访问到它的原型对象

function Person() {

}
// 虽然写在注释里,但是你要注意:
// prototype是函数才会有的属性
console.log(Person.prototype)

通过构造函数Preson实例化出来的person可以通过__proto__属性访问到Man的原型对象

function Person() {

}
var person = new Person();
console.log(person.__proto__ === Person.prototype); // true

Person的原型对象可以通过constructor(构造器)属性访问其关联的构造函数

function Person() {

}
console.log(Person === Person.prototype.constructor); // true

so 我们可以通过三种方式来访问原型对象

1.构造函数.prototype

2.实例对象.__proto__

3.object.getPrototypeOf(实例对象)

 


 

原型链的访问规则

对象在访问属性或方法时,先检查自己的实例,如果存在就直接使用。如果不存在那么就去原型对象上去找,存在就直接使用,如果没有就顺着原型链一直往上查找,找到即使用,找不到就重复该过程直到原型链的顶端,如果还没有找到相应的属性或方法,就返回undefined,报错

function Person() {

}

Person.prototype.name = 'Kevin';

var person = new Person();

person.name = 'Daisy';
console.log(person.name) // Daisy

delete person.name;
console.log(person.name) // Kevin

在这个例子中,我们给实例对象 person 添加了 name 属性,当我们打印 person.name 的时候,结果自然为 Daisy。

但是当我们删除了 person 的 name 属性时,读取 person.name,从 person 对象中找不到 name 属性就会从 person 的原型也就是 person.proto ,也就是 Person.prototype中查找,幸运的是我们找到了 name 属性,结果为 Kevin。

 

原型与原型(看头部大图理解)

var obj = new Object();
obj.name = 'Kevin'
console.log(obj.name) // Kevin

console.log(Object.prototype.__proto__ === null) // true

JavaScript 默认并不会复制对象的属性,相反,JavaScript 只是在两个对象之间创建一个关联,这样,一个对象就可以通过委托访问另一个对象的属性和函数,所以与其叫继承,委托的说法反而更准确些

 


三种检验方法

Object.getPrototypeOf方法用于获取指定实例对象的原型对象。

//01 声明构造函数F
function F() {}

//02 使用构造函数F获取实例对象f
var f = new F();

//03 测试getPrototypeOf方法的使用
console.log(Object.getPrototypeOf(f));  //打印的结果为一个对象,该对象是F相关联的原型对象
console.log(Object.getPrototypeOf(f) === F.prototype);  //true
console.log(Object.getPrototypeOf(f) === f.__proto__);  //true

 

isPrototypeOf方法用于检查某对象是否在指定对象的原型链中。

function Demo(){}
var demo = new Demo();

console.log(Demo.prototype.isPrototypeOf(demo)); // true
console.log(Object.prototype.isPrototypeOf(demo)); //true
console.log(Object.prototype === person.__proto__.__proto__) //true

instanceof运算符的作用跟isPrototypeOf方法类似,左操作数是待检测的实例对象,右操作数是用于检测的构造函数。如果右操作数指定构造函数的原型对象在左操作数实例对象的原型链上面,则返回结果true,否则返回结果false。

// 1 声明构造函数Demo
function Demo(){};
// 2 获取实例化对象demo
var demo = new Demo();
// 3 使用isPrototypeOf
console.log(demo instanceof Demo);     //true
console.log(demo instanceof Object);   //true
// 4 Object构造函数的原型对象在Function这个实例对象的原型链中
console.log(Function instanceof Object); //true
console.log(Object.prototype === Function.__proto__.__proto__); //true
// 5 Function构造函数的原型对象在Object这个实例对象的原型链中
console.log(Object instanceof Function); //true
console.log(Function.prototype === Object.__proto__); //true

注意:不要错误的认为instanceof检查的是该实例对象是否从当前构造函数实例化创建的,其实它检查的是实例对象是否从当前指定构造函数的原型对象继承属性。

 

//01 声明构造函数Person
function Person() {}

//02 获取实例化对象p
var p1 = new Person();

//03 测试isPrototypeOf的使用
console.log(p1 instanceof Person);   //true

//04 替换Person默认的原型对象
Person.prototype = {
    constructor:Person,
    showInfo:function () {
        console.log("xxx");
    }
};

//05 重置了构造函数原型对象之后,因为Person
console.log(p1 instanceof Person); //false

//06 在Person构造函数重置了原型对象后重新创建实例化对象
var p2 = new Person();
console.log(p2 instanceof Person);   //true

//==> 建议开发中,总是先设置构造函数的原型对象,之后在创建实例化对象

原型链相关的继承

原型式继承基本写法

//01 提供超类型|父类型构造函数
function SuperClass() {}

//02 设置父类型的原型属性和原型方法
SuperClass.prototype.info = 'SuperClass的信息';
SuperClass.prototype.showInfo = function () {
    console.log(this.info);
};

//03 提供子类型
function SubClass() {}

//04 设置继承(原型对象继承)
SubClass.prototype = SuperClass.prototype;
SubClass.prototype.constructor = SubClass;

var sub = new SubClass();
console.log(sub.info);          //SuperClass的信息
sub.showInfo();                 //SuperClass的信息

原型链继承

实现思想

核心:把父类的实例对象设置为子类的原型对象 SubClass.prototype = new SuperClass();
问题:无法为父构造函数(SuperClass)传递参数

//01 提供超类型|父类型
function SuperClass() {
    this.name = 'SuperClass的名称';
    this.showName = function () {
        console.log(this.name);
    }
}

//02 设置父类型的原型属性和原型方法
SuperClass.prototype.info = 'SuperClass的信息';
SuperClass.prototype.showInfo = function () {
    console.log(this.info);
};

//03 提供子类型
function SubClass() {}

//04 设置继承(原型对象继承)
var sup = new SuperClass();
SubClass.prototype = sup;
SubClass.prototype.constructor = SubClass;

var sub = new SubClass();
console.log(sub.name);          //SuperClass的名称
console.log(sub.info);          //SuperClass的信息
sub.showInfo();                 //SuperClass的信息
sub.showName();                 //SuperClass的名称

组合继承

实现思想

① 使用原型链实现对原型属性和方法的继承
② 通过伪造(冒充)构造函数来实现对实例成员的继承,并且解决了父构造函数传参问题

//01 提供超类型|父类型
function SuperClass(name) {
    this.name = name;
    this.showName = function () {
        console.log(this.name);
    }
}

//02 设置父类型的原型属性和原型方法
SuperClass.prototype.info = 'SuperClass的信息';
SuperClass.prototype.showInfo = function () {
    console.log(this.info);
};

//03 提供子类型
function SubClass(name) {
    SuperClass.call(this,name);
}

//(1)获取父构造函数的实例成员  Person.call(this,name);
//(2)获取父构造函数的原型成员  SubClass.prototype = SuperClass.prototype;
SubClass.prototype = SuperClass.prototype;
SubClass.prototype.constructor = SubClass;

var sub_one = new SubClass("zhangsan");
var sub_two = new SubClass("lisi");
console.log(sub_one);
console.log(sub_two);

 

长风破浪会有时,直挂云帆济沧海