初学者跪着学JavaScript Day21 : 原型毕露(下)

一日客语:中文:圆 客语: 眼ienˇ

学习内容

  • 检查实例的建构器类型:instanceof、constructor
  • 利用实例的constructor属性来创建物件
  • 解惑昨天问题
  • 透过原型可以做到复制

前述

昨天讲述为啥修改了Cat原型後使用Object.getPrototypeOf(niki).constructor
为何是印出 Object 呢
一开始 Object.getPrototypeOf(nini).constructor
明明是Cat为何原型有这种差异呢

就继续看下去~


function Cat() {
    this.eat = () => 'apple';
}

const nini = new Cat();
Cat.prototype.sleep = 'sleep';
console.log(Object.getPrototypeOf(nini)); //{ sleep: 'sleep' }

//原型变动
//新物件改掉Cat原型
Cat.prototype = {
    go: function () {
        return 'go';
    },
};

const niki = new Cat();

console.log(Object.getPrototypeOf(niki)); //{ go: [Function: go] }
Object.getPrototypeOf(niki).constructor//Object

检查实例的建构器类型:instanceof、constructor

instanceof :niki instanceof Cat 判断实例是不是透过建构器函式建立的
constructor:实例.constructor 可以找到创建实例的建构函式

instanceof

wendy instanceof Wendy

运作方式:
会先查Wendy的原型

此原型是否出现在wendy的原型链上

function Wendy() {}
function Ann() {}
Ann.prototype = new Wendy(); //目前 Ann.prototype ={}
const smallAnn = new Ann();

console.log(smallAnn instanceof Ann);//true
//Ann的原型是否出现在smallAnn上

console.log(smallAnn instanceof Wendy);//true
//Wendy的原型是否出现在smallAnn上

实例的属性:constructor

实例.constructor:实例的原始函式参照(物件的起源)

要注意一件事情:
let a = new Apple()
什麽property都没有只剩下[[prototype]]

点开[[prototype]],从这区开始(记得这里是物件喔)会是Apple.prototype

此时就要想想,prototype是建构函式才会有,就要思考来自哪个函式就会是他的constructor

所以a.constructor是来自Apple的prototype
这个prototype是因为Apple function 才有的,所以constructor是Apple

(忍耐一下这边是需要停下来好好思考的,想个几次就会海阔天空)


现在在实例来找constructor

function Cat() {
    this.eat = () => 'apple';
}
const nini = new Cat();

console.log(nini.constructor);//[Function: Cat]
function Dog() {
    this.sleep = () => 'sleep';
}
const doggy = new Dog();
console.log(doggy.constructor);//Function: Dog]

两个实例一个nini和一个doggy 找找他们建构子

可以看到nini.constructor和doggy.constructor的原始函式参照是[Function: Cat][Function: Dog]


利用实例constructor属性来创建物件

可以说是 nini.constructor等同於Cat

因此可以使用 nini.constructor来创建新物件

function Cat() {
    this.eat = () => 'apple';
}
const nini = new Cat();
const kiki = new nini.constructor();//我直接找Cat function的参照来建立实例
console.log('nini使用建构函式创出', nini);

//nini使用建构函式创出 Cat { eat: [Function (anonymous)] }
console.log('kiki使用nini的建构子创出', kiki);

//kiki使用nini的建构子创出 Cat { eat: [Function (anonymous)] }
console.log(nini.constructor == kiki.constructor);//true
console.log(nini instanceof Cat);//true

nini.constructorkiki.constructor都是同样的指向Cat

也可以试试看基本型别

let name = 'wendy';
let mystring = name.constructor('abc');
console.log(mystring);//'abc'

name由String()产生的实例所以他的mystring.__proto__会是String的prototype
String的prototype内属性:[[prototype]]会是Object的prtotype
Object的prtotype内属性[[prototype]]会是null


会了这些,继续讲昨天的疑问

function Cat() {
    this.eat = () => 'apple';
}
const nini = new Cat();
Cat.prototype.sleep = 'sleep';
console.log(Object.getPrototypeOf(nini)); 
//{ sleep: 'sleep' }
//改变Cat的原型
Cat.prototype = {
    go: function () {
        return 'go';
    },
};

const niki = new Cat();
console.log(nini instanceof Cat); //Cat原型是否出现在nini上:false
console.log(nini.constructor); //[Function: Cat]

// 那新实例(niki)呢?
console.log(niki instanceof Cat);///Cat原型是否出现在niki上:true
console.log(niki.constructor); //[Function: Object]

问题:为何niki.constructor 印出是[Function: Object]
因为

//这是Cat修改後的原型(没有constructor属性)
{ go: function () {
        return 'go';
    }, }

这个object没有constructor属性,通常建构函式的prototype内有constructor属性,因为修改了建构函式的prototype所以constuctor消失了!!!

所以跑的这句niki.constructor,其实niki会往上一层找到Cat的prototype

但自建object 没有constructor,所以会根据原型链再往上找,

找到{ }本身的[[prototype]]里的constructor

会像是长这样

此时这区是Object地盘(是大写O),他是建构函式的其中一种,因此他的prototype里会有constructor,
Object.prototype.constructor 登愣!!!

所以niki才会印出Object


透过原型可以做到复制

1.复制别人的方法/属性
把constructor的prototype的某一个methods复制过来,
像是底下把Wendy.prototype.sleep methods贴到另一个建构函式的methods内

function Wendy() {}
Wendy.prototype.sleep = '今天睡满24hr,超爽的';
Wendy.prototype.eat = '今天吃满24hr';

function Ann() {}
Ann.prototype = { goSleep: Wendy.prototype.sleep }; //wendy 的sleep能力挂在ann身上
const a = new Ann();
console.log(a.goSleep);//今天睡满24hr,超爽的
console.log(a.eat);//undefined

console.log(a instanceof Ann); //true
console.log(a.constructor); //[Function: Object]

这只是复制不是继承

console.log(a instanceof Wendy); //false

那要说说继承了~~

使用原型来实现继承

function Go() {}
function Run() {}
Go.prototype.school = 'sleep';
Run.prototype = new Go();
const student = new Run();
console.log(student instanceof Go);//true
console.log(student instanceof Run);//true
console.log(student.school);//sleep

Go的原型换成new Go()物件实例

透过student物件来存取school方法,JS执行环境会先对student物件找寻有没有school方法,没有方法就回去找他的原型:Go物件

先到这里~明天再接再厉


<<:  Day21 - Sort

>>:  【Day 20】全局储存库 Context

30天学习笔记 -day 30 -感言

LAST Day 终於到了铁人赛的最後一天,过程中复习了不少的东西,对某些用法有了更加的认识,过程中...

JavaScript 函数 | 一级函数

一级函数 (First Class Functions) Everything you can do...

为什麽要使用VPN?综合3款VPN推荐给大家

最近有朋友回中国内地工作,刚好问我有什麽VPN推荐一下给他,需要在内地能翻墙,连脸书就可以了,了解他...

Day03基本架构(HTML)

HTML的基本架构 首先 我使用的编辑器是VS CODE 因为它的介面还蛮适合新手 而且提供很多套件...

【Day26】 音乐如何引起人的情绪

前言 音乐家的艺术不在於直接描绘形象,而在於把心灵置於这些物件能够在心灵里创造的情绪中去。—— 卢梭...