在聊原型之前先聊一聊如何拷贝结构的方法,我们知道深拷贝对象的方法可以用递归和JSON,那如果只拷贝结构呢?
可以用工厂模式,也可以称为工厂函数,简单理解,就是将一些原材料放到函数中加工出全部一样的产品。工厂函数写法类似于构造函数,如下:
function createUser(name, age) {
return {
name, age, getName() {
return this.name
}
}
}
let user1 = createUser('Peter', 28)
let user2 = createUser('Andy', 18)
user1.name // Peter
工厂函数的缺点就是:user1 和 use2 虽然用的是同一个函数,但是是独立存在的,这样创建会很浪费空间
console.log(user1.getName == user2.getName) // false
所以我们就可以用到原型,原型就是让对象之间有共享的部分,不管创建多少个user都是引用的同一个地址,大大节约了存储空间
function createUser(name, age) {
this.name = name
this.age = age
}
createUser.prototype.getName = function () {
console.log(this.name)
}
let user1 = new createUser('Peter', 28)
let user2 = new createUser('Andy', 18)
user1.name // Peter
console.log(user1.getName == user2.getName) // true
原型
构造函数的prototype属性
每个函数都有一个 prototype 属性, 它默认指向一个Object空对象(即称为: 原型对象)
原型对象中有一个属性 constructor, 它指向函数对象
prototype的作用
用于将一些方法或者属性添加在 prototype 属性上,每次 new 创建的实例对象都可以共享到 prototype 上的方法和属性
实例对象的__proto__属性
每个实例对象都有一个 __proto__ 属性,它指向函数中的 prototype 属性(即称为:对象原型)
显示原型和隐式原型
函数 => 构造函数 显示原型 prototype
对象 => 实例对象 隐式原型 __proto__
构造函数中的 prototype 相等于 实例对象中的 __proto__
原型链
每创建一次函数,解析器就会按照特定的规则给函数添加一个 prototype 属性指向原型对象。原型对象会默认获得一个 constructor 属性,指回与之关联的构造函数。
显示原型和隐式原型指向同一个空对象,这个空对象也是一个实例对象,由Object实例出来的,该空对象的隐式原型指向Object的显示原型,但是Object比较特殊,他的隐式原型是null
但是 Object 是由 function 实例出来的,所以 Object的隐式原型指向函数的显示原型,函数的显示原型的隐式原型又指向对象的显示原型
构造函数也是由Function实例出来的,所以构造函数的隐式原型指向function的显示原型
整个原型链的结构如下:
原型链查找规则
在通过对象访问属性时,会按照这个属性的名称开始搜索,它会遵循以下的查找顺序:
1. 搜索开始于实例对象本身,如果在这个实例上发现了属性名,则返回该名称对应的值,不再继续搜索
2. 如果没有找到这个属性,则会沿着实例对象的 __proto__ 去原型对象中查找,如果在原型对象中找到了这个属性,再返回对应的值,不再继续搜索
3.如果还没有找到,继续沿着原型对象的__proto__中去找,Object的原型对象的__proto__是null,如果在Object对象的原型中都没有找到,就到头了,直接返回undefined
总结
原型
就是用来使对象中需要共享的部分共享,节约存储空间
每创建好一个构造函数,解析器就会自动添加一个prototype属性执行一个空对象(原型对象),原型对象也会添加一个constructor属性指回该构造函数
用new创建出来的对象称为实例对象,在创建的时候也会自动添加一个__proto__属性指向空对象 构造函数的.prototype == 实例对象的.__proto__
原型链
原型链查找规则:先在自身上找,没有就沿着 __proto__ 去原型对象中查找,都找不到就返回 undefined