【JS】图解原型链相关练习题,带你彻底搞懂原型链!!!(这可能是掘金画原型链画的最正的😃)

5,772 阅读5分钟

今天看了心哥的博文这可能是掘金讲「原型链」,讲的最好最通俗易懂的了,附练习题!,让我对原型链又有了新的认识,那我现在就把我新的认识,通过解析心哥博文后面的五道练习题的方式,分享给大家吧~

大家在看题解之前可以先去看看心哥的博文~ 不过我也会简单带大家回顾一下的~

什么是原型链呢?其实俗话说就是:__proto__的路径就叫原型链

自由变量的查找,就是顺着 原型链 往上查找的!

我先把 FunctionObject 的原型图画出来(这里省略了constructor),因为这里我们更多的关注原型链__proto__,也就是图中画的绿色的线!

  • 橙黄色是显式原型prototype,在JavaScript中,所有的函数都有显式原型,显式原型是一个对象

  • 草绿色是隐式原型__proto__,在JavaScript中,所有的对象都有隐式原型,指向其构造函数的显式原型

这里Object是一个构造函数,所以他有显式原型,Object的显式原型的隐式原型,指向原型链的尽头——null

image.png

Function是一个构造函数,他有显式原型,显式原型是一个对象,所以他有隐式原型,指向他的构造函数的显式原型,也就是Function.prototype.__proto__ === Object.prototype

image.png

练习题1

题目

var F = function() {};

Object.prototype.a = function() {
  console.log('a');
};

Function.prototype.b = function() {
  console.log('b');
}

var f = new F();

f.a();
f.b();

F.a();
F.b();

图解

我们一行一行的分析

var F = function() {};

这里通过函数表达式的方式创建一个函数F,这里的函数F相当于是Function实例对象,所以F.__proto__指向Function的显式原型对象上

F是一个函数,他就有显式原型对象,这个显式原型对象是一个对象,所以他的隐式原型指向Object的显式原型(看下图就清晰明了了!)

image.png

Object.prototype.a = function() {
  console.log('a');
};

这里是给Object的显式原型上创建一个a函数,打印输出a

Function.prototype.b = function() {
  console.log('b');
}

这里是给Function的显式原型上创建一个b函数,打印输出b

image.png

var f = new F();

这里是创建一个F实例对象f,这里的f是一个对象,所以他有隐式原型,指向其构造函数的显式原型

image.png

最后就是考察fF的原型链啦

跟着草绿色的原型链走,就可以了~ 途中用定位标记标记了

可以看到F的原型链上有Function.prototypeObject.prototypenull

f的原型链上有F.prototypeObject.prototypenull

image.png

根据图示,随后的答案就很明了了!

答案

f.a(); // a
f.b(); // TypeError: f.b is not a function

F.a(); // a
F.b(); // b

练习题2

题目

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);
console.log(b.m);

console.log(c.n);
console.log(c.m);

图解

var A = function() {};

这里创建一个A函数,原型图是下面这样的,我们这次就省略原型链的尽头null了!

image.png

A.prototype.n = 1;

在A的显式原型上添加一个属性n,值为1

image.png

var b = new A();

创建一个A的实例对象b

image.png

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

这里给A的显式原型对象重新赋值,指向了一个新的对象

但是原来的对象还保持着b对他的引用,所以垃圾回收机制不会清除原来这个对象

image.png

var c = new A();

这时,又创建了一个A的实例对象c,这里c对象会指向A的新的显式原型对象

image.png

最后就是考察bc的原型链啦,如图所示

image.png

答案

console.log(b.n); // 1
console.log(b.m); // undefined

console.log(c.n); // 2
console.log(c.m); // 3

练习题3

题目

var foo = {},
    F = function(){};
Object.prototype.a = 'value a';
Function.prototype.b = 'value b';

console.log(foo.a);
console.log(foo.b);

console.log(F.a);
console.log(F.b);

图解

var foo = {},
    F = function(){};

这里首先创建一个对象foo

然后创建一个函数F

image.png

Object.prototype.a = 'value a';
Function.prototype.b = 'value b';

Objetct的显式原型对象上添加一个a属性

Function的显式原型对象上添加一个b属性

image.png

最后就是考察Ffoo的原型链

image.png

答案

console.log(foo.a); // value a
console.log(foo.b); // undefined

console.log(F.a); // value a
console.log(F.b); // value b

练习题4

题目

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

console.log(new A().a); 
console.log(new B().a);
console.log(new C(2).a);

图解

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

创建三个函数

image.png

A.prototype.a = 1;
B.prototype.a = 1;
C.prototype.a = 1;

函数的显式原型上添加属性a

image.png

new A()
new B()
new C(2)

分别创建三个实例对象,执行构造函数里的代码,this指向实例对象,相当于给实例对象自身添加属性

image.png

答案

image.png

console.log(new A().a);  // 1
console.log(new B().a); // undefined
console.log(new C(2).a); // 2

练习题5

题目

console.log(123['toString'].length + 123)

图解

这里123后面调用toString方法会使JavaScript产生一个原始包装对象,将123包装成Number对象类型的,其原型上有toString方法,其length1

在此感谢 @peng_tianyu 的指正~


这里123后面调用toString方法会使JavaScript产生一个原始包装对象,将123包装成Number对象类型的,然后再顺着原型链找toString方法,是在Object对象身上,其length1

这里错了,实际上 Object.prototype.toString.length === 0

Number.prototype.toString.length === 1

image.png

length 是函数对象的一个属性值,指该函数期望传入的参数数量,即形参的个数。 形参的数量不包括剩余参数个数,仅包括第一个具有默认值之前的参数个数。 与之对比的是, arguments.length 是函数被调用时实际传参的个数。

image.png


答案

console.log(123['toString'].length + 123) // 124

最后

你还有题嘛?

砸过来,我来图解!!!算了,熟了之后是不需要画图的!

你知道记得 函数对象 分别有隐式原型和显式原型就行了,原型链是顺着隐式原型找的,而隐式原型是指向其构造函数的显式原型的!!!