1. 程式人生 > >【JavaScript高階】5、函式高階(原型與原型鏈)

【JavaScript高階】5、函式高階(原型與原型鏈)

一、原型

1. 函式的prototype屬性(圖)
  * 每個函式都有一個prototype屬性, 它預設指向一個Object空物件(沒有我們指定的屬性和方法)(即稱為: 原型物件)
  * 原型物件中有一個屬性constructor, 它指向函式物件
2. 給原型物件新增屬性(一般都是方法)
  * 作用: 函式的所有例項物件自動擁有原型中的屬性(方法)
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>01_原型(prototype)</title>
</head>
<body>

<script type="text/javascript">

  //每個函式都有一個prototype屬性,它預設指向一個object空物件(即稱為:原型物件)
  console.log(Date.prototype,typeof Date.prototype)
  function Fun() { //alt + shift +r(重新命名rename)  // webstorm整體重新命名替換

  }

  console.log(Fun.prototype)  // Object 'object'  預設指向一個Object空物件,沒有我們的屬性
  // 原型物件中有一個屬性constructor, 它指向函式物件
  console.log(Date.prototype.constructor === Date)  // true
  console.log(Fun.prototype.constructor === Fun)  // true

  //給原型物件新增屬性(一般是方法)===》例項物件可以訪問
  Fun.prototype.test = function () {
    console.log("test()")
  }
  var fun = new Fun()
  fun.test()  // test()

</script>
</body>
</html>

二、顯式原型與隱式原型

1. 每個函式function都有一個prototype,即顯式原型(屬性)
2. 每個例項物件都有一個__proto__,可稱為隱式原型(屬性)
3. 物件的隱式原型的值為其對應建構函式的顯式原型的值
4. 記憶體結構(如下圖2-1)
5. 總結:
  * 函式的prototype屬性: 在定義函式時自動新增的, 預設值是一個空Object物件
  * 物件的__proto__屬性: 建立物件時自動新增的, 預設值為建構函式的prototype屬性值
  * 程式設計師能直接操作顯式原型, 但不能直接操作隱式原型(ES6之前)

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>02_顯式原型與隱式原型</title>
</head>
<body>

<script type="text/javascript">
  //定義建構函式
  function Fn() {   // 內部語句: this.prototype = {}

  }
  // 1. 每個函式function都有一個prototype,即顯式原型屬性, 預設指向一個空的Object物件
  console.log(Fn.prototype)
  // 2. 每個例項物件都有一個__proto__,可稱為隱式原型
  //建立例項物件
  var fn = new Fn()  // 內部語句: this.__proto__ = Fn.prototype
  console.log(fn.__proto__)
  // 3. 物件的隱式原型的值為其對應建構函式的顯式原型的值
  console.log(Fn.prototype===fn.__proto__) // true
  //給原型新增方法
  Fn.prototype.test = function () {
    console.log('test()')
  }
  //通過例項呼叫原型的方法
  fn.test()  // test()


</script>
</body>
</html>

                                                                         圖2-1

三、原型鏈

1. 原型鏈(圖解見下方3-1)
  * 訪問一個物件的屬性時,
    * 先在自身屬性中查詢,找到返回
    * 如果沒有, 再沿著__proto__這條鏈向上查詢, 找到返回
    * 如果最終沒找到, 返回undefined
  * 別名: 隱式原型鏈
  * 作用: 查詢物件的屬性(方法)
2. 建構函式/原型/實體物件的關係(圖解3-2)
3. 建構函式/原型/實體物件的關係2(圖解3-3) 

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>03_原型鏈</title>
</head>
<body>

<script type="text/javascript">
  // console.log(Object)
  //console.log(Object.prototype)
  console.log(Object.prototype.__proto__)  // null
  function Fn() {
    this.test1 = function () {
      console.log('test1()')
    }
  }
  console.log(Fn.prototype)  // Object
  Fn.prototype.test2 = function () {
    console.log('test2()')
  }

  var fn = new Fn()

  fn.test1()  // 'test1()'
  fn.test2()  // 'test2()'
  console.log(fn.toString())  // '[object Object]'
  console.log(fn.test3)  // undefined
  // fn.test3()  // 報錯


  /*
  1. 函式的顯示原型指向的物件預設是空Object例項物件(但Object不滿足)
   */
  console.log(Fn.prototype instanceof Object) // true
  console.log(Object.prototype instanceof Object) // false
  console.log(Function.prototype instanceof Object) // true
  /*
  2. 所有函式都是Function的例項(包含Function)(Function是它自身的例項)
  */
  console.log(Function.__proto__===Function.prototype)
  /*
  3. Object的原型物件是原型鏈盡頭
   */
  console.log(Object.prototype.__proto__) // null

</script>
</body>
</html>

                                                                          圖3-1

                                                                           圖3-2

                                                                           圖3-3

四、原型鏈—屬性問題

1. 讀取物件的屬性值時: 會自動到原型鏈中查詢
2. 設定物件的屬性值時: 不會查詢原型鏈, 如果當前物件中沒有此屬性, 直接新增此屬性並設定其值
3. 方法一般定義在原型中, 屬性一般通過建構函式定義在物件本身上

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>04_原型鏈_屬性問題</title>
</head>
<body>

<script type="text/javascript">
  function Fn() {

  }
  Fn.prototype.a = 'xxx'
  var fn1 = new Fn()
  console.log(fn1.a,fn1)  // 'xxx' Fn  讀取物件的屬性值時: 會自動到原型鏈中查詢

  var fn2 = new Fn()
  fn2.a = 'yyy'  // 設定物件的屬性值時: 不會查詢原型鏈, 如果當前物件中沒有此屬性, 直接新增此屬性並設定其值
  console.log(fn2.a,fn2)  // 'yyy' Fn

  function Person(name,age) {
    this.name = name
    this.age = age
  }
  Person.prototype.setName = function (name) {
    this.name = name
  }
  var p1 = new Person('tom',12)
  p1.setName('bob')
  console.log(p1)  // Person {name: "bob", age: 12}

  var p2 = new Person('jack',13)
  p2.setName('cat')
  console.log(p2)  // Person {name: "cat", age: 13}
  console.log(p1.__proto__ === p2.__proto__)  // true


</script>
</body>
</html>

 五、探索instanceof

1. instanceof是如何判斷的?
  * 表示式: A instanceof B
  * 如果B函式的顯式原型物件在A物件的原型鏈上, 返回true, 否則返回false
2. Function是通過new自己產生的例項

 

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>05_探索instanceof</title>
</head>
<body>

<script type="text/javascript">
  //以下兩個案例可由上面圖3-3分析
  /*
  案例1
   */
  function Foo() {

  }
  var f1 = new Foo()
  console.log(f1 instanceof Foo)  // true
  console.log(f1 instanceof Object)  // true

  /*
  案例2
   */
  console.log(Object instanceof Function)  // true
  console.log(Object instanceof Object)  // true
  console.log(Function instanceof Function)  // true
  console.log(Function instanceof Object)  // true

  function Foo() {

  }
  console.log(Object instanceof Foo)  // false


</script>
</body>
</html>

六、面試題

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>06_面試題</title>
</head>
<body>

<script type="text/javascript">
  /*
  測試題1
   */
  var A = function() {

  }
  A.prototype.n = 1

  var b = new A()

  A.prototype = {
    n: 2,
    m: 3
  }

  var c = new A()
  console.log(b.n, b.m, c.n, c.m)  // 1 undefined 2 3


  /*
   測試題2
   */
  var F = function(){};
  Object.prototype.a = function(){
    console.log('a()')
  };
  Function.prototype.b = function(){
    console.log('b()')
  };
  var f = new F();
  f.a()  // a()
  f.b()  // b()
  //F.a()  //報錯
  F.b()  // b()

</script>
</body>
</html>