深入浅析js原型链和vue构造函数


Posted in Javascript onOctober 25, 2018

一、什么是原型链?

深入浅析js原型链和vue构造函数

简单回顾下构造函数,原型和实例的关系:

     每个构造函数(constructor)都有一个原型对象(prototype),原型对象都包含一个指向构造函数的指针,而实例(instance)都包含一个指向原型对象的内部指针.

然鹅,在js对象里有这么一个规则:

如果试图引用对象(实例instance)的某个属性,会首先在对象内部寻找该属性,直至找不到,然后才在该对象的原型(instance.prototype)里去找这个属性.

少废话,先来看个例子:

function Fun1 () {
  this.win = "skt"
 }
 Fun1.prototype.getVal = function () {
  return this.win
 }
 function Fun2 () {
  this.other_win = "rng"
 }
 Fun2.prototype = new Fun1 ()
 Fun2.prototype.getOtherVal = function () {
  return this.other_win
 }
 let instance = new Fun2()
 console.log(instance.getVal()) //skt

在上述例子中,有一个很有意思的操作,我们让原型对象指向了另一个类型的实例,即: constructor1.property = instance2

那么他是怎么找到instance.getVal()的?这中间又发生了什么?

1).首先会在instance1内部属性中找一遍;

2).接着会在instance1.__proto__(constructor1.prototype)中找一遍,而constructor1.prototype 实际上是instance2, 也就是说在instance2中寻找该属性;

3).如果instance2中还是没有,此时程序不会灰心,它会继续在instance2.__proto__(constructor2.prototype)中寻找...直至Object的原型对象

    搜索轨迹: instance1--> instance2 --> constructor2.prototype…-->Object.prototype

这种搜索的轨迹,形似一条长链, 又因prototype在这个游戏规则中充当链接的作用,于是我们把这种实例与原型的链条称作 原型链

二、prototype 和 __proto__ 都是个啥?

1.prototype是函数才有的属性

let fun = function () {}
 console.log(fun.prototype) // object
 console.log(fun.__proto__) // function

2.__proto__是对象具有的属性,但__proto__不是一个规范的属性,对应的标准属性是 [[Prototype]]

let obj = {}
 console.log(obj.prototype) // underfined
 console.log(obj.__proto__) // object

我们可以把__proto__理解为构造器的原型,大多数情况下 __proto__ === constructor.prototype      ( Object.create()除外 )

三、new又是个什么鬼?

我们都知道new是一个实例化的过程,那么他是怎么实例化的?下面我们来看一个简单的例子:

function Fun() {
  this.team = "rng"
 }
 let f = new Fun()
 console.log(f.team) // rng

上述代码中,我们通过new命令实例化了一个叫Fun的函数并赋值给f,这个新生成的实例对象f从构造函数Fun中得到了team属性,其实构造函数内部的this,就代表了新生成的实例对象,所以我们打印f.team的值就取到了rng这个值

这又是哪门子原理?答案如下?

1.创建一个空对象,作为将要返回的对象实例。
2.将这个空对象的原型,指向构造函数的prototype属性。
3.将这个空对象赋值给函数内部的this关键字。
4.开始执行构造函数内部的代码

也就是说,构造函数内部,this指的是一个新生成的空对象,所有针对this的操作,都会发生在这个空对象上。这也是为什么构造函数叫"构造函数"的原因,就是操作一个空对象(即this对象),将其“构造”为所需要的样子。

如果我不加new呢?

function Fun() {
  this.team = "rng"
 }
 let f = Fun()
 console.log(f) // undefined
 console.log(team) // rng

我们可以看出上面打印f为undefined,而team却有值,这又是为什么?

其实在这种情况下,构造函数就变成了普通的函数,而且不会被实例.而此时的this指向了全局,team就变成了全局变量,因此我们取到了值

四、 __proto__指向哪?

说到__proto__的指向问题,还得取决于该对象创建时的实现方式.

辣么,到底有那些实现方式?

1.字面量方式

let obj = {}
 console.log(obj.__proto__) // object
 console.log(obj.__proto__ === obj.constructor.prototype) // true 证明用字面量创建的函数,他的__proto__ 等于 该对象构造器的原型

2.构造器方式

function Func () {}
 let a = new Func()
 console.log(a.__proto__) // object
 console.log(a.__proto__ === a.constructor.prototype) // true

3.Object.create()方式

let obj1 = {name:"rng"}
 let obj2 = Object.create(obj1)
 console.log(obj2.__proto__) //{name: "rng"}
 console.log(obj2.__proto__ === obj2.constructor.prototype) // false

注: Object.create(prototype, descriptors) 创建一个具有指定原型且可选择性地包含指定属性的对象

五、如何确定原型和实例的关系?

想要确定原型和实例的关系,坦率的讲,有两种方式:  instance  和  isPrototype()

1.instanceof

我们用这个操作符来测试实例(instance)与原型链中出现过的构造函数,如果出现过则返回true,反之则为false

来来来,我们来测试一下:

function Fun1 () {
  this.laji = "uzi"
 }
 function Fun2 () {
  this.strong = "faker"
 }
 Fun2.prototype = new Fun1()

 let fun2 = new Fun2 ()

 console.log(fun2 instanceof Fun1) // true
 console.log(fun2 instanceof Fun2) // true
 console.log(fun2 instanceof Object) // true

由于原型链的关系,我们可以说fun2是一个对象Object,Fun1或是Fun2中任何一个类型的实例,所以这三个结果都返回了true

2.isPrototype()

这个方法同样,只要是原型链中出现过的原型,该方法就会返回true,用法如下

console.log(Fun1.prototype.isPrototypeOf(fun2)) // true
 console.log(Fun2.prototype.isPrototypeOf(fun2)) // true
 console.log(Object.prototype.isPrototypeOf(fun2))// true

六、原型链的问题

什么?原型链还有问题?买了佛冷,why?

原因一: 当原型链中包含引用类型值的原型时,该引用类型值会被所有实例共享;

原因二:在创建子类型时,不能向超类型的构造函数中传递参数.

七、如何解决原型链问题?

1.借用构造函数,也叫经典继承

基本思想: 在子类型构造函数的内部调用超类型构造函数

函数只是在特定环境中执行的代码的对象,因此通过使用 apply() 和 call() 方法也可以在(将来)新创建的对象上执行构造函数

看例子:

function Father () {
  this.team = ["letme","mlxg"]
 }
 function Son () {
  Father.call(this)
 }
 let son = new Son()
 son.team.push("uzi")
 console.log(son.team)  // ["letme", "mlxg", "uzi"]
 let little_son = new Son() 
 console.log(little_son.team) // ["letme", "mlxg"]

我们可以看出,借用构造函数一举解决了原型链的两大问题:

其一, 保证了原型链中引用类型值的独立,不再被所有实例共享;

其二, 子类型创建时也能够向父类型传递参数.

但是还还还有一个问题,如果仅仅借用构造函数,那么将无法避免构造函数模式存在的问题:

方法都在构造函数中定义, 因此函数复用也就不可用了.而且超类型(如Father)中定义的方法,对子类型而言也是不可见的. so,借用构造函数的技术也很少单独使用.

2.组合继承

组合继承, 有时候也叫做伪经典继承,指的是将原型链和借用构造函数的技术组合到一块,从而发挥两者优点的一种继承模式.

基本思想: 使用原型链实现对原型属性和方法的继承,通过借用构造函数来实现对实例属性的继承.

这样,既通过在原型上定义方法实现了函数复用,又能保证每个实例都有它自己的属性.

接着看例子:

function Father (team) {
  this.team = team
  this.people = ["mlxg","letme"]
 }
 Father.prototype.sayTeam = function () {
  return console.log(this.team)
 }
 function Son (team,age) {
  this.age = age
  Father.call(this,team)
 }
 Son.prototype = new Father()
 Son.prototype.sayAge = function () {
  return console.log(this.age)
 }
 let son = new Son("faker",8)
 son.people.push("uzi")
 console.log(son.people) // ["mlxg", "letme", "uzi"]
 son.sayAge()    //8
 son.sayTeam()    // faker
 let little_son = new Son("bang",3)
 console.log(little_son.people) // ["mlxg", "letme"] 
 little_son.sayAge()    // 3
 little_son.sayTeam()   // bang

我们可以看出,组合继承既保证了引用类型不再被所有实例所共享,也能够让子类型创建时向父类型传参,同时,原型中的方法又能够被复用,可以说是避免了原型链中的两大问题以及借用构造函数的缺陷,因此他也是js中最常用的继承方式,而且

instanceof 和 isPrototypeOf( )也能用于识别基于组合继承创建的对象.

3.原型继承

基本思想: 借助原型可以基于已有的对象创建新对象, 同时还不必因此创建自定义类型

绳么意思?

比如我们在fun()函数内部, 先创建一个临时性的构造函数, 然后将传入的对象作为这个构造函数的原型,最后返回了这个临时类型的一个新实例.

function fun(o){
   function F(){}
   F.prototype = o;
   return new F();
  }
  let obj = {arr:[11,22]
  fun(obj).arr.push(33)
  console.log(fun(obj).arr) // [11,22,33]

在这个例子中,可以作为另一个对象基础的是obj对象,于是我们把它传入到fun()函数中,然后该函数就会返回一个新对象. 这个新对象将arr作为原型,因此它的原型中就包含引用类型值属性. 然后我们向该属性中又增加了一个元素,所以我们能够将它打印出来

       *在原型继承中, 包含引用类型值的属性始终都会共享相应的值, 就像使用原型模式一样.

4.寄生式继承

基本思想:创建一个仅用于封装继承过程的函数,该函数在内部以某种方式来增强对象,最后再像真的是它做了所有工作一样返回对象

function fun(o){
  function F(){}
  F.prototype = o;
  return new F();
 }
 let obj = {a:[11,22]}
 function createAnother(z) {
  // 通过调用函数创建一个新对象
  var clone = fun(z);
  clone.sayHi = function () {
   alert("hi");
  }
  return clone;
 }

createAnother(obj)

上面的例子中,我们把obj传入createAnother()函数中,返回的新对象clone不仅拥有了该属性,而且还被增强了,拥有了sayHi()方法;

等一下,这里要注意: 使用寄生式继承来为对象添加函数, 会由于不能做到函数复用而降低效率;这一点与构造函数模式类似.

5.寄生组合式继承

前面讲过,组合继承是 JavaScript 最常用的继承模式; 不过, 它也有自己的不足. 组合继承最大的问题就是无论什么情况下,都会调用两次父类构造函数: 一次是在创建子类型原型的时候, 另一次是在子类型构造函数内部. 寄生组合式继承就是为了降低调用父类构造函数的开销而诞生的

基本思想:不必为了指定子类型的原型而调用超类型的构造函数

function inheritPrototype(subType, superType) {
  var protoType = Object.create(superType.prototype); //创建对象
  protoType.constructor = subType;      //增强对象
  subType.prototype = protoType;       //指定对象
 }
 function Father(name) {
  this.name = name;
  this.colors = ["red", "blue", "green"];
 }
 Father.prototype.sayName = function () {
  console.log(this.name);
 }
 function Son(name, age) {
  Father.call(this, name);
  this.age = age;
 }
 inheritPrototype(Son, Father)
 Son.prototype.sayAge = function () {
  console.log(this.age);
 }
 var instance = new Son("uzi", 3);
 instance.sayName(); //uzi
 instance.sayAge(); //3

 

inheritPrototype函数接收两个参数:子类型构造函数和超类型构造函数。

1. 创建超类型原型的副本。

2. 为创建的副本添加constructor属性,弥补因重写原型而失去的默认的constructor属性

3. 将新创建的对象(即副本)赋值给子类型的原型

inheritPrototype的高效率体现在它没有调用superClass构造函数,因此避免了在subClass.prototype上面创建不必要多余的属性. 同时,原型链还能保持不变,可以说是相当奈斯

由于寄生组合式继承,集寄生式继承和组合继承的优点于一身,是实现基于类型继承的最有效方法.

八.vue构造函数

我们在使用的vue的时候,经常会用new操作符去将他实例化,这说明vue也是一个构造函数,那么他是如何被创建的呢?我怀着无比激动的心情clone了vue的源码,仔细研究了一番

vue源码地址

我首先找到了src/core/instance/index.js文件,打开一看,惊了


在第八行代码中,创建了一个Vue的函数,这不就是Vue的构造函数么,而且在12行的警告中我更加肯定了,他说:Vue是一个构造函数,应该使用“new”关键字调用
然后他在下面,他分别在

initMixin()

stateMixin()

eventsMixin()

lifecycleMixin()

renderMixin()

这五个方法中讲Vue作为形参传入,最后将Vue导出.

那么这五个方法是干什么的呢?我们先来看看initMixin()方法,打开./init.js文件,找到该方法


其他的代码我们先不管,我们就看该方法的前几行,他在Vue的原型中注入了_init方法,这个方法有点眼熟,我们好像在哪见过,对,就是刚才的index.js文件中

深入浅析js原型链和vue构造函数

这个this_init(options)看上去像是一个内部初始化的一个方法,而option应该就是初始化时的一些配置项了,在Vue被实例化的时候,this._init()方法就会执行

接下来,我们来看一下./state.js文件,找到stateMixin方法

深入浅析js原型链和vue构造函数

总结

以上所述是小编给大家介绍的js原型链和vue构造函数,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!

Javascript 相关文章推荐
js新闻滚动 js如何实现新闻滚动效果
Jan 07 Javascript
jQuery回车实现登录简单实现
Aug 20 Javascript
jQuery固定元素插件scrolltofixed使用指南
Apr 21 Javascript
javascript中checkbox使用方法实例演示
Nov 19 Javascript
Bootstrap导航条学习使用(二)
Feb 08 Javascript
node.js连接MongoDB数据库的2种方法教程
May 17 Javascript
node.js中cluster的使用教程
Jun 09 Javascript
angularjs 动态从后台获取下拉框的值方法
Aug 13 Javascript
详解vue使用$http服务端收不到参数
Apr 19 Javascript
微信小程序后端实现授权登录
Feb 24 Javascript
解决echarts数据二次渲染不成功的问题
Jul 20 Javascript
vue缓存之keep-alive的理解和应用详解
Nov 02 Javascript
AngularJS 多指令Scope问题的解决
Oct 25 #Javascript
jQuery+Datatables实现表格批量删除功能【推荐】
Oct 24 #jQuery
webpack打包非模块化js的方法
Oct 24 #Javascript
如何实现一个webpack模块解析器
Oct 24 #Javascript
vue项目中使用Svg的方法
Oct 24 #Javascript
js中获取URL参数的共用方法getRequest()方法实例详解
Oct 24 #Javascript
小程序云开发初探(小结)
Oct 24 #Javascript
You might like
Codeigniter+PHPExcel实现导出数据到Excel文件
2014/06/12 PHP
PHP实现的简单对称加密与解密方法实例小结
2017/08/28 PHP
js 浮动层菜单收藏
2009/01/16 Javascript
Jquery中"$(document).ready(function(){ })"函数的使用详解
2013/12/30 Javascript
jQuery中Ajax的load方法详解
2015/01/14 Javascript
js根据鼠标移动速度背景图片自动旋转的方法
2015/02/28 Javascript
jQuery的几个我们必须了解的特点
2015/05/03 Javascript
javascript实现的登陆遮罩效果汇总
2015/11/09 Javascript
JS函数定义方式的区别介绍
2016/03/22 Javascript
快速使用Bootstrap搭建传送带
2016/05/06 Javascript
Bootstrap中datetimepicker使用小结
2016/12/28 Javascript
ES6(ECMAScript 6)新特性之模板字符串用法分析
2017/04/01 Javascript
Nodejs--post的公式详解
2017/04/29 NodeJs
深入浅析javascript继承体系
2017/10/23 Javascript
JavaScript时间戳与时间日期间相互转换
2017/12/11 Javascript
Angular实现的敏感文字自动过滤与提示功能示例
2017/12/29 Javascript
微信小程序实现购物车代码实例详解
2019/08/29 Javascript
node+multer实现图片上传的示例代码
2020/02/18 Javascript
[57:22]2018DOTA2亚洲邀请赛 4.7总决赛 LGD vs Mineski 第五场
2018/04/10 DOTA
python使用PIL模块实现给图片打水印的方法
2015/05/22 Python
Python numpy 常用函数总结
2017/12/07 Python
python+django+sql学生信息管理后台开发
2018/01/11 Python
使用python实现滑动验证码功能
2019/08/05 Python
详解Python中的正斜杠与反斜杠
2019/08/09 Python
python 并发编程 阻塞IO模型原理解析
2019/08/20 Python
浅谈Python 参数与变量
2020/06/20 Python
德国最新街头服饰网上商店:BODYCHECK
2019/09/15 全球购物
少先队活动总结
2014/08/29 职场文书
企业授权委托书范本
2014/09/22 职场文书
农村党建工作汇报材料
2014/10/27 职场文书
2014年社区矫正工作总结
2014/11/18 职场文书
社会治安综合治理责任书
2015/01/29 职场文书
垂直极限观后感
2015/06/08 职场文书
运动会新闻报道稿
2015/07/22 职场文书
Python Pytorch查询图像的特征从集合或数据库中查找图像
2022/04/09 Python
KVM基础命令详解
2022/04/30 Servers