结合真题理解原型与面向对象(进阶必备知识)

340 阅读3分钟

这是我参与8月更文挑战的第16天,活动详情查看:8月更文挑战

TIP 👉 盛年不重来,一日难再晨。及时当勉励,岁月不待人。——陶渊明

前言

原型侧知识偶见单独命题,但更多的是和其它 JS 核心知识结合起来命题。这样做,可以从整体上拔高题目的区分度、同时实现对候选人基本功的全面考察。处理这样的题目,大家首先要保持冷静的头脑,甄别出题目中所涉及的知识点,作答过程中梳理出一条清晰正确的原型链,只要你能静下心来把原型链抓出来,整个作答的脉络也随之清晰起来

来看一个示例

原型基础+构造函数基础

var A = function() {}; 
A.prototype.n = 1;
var b = new A();
A.prototype = {
    n2,
    m3
}

var c = new A();
console.log(b.n);
console.log(b.m);
console.log(c.n);
console.log(c.m);

思考一下,可能很多人回给出这样的答案

2
3
2
3

把它丢进控制台,你会发现答案是:

image.png

为什么会这样呢?我们一起来看一下这个例子中几个对象间的关系:

1. 明确原型关系:

b 实例与 A 之间的关系:

image.png

c 实例与 A 之间的关系:

image.png

2. 构造函数的工作机理

b 实例为什么明明和 c 实例继承自一个原型,却有着不同的表现

当我们用 new 去创建一个实例时,new 做了什么?它做了这四件事:

  • 为这个新的对象开辟一块属于它的内存空间
  • 把函数体内的this指到1中开辟的内存空间去
  • 将新对象的_ proto_这个属性指向对应构造函数的prototype属性,把实例和原型对象关联起来
  • 执行函数体内的逻辑,最后即便你没有手动 return,构造函数也会帮你把创建的这个新对象 return 出来

b实例在创建的时候,构造函数的prototype

image.png

可是后面对A的prototype做了修改,b如果存的是引用,它应该会感知这个修改啊!

但是,修改A的prototype的形式严格意义上来说不算修改,而是一种重新赋值操作,这个动作的本质就是把A的prototype指向了一个全新的js对象,A单方面切断了和旧prototype的关系,而b却仍然保留着旧prototype的引用,这就是b和c之间差异的原因。

如果我们把b.prototype重写了呢?

var A = function() {}; 
A.prototype.n = 1;
var b = new A();
b.prototype = {
    n2,
    m3
}
console.log(b.prototype.__proto__)

它会指向Object.prototype

自有属性与原型继承属性

function A() {
    this.name = 'a'
    this.color = ['green', 'yellow']
}

function B() {
    
}

B.prototype = new A()

var b1 = new B()
var b2 = new B()

b1.name = 'change'
b1.color.push('black')

console.log(b2.name) // a
console.log(b2.color) // ['green', 'yellow', 'black']

1. 原型链图

image.png

b1.color.push 它实际上并没有改变对象的引用,而仅仅是在原有对象的基础上修改了它的内容而已。像这种不处罚引用指向改变的操作,它走的是原型链查询+修改的流程。

构造函数综合

function A() {}
function B() {
    this.a = a;
}
function C() {
    if(a) {
        this.a = a
    }
}
A.prototype.a = 1
B.prototype.a = 1
C.prototype.a = 1

console.log(new A().a) // a
console.log(new B().a) // undefined
console.log(new C(2).a) // 2
  • new A().a: 构造函数逻辑唯恐,返回的实力对象__proto__中包含了a = 1这个属性。new A().a时,发现实例对象本身没有a,于是沿着原型链找到了原形中的a,输出1

  • new B().a: 构造函数中会无条件为实例对象创建一个自有属性a,这个a的值以入惨为准。这里我们的入参是undefined,所以a值也是undefined

  • new C(2).a: 构造函数中会有条件地为实例对象创建一个自有属性a ----- 若确是存在一个布尔判定不为false的入参a,那么为实例对象创建对应的a值;否则,不做任何事情。这里我们传入了2,因此实例输出的a值就是2。