Es6 中引入了 class
关键字,但只是语法糖, js 仍然是一门基于原型的语言。jquery
当谈到继承时,js 只有一种结果:对象。es6
对象是动态的属性包。函数
每一个对象都有一个私有属性(非标准属性:__proto__
,应经过 Object.getPrototypeOf()
获取) ,指向它的构造函数的原型对象 (prototype
)——「它的构造函数的原型对象」也有一个本身的原型对象 (__proto__
) ,以此类推直到一个对象的原型对象为 null
。性能
null
没有原型,null
是原型链中最后一环。this
好比:spa
function Child(){
this.name = 'xiaoyu'
}
const child = new Child()
child.__proto__ === Child.prototype // > true
// 等同于:
Object.getPrototypeOf(child) === Child.prototype // > true
复制代码
child.__proto__.__proto__.__proto__ // > null
复制代码
基于原型链的继承,包括继承属性和继承方法(函数),其中函数的继承与属性继承没有差异,任何函数均可以添加到对象上做为对象的属性。prototype
Es6 引入了新的关键字实现 class
,除了 class
以外,还有 constructor
, static
, extends
,super
。3d
class Person {
constructor({name = 'xiaoyu', age = 18, sex = 0}){
Object.assign(this, {
name, age, sex
})
}
}
class Child extends Person {
constructor(options = {}) {
super(options)
this.task = options.task
this.canTravelAlone = false
}
}
class Baby extends Child {
constructor(options = {}) {
super(options)
this.food = 'neinei'
}
}
const baby = new Baby({age: 1})
baby // > Baby {name: "xiaoyu", age: 1, sex: 0, task: undefined, canTravelAlone: false, food: "neinei”}
const child = new Child({task: ‘study’, age: 10})
child // > Child {name: "xiaoyu", age: 10, sex: 0, task:’study’}
复制代码
Object.create()
实现类式继承单继承:code
function Parent() {
this.x = 0
this.y = 0
}
Parent.prototype.move = function (x, y) {
this.x += x
this.y += y
console.log('Parent moved')
}
function Child() {
Parent.call(this)
}
Child.prototype = Object.create(Parent.prototype)
Child.prototype.constructor = Parent
console.log(Child.prototype)
/* >
constructor: ƒ Parent()
__proto__: Object
*/
var child = new Child()
console.log(child instanceof Parent) // > true
child.move(1,1) // > Parent moved
复制代码
多类混入继承:orm
function Parent () {
this.name = 'dayu'
}
function AnotherParent () {
this.nickName = 'peppa'
}
function Child () {
Parent.call(this)
AnotherParent.call(this)
}
Child.prototype = Object.create(Parent.prototype)
// Object.assign 把 AnotherParent 原型上的函数 copy 到 Child 原型上
Object.assign(Child.prototype, AnotherParent.prototype)
Child.prototype.constructor = Child
Child.prototype.play = function () {
console.log('play')
}
var child = new Child()
console.log(child)
/* >
Child {name: "dayu", nickName: "peppa"}
name: "dayu"
nickName: "peppa"
__proto__: Parent
constructor: ƒ Child()
play: ƒ ()
__proto__: Object
constructor: ƒ Parent()
__proto__: Object
*/
复制代码
优点:能够经过 Object.create(null)
来建立一个没有原型的对象 缺陷:Object.create()
第二个参数使用之后,因为每一个对象的描述符属性都有本身的描述对象,以对象的格式处理成百上千个对象描述的时候,可能会形成严重的性能问题。
能够使用 new
建立实例,以及 Constructor.prototype
链接到这个实例造成原型连接
function Child(){
this.name = 'xiaoyu'
this.age = 18
}
var child = new Child()
Child.prototype.age = 10
Child.prototype.task = 'play'
// 自有属性
console.log(child.name) // > xiaoyu
// 访问不到原型上的 age ,属性遮蔽
console.log(child.age) // > 18
// 顺着原型链向上查找,找到了 task 属性
console.log(child.task) // > play
console.log(Child.prototype)
/* >
age: 10
task: "play"
constructor: ƒ Child()
__proto__:
constructor: ƒ Object()
__defineGetter__: ƒ __defineGetter__()
__defineSetter__: ƒ __defineSetter__()
hasOwnProperty: ƒ hasOwnProperty()
__lookupGetter__: ƒ __lookupGetter__()
__lookupSetter__: ƒ __lookupSetter__()
isPrototypeOf: ƒ isPrototypeOf()
propertyIsEnumerable: ƒ propertyIsEnumerable()
toString: ƒ toString()
valueOf: ƒ valueOf()
toLocaleString: ƒ toLocaleString()
get __proto__: ƒ __proto__()
set __proto__: ƒ __proto__()
*/
console.log(child)
/* >
name: "xiaoyu"
age: 18
__proto__:
age: 10
task: "play"
constructor: ƒ Child()
__proto__:
constructor: ƒ Object()
__defineGetter__: ƒ __defineGetter__()
__defineSetter__: ƒ __defineSetter__()
hasOwnProperty: ƒ hasOwnProperty()
__lookupGetter__: ƒ __lookupGetter__()
__lookupSetter__: ƒ __lookupSetter__()
isPrototypeOf: ƒ isPrototypeOf()
propertyIsEnumerable: ƒ propertyIsEnumerable()
toString: ƒ toString()
valueOf: ƒ valueOf()
toLocaleString: ƒ toLocaleString()
get __proto__: ƒ __proto__()
set __proto__: ƒ __proto__()
复制代码
能够看出,child.__proto__ === Child.prototype
。
任何函数的 __proto__
都是(window.)Object.prototype
,原型链上的属性查找终止于 Object.prototype.__proto__
(null
),找不到则返回 undefined
。
child —> Child.prototype —> Function.prototype —> Object.prototype —> null
缺陷:这种方法强制在每一个对象中生成类似的信息,可能会给生成对象带来并不想要的方法。
经过复制源对象的属性,一个对象直接继承另外一个对象。js 中,源对象的属性一般被称做 mixins
,从 es6 开始,js 使用 Object.assign()
来实现这个过程,es6 以前,一般使用 lodash/underscore 的 .extend()
和 jquery 的 $.exntend()
实现。
const name = {name: 'xiaoyu'}
const age = {age: 18}
const sex = {sex: 0}
const task = {task: 'study'}
const canTravelAlone = {canTravelAlone: false}
const food = {food: 'normal'}
const months = {months: 8}
const Person = (options) => {
return Object.assign({}, name, age, sex, options)
}
const Child = (options) => {
return Object.assign({}, name, age, sex, task, canTravelAlone, options)
}
const Baby = (options) => {
return Object.assign({}, name, months, sex, food, options)
}
const baby = Baby({food: 'neinei'})
baby // > {name: "xiaoyu", months: 8, sex: 0, food: "neinei"}
const child = Child()
child // > {name: "xiaoyu", age: 18, sex: 0, task: "study", canTravelAlone: false}
复制代码
能够发现,对象组合可以确保对象按需继承,这和类式继承不一样,当继承一个类时,类里全部的属性都会被继承。
Object.create()
实现拼接继承能够使用 Object.create()
实现原型连接,或者与拼接继承混用。
var o = {
a: 2,
m: function(){
return this.a + 1
}
}
console.log(o.m()) // > 3
复制代码
// 原型链:o —> Object.prototype —> null
使用字面量建立的对象继承了 Object.prototype
上的全部属性:
Object.prototype
/* >
constructor: ƒ Object()
__defineGetter__: ƒ __defineGetter__()
__defineSetter__: ƒ __defineSetter__()
hasOwnProperty: ƒ hasOwnProperty()
__lookupGetter__: ƒ __lookupGetter__()
__lookupSetter__: ƒ __lookupSetter__()
isPrototypeOf: ƒ isPrototypeOf()
propertyIsEnumerable: ƒ propertyIsEnumerable()
toString: ƒ toString()
valueOf: ƒ valueOf()
toLocaleString: ƒ toLocaleString()
get __proto__: ƒ __proto__()
set __proto__: ƒ __proto__()
*/
// p 继承自 o ,p 也有自身属性 a
var p = Object.create(o)
p.a = 4
console.log(p.m()) // > 5
p.__proto__.m() // > 3
复制代码
原型链 p —> a —> Object.prototype —> null
Js 中,任何函数均可以建立对象。若是一个函数既不是构造函数也不是 class ,并且这个函数返回一个不是经过 new
建立的对象,这个函数就是一个工厂函数。
function createBook(params = {}) {
return {
title: '我是一本书',
// 多是一个带有参数的工厂函数
author: params.author
}
}
复制代码
经过工厂函数建立对象并经过直接赋予属性使用拼接继承,这就是函数继承的原理。
function createEbook(params = {}) {
return {
…createBook(),
cover: ‘xxxx.jpg’
}
}
createEbook() // > {title: "我是一本书", author: undefined, cover: "xxxx.jpg"}
复制代码
原型链上查找属性比较耗时,性能要求苛刻的状况下应该注意
备注: hasOwnProperty 是 js 中惟一一个处理属性不会遍历原型链的方法。
试图访问不存在的属性会遍历整个原型链