在此我会主要记录下三大点,属性类型、创建对象模式和继承方式
一、属性类型
描述了属性的各种特征,可以使用Object.getOwnPropertyDescriptor()/Object.getOwnPropertyDescriptors查看单个属性或则对象所有属性的属性特征。
1、数据属性
数据属性包含有四个描述其特征的属性
configurable:表示能否删除属性或修改属性特性。
enumerable:表示是否具有可枚举性(被for…in遍历)
writable:表示此属性值能否被修改
value:表示此属性值
2、访问器属性
访问器属性不包含属性值,但包含另外两个函数,set\get
configurable:表示能否删除属性或修改属性特性。
enumerable:表示是否具有可枚举性(被for…in遍历)
set:在读取属性时调用函数。默认返回值为undefined
get:在写入属性时调用函数。默认返回值为undefined
注意:set、get不能操作自身即
Object.defineProperty(person, 'basic', {
enumerable: true,
configurable: false,
get: function() {
console.log(this)
return this.basic
},
set: function(value) {
this.basic = value
}
})
使用场景:一般使用在利用另外一个特定的属性来约束此属性,即
const object = (data) => {
function Person() {
this.leg = 'two'
this._name = '你不是chujiu'
}
const person = new Person()
person.basic = data
return person
}
const person = object(prototypeObj)
Object.defineProperty(person, 'basic', {
enumerable: true,
configurable: false,
get: function() {
return this._name
},
set: function(value) {
this._name = (this._name !== value ? '你是chujiu' : '你不是chujiu')
}
})
console.log(person.basic) // 你不是chujiu
person.basic = 'chujiu'
console.log(person.basic) // 你是chujiu
二、创建对象模式
关于对象创建,本质上是利用new字符或则原型对象继承(构造函数.prototype)
1、工厂模式(通过内部new字符)
// 工厂模式
const createPerson = (...data) => {
const [name, gender] = data
const result = new Object()
result.name = name
result.gender = gender
return result
}
console.log(createPerson('埋埋', '女'))
// {name: "埋埋", gender: "女"}
2、构造函数模式(通过为外部new字符)
// 构造函数模式
const createPerson = function(name, gender) {
this.name = name
this.gender = gender
}
console.log(new createPerson('埋埋', '女'))
// createPerson {name: "埋埋", gender: "女"}
注意:
2.1、new字符所经历的过程,从而绑定this指向新对象
(1、创建一个新对象(空的)
(2、将构造函数的作用域赋值给新对象,即this绑定到新对象
(3、执行构造内部代码(给对象添加属性和方法)
(4、返回新的对象
2.2、任何函数,只要通过new操作符来调用,那它就是构造函数;而任何构造函数没有通过new操作符来调用,那它和其他函数也没有区别。
3、原型模式
也就是将属性写在其构造函数的原型对象上(构造函数.prototype)。
// 原型模式
const createPerson = function() {
}
createPerson.prototype.name = '埋埋'
createPerson.prototype.gender = '女'
console.log(new createPerson())
// createPerson {} 【属性都在其原型对象上】
注意:
3.1、原型,原型链
原型:每个实例或则构造函数都有个原型对象,即
obj.__proto__ => 原型对象
原型对象.constructor => 构造函数
构造函数.prototype => 原型对象
// 注意obj.__proto__、原型对象.constructor、构造函数.prototype只是指针
// 内部保存着指向地址
原型链:原型链就是以上述为单元,下一个单元就是以原型对象为实例,然后再以上述为链接。最终作用,可以通过原型链来继承,使子类可以使用父类的方法和属性。
3.2、作用域,作用域链
作用域:也可称之为执行环境,决定了变量和函数有权访问的其他数据。每个作用域都有一个变量对象储存着这个作用域内的变量及函数,当关闭网页/函数执行完毕后全局作用域/函数作用域就会销毁,并且接触变量对象中的变量和函数。
作用域链:链接以自身作用域变量对象为开始节点,其后节点为其父类(可能是爸爸,也可能是爷爷)作用域变量对象,最后以全局作用域祖宗变量对象为终止节点。
3.3、in
操作符作用于整个原型链
const createPerson = function() {
}
createPerson.prototype.name = '埋埋'
createPerson.prototype.gender = '女'
const person = new createPerson()
console.log(person.hasOwnProperty('name')) // false
console.log('name' in person) // true
3.4、原型的动态性
const createPerson = function() {
}
createPerson.prototype.name = '埋埋'
createPerson.prototype.gender = '女'
const person = new createPerson()
createPerson.prototype.toSay = () => {
console.log('小埋超可爱')
}
person.toSay()
// 小埋超可爱
实例person依旧可以调用toSay函数,这是因为当我们调用person.toSay()时,首先会在实例中寻找是否有这个方法;若没有,则会沿着原型链上寻找,直到最后找到或没找到。
4、动态原型模式
通过判断条件,动态添加
const createPerson = function(name, gender) {
this.name = name
this.gender = gender
if( !(this.toSay instanceof Function) ) {
createPerson.prototype.toSay = () => {
console.log(`${this.name}超可爱`)
}
}
}
const person = new createPerson('埋埋', '女')
person.toSay()
// 埋埋超可爱
判断是否传入了toSay,传入类型是否为函数,例如若传入函数
const createPerson = function (name, gender, toSay) {
this.name = name
this.gender = gender
this.toSay = toSay
if (!(this.toSay instanceof Function)) {
createPerson.prototype.toSay = () => {
console.log(`${this.name}超可爱`)
}
}
}
const person = new createPerson('埋埋', '女', function() {
console.log(`${this.name}可爱`)
})
person.toSay()
// 埋埋可爱
注意:不能用箭头函数,因为没有this,导致this.toSay = toSay
绑定this失败
5、寄生构造函数模式
const createPerson = function (name, toSay) {
const result = new Object()
result.name = name
result.toSay = toSay
return result
}
const person = new createPerson('埋埋', function() {
console.log(`${this.name}可爱`)
})
console.log(person)
注意:result对象代替默认的createPerson对象
作用:一般用于为某个特殊对象,在不改动相关内置构造函数(比如:Object
)的情况下,统一封装相关的特殊方法或属性,返回含有特殊方法的实例。
6、稳妥构造函数模式
就是在不使用this和new的情况下
就是上述寄生构造函数模式,生成实例不使用new字符的情况
三、继承
继承的本质是通过原型链共享某些属性和方法,方式可分为直接操作原型链或则通过this
绑定,再或者通过class
中的extends
。
1、组合继承
在构造函数中,通过强制绑定this,所实现的继承方式。
function Mammal(mouse) {
this.mouse = mouse
}
function Person(mouse, name, toSay) {
Mammal.call(this, mouse)
this.name = name
this.toSay = toSay
}
const person = new Person('mouse', '埋埋', function() {
console.log(`${this.name}超可爱`)
})
console.log(person)
// Person {mouse: "mouse", name: "埋埋", toSay: ƒ}
2、原型式继承
在原型对象上直接添加属性或方法
const originalObj = {
name: '埋埋',
}
function Person(original) {
function Mammal() {}
Mammal.prototype = original
return new Mammal()
}
const person = Person(originalObj)
console.log(person.name)
// 埋埋
注意:这种方式已经可以Object.create(original)
方法优化了。
3、寄生式继承
就是在已有的对象上添加属性
const originalObj = {
name: '埋埋',
}
function Person(original) {
function Mammal() {}
Mammal.prototype = original
return new Mammal()
}
function test(gender) {
const person = Person(originalObj)
person.gender = gender
return person
}
const person = test("女")
console.log(person.gender)
// 女
4、寄生组合式继承
对于属性,在子类构造函数中通过强制绑定this执行父类构造函数,使得继承父类构造函数属性;对于方法,事先写在父类构造函数的原型对象上,再将由父类构建的实例成为子类的原型对象,从而使得由子类创建的实例继承了父类原型上的方法。
function Parents(name = '') {
this.name = name
}
Parents.prototype.toSay = function() {
console.log(`${this.name}超可爱`)
}
function Son(name) {
Parents.call(this, name)
}
Son.prototype = new Parents()
const son = new Son('埋埋')
console.log(son)
// Son {name: "埋埋"}
console.log(son.toSay())
// 埋埋超可爱