美文网首页
面向对象,原型/原型链/继承

面向对象,原型/原型链/继承

作者: _咻咻咻咻咻 | 来源:发表于2021-01-28 17:33 被阅读0次

一、设计模式

工厂模式

优点:解决了创建多个类似对象的问题
缺点:不知道创建的对象的类型

function factory(name, age) {
  var obj = new Object()
  obj.name = name
  obj.age = age
  obj.sayName = function () {
    console.log(this.name)
  }
  return obj
}
var factory1 = factory('zhangsan', 20)
var factory2 = factory('lisi',15)

构造函数模式

Object,Array等都是原生构造函数,我们也可以自定义构造函数。
构造函数名首字母大写来区分普通函数。
优点:可以知道创建的对象的类型
缺点:构造函数内定义的方法,在每个实例上都重新创建一遍。即使将方法放在全局,构造函数内将全局函数的指针给方法,也会造成大量全局函数,没有封装性了。

function Persion(name, age) {
  this.name = name
  this.age = age
  this.sayName = function () {
    console.log(this.name)
  }
  this.sayAge = sayAge
}
function sayAge(){
  console.log(this.age)
}
var persion1 = new Persion('lisi', 20)
persion1.sayName() //lisi
var persion2 = new Persion('wangwu', 19)
persion2.sayName()  //wangwu

构造函数与工厂函数的不同:
1)没有显式的创建对象
2)直接将属性赋给this
3)没有return语句
创建一个实例用的是new操作符。new操作符会经历以下步骤:
1)隐式的创建一个对象
2)将this指向这个对象
3)执行构造函数中的代码
4)return这个对象
persion1和persion2是Persion的两个实例,他们都有一个construtor属性,指向Persion。
persion1.constructor == Persion //true
可以用instanceof检查实例的类型:

console.log(persion1 instanceof Persion)   //true
console.log(persion1 instanceof Object)    //true

构造函数直接调用时,this指向window。

原型模式

每个函数都有原型属性prototype,它是一个指针,指向一个对象,这个对象包含这个类型的所有实例共享的属性和方法。原型对象会自动获得constructor属性,这个属性也是一个指针,指向prototype所在函数。Person.prototype.constructor == Person。

当调用构造函数(Person)创建一个实例(person1)后,这个实例(person1)会有一个属性proto指向构造函数的原型(Person.prototype)。proto对脚本不可见,且这个关系只存在于实例和构造函数的原型对象之间,不存在于实例和构造函数之间。

优点:公共的方法不需要在每个实例创建一次,他们用的是同一个方法
缺点:如果原型中的属性有引用类的值,比如数组,其中一个实例修改这个值,所有实例的这个属性都将被修改。

functon Person(){

}
Person.prototype.name = 'zhangsan'
Person.prototype.age = 20
Person.prototype.sayName = function(){
  console.log(this.name)
}
var person1 = new Person()
person1.sayName() //zhangsan
var person2 = new Person()
person2.sayName() //zhangsan

组合使用构造函数模式和原型模式

这是最常用的创建自定义类型的方式,构造函数模式定义实例属性,原型模式定义方法和共享的属性。所以每个实例都会有自己的一份实例属性的副本,但又共享对方法的引用。

functon Person(name){
  this.name = name
  this.friend = ['zhang','li']
}
Person.prototype = {
  constructor: Person,
  sayName: function () {
    console.log(this.name)
  }
}
var person1 = new Person('wang')
var person2 = new Person('chen')
person1.friend.push('aaa')
console.log(person2.friend) // ['zhang','li']
console.log(person1.sayName === person2.sayName) //true

动态原型模式

将所有信息都封装在构造函数内。

functon Person(name){
  this.name = name
  this.friend = ['zhang', 'li']
  if (typeof this.sayName != 'function') {
    Person.prototype.sayName = function () {
      console.log(this.name)
    }
  }
}

二、继承

原型链

原型链是实现继承的主要方法。利用原型让一个引用类型的继承另一个引用类型的属性和方法。
实现方式:让一个构造函数的原型对象等于另一个类型的实例。

function A() {
  this.name = 'A'
}
A.prototype.sayA = function () {
  console.log(this.name)
}
function B() {
  this.age = 10
}
B.prototype = new A()
B.prototype.sayB = function () {
  console.log(this.age)
}
var c = new B()
c.sayA()  // A

以上,B继承了A,通过将A的实例赋给B.prototype,实现的本质是重写原型对象,代之以一个新类型的实例,A的实例中的所有属性和方法也存在于B.prototype中了。此时,A的属性name存在于B的原型中,c.constructor = A。

当读取一个实例属性时,首先在实例中搜索该属性,如果没有搜索实例的原型,如果没有顺着原型链向上搜索。
所以以上例子搜索c.sayA()的过程是,1)搜索实例c;2)搜索B.prototype;3)搜索A.prototype,找到方法。(若A.prototype中也没有找到,则会搜索Object.prototype)
此时:

console.log(c instanceof B) //true
console.log(c instanceof A) //true
console.log(c instanceof Object) //true

原型链的问题:

  1. 原本在实例上的属性被继承后变成了原型属性,就出现了引用类型的属性的问题。
  2. 创建自类型的实例时,不能向超类型的构造函数传递参数。

借用构造函数

解决原型链的第一个问题,使用借用构造函数。

function A(name) {
  this.arr = [1, 2]
  this.name = name
}
function B(name) {
  A.call(this, name)
}
var b1 = new B('zhangsan')
b1.arr.push(3)
console.log(b1.name) // zhangsan
console.log(b1.arr) // [1,2,3]
var b2 = new B('lisi')
console.log(b2.name) // lisi
console.log(b2.arr) // [1, 2]

优点:解决了引用类型值的问题;可以在子类型构造函数中向超类型构造函数传递参数。
缺点:无法避免将方法放在构造函数内部的问题

组合继承

又叫伪经典继承。将原型链和借用构造函数的技术组合,发挥二者之长。

function A(name) {
  this.arr = [1, 2]
  this.name = name
}
A.prototype.sayName = function(){
  console.log(this.name)
}
function B(name, age) {
  A.call(this, name)
  this.age = age
}
B.prototype = new A()
B.prototype.constructor = B
B.prototype.sayAge = function(){
  console.log(this.age)
}
var b1 = new B('zhangsan',20)
b1.arr.push(3)
console.log(b1.sayName()) // zhangsan
console.log(b1.arr) // [1,2,3]
var b2 = new B('lisi', 10)
console.log(b2.sayName()) // lisi
console.log(b2.arr) // [1, 2]

相关文章

  • lesson 5 面向对象及原型链 2021-04-29

    课程标题 面向对象及原型链 课程目标 面向对象思想 原型及原型链 继承 知识点 面向对象思想 原型链的指向 new...

  • JS汇总---面向对象&数组

    面向对象 js原型链的继承 静态属性怎么继承 js原型链以及特点 面向对象有哪几个特点 封装,继承,多态 对象的继...

  • Javascript继承的原理

    JavaScript的继承是通过原型链继承,与传统面向对象的继承方式不同。 prototype与原型 我们从头开始...

  • 面向对象(三)

    内容承接 面向对象(二) 继承 ECMAScript中描述了原型链的概念,并将原型链作为实现继承的主要方法。其基本...

  • js_继承及原型链等(四)

    js_继承及原型链等(三) 1. 继承 依赖于原型链来完成的继承 发生在对象与对象之间 原型链,如下: ==原型链...

  • JavaScript之面向对象编程

    五、面向对象编程 目录:面向对象原型继承、面向对象class继承(ES6引入的) 1.面向对象原型继承 类:模板 ...

  • js 继承

    4种继承方式 原型式继承给原型对象添加属性原型替换子类的原型对象替换为父类的原型对象 原型链继承 借用构造函数继承...

  • Js面向对象

    一、Js面向对象 二、Js原型及原型链 1、原型 JavaScript 的每个对象都继承另一个父级对象,父级对象称...

  • 面向对象&原型&原型链&继承

    面向对象 面向对象的基本特征1.继承2.封装3.多态 创建对象的方法1.基本方法 缺点:每创建一个方法都要把这些代...

  • 面向对象,原型/原型链/继承

    一、设计模式 工厂模式 优点:解决了创建多个类似对象的问题缺点:不知道创建的对象的类型 构造函数模式 Object...

网友评论

      本文标题:面向对象,原型/原型链/继承

      本文链接:https://www.haomeiwen.com/subject/ejjttltx.html