再也不怕面试官问我原型和原型链啦

203 阅读4分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第33天,点击查看活动详情

1. 原型三角关系

原型应该是js设计中最为变态,比this指向还变态的一个东西,但是由于原型的存在拓展了js的类,js的继承,js的属性方法设计等。因此我们还是要忍痛割爱去弄懂它。想要弄懂原型,还得从三角恋开始。

image.png

根据上图关系,我们很容易得出结论(三角关系十分十分十分重要,一定要记住)

  1. 实例对象.__proto__ = 函数

  2. 函数.prototype = 函数原型

  3. 函数原型.constructor = 函数

     //构造函数
     function People (name) {
       this.name = name
     }
    
     //实例对象
     let p = new People("xiaoming") 
    
     //测试三角关系
     console.log(p.__proto__===People.prototype)
     console.log(People.prototype.constructor === People)
     console.log(p.__proto__.__proto__)//Object.prototype
     console.log(p.__proto__.__proto__.__proto__)//null
     console.log(People.__proto__)//Function.prototype
     console.log(People.__proto__.__proto__)//Object.prototype
    

2. 原型链

原型链:原型链就是对象和函数的属性可以按照原型链的顺序,一层一层的找下去。它们都是从自己o开始找,如果没找到就按 o.__proto__的原型链一直找到NULL为止。

image.png

这份图是比较完整的原型链组成图,马勒戈巴子画这个累死我了。图中的红色和绿色是两类原型链,其中红色代表的是函数的原型链,而绿色是普通对象的原型链。它们两个最后都指向了NULL,用黄色箭头表示。

函数原型链测试

此处还未讲new的构造原理和原型链变化,所以先不测试

对象原型链测试

Function.test1 = function() {
  console.log("Function.test1")
}

Function.prototype.test1 = function() {
  console.log("Function.prototype.test1")
}

Object.test1 = function() {
  console.log("Object.test1")
}

Object.prototype.test1 = function() {
  console.log("Object.prototype.test1")
}

let o = {
  name:"dzp"
}
o.test1()//Object.prototype.test1

最后的输出是Object.prototype test1。按照原型链很容易明白,对象的原型链从绿色箭头开始寻找,先找自己o对象本身是否有test1函数,没有则按照原型链箭头来到了Object原型。发现有test1函数,成功输出。

3.new的原理和原型链

new的原理是十分十分重要,在前端面试中经常会被问,甚至要求手写new的过程。接下来,我将为大家细细讲解

function People (name) {
  this.name = name
}
People.prototype.info = function(){
    console.log("info")
}

//new的模拟
function myNew (name) {
  let o = {}
  People.call(o,name)
  o.__proto__ = People.prototype
  return o
}

//测试
let p = myNew("dzp")
console.log(p.name)//dzp
p.info();//info

可以看到,new的核心就三行代码。我来依依解答下

  1. 首先创建一个空的对象o

  2. 调用函数People,并使用apply或者call改变People的this,让this动态绑定到o上。此时对象o就已经添加了属性name

    o = {
        name:"dzp"
    }
    
  3. 此时有人说,这不是已经成功了?name的确绑定到了对象o上,但是这个o可以访问People原型链上的属性和函数?答案肯定是不能。因此我们执行最后一步,将对象o的__proto__链接到People的原型上

new在原型链上表现

image.png

经过上面new的过程,我们可以用原型链描述整个new的原理,对象o将自己的__proto__指向了People的原型,此时红色的原型链就形成了。

原型链测试

function People (name) {
  this.name = name
}

Function.test1 = function() {
  console.log("Function.test1")
}

Function.prototype.test1 = function() {
  console.log("Function.prototype.test1")
}

Object.test1 = function() {
  console.log("Object.test1")
}

Object.prototype.test1 = function() {
  console.log("Object.prototype.test1")
}

let p = new People("dzp")
p.test1()//Object.prototype.test1

按照上面红色原型链分析,很容易得到答案。

4.Object.create的原理

Object.create的原理和this都是面试官经常会问的,接下来我将讲解Object.create的原理并使用原型链模拟其内部过程。

 //模拟Object.create原理
 Object.create = function(o) {
  function f() {}
  f.prototype = o
  return new f()
}


let obj = {
  name:'dzp'
}

let o = Object.create(obj)

console.log(o)//{}
console.log(o.name)//dzp

经过测试我们发现,对象o自己本身是空的,但是仍然可以访问对象obj的属性,不用想,肯定是对象obj成为了o的原型链一部分。

原型链模拟

image.png

我们看到返回的最终对象是一个空的,但是它自己通过__proto__链接了原型链到对象o上,因此它可以访问对象o的属性和函数

5.练习

记住上面的原型图,按步骤分析就十分的简单。

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

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

5.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();//null

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

总结

以上是我对原型链,this,Object.create的理解,个人如果理解不到位,请评论区多多批评。大家共同进步。