JS原型链
含义
JS是面向对象的,每个实例对象都有一个__proto__属性,该属性指向它的原型对象,这个实例对象的构造函数有一个原型属性prototype,与实例的__proto__属性指向同一个对象。当一个对象在查找一个属性的时候,自身没有就会根据__proto__向它的原型进行查找,如果还没有,则向它的原型的原型继续查找,直到查到Object.prototype.__proto__为null,这样也就形成了原型链。
重要属性
-
prototype :构造函数的原型对象。当创建一个构造函数后,这个函数会自动创建一个
prototype属性,这个属性是个指针,指向一个对象。这个对象称为自己创建的构造函数的原型对象。 -
__ proto __ :对象的原型。对于已经创建好了的构造函数,当用户创建了这个构造函数的实例后(new出来的),这个新实例下也会自动添加一个属性
__proto__,这称为内部属性。ES5中用
[[prototype]]表示,一般用户是没有办法访问的。但Firefox,Safari,Chrome,以及IE9以上可以用Object.getPrototypeOf(实例)来访问这个属性。其实这个属性指向的也是这个实例的构造函数的原型对象。 -
constructor :构造器。原型上的构造器属性,指向该原型的构造函数。
特别声明
- Function是一个构造函数,也可以说是一个已经由function声明好的函数对象。function是一个关键字,用来声明一个函数对象。
- Function这个函数对象和其他函数对象不同,他的
prototype和__proto__都指向Function的原型,即Function.prototype。这是因为函数Function是由它本身创造出来的,所以它的__proto__指向它自己的原型。 - prototype开发者可以用方法直接访问,__ proto __代表的是实例对象的内部属性。
- __ proto __属性里不会有
prototype属性的,prototype里也不会嵌套prototype属性。但是constructor属性中会同时存在__proto__和prototype属性。
概念
-
JS分为函数对象和普通对象,每个对象都有
__proto__属性,但是只有函数对象才有prototype属性; -
Object、Function都是JS内置的函数,类似的还有我们经常用到的Array、RegExp、Date、Boolean、Number、String;
-
属性
__proto__是一个对象,它有两个属性,constructor和__proto__; -
原型对象
prototype有一个默认的constructor属性,用于记录实例是由哪个构造函数创建的。
分析
-
函数对象
// 在JS中,任何事物皆为对象,所以Person既是一个函数,也是一个对象 function Person(){}; // 此时,Person就是一个函数对象 let person = new Person(); // 此时,person就是一个普通对象,也是Person的一个实例Object、Function、Array、RegExp、Date、Boolean、Number、String这些JS内置的函数也都是函数对象,都有
prototype属性。同样,我们通过function声明的函数,类如上述代码中的Person也是一个函数对象。 -
原型对象
原型也是一个对象,而所谓原型,就是函数对象的
prototype属性。拿上述函数对象Person来举例,就是原型对象=Person.prototype。 -
普通对象
let person = {}; // 等价于 let person = new Object(); // 等价于 let person = Object.create(null) // 此时,person就是一个普通对象 -
构造函数(通过new关键字来实例化对象的函数)
// 声明一个函数,名为Person function Person(){}; // 实现1: let person = new Person();// 此时,Person为构造函数 // 实现2: let person = Person();// 此时,Person为一个普通函数任何函数都可以称之为构造函数。之所以有构造函数和普通函数之分,主要是从功能上进行区别。构造函数的主要功能为初始化对象,为其添加属性和方法,特点是和new一起使用。
准则
- 原型对象
prototype的constructor指向构造函数本身; - 实例的
__proto__和原型对象指向同一个地方。
分析
-
function Person(){}; console.log(Person.prototype.constructor === Person); // 打印结果为 true -
function Person(){}; let person = new Person();// 此时person是Person的一个实例 console.log(person.__proto__ === Person.prototype); // 打印结果为 true
原型链指向
function Person(){};
let person = new Person();
console.log(Person.prototype.constructor === Person);// true
console.log(person.__proto__ === Person.prototype);// true
console.log(Person.__proto__ === Function.prototype);// true
console.log(Function.prototype.__proto__ === Object.prototype);// true
console.log(Person.__proto__.__proto__ === Object.prototype);// true
console.log(Object.prototype.__proto__ === null);// true
console.log(Person.__proto__.__proto__.__proto__ === null);// true
可以看到,原型链的最终指向是null。
实际使用
-
读取对象的
hasOwnProperty方法:function Person(){ this.name = "一路星河"; }; let person = new Person(); console.log(person.name);// "一路星河" // Object的hasOwnProperty方法返回一个布尔值,判断对象是否包含特定的自身(非继承)属性 console.log(person.hasOwnProperty("age"));// false console.log(person.hasOwnProperty("name"));// true我们可以看到,在构造函数内部,我们并没有声明
hasOwnProperty方法,但是它执行了,并且根据传入的参数不同,返回了不同的结果!我们来按步骤分析一下它的执行过程:-
从
person本身查找hasOwnProperty方法,没找到; -
从
person.__proto__(同Person.prototype)上查找hasOwnProperty方法,没找到; -
从
person.__proto__.__proto__(同Object.prototype)上查找hasOwnProperty方法,找到了; -
执行
person.hasOwnProperty(key)。 -
至此,整个原型链的查找过程结束。
-
假设,在第3步的时候依然没有找到,那么再深入去查找
person.__proto__.__proto__的原型对象的结果就为null了,因为Object.prototype.__proto__=null已经到头了。此时,在整个原型链中都没有找到hasOwnProperty方法,所以不会正常打印结果(结果为undefined或报错)。
当然,我们可以重写
hasOwnProperty方法。比如我们对上面的代码进行一番改造:-
function Person(){ this.name = "一路星河"; }; let person = new Person(); console.log(person.name);// "一路星河" // 此时,hasOwnProperty方法会在第1步被找到 person.hasOwnProperty = function(key){ return key; }; // Object的hasOwnProperty方法返回一个布尔值,判断对象是否包含特定的自身(非继承)属性 console.log(person.hasOwnProperty("age"));// "age" console.log(person.hasOwnProperty("name"));// "name" -
function Person(){ this.name = "一路星河"; // 声明方式一:此时,hasOwnProperty方法会在第2步被找到 this.hasOwnProperty = function(key){ return key; }; }; // 声明方式二:此时,hasOwnProperty方法会在第2步被找到 Person.prototype.hasOwnProperty = function(key){ return key; }; let person = new Person(); console.log(person.name);// "一路星河" // Object的hasOwnProperty方法返回一个布尔值,判断对象是否包含特定的自身(非继承)属性 console.log(person.hasOwnProperty("age"));// "age" console.log(person.hasOwnProperty("name"));// "name"
-