说一下你对原型与原型链的了解度,有几种方式可以实现继承,用原型实现继承有什么缺点,怎么解决?

构造函数、原型对象、实例对象的关系?
每一个构造函数都有一个原型对象
原型对象都包含一个指向构造函数的指针
实例对象都包含一个指向原型对象的内部指针

构造函数(Constructor)是什么?
构造函数就是一个普通的函数,创建方式和普通函数没有区别,但是函数名首字母要大写,普通函数可以函数名加括号直接调用,构造函数必须通过new关键字创建实例对象来调用函数
比如:Person是构造函数

function Person() {}

函数对象和普通对象:
通过 new 创建的实例对象都是函数对象,其它的就是普通对象
比如:

//函数对象
var foo = new function() {

}
//或者
function Foo() {

}
var foo = new Foo()

**实例对象:**通过new调用构造函数实例化出来的对象
比如:Person 是构造函数,person1是实例对象

function Person() {} //声明构造函数Person
var person1 = new Person() //使用new操作符调用Person实例化了一个对象person1

prototype(原型):
prototype代表原型,原型是每一个函数对象都具有的属性
普通对象没有prototype原型属性
比如:prototype是原型属性Person.prototype就是原型对象,也就是构造函数的一个prototype属性,prototype属性指向原型对象Person.prototype

function Person() {}
var person1 = new Person()

原型的作用:
就是为了把所有的对象连接起来,共享公共属性和方法,通过构造函数生成的所有实例对象都能够共享属性,这个共享属性就存放原型对象

原型与构造函数的关系:
有构造函数,就可以在原型上创建可以 继承 的属性,也就是在原型上添加属性,这些属性被new操作符创建的实例对象所共享

proto(隐式原型属性):
每一个对象隐含的一个属性。函数对象和普通对象都有 __proto__隐式原型属性,__proto__用于指向自身构造函数的原型对象prototype
比如:
实例对象的隐式原型属性指向构造函数原型对象

function Person() {}
var person1 = new Person()
//实例对象通过隐式原型指向原型对象
person1.__proto__ === Person.prototype 
//构造函数的隐私原型指向null
Person.__proto__ === null

总结:属性用来指向对象
构造函数的原型属性指向这个构造函数的原型对象
实例对象的隐式原型属性__proto__指向构造函数的原型对象
构造函数的__proto__指向Function的原型对象
Function的__proto__指向Function的原型对象
Function的原型对象的__proto__执向Object的原型对象
Object的原型对象的__proto__指向null
比如:

function Person(name) {
   this.name = name;
}
let person = new Person('xiaoming')

console.log(person.__proto__ == Person.prototype) //true
console.log(Person.__proto__ == Function.prototype) //true
console.log(Function.__proto__ === Function.prototype) //true
console.log(Person.prototype.__proto__ == Object.prototype) //true
console.log(Function.prototype.__proto__ === Object.prototype) //true
console.log(Object.prototype.__proto__ === null) //true

Object作为JavaScript的内建函数由Function构造出来,Function作为内建构造函数又是对象,函数都是对象

原型链的理解:
原型链是实现继承的一种重要方式。
每个构造函数都有一个原型对象prototype,每一个实例对象都存在一个__proto__,即隐式原型属性。查找对象的属性时,首先查找对象本身是否存在该属性,不存在的话就会通过对象的隐式原型属性__proto__在原型链上查找

因为JS中万物皆对象,所有的对象都存在__proto__属性,就会形成一条__proto__链条,__proto__最终会指向原型链的顶端,即Object.prototype,Object.prototype.proto 指向null

// 定义一个构造函数 Fun
var Fun = function() {

}
// 在构造函数的原型上添加共享方法 name
Fun.prototype.name = function() {
   console.log('xiaoming')
}
// new调用构造函数,生成实例化对象fn
var fn = new Fun(); 
// fn调用name方法
fn.name(); //输出xiaoming 

为什么调用fn.name()时,会输出 ‘xiaoming’?
fn本身不存在name方法,那么就会通过__proto__去寻找name方法,fn是Fun的实例,所以 fn.__proto === Fun.prototype,在原型上添加的方法是被实例对象共享的,所以name方法 可以被实例对象fn调用

继承的方式有哪些?
(1)原型链继承
将子类的原型对象赋值给父类的实例
比如:

// 父类型
function Father(name) {
    this.name = name
}
Father.prototype.age = function() {
    console.log('10')
}
function Son() {

}
// 将子类的原型对象赋值给父类的实例
Son.prototype = new Father('5') 
const son = new Son()
console.log(son.name)  //输出 5
son.age(); // 输出 10

原型链实现继承本质就是 将子类原型对象指向父类的实例对象,所以当子类的实例通过__proto__访问子类的原型对象时,也可以访问到父类的实例对象,

(2)借用构造函数继承
在子类型构造函数通过call()调用父类型的构造函数,改变this的指向执行一次构造函数

function Father() {
     this.name1 = '1'
 }
 function Son() {
     Father.call(this)
     this.name2 = '2'
 }
 const son = new Son()
 console.log(son)

结果:
在这里插入图片描述

(3)组合继承(原型链 + 构造函数)
通过调用父类构造函数,继承父类的属性并保留传参的优点,然后通过将父类实例作为子类原型,实现函数复用

function Father() {
     this.numbers = ['1', '2', '3']
 }
 function Son() {
     Father.call(this)
 }
 const son = new Son()
 // 父类的实例作为子类的原型
 son.prototype = new Father()
 console.log(son.numbers)
 console.log(son)

结果:
在这里插入图片描述

(4)寄生式继承
通过寄生方式,砍掉父类的实例属性,这样,在调用两次父类的构造的时候,就不会初始化两次实例方法/属性,避免组合继承的缺点

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值