constructor VS 构造函数 和 实例方法 VS prototype方法
class Person {
constructor(name) {
this.name = name
}
sayHello() {
return 'hello, I am ' + this.name
}
}
var brynn = new Person('Kevin')
// 相同点
console.log(brynn)
// 不同点
console.log(Object.keys(Person.prototype))
console.log(Object.getOwnPropertyNames(Person.prototype))
prototype上方法是否可枚举,class:否
function Person(name) {
this.name = name
}
Person.prototype.sayHello = function () {
return 'hello, I am ' + this.name
}
var brynn = new Person('Kevin')
// 相同点
console.log(brynn)
// 不同点
console.log(Object.keys(Person.prototype))
console.log(Object.getOwnPropertyNames(Person.prototype))
prototype上方法是否可枚举,function:是
是否可枚举是通过defineProperty方法实现的
setter/getter和value/writable:true不能共存
let brynn = {}
brynn.age = 21
Object.defineProperty(brynn, 'description', {
enumerable: false,
get: () => {
if (brynn.age > 18) return 'adult'
else return 'teenager'
},
set: (newSex) => {
if (brynn.age > 20) brynn.description = newSex
},
})
console.log(brynn)
console.log(Object.keys(brynn))
console.log(Object.getOwnPropertyNames(brynn))
实例属性 VS 构造函数
class Person {
name = 'brynn'
}
class Person {
constructor(name) {
this.name = name
}
}
function Person(name) {
this.name = name
}
静态方法和静态属性 && 构造函数的属性
class Person {
constructor(name) {
this.name = name
}
static sayHello() {
return 'hello, I am ' + this.name
}
static age = 20
}
var brynn = new Person('Kevin')
console.log(brynn)
console.log(Person.sayHello()) // √
console.log(brynn.sayHello()) // ×
function Person(name) {
this.name = name
}
Person.sayHello = function () {
return 'hello, I am ' + this.name
}
Person.age = 20
var brynn = new Person('Kevin')
// 相同点
console.log(brynn)
console.log(Person.sayHello()) // √
console.log(brynn.sayHello()) // ×
setter/getter
class Person {
constructor() {
this.sex = 'female'
}
set name(newName) {
console.log('查看this.name', this.name)
console.log(newName)
}
get name() {
return 'brynn'
}
}
var brynn = new Person()
console.log(brynn)
brynn.name = 'brown'
function Person() {
this.sex = 'female'
}
Person.prototype = {
set name(newName) {
console.log('查看this.name', this.name)
console.log(newName)
},
get name() {
return 'brynn'
},
}
var brynn = new Person()
console.log(brynn)
brynn.name = 'brown'
注意,这里的getter和setter都是把属性放到构造函数的prototype上,对象上面并没有(虽然这里看到了),用brynn.hasOwnProperty('name')
返回依然是false
entends VS 组合寄生式继承
class Parent {
constructor(name) {
this.name = name
}
getName() {
console.log(this.name)
}
}
class Child extends Parent {
constructor(name, age) {
super(name) // 调用父类的 constructor(name)
this.age = age
}
}
var child1 = new Child('brynn', '20')
console.log(child1)
console.log(Child)
console.log(Child.prototype)
es6的extends对应的是组合寄生式继承,
function Parent(name) {
this.name = name
}
Object.defineProperty(Parent.prototype, 'getName', {
value: function () {
console.log(this.name)
},
enumerable: false,
})
// Parent.prototype.getName = function () {
// console.log(this.name)
// }
function Child(name, age) {
Parent.call(this, name)
this.age = age
}
Child.prototype = Object.create(Parent.prototype, {
constructor: {
value: Child,
enumerable: false,
writable: true,
configurable: true,
},
})
var child1 = new Child('brynn', '20')
console.log(child1)
console.log(Child)
console.log(Child.prototype)
关键点是:
- ES6的extends自动给子类的prototype添加一个指向子类本身的constructor属性,所以在ES5中模拟时也要添加上去(Object.create的第三个参数)
- ES6的定义的实例方法都是不可枚举的(不能直接将函数方法添加到prototype上,要用defineProperty的value,并且不能用箭头函数,否则this的指向有问题)
对比组合式继承 组合式继承和组合寄生式继承都是将父类的prototype作为子类prototype的proto
区别是在这个过程中是否调用了父类的构造函数
组合继承调用了父类的构造函数,所以会在子类的原型链上添上一些不必要的属性
// 组合寄生继承
Child.prototype = Object.create(Parent.prototype, {
constructor: {
value: Child,
enumerable: false,
writable: true,
configurable: true,
},
})
// 组合继承
Child.prototype = new Parent()