目录
- 对象是什么
- 构造函数
- 原型对象
- 实现继承以及不同继承方式
对象
为什么要面向对象编程
代码逻辑迁移更加灵活、代码复用性高、高度模块化
构造函数
function Person(name) {
this.name = name
this.getName = function(name) {
return name
}
}
const person = new Person()
- 函数体内部的
this
指向生成的实例对象 - 生成对象用
new
关键词进行实例化 - 可以做初始化传参
new实例的过程
- 创建一个空对象,作为返回的对象实例
- 将生成空对象的原型对象指向了构造函数的prototype属性
- 将当前实例对象赋值给内部的
this
- 执行构造函数初始化代码
function myNew(Fn, params) {
// 创建一个空对象,作为返回的对象实例,将实例对象的__proto__属性指向构造函数的原型(Fn.prototype)
const obj = Object.create(Fn.prototype)
// 将当前实例对象赋值给内部的`this`,执行构造函数初始化代码
const result = Fn.apply(obj, params)
return (result && (typeof result === 'object' || typeof result === 'function')) ? result : obj
}
如何创建没有news实例化的构造函数(不被外部感知)
function Person() {
// 判断是否为new实例对象
if(!this instanceof Person) {
return new Person()
}
this.name = "Tom";
this.getName = function() {
return this.name
}
}
const person = Person();
使用构造函数的缺点
- 构造函数中的方法,会存在每一个生成的实例对象中,重复挂载其实是会导致资源浪费。
所以要使用原型对象来解决上面的问题。
原型对象
每个函数都有一个属性——prototype。这个prototype的属性值是一个对象(属性的集合),默认只有一个叫做constructor的属性,指向这个函数本身。 如下图所示:
上图中,SuperType
是一个函数,右侧的方框就是它的原型。
原型既然作为对象(属性的集合),除了constructor
外,还可以自定义许多属性,比如下面这样的:
function Person(name) {
this.name = name;
}
// 赋值原型对象的属性做为实例对象上的继承属性,避免重复挂载方法
Person.prototype.getName = function() {
return this.name
}
“隐式原型”proto
每个对象都有一个__proto__
属性,指向创建该对象的构造函数的prototype。
注意: Object.prototype
确实一个特例——它的__proto__
指向的是null
,切记切记!!!
从上图可以看出:自定义函数Foo.__proto__
指向Function.prototype
,Object.__proto__
指向Function.prototype
。
但是,为什么有Function.__proto__指向Function.prototype呢?
其实原因很简单:Function也是一个函数,函数是一种对象,也有__proto__属性。既然是函数,那么它一定是被Function创建。所以Function是被自身创建的。所以它的__proto__指向了自身的Prototype
最后一个问题:Function.prototype
指向的对象,它的__proto__
是不是也指向Object.prototype
?
答案是肯定的。因为Function.prototype
指向的对象也是一个普通的被Object
创建的对象,所以也遵循基本的规则。
继承
- 原型链继承
function Person() {
}
Person.prototype.getName = function() {
return this.name
}
function Man() {
}
Man.prototype = new Person();
Man.prototype.constructor = Man
本质: 重新原型对象的方式,将父对象的属性和方法,作为子对象原型对象的属性和方法
缺点:
- 父类属性一旦赋值给子类的原型属性,此时属性属于子类的共享属性了
- 实例化子类时,无法向父类传参
- 构造函数继承
function Person() {
}
Person.prototype.getName = function() {
return this.name
}
function Man(arg) {
Person.call(this,arg)
}
// 解决了共享属性的问题 + 子向父传参问题
- 组合继承
function Person() {
}
Person.prototype.getName = function() {
return this.name
}
function Man(arg) {
Person.call(this,arg)
}
Man.prototype = new Person();
Man.prototype.constructor = Man
缺点:无论何种场景,都会调用2次父构造函数
- 初始化子类原型
- 子类调用函数内部call父类的时候
- 寄生组合继承
function Person() {
}
Person.prototype.getName = function() {
return this.name
}
function Man(arg) {
Person.call(this,arg)
}
Man.prototype = Object.create(Person.prototype);
Man.prototype.constructor = Man
如何实现多重继承
function Person(name) {
this.name = name
}
Person.prototype.getName = function() {
return this.name
}
function Worker(salary) {
this.salary = salary
}
Worker.prototype.getSalary = function() {
return this.salary
}
function Man(arg) {
Person.call(this,arg)
Worker.call(this,arg)
}
Man.prototype = Object.create(Person.prototype);
Object.assign(Man.prototype, Worker.prototype);
Man.prototype.constructor = Man
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。