【JavaScript高级】原型与原型链

·  阅读 891
【JavaScript高级】原型与原型链

原型与原型链一直是JavaScript的重难点,掌握这部分内容将会使我们的工作更加的高效,而且这也是面试官必问的内容。

首先,我们要明确的是,在 ES6 之前,我们创建一个实例并不是通过类(class),而是直接使用构造函数来实现的。

一、构造函数

通过 new 函数名 来实例化对象的函数叫构造函数。任何的函数都可以作为构造函数存在。构造函数首字母一般大写。

那么,我们使用构造函数来创建一个对象。

function Person(name , age , sex) {
    this.name = name;
    this.age = age;
    this.sex = sex;
}
var person = new Person("Tony" , 18 , "男");
console.log(person.name);  // Tony
复制代码

上面这段代码就是创建一个了Person 的构造函数,在Person 构造函数中,为每一个对象都添加了三个属性(name,age,sex),也就是说构造函数每执行一次就会创建一个新的Person对象。

构造函数简单复习一下,下面步入正题。

二、原型

prototype

在JS中,每当定义一个函数时候,都会默认自带一个prototype属性,这个属性指向的是该构造函数创建的实例的原型,并且这个属性是一个对象数据类型的值。

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

举个原型的Demo:

function Person() { }
Person.prototype.name = 'Tony';  // 注意:prototype是函数才会有的属性
var person1 = new Person();
var person2 = new Person();
console.log(person1.name);  // Tony
console.log(person2.name);  // Tony
复制代码

构造函数和实例原型之间的关系: 构造函数和实例原型之间的关系: 在这里 Person.prototype 表示实例原型。原型对象就相当于一个公共的区域,所有同一个类的实例都可以访问到这个原型对象,我们可以将对象中共有的内容,统一设置到原型对象中。

三、原型链

上面,我们说明了实例和实例原型,那么我们该怎么表示这两者之间的关系呢?这时候我们就要说到下面两个属性了

__proto__

这是每一个JS 对象(除了 null )都具有的一个属性,叫__proto__,这个属性会指向该对象的原型。

person.__proto__ === Person.prototype   // true
复制代码

__proto__ 绝大部分浏览器都支持这个非标准的方法访问原型,然而它并不存在于 Person.prototype 中,实际上,它是来自于 Object.prototype ,与其说是一个属性,不如说是一个 getter/setter,当使用 obj.__proto__ 时,可以理解成返回了 Object.getPrototypeOf(obj)

constructor

每个原型都有一个 constructor 属性指向关联的构造函数。

Person === Person.prototype.constructor  // true
复制代码

当获取 person.constructor 时,其实 person 中并没有 constructor 属性,当不能读取到constructor 属性时,会从 person 的原型也就是 Person.prototype 中读取,正好原型中有该属性,所以:

person.constructor === Person.prototype.constructor
复制代码

实例原型与构造函数的关系图: 实例原型与构造函数的关系图 原型链:在JS中,万物皆对象,对象和对象之间也是有关系得,并不是孤立存在的。对象之间的继承关系,在JS 中是通过prototype对象指向父类对象,直到指向Object对象为止,这样就形成了一个原型指向的链条,专业术语称之为原型链。

实例和原型

当我们读取实例的一个属性或方法时,它会先在对象自身中寻找,如果有则直接使用,如果没有则会去原型对象中寻找,如果找到则直接使用。如果没有则去原型的原型中寻找,一直找到最顶层Object为止,Object对象的原型没有原型(Object是JS中所有对象数据类型的基类(最顶层的类)在Object.prototype上没有__proto__这个属性),如果在Object原型中依然没有找到,则返回undefined

function Person() {}
Person.prototype.name = 'Tony';
var person = new Person();

// 当我们给实例对象person添加了name属性,打印 person.name 时,结果为name的值Ken。
person.name = 'Ken';
console.log(person.name) // Ken

// 当我们删除了person的name属性时,再次读取person.name,从person 对象中找不到name属性就会从person的原型也就是person.__proto__和Person.prototype中查找name属性,因为之前我们给他添加了,所以找到了 name属性为 Tony。
delete person.name;
console.log(person.name) // Tony
复制代码

我们可以使用对象的hasOwnProperty()来检查对象自身中是否含有该属性,如果自身属性存在,则返回 true,否则为false;使用in检查对象中是否含有某个属性时,如果对象中没有但是原型中有,则会返回true,如果都没有则返回false

function Person() {
    this.name = 'Tony'
}

Person.prototype.age = 18;

var person = new Person();

console.log(person.hasOwnProperty('name')); // true
console.log(person.hasOwnProperty('age'));  // false

console.log('name' in person);  // true
console.log('age' in person);   // true
console.log('a' in person);     // false
复制代码

最后,相互关联的原型组成的链状结构就是原型链,也就是蓝色的这条线: 在这里插入图片描述


用心读完上面的总结,应该会对 JS 的原型与原型链有了一个深层次的认识吧,其实这部分还是需要细心琢磨的,毕竟是比较底层的原理。

分类:
前端
标签: