原型和原型链很重要,但是很容易忘,以前懂了现在又忘了,所以总结一下原型链的来龙去脉,帮助大家同时也让自己能够快速的捡起来
一、原因:
1、构造函数的弊端
function Person(name, age) {
// 在构造函数内部可以通过this来给创建的对象添加属性和方法
// this.xxx = yyy
this.name = name
this.age = age
this.sayHi = function () {
console.log(`${name}今天你吃了吗?`)
}
}
let p1 = new Person('张三',18)
p1.sayHi() // '张三今天你吃了吗?'
let p2 = new Person('李四',19)
p2.sayHi() // '李四今天你吃了吗?'
console.log('p1 == p2的结果为:',p1 == p2); // false
console.log('p1 == p2的结果为false的原因是:== 两边都是new出来的新对象,内存地址不相同')
// 弊端:假如创建了100个对象,那么在内存里面就会创建100个sayHi函数,这样会造成内存浪费的问题。其实在内存里面只需要一份sayHi方法,就行了
console.log('p1.sayHi == p2.sayHi的结果:',p1.sayHi == p2.sayHi) // false
console.log([] == []) // false
console.log([].push == [].push); // true
// 数组的两个push,说明两个数组的push方法都是同一个
补充:new做了什么事?
- 创建了一个新的空对象
- 让this指向了新对象
- 执行构造函数的内部代码
- 将新对象给返回了
注:执行构造函数的代码,就是给新创建的对象添加属性和方法
2、解决构造函数创建对象造成内存浪费的问题
那我们是否可以
function sayHi(){
console.log("你今天吃了吗");
}
function running(){
console.log("running");
}
// 这个代码只执行了一次,在内存里面只有一份,
// 这样已经解决了构造函数创建对象造成内存浪费问题,因为方法在内存里面只有一份。
// 这样虽然能解决问题,间接的造成了全局污染的问题
还有没有更好的方法:
var obj = {
sayHi: function (){
console.log("你今天吃了吗");
},
running: function (){
console.log("running");
}
// ...
}
function Person(name, age){
this.name = name;
this.age = age;
this.sayHi = obj.sayHi;
this.running = obj.running;
}
var p = new Person("慧姐", 20);
// console.log(p); //
// p.sayHi()
p.running();
var p2 = new Person("燕哥", 19);
// p2.sayHi();
p2.running();
// 两个对象访问的sayHi都是同一个,来源于全局里面sayHi函数
console.log(p.sayHi == p2.sayHi); // true
// 这样可以解决全局污染的问题
// 也解决了构造函数创建对象造成内存浪费问题
// 也不这么做,写代码固定了,所以产生了原型
二、原型
- 任何函数都有 prototype 属性
- 函数的prototype属性值是个对象,我们把这个对象叫做原型(原型对象)
- 通过构造函数创建的实例可以直接访问构造函数的prototype属性上的任何成员
- 任何对象都有__proto__ 属性, 指向的是构造函数的prototype属性,也就是原型
- constructor 属性是原型中自带的属性,指向了当前的构造函数
prototype
// 构造函数
function Person(){
}
// console.dir(Person);
// console.log(Person.prototype);
// 原型对象
// 这是给原型对象添加color属性
Person.prototype.color = "lime";
Person.prototype.gender = "male";
// 把sayHi方法添加给原型,谁可以访问
// 构造函数创建的实例就可以访问到了
Person.prototype.sayHi = function(){
console.log("hello");
}
// console.log(Person.prototype);
// 实例对象:p1
var p1 = new Person();
// console.log(p.color); // lime
// console.log(p.gender); // male
// p.sayHi();
// 实例对象:p2
var p2 = new Person();
console.log(p2); // 是个空对象
p2.sayHi();
// p的sayHi和p2的sayHi是同一个函数
// 来源于原型上的sayHi方法
// 这样就解决了内存浪费问题
console.log(p.sayHi == p2.sayHi); // true
** proto **
function Person() {
}
var p = new Person()
console.log(p.__proto__ == Person.prototype) // true
// 从上面的代码可以看出访问原型有两种途径:
// 1. 构造函数通过prototype 属性可以访问到原型
// 2. 实例对象通过__proto__ 属性可以访问到原型
constructor
// constructor是原型中自带的属性,指向了当前的构造函数
function Person(){
}
// console.log(Person.prototype);
console.log(Person.prototype.constructor == Person) // true
var p = new Person();
// 实例对象p访问的原型上的constructor属性,
console.log(p.constructor == Person); // true
console.log(p.constructor == Person.prototype.constructor); // true
用这个图来进一步了解一下原型链
总结:
// 完整的原型三角关系:
// 构造函数、实例对象、原型对象
// 妈妈 孩子 爸爸
// 构造函数与原型对象的关系: 配偶关系
// 构造函数通过prototype 属性可以找到原型
// 原型通过 constructor 属性找到了构造函数
// 构造函数与实例对象的关系:母子
// 构造函数创建了实际对象,但是实例对象不能直接访问到构造函数
// 实例对象与原型对象的关系:父子
// 实例对象可以通过__proto__ 来访问原型
// 实例对象可以通过爸爸的constructor属性间接的访问到构造函数
三、原型链
定义: 任何对象都有__proto__属性,指向的是原型对象,原型对象也是对象,那也有__proto__属性,也就是说原型对象也有自己的原型对象。这样形成的链式结构,叫做原型链
简单理解为: 对象都有爸爸,爸爸也有自己的爸爸, 这样形成的链式结构,叫做原型链
function Person(){
}
var p = new Person();
// 构造函数: Person
// 原型对象: Person.prototype
// 实例对象: p
// console.log(Person.prototype.__proto__ == Object.prototype) // 找爸爸
// console.log(Person.prototype.__proto__.constructor == Object) // 找妈妈
// console.log(Object.prototype.__proto__ == null) // 找爸爸
// 实例对象p的原型链:
// p ==> Person.prototype ==> Object.prototype ==> null
// console.log(Person.__proto__ == Function.prototype) // true
// console.log(Function.prototype.__proto__ == Object.prototype) // true
// 构造函数Person的原型链
// Person ==> Function.prototype ==> Object.prototype ==> null
内置对象的原型链
var arr = new Array()
console.log(arr.__proto__ == Array.prototype) // true
console.log(Array.prototype.__proto__ == Object.prototype) // true
// arr的原型链:
// arr ==> Array.prototype ==> Object.prototype ==> null
console.log(Array.__proto__ == Function.prototype) // true
console.log(Function.__proto__ == Function.prototype) // true
console.log(Function.prototype.constructor == Function) // true Function 的 prototype 和 __proto__ 都指向的了Function.prototype(Function的爸爸是Function.prototype ,Function的妈妈是她自己,自己创造了自己)
console.log(Function.prototype.__proto__ == Object.prototype) // true
console.log(Object.__proto__ == Function.prototype) // ture
// Function 的原型链
// Function ==> Function.prototype ==> Object.prototype ==> null
// Object 的原型链
// Object ==> Function.prototype ==> Object.prototype ==> null
// Array 的原型链
// Array ==> Function.prototype ==> Object.prototype ==> null
// String 的原型链
// String ==> Function.prototype ==> Object.prototype ==> null
// Number 的原型链
// Number ==> Function.prototype ==> Object.prototype ==> null
// Boolean 的原型链
// Boolean ==> Function.prototype ==> Object.prototype ==> null
// Date 的原型链
// Date ==> Function.prototype ==> Object.prototype ==> null
// Math 的原型链
// Math ==> Object.prototype ==> null
最后我们看看完整的原型链
到此我们讲完了原型链
我在网上看到一篇不错的,从null推理的原型链,大家可以再看看 zhuanlan.zhihu.com/p/22989691