从V8那里搞一点原型继承的小提示778,原型链, 原型 ,原型对象 函数隐藏属性 对象隐藏属性

1,174 阅读5分钟

原型链

在js中我们没有类型这个说法,只有对象这个概念,后面es6中使用class创建对象 并实现继承。但是class只是语法糖,class本质背后还是原型机制 构造函数实现继承

继承:一个对象可以访问另外一个对象中的属性和方法

javaScript仅仅在对象当中引入了一个原型的属性,就实现了语言的继承机制,基于原型的继承省去了很多基于类继承时的繁文缛节,简洁而优美 --- 李兵

原型继承是如何实现的

proto 称为该对象的原型(prototype) __proto__指向的对象称为该对象的原型对象 通过__proto__产生的链为原型链

javaScript每个对象都包含一个隐藏属性__proto__,我们就把该隐藏属性__proto__称之为对象的原型(prototype),javaScript每个对象都包含一个隐藏属性__proto__指向了内存中的另外一个对象, 我们就把__proto__指向的对象称为该对象的原型对象,那么该对象就可以直接访问其原型对象的方法或者属性

原型链访问属性
从图中可以看到,对象A有个属性是color,通过C.color访问color属性时,V8会在C对象的内部查找,但是没有查找到,接着继续在C对象的原型对象B中查找,但是依旧没有查到,那么继续去对象B的原型对象A中查找,因为color在对象A中,那么就返回该属性值

我们看到使用 C.name 和 C.color 时,给人的感觉属性name和color都是对象c本身的属性,但实际上这些属性都是位于原型对象上,我们把这个查找属性的路径称为原型链,他像一个链条一样,将几个原型链接起来

使用__proto__实现继承

var animal = { 
  type: "Default", 
  color: "Default", 
  getInfo: function () { 
    return `Type is: ${this.type},color is ${this.color}.`
 }}
var dog = { type: "Dog", color: "Black",}

// 实现继承
dog.__proto__  = animal
// 这样之后DOG就可以访问animal属性啦
dog.getInfo() 

注意:虽然__proto__可以实现继承,但是隐藏属性不能直接用来使用的。虽然现代浏览器留了口子但是不推荐使用,因为很耗性能

  1. 隐藏属性,并不是标准定义的
  2. 使用该属性造成严重的性能问题

这个__proto__是不可行的,我们只能另找其他方法了

首先,构造函数是怎么创建对象的?

比如创建一个dog对象,先创建一个DogFactory的函数,属性通过参数进行传递,在函数体内,通过this设置属性值。

function DogFactory(type, color) {
  this.type = type
  this.color = color
}

然后再结合new关键字,就可以创建对象啦

var dog = new DogFactory('Dog','black')

new后面配合一个函数(叫构造函数),javascript虚拟机便会返回一个对象,为什么就会返回一个对象呢,接着往下看


v8有所处理,v8在处理上面这段代码是,V8会在背后悄悄的做了以下几件事情,类似下面模拟代码

var dog = {}
dog.__proto__ = DogFactory.prototype
DogFactory.call(dog, 'Dog', 'Black')

看图我们加深理解

V8处理创建对象过程
创建对象分为三步:

  1. 创建一个空白对象 dog
  2. 将 DogFacttory的prototype属性设置为dog的原型对象,这就给dog对象设置原型对象的关键一步。
  3. 再使用dog来调用DogFactory,这时候DogFactory函数中的this就指向了对象dog,然后在DogFactory函数中,利用this对对象dog执行属性填充操作,最终创建了dog对象

  1. 我们应该知道:函数其实有三个隐藏属性,name,code (这个是v8用来做函数执行优化的)还有一个prototype。
  2. 当函数作为构造函数来创建一个新的对象时,新创建的对象的原型对象就指向了该函数的prototype属性。
  3. 普通函数prototype不起作用。

我们很自然的就知道,当通过一个构造函数创建多个对象的时候,这几个对象的原型都指向了该函数的prototype属性

对象指向原型
这就很明白啦,我们只要把constant_temperature属性添加到 构造函数里面 DogFactory的prototype属性上


function DogFactory(type,color){
    this.type = type
    this.color = color
    //Mammalia
}
DogFactory. prototype.constant_temperature = 1
var dog1 = new DogFactory('Dog','Black')
var dog2 = new DogFactory('Dog','Black')
var dog3 = new DogFactory('Dog','Black')

这样三个dog对象的原型对象都指向了prototype,而prototype又包含了constant_temperature,这就实现了一个对象方位另外一个对象的属性,也就是实现了继承

一段关于new的历史

javascript和java是同一时期出来的,但是java当时如日中天,javascript只是为了浏览器的页面能够动起来简单脚本语言。当时为了蹭java热度,就叫javascript 同时为了进一步吸引java程序员,在语法上也蹭java的热点,加上了不协调的new来创造对象,和java类似

总结

javascript的对象都有一个隐藏属性__proto__, 这个属性的值就是这个对象的原型对象,通过__proto__形成的链就是原型链,从原型链上,就可以对象使用一层一层原型对象的属性。 继承的概念简单的讲就是,一个对象可以访问另一个对象的属性,就是继承 然而直接使用对象的__proto__来实现继承并不可取,因为他不是一个标准定义的,而且使用它来继承性能特别差 所以我们使用构造函数来创建对象,构造函数前面加一个new javascript就会返回一个对象。这里注意函数有隐藏属性code name prototype,如果一个函数是 构造函数的时候,新创建的原型对象就指向了构造函数的prototype属性。即obj.proto = Object.prototype,只要把属性放到构造函数里面,创建的新对象就完成了继承,即基于原型继承

  1. 对象有一个隐藏属性,proto 这个属性指向原型对象
  2. 函数有一个隐藏属性,prototype 当函数为构造函数时,这个函数创建出来的对象的__proto__属性指向构造函数的prototype 指向原型对象

继承还是有很多可以面试的东西,上面说的都是一些v8 一个继承的基本原理的浅出,还需要大家自己结合区深入,深挖。 那里