JS:原型和原型链

一、原型

原型:每当定义一个对象(函数也是对象)时,就会生成一个__proto__属性,被称为隐式原型;这个__proto__属性指向的是这个对象的构造函数的prototype,被称为显式原型。每一个对象都会从原型中“继承”属性。

首先看一个例子,创建一个Student类,并创建类的实例对象student:

class Student{
    constructor(name, score) {
        this.name = name;
        this.score = score;
    }

    introduce() {
        console.log(`我是${this.name},考了${this.score}分。`)
    }
}

const student = new Student('张三', 99)
console.log('student', student); // student Student { name: '张三', score: 99}
student.introduce(); // 我是张三,考了99分。

控制台能访问到属性和方法。

JS:原型和原型链_第1张图片

但是直接在控制台输入student,会发现只有name和score属性,没有introduce方法,但是有一个[[Prototype]]属性,用两个中括号括起来。

JS:原型和原型链_第2张图片

把[[Prototype]]展开,会发现introduce方法在[[Prototype]]中。

JS:原型和原型链_第3张图片

[[Prototype]]属性被称为student对象的隐式原型。当我们要去找一个对象的属性或者方法时,如果在当前对象上找不到,就会去当前对象的隐式原型[[Prototype]]属性上去找。

可通过.__proto__属性访问原型,注意__proto__两边分别有两条下划线。

JS:原型和原型链_第4张图片

而Student()构造函数也有一个prototype属性,Student()构造函数的prototype属性实际上就等于student对象的__proto__属性。

JS:原型和原型链_第5张图片

下面用一张图来说明:

JS:原型和原型链_第6张图片

因此,构造函数的prototype属性就等于实例对象的__proto__属性,构造函数的prototype属性被称为显式原型,实例对象的__proto__属性被称为隐式原型


二、原型链

原型链:当访问一个对象的属性或方法时,首先对象会从自身去找,如果找不到,就会往原型中去找,即__proto__,也就是它构造函数的prototype中;如果原型中找不到,即构造函数中也没有该属性,因为构造函数也是对象,也有__proto__,就会往原型的原型上去找,这样就形成了链式的结构,称为原型链,本质描述的是对象的一种继承关系。

再来看一个例子,创建一个Person类,再创建一个Teacher类继承自Person类,并创建实例对象teacher:

class Person {
    constructor(name) {
        this.name = name;
    }

    drink(){
        console.log('喝水');
    }
}

class Teacher extends Person {
    constructor(name, subject) {
        super(name);
        this.subject = subject;
    }

    teach() {
        console.log(`我是${this.name}, 教${this.subject}`)
    }
}

const teacher = new Teacher('哈默', '前端开发')
console.log('teacher', teacher);
teacher.teach();
teacher.drink();

控制台输出如下,teacher可以执行teach()和drink()方法。

JS:原型和原型链_第7张图片

展开teacher对象,发现找不到这两个方法,于是去找对象的原型,即__proto__属性,找到teach()方法,再展开下一层__proto__属性,找到drink()方法。

JS:原型和原型链_第8张图片

下面用一张图说明:

JS:原型和原型链_第9张图片

可以看到,teacher实例对象本身是没有teach()方法的,这时就会去teacher对象的__proto__隐式原型指向的Teacher.prototype显式原型上去找,此时找到了teach()方法并执行;同时,Teacher.prototype上仍然没有找到drink()方法,而Teacher.prototype也是一个对象,有自己的__proto__隐式原型,那么就去Teacher.prototype.__proto__上去找,Teacher.prototype.__proto__会指向Person()构造函数的显式原型Person.prototype,此时找到了drink()方法并执行,这就是原型链

注:

(1)通过__proto__形成原型链而非protrotype。

(2)__proto__属性是对象所独有的。

(3)prototype属性是函数所独有的。但是由于JS中函数也是一种对象,所以函数也拥有__proto__属性。


三、判断对象自身是否有某属性或方法

hasOwnProperty()方法会返回一个布尔值,用于判断对象自身是否有某属性或方法。返回true,代表是该对象自身的属性或方法;返回false,代表是该对象原型上的属性或方法。

JS:原型和原型链_第10张图片

由于Person类继承自Object类,那么执行teacher.hasOwnProperty()方法时,实际会找到Object.prototype中的hasOwnProperty()方法并执行。

因此,所有继承了Object的对象都会继承到hasOwnProperty方法。

同时可以看到,Object.prototype.__proto__ 的值为 null ,即 Object.prototype 没有原型,所以可以想象在原型链中,当找到顶层原型还没有属性时,那就是没有这个属性,返回返回undefined。

JS:原型和原型链_第11张图片


四、使用instanceof作类型判断

instanceof 运算符:用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。

看一个例子,使用typeof判断array的数据类型时,返回的是object,因此无法使用typeof判断array的类型。

const object = {};
const array = [];

// 使用typeof判断数据类型,array返回的是object 
console.log(typeof object); // object
console.log(typeof array); // object

下面使用instanceof运算符判断array的数据类型:

// 使用instanceof判断数据类型
const flagObject = object instanceof Array;
const flagArray = array instanceof Array;

console.log(flagObject); // false
console.log(flagArray); // true

object instanceof Array返回false,表示Array.prototype不在object的原型链上;array instanceof Array返回true,表示Array.prototype在array的原型链上,由此可以区分object和array的数据类型。

也可通过控制台查看object和array的原型。

JS:原型和原型链_第12张图片

注:[] instanceof Object 为 true

你可能感兴趣的:(javascript,原型模式,前端)