在 JavaScript 中,最让人困扰的几根莫过于 this、闭包、作用域、原型链,而却常常看又常常忘,最近又看了一下高程感觉清晰了很多,于是就想写在这里(水一篇文章)。
面向对象的 JS
面向对象
原型编程
MDN 上的解释
原型编程
基于原型的编程不是面向对象编程中体现的风格,且行为重用(在基于类的语言中也称为继承)是通过装饰它作为原型的现有对象的过程实现的。这种模式也被称为弱类化,原型化,或基于实例的编程。
大概为了实现面向对象引入了原型链等。
继承
在此之前,先看一下 mdn 上 new 的解释:
当代码 new Foo(…) 执行时,会发生以下事情:
一个继承自 Foo.prototype 的新对象被创建。
使用指定的参数调用构造函数 Foo ,并将 this 绑定到新创建的对象。new Foo 等同于 new Foo(),也就是没有指定参数列表,Foo 不带任何参数调用的情况。
由构造函数返回的对象就是 new 表达式的结果。如果构造函数没有显式返回一个对象,则使用步骤 1 创建的对象。(一般情况下,构造函数不返回值,但是用户可以选择主动返回对象,来覆盖正常的对象创建步骤
继承的几种方法
原型链继承
function Parent() {
this.name = 'Parent'
this.children = []
}
Parent.prototype.getAttr = function () {
console.log(this.name, this.children)
}
function Child() {}
Child.prototype = new Parent()
var instance = new Child()
instance.constructor == Parent //true
instance.getAttr() //Parent []
//此时所有引用实例共享属性(弊端)
var instance2 = new Child()
instance.name = 'instance'
instance.children.push('instance')
child2.getAttr() //Parent ["instance"]
借用构造函数
此方法是使用 call 或者 apply 方法,将父类的构造函数 this 绑定到子类上,当实例化子类式调用父类构造函数并绑定 this。所以每个实例都有自己的一份拷贝。
function Parent() {
this.name = 'Parent'
this.children = []
// 此时方法也需在父类构造函数中声明,每次都会生成一个全新的 func
this.getAttr = function () {}
}
function Child() {
Parent.call(this)
}
var instance = new Child()
var instance2 = new Child()
instance.children.push('instance1')
instance2.children.push('instance2')
//["instance1"] ["instance2"]
console.log(instance.children, instance2.children)
组合继承
function Parent() {
this.name = 'Parent'
this.children = []
// 此时方法也需在父类构造函数中声明
this.getAttr = function () {}
}
function Child() {
Parent.call(this)
}
// 直接继承公共方方法
Child.prototype = new Parent()
// 修成原型链
Child.prototype.constructor = Child
// 此时继承自 Child.prototype 的新对象被创建
var instance = new Child()
var instance2 = new Child()
instance.children.push('instance1')
instance2.children.push('instance2')
//["instance1"] ["instance2"]
console.log(instance.children, instance2.children)
寄生继承
function Parent() {
this.name = 'Parent'
this.children = []
}
var instance = Object.create(Parent)
寄生 + 组合
function Parent() {
this.name = 'Parent'
this.children = []
}
function Child() {
Parent.call(this)
}
var prototype = Object.create(Parent)
//继承原型
prototype.constructor = Child
Child.prototype = prototype
ES6 中的继承
class Child extend Parent{}
ES6 多继承
- mixins
function mixins(derivedCtor, baseCtors) {
baseCtors.forEach((baseCtor) => {
Object.getOwnPropertyNames(baseCtor.prototype).forEach((name) => {
derivedCtor.prototype[name] = baseCtor.prototype[name]
})
})
}
mixins(child, [parent, ...other])