JS高级—08—原型;原型链;如何通过原型链来实现js的继承;

原型是什么?对象原型+函数原型;

原型链是什么?

js继承如何实现?

 

 

 

 

一、原型(隐式原型显式原型)(对象原型、函数原型、函数原型对象)

 原型分为两种,分别是

  • 隐式原型,也就是对象的[[prototype]]属性,也可以叫对象原型

  • 显式原型,也就是函数的protutype属性,也可以叫函数原型;函数原型指向的对象就是函数原型对象,每一个通过new构造函数创造出的实例对象的隐式原型也指向这个实例对象;

  • 注意:因为在js中所有东西都是对象,函数也是一种特殊的对象,所以函数也有隐式原型;

1.1对象原型

js中规定了每个对象都要有内置[[prototype]]属性,也叫隐式原型;

隐式原型的作用:

let obj = { } obj.__proto__.age = 18;  console.log(obj.age)   //18;自己属性里没有,会去隐式原型中查找

 

浏览器给每个对象加了一个__proto__属性,这个属性可以指向对象的隐式原型;即obj.__proto__  === obj.[[prototype]](当然了内置属性是不能访问的,所以obj.[[prototype]]这种写法有问题,这里这样写只是方便理解;)

但是这个__proto__属性是某些浏览器自己加的,并不在ecma规范,所以测试环境可以使用,生产环境可能面临客户使用不同浏览器的情况,生产环境不要用;

 

 

 

1.2函数原型 

函数的prototype属性(显式原型);

根据ecma规范,函数也是一个对象,所以也有隐式原型属性;

根据ecma规范,函数有显式原型属性,显式原型可以直接通过foo.prototype属性调用,这个是和隐式原型不同的点;

 

在我们new一个构造函数时,js引擎内部会自动帮助我们做,将函数的prototyp属性所指向的内存地址赋值给实例对象的内置[[prototype]]属性,所以实例对象的内置[[prototype]]属性将会指向函数的原型对象;

简言之,实例对象的隐式原型等于函数的显式原型;

 

 

 

如果是一个普通函数,那么调用的时候创建执行函数上下文等;

返回的函数,fn1和fn2的堆内存地址也是不一样的,因为每次调用普通函数时,都会重新创建一个ao对象,ao对象不一样,所以返回的bar函数的地址也不一样;

 

但如果用new调用,将会创建一个对象巴拉巴拉;每一个实例对象都是不同的,在堆内存中都有不同的地址,这个是ecma规范;

因为p1和p2是不同的实例对象,所以p1的eating方法、running方法和p2的eating方法、running方法在堆内存中地址也是不一样的;

但是这样就会造成浪费,毕竟eating方法、running方法里很多代码都是一样的;能不能只定义一次,然后所以子都可以使用呢?

 

解决办法:函数原型。说白了,就是通过函数原型的方式实现继承和多态;

 

函数的原型是每个实例对象的父亲,实例对象在自己作用域里找不到的方法,就去父亲中找即实例对象的原型也就等于构造函数的原型也就等于函数原型对象,找到后多态,父亲方法中的每个this表示的都是自己这个实例对象。为什么this指向调用它的实例对象,因为这个事this的4项绑定规则之隐式绑定,通过哪个调用时指向谁呀;

 

 

 

 

1.3函数原型的constructor属性

 

 

 

 

 

1.4创建对象的第4中方式

除了new Object、字面量创建、构造函数创建

还可以使用构造函数+原型的方式创建;

 

 

 

 

 

 

js也是一门面向对象的语言,封装继承多态,都具备,那么继承如何实现?

如何实现继承?

单纯的原型链实现继承有很多问题,所有需要加以改造;