目录
1、构造函数的prototype原型修改
在JavaScript中,用new关键字来调用的函数,称为构造函数,构造函数首字母一般大写
function foo() {}
// 给原型添加数据
foo.prototype = {
name: 'test',
age: 18
}
let p1 = new foo()
// 通过Object.defineProperty方式添加constructor
Object.defineProperty(foo.prototype, 'constructor', {
enumerable:false,
configurable: true,
writable: true,
value: foo
})
2、原型式继承
注意:是原型式继承,不是原型链继承
该方法的原理是创建一个构造函数,构造函数的原型指向对象,然后调用 new 操作符创建实例,并返回这个实例,本质是一个浅拷贝。
let obj = {
name: 'why',
age: 18
}
// 原型式继承函数
function createObject (o) {
let newObj = {}
Object.setPrototypeOf(newObj, o)
return newObj
}
let info = createObject(obj)
console.log(info) // 输出 {}
console.log(info.__proto__) // 输出 {name: 'why', age: 18}
// 或者直接用Object.create() 也是一样的效果
let obj = {
name: 'why',
age: 18
}
let info = Object.create(obj)
3、借助构造函数继承
在解决原型中包含引用类型值所带来问题的过程中,开始使用一种叫做借用构造函数(伪造对象/经典继承)的技术。基本思想:在子类型构造函数的内部调用超类型构造函数。
借用构造函数的优势:可以在子类型构造函数中向超类型构造函数传递参数。
function Person(name, age) {
this.name = name
this.age = age
}
function Student(name, age) {
Person.call(this, name, age)
}
let p1 = new Student('test1', 18)
let p2 = new Student('test2', 19)
console.log(p1) // 输出 Student {name: 'test1', age: 18}
console.log(p2) // 输出 Student {name: 'test2', age: 19}
4、寄生式工厂继承 (不推荐使用)
寄生式继承的思路与寄生构造函数和工厂模式类似,即创建一个仅用于封装继承过程的函数,该函数在内部以某种方式来增强对象,后再像真的是它做了所有工作一样返回对象。
let personObj = {
running: function() {
console.log('running')
}
}
function createStudent(name) {
let stu = Object.create(personObj)
stu.name = name
stu.studing = function() {
console.log('studing')
}
return stu
}
let stuObj = createStudent('why')
5、寄生组合式继承 (推荐)
通过借用构造函数来继承属性,通过原型链的方式来继承方法,而不需要为子类指定原型而调用父类的构造函数,我们需要拿到的仅仅是父类原型的一个副本。因此可以通过传入子类和父类的构造函数作为参数,首先创建父类原型的一个复本,并为其添加constrcutor,最后赋给子类的原型。这样避免了调用两次父类的构造函数,为其创建多余的属性。
JS的继承方式有很多种,最理想的继承方式是寄生组合式继承。
function Person(name, age, friends) {
this.name = name
this.age = age
this.friends =friends
}
Person.prototype.running = function() {
console.log('running')
}
function Student(name, age, friends, son, score) {
Person.call(this, name, age, son, friends)
this.son = son
this.score = score
}
Student.prototype = Object.create(Person.prototype)
// 将stu.constructor.name的指向为Student
Object.defineProperty(Student.prototype, 'constructor', {
enumerable: false,
configurable: true,
writable: true,
value: Student
})
Student.prototype.studing= function() {
console.log('studing')
}
let stu = new Student('why', 18, ['kobe'], 111, 100)
console.log(stu)
// 输出
stu.running()
stu.studing()
// 如果是多个需要更改construcotr 可以封装一个工具类函数
// 第一个参数是子参数,第二个参数是要继承的
function inheritPrototype(SubType, SuperType) {
SubType.prototype = Object.create(SuperType.prototype)
// 将stu.constructor.name的指向为Student
Object.defineProperty(SubType.prototype, 'constructor', {
enumerable: false,
configurable: true,
writable: true,
value: SubType
})
}
function Person(name, age, friends) {
this.name = name
this.age = age
this.friends =friends
}
Person.prototype.running = function() {
console.log('running')
}
function Student(name, age, friends, son, score) {
Person.call(this, name, age, son, friends)
this.son = son
this.score = score
}
inheritPrototype(Student, Person)
Student.prototype.studing= function() {
console.log('studing')
}
let stu = new Student('why', 18, ['kobe'], 111, 100)
console.log(stu)
// 输出
stu.running()
stu.studing()
6、原型内容判断方法
let obj = {
name: 'why',
age: 18
}
let info = Object.create(obj, {
address: {
value: '长沙市',
enumerable: true
}
})
// 判断属性是否在自己原型上
// hasOwnProperty方法判断
console.log(info.hasOwnProperty('address')) // 输出 true
console.log(info.hasOwnProperty('name')) // 输出 false
// in 操作符: 不管在当前对象还是原型中返回的都是true
console.log('address' in info) // 输出 true
console.log('name' in info) // 输出 true
hasOwnProperty: 对象是否有某一个属于自己的属性(不是在原型上的属性)
in / for in:判断某个属性是否在某个对象或对象的原型上
instanceof:用于检测构造函数的prototype,是否出现在某个实例对象的原型链上
isPrototypeOf:用于检测某个对象,是否出现在某个实例对象的原型链上 ,譬如
function Person() {
}
let p = new Person()
console.log(p instanceof Person) // 输出 true
console.log(Person.prototype.isPrototypeOf(p)) // 输入 true