Day 20 - Constructor Function & Prototype

Constructor Function

  • 用来制作大量相似的 object,通常用大写字母做开头
  • 搭配 new 来使用(功能为创造一个新的空的 object)
function Person(name,sex,age){
  console.log(this); // {}
  this.name = name;
  this.sex = sex;
  this.age = age;
  this.sayHi= function(){
    console.log(this.name+" says Hi");
  }
}
let Helen = new Person("Helen","Female",24);
console.log(Helen); // {name:"Helen",sex:"Female",age:24}

let Daniel = new Person("Daniel","Male",23);
console.log(Daniel); // {name:"Daniel",sex:"Male",age:23}

Helen.sayHi(); // Helen says Hi
Daniel.sayHi(); // Daniel says Hi

console.log(Helen.sayHi == Daniel.sayHi); // false

Prototype

假如今天有 100 个物件,使用前面介绍的 Constructor Function,就会产生 100 个 sayHi function,造成记忆体的负担,更好的做法是让这 100 个物件只执行一个 sayHi function,让这 100 个 object 共同使用,这个概念就是 Prototype。

function Person(name,sex,age){
  console.log(this); // {}
  this.name = name;
  this.sex = sex;
  this.age = age;
}

Person.prototype.sayHi = function(){
    console.log(this.name+" says Hi");
}

let Helen = new Person("Helen","Female",24);
console.log(Helen); // {name:"Helen",sex:"Female",age:24}

let Daniel = new Person("Daniel","Male",23);
console.log(Daniel); // {name:"Daniel",sex:"Male",age:23}

Helen.sayHi(); // Helen says Hi
Daniel.sayHi(); // Daniel says Hi

console.log(Helen.sayHi == Daniel.sayHi) // true

使用 prototype 後,可以看到不管是谁执行 sayHi 这个 function,都是指向同一个,所以 console.log(Helen.sayHi == Daniel.sayHi) 会是 true

当我们 console.log Daniel 或是 Helen 时都只能看到姓名性别和年龄,那麽为什麽 Helen.sayHi() 可以看到 Helen says Hi 呢?

事实上就是从 prototye 去继承而来的,在 Helen 这个 object 里面会有 prototype,而里面有 constructor 和 sayHi 这个 function

Prototype Inheritance

下面这个例子因为 name,sex,age 都是一样的参数,所以在 Student() 里面 call 了 Person 的 property 来用,但要特别注意的是,这样的作法,使 Student() 从头到尾都和 Person.prototype 里面的东西毫无关联,所以当我们执行 Daniel.sayHi() 的时候,会出现 Uncaught TypeError: Daniel.sayHi is not a function,是因为 Daniel 根本没有继承 Person.prototype 里面的 function

function Person(name,sex,age){
    console.log(this); // {}
    this.name = name;
    this.sex = sex;
    this.age = age;
  }
  
  Person.prototype.sayHi= function(){
      console.log(this.name+" says Hi");
  }
  function Student(name,sex,age,height,weight){
    Person.call(this,name,sex,age);
    this.height;
    this.weight;
  }
  let Daniel = new Student("Daniel","Male",23,172,56);
  console.log(Daniel);
  Daniel.sayHi(); // Uncaught TypeError: Daniel.sayHi is not a function

如下图可以看到 prototype 里根本没有 sayHi() 这个 function

要解决上述的问题,让 Daniel 继承 prototype 里面的 function 有两种做法。

  1. 直接复制 prototype.function
Student.prototype.sayHi= function(){
    console.log(this.name+" says Hi");
}


此时可以看见 prototype 里有 sayHi() 这个 function,但是这做法并不妥,如果程序码很多的时候要一个一个改,不易维护。
2. Object.create()
使用此方式是较正确的做法
创造一个新的物件来当作 Student 的 prototype,而我们要创造的物件就是 Person.prototype
Student.prototype = Object.create(Person.prototype)

function Person(name,sex,age){
    console.log(this); // {}
    this.name = name;
    this.sex = sex;
    this.age = age;
}

Person.prototype.sayHi= function(){
    console.log(this.name+" says Hi");
}

function Student(name,sex,age,height,weight){
    Person.call(this,name,sex,age);
    this.height;
    this.weight;
}
Student.prototype = Object.create(Person.prototype);

let Daniel = new Student("Daniel","Male",23,172,56);
console.log(Daniel);
Daniel.sayHi(); // Daniel says Hi

此时 Daniel 就已经继承了 Person.prototype 来当作自己的prototype,因此执行 Daniel.sayHi() 可以看到 Daniel says Hi
要注意的是,Daniel 这个物件本身是不含有 sayHi 这个 function 的,在执行 Daniel.sayHi() 的时候因为找不到,就会去 prototype 里面找,如果找到了这个 function 就去执行,如果没有找到,又继续往下面的 prototype 里面找

当然也可以替 Student 做自己的 prototype 和 method

Student.prototype = Object.create(Person.prototype);
Student.prototype.study = function(){
    console.log("I am studying")
}
let Daniel = new Student("Daniel","Male",23,172,56);
console.log(Daniel);
Daniel.sayHi(); // Daniel says Hi
Daniel.study(); // I am studying

let Helen = new Person("Helen","Female",24,160,56);
Helen.sayHi(); // Helen says Hi
Helen.study(); // Uncaught TypeError: Helen.study is not a function
console.log(Helen);

因为 Person 的 proptotype function 只有 sayHi(),study() 是 Student 的 prototype 里面的,所以执行 Helen.study(),会得到 Uncaught TypeError

let Helen = new Student("Helen","Female",24,160,56);
Helen.sayHi(); // Helen says Hi
Helen.study(); // I am studying
console.log(Helen);

如果是 new Student,Helen 这个 object 里面 prototype 才会有 study(),并且也有 sayHi()

补充:

let array = [1,2,3,4];
array.push(5);
console.log(array)

array 可以用 push() 这个 function 是因为我们在创造 array 的时候,他也从 prototype 里面去继承了很多 function,例如 : forEach(),indexOf(),map(),reduce(),slice(),some()....等

而前面我们也有提过 coercion 的概念

let myName = "Helen";
console.log(myName.toUpperCase());

可以直接用 string.toUpperCase() 是因为其实程序码在执行时,默默的执行了 myName = new String("Helen") 从 prototype 继承了 method 来用,而在执行完後又马上恢复原状,让 myName 变回 string


<<:  Day6# 流程控制

>>:  DAY 6 - 狗狗

Day13 原来Background可以有这麽多设定

Background包含哪些属性? Background是缩写,包含了以下几个CSS属性 back...

[Day24]-开发GUI程序使用tkinter

建立视窗 视窗元件配置管理员 Pack() 方法 Drid() 方法 功能纽 button 变数类...

Day21 - [丰收款] 以Django Web框架实作永丰API线上支付模拟情境(2) - 购物车与付款方式确认页

我们今天简单带一点Django Template继承的概念,也就是当你有每一页面具备无论在哪一样都有...

[Day 05] 开发之前,先把需求弄清楚

今天我们终於要开始进入主题了, 但是在我们写程序之前, 我们还需要先搞清楚一个东西, 那就是需求, ...

[Java Day05] 1.3. 基本资料的转型

教材网址 https://coding104.blogspot.com/2021/06/java-t...