原型和繼承

原型和繼承

1、原型

說到原型以前,先複習一下對象建立方式:app

1. 使用對象字面量的形式建立 (最簡單)

<script>

  var obj = { a: 1}
  obj.b = 2
  console.log(obj)

</script>

2. 使用工廠函數的方式

<script>

  function CreatePerson (name, age) {
    var obj = new Object ()
    obj.name = name
    obj.age = age
    obj.sayHi = function () {
      console.log('哈嘍')
    }
    return obj
  }
  var person1 = CreatePerson('張三', 18)
  var person2 = CreatePerson('李四', 20)

</script>

3. 使用構造函數的方式(對工廠函數的改進)

<script>

  function Person (name, age) {
    this.name = name
    this.age = age
    this.sayHi = function () {
      console.log('哈嘍')
    }
  }
  var person1 = new Person('張三',18)
  var person2 = new Person('李四',20)

</script>
  • 用new操做符調用函數時,會自動執行下面的操做函數

    • 建立(或構造)一個新的對象
    • 這個新對象會執行prototype鏈接
    • 這個新對象會綁定到函數調用的this
    • 若是函數沒有返回其餘對象, 那麼new表達式中的函數調用自動返回這個新對象
//咱們來舉個例子
var Foo() {
  //這是一個普通函數
}
Foo() //普通函數調用
var a = new Foo()//在普通調用前加一個new關鍵字後,這個調用就變成了構造函數調用
  • new會劫持全部普通函數,並用構造函數的形式調用它優化

    var a = new Foo() 這句話的執行機制: Foo是一個普通函數,在使用new調用時,它就會構造一個對象,而且賦值給athis

  • 簡單點來講 js中的 構造函數 :全部帶new的函數調用
  • 用new的方法來調用Foo會產生一個新對象a,這個新對象的內部連接__proto__會關聯到Foo的原型對象上
    a.__proto__ == Foo.prototypeprototype

4. 使用原型的方式

  • 原型的初步模式
<script>
  function Person () { }

  Person.prototype.name = '張三'
  Person.prototype.age = 18
  Person.prototype.sayHi = function () {
      console.log('哈嘍')
    }

  var person1 = new Person()
  var person2 = new Person()
</script>
  • 優勢:原型中全部的屬性和方法均可以被實例對象共享
  • 缺點:全部的實例對象都公用一套原型和方法,沒有差別化,不能修改
咱們來了解一下實例對象、構造函數和原型之間的聯繫

咱們來了解一下實例對象、構造函數和原型之間的聯繫

  • 優化模式(原型重構)
    上面例子,每次添加一個屬性和方法,就要從新寫一遍,比較繁瑣,如今把這些屬性和方法都放入一個對象中,再賦值給Person的原型
<<script>

  function Person () { }

  Person.prototype = {
    name: '張三',
    age: 18,
    sayHi: unction () {
      console.log('哈嘍')
    }
  }

  var person1 = new Person()
  var person2 = new Person()

  console.log(person1.constructor == Person) // false
  console.log(person1.constructor == Object) // true

</script>
  • js 中全部的對象都有一個特殊的內置屬性[[prototype]] ,這個屬性其實就是用來對其餘對象的引用
  • 這種寫方法改變了原型內部的constructor指針的屬性了 ,再也不指向Person函數了,若是這個指針很重要,那麼能夠手動設置
<<script>

  function Person () { }

  Person.prototype = {
    constructor:Person,
    name: '張三',
    age: 18,
    sayHi: unction () {
      console.log('哈嘍')
    }
  }

  var person1 = new Person()
  var person2 = new Person()

  console.log(person1.constructor == Person) // true
  console.log(person1.constructor == Object) //false

</script>
若是要訪問對象中的屬性,咱們要通過哪些機制呢?
<script>

  var obj = {
    a: 2
  }

  console.log(obj.a) // 2

</script>

總結:prototype 的機制

prototype機制就是存在於對象中的一個內部連接,它會引用其餘對象。指針

這個連接的做用就是: 若是在對象上沒有找到須要的屬性或者方法,引擎就會在prototype關聯的對象上進行查找。若是在後者的對象中沒有找到這個屬性和方法,引擎就會繼續查找他的prototype,以此內推,若是沒有找到,就會返回underfind。
這就是所謂的原型鏈。(有點像嵌套的做用域鏈)code


2、繼承

構造函數就是初始化一個實例對象,對象的prototype屬性是繼承一個實例對象。對象

繼承的方式:

1. 經過原型的方式繼承

<script>
  function Bird(legNum, wingNum) {
    this.leg = legNum;
    this.wing = wingNum;
  }
  Bird.prototype.appearance = function () {
    console.log('鳥類都有漂亮的羽毛')
  }
  function Swan (beh,legNum,wingNum) {
    this.behaviour =  beh;
  }
  Swan.prototype = new Bird(2,2,);// 繼承了Bird中的屬性和方法,可是不能經過傳參修改這些值

  Swan.prototype.story = function () {
    console.log('是由醜小鴨變成的');
  }
   // 白天鵝
  var whiteSwan = new Swan('會游泳')
  console.log(whiteSwan)
  whiteSwan.appearance() // 鳥類都有漂亮的羽毛
  whiteSwan.story() // 是由醜小鴨變成的
</script>
  • 優勢:全部的屬性和方法都可以進行繼承
  • 缺點:若是繼承了屬性,屬性值在後期沒法修改

2. 使用call()方法調用實現繼承

<script>
  function Person (name, age) {
  this.name = name;
  this.age = age;
  }
  Person.prototype.behaviour = function () {
    console.log('你們好,我是一類人')
  }


  function Man(name, age, gender) {
    Person.call(this, name ,age);
    this.gender = gender;
  }
  //能夠建立多個實例對象
  var man1 = new Man('張三', 18, '男')
  var man2 = new Man('李四', 20, '女')
  //可是 不能調用原型中的屬性和方法
  console.log(man1)
  console.log(man2)
  man1.behaviour()//會報錯,這種方法繼承不到Person函數中原型中的屬性方法
</script>

call(this, 參數1 , 參數2...)繼承

  • 使用call方法調用中的參數this 把this指向了調用者
  • 優勢:能夠繼承全部在構造函數體中書寫的屬性,而且能夠進行傳參
  • 缺點: 沒法繼承原型中的屬性和方法

3. 組合方式繼承(使用call和原型的方式組合繼承)

  • call方法:繼承構造函數體中的屬性,並能夠傳參修改
  • 原型方式:能夠繼承原型鏈中的屬性ip

  • 看實例

<script>
 // 組合繼承
 function Person (name, age) {
    this.name = name;
    this.age = age;
  }
  //Person的原型
  Person.prototype.behaviour = function () {
    console.log('你們好,我是一類人')
  }

  //console.log(Person())//underfined

  //Man 的構造函數
  function Man(name, age, gender) {
    //call調用的方法進行繼承,這個方法改變了Man這個構造函數內部的指向(即constructor指針)
    Person.call(this, name ,age);//第二次調用Person
    this.gender = gender;
  }
  //這裏的經過new建立了一個實例對象,並把這個實例對象賦值給了Man的原型對象, 因此改變了 Man原型對象的內部指針(constrouctor)
  //本來Man 的原型對象中的constructor指向Man這個函數的,可是如今被改變成指向Person這個函數了

  Man.prototype =  new Person() //第一次調用Person
  //Man 原型對象中的方法
  Man.prototype.action = function () {
    console.log('吃飯、睡覺、打豆豆')
  }
  //能夠建立多個實例對象
  var man1 = new Man('張三', 18, '男')
  var man2 = new Man('李四', 20, '女')
  console.log(man1)
  man1.behaviour()
  man1.action()
</script>
  • 優勢:全部的屬性和方法都可以被繼承,而且全部的屬性都可自由傳參。
  • 缺點:每調用一次Man構造函數時,就會調用兩次Person函數

4. 拷貝繼承方法 (將原對象進行遍歷,把它的屬性和方法賦值給新的對象)

  • 看代碼
<script>
//構造函數
  function Person (name, age) {
    this.name = name;
    this.age = age;
  }
  Person.prototype.behaviour = function () {
    console.log('你們好,我是一類人')
  }

  function Man (name, age, gender) {
    this.gender = gender
    var person = new Person(name,age)
    // 在Man函數內部對Person進行遍歷, 把People具備的屬性賦值給Man
    for (var k in person) {
      this[k] = person[k]
    }
  }
  Man.prototype.action = function () {
    console.log('吃飯、睡覺、打豆豆')
  }
  //建立實例對象man
  var man = new Man('張三', 18, '男')
  var man2 = new Man('李四', 20, '女')
  console.log(man.name)
  console.log(man.age)
  console.log(man)
  console.log(man2)
  man.behaviour()

</script>
  • 優勢:能夠傳參,能夠繼承到原型中屬性和方法

    5. this的方式(這種方式與call的方式相似)

<<script>
   //構造函數
  function Person (name, age) {
    this.name = name
    this.age = age
  }
  Person.prototype.behaviour = function () {
    console.log('你們好,我是一類人')
  }

  function Man (name, age, gender) {
  //在Man中添加一個屬性people,把People這個函數體賦值給Man的屬性people,而後再進行調用
    this.people = Person
    //進行調用
    this.people(name, age)

    this.gender = gender
  }
  Man.prototype = function () {
    console.log('吃飯、睡覺、打豆豆')
  }
  //實例對象
  var man = new Man('張三', 18 , '男')

  console.log(man)
  man.behaviour()// 報錯  不能繼承到Person原型中的屬性方法
  </script>
  • 優勢: 能夠傳參
  • 缺點:原型內的方法沒法繼承,而且還多了一個屬性people
相關文章
相關標籤/搜索