JS进阶系列 --- 原型

非常感谢你能看到这篇文章,也希望你能继续看下去……

何为原型

以一段简单的代码为例:

function Dog(param){
    this.name = param.name;
    this.age = param.age;
    this.bark = function(){
        console.log("汪汪汪!!!");
    };
}
var wc = new Dog({name:"旺财",age:3});

在上面这段代码里,Dog是构造函数(区别普遍函数,构造函数一般大写);wc是构造函数Dog的实例对象。
那么我们说:当构造函数创建时,会存在一个神秘对象。构造函数的prototype指向该神秘对象,实例化对象的__proto__会指向该神秘对象。

这个神秘对象就是原型。
wc.__proto__ == Dog.prototype == 原型所在的地址
下图描述了这种关系:
这里写图片描述

构造函数有原型属性,实例对象有原型继承
这里写图片描述

原型的两种使用方式

前面说过,我们一旦定义一个函数对象,系统会自动给这个函数对象附上原型属性prototype,这是默认原型链结构

沿用默认原型链结构
Dog.prototype.bark = function(){
    console.log("汪汪汪!!!");
}

这样子做,只是给Dog.prototype新添加了一个方法bark,没有破坏默认原型链结构

替换默认原型链结构
Dog.prototype = {
    bark:function(){
        console.log("汪汪汪!!!");
    }
}

这样子做,Dog.prototype指向了新的内存地址,丢失了默认原型链结构的一些默认方法,比如constructor
下图说明了这两种方式的区别:
这里写图片描述

原型的作用

原型的作用是为了实现继承
同样的,我们看一段代码:

function Dog(param){
    this.name = param.name;
    this.age = param.age;
    this.bark = function(){
        console.log("汪汪汪!!!");
    };
}
var wc = new Dog({name:"旺财",age:3});
var dh = new Dog({name:"大黄",age:2});

在上面的代码中构造函数Dog创建了两个实例:wc和dh,我们看一下内存分析图:
这里写图片描述

console.log(wc.bark == dh.bark);//结果为false

这说明结果正如上图所示wc.bark和dh.bark指向不同的内存地址,即构造函数Dog每创建一个实例对象,都必须要为公共的方法bark创建一段新的内存!
这很浪费内存
那么我们怎么办?解决方法就是把公共方法存放在构造方法的原型属性中,我们看修改后的代码:

function Dog(param){
    this.name = param.name;
    this.age = param.age;
}
Dog.prototype.bark = function(){
    console.log("汪汪汪!!!");
}
var wc = new Dog({name:"旺财",age:3});
var dh = new Dog({name:"大黄",age:2});

同样的,我们看一下内存分析图:
这里写图片描述

console.log(wc.bark == dh.bark);//结果为true

把公有的方法存放在构造函数的原型中,实例对象本身并不存放该方法。这样子做,不论构造函数创建多少个实例,该公有方法始终只存在一个。
这大大减少了内存的浪费

也许有人会问:在你的图上面wc和dh并没有bark方法,那么它们怎么能调用Dog.prototype里的bark方法?
原因就是js的原型继承特性,在后面谈到原型链的时候会做详细的探讨

也许有人会问:既然帮公有方法都放在构造函数的原型属性里能节省内存,那么为什么不把name,age等属性也都放在里面呢?
这样做是不对的,对象有共性和特性。比如人都会吃饭睡觉,这是共性。但不同的人姓名年龄不同,这是特性。我们把共性放在原型里,只维护一个。但是特性要放在构造函数里

原型链结构

前面已经说了原型的作用是为了实现继承,提高代码的复用。那么原型实现继承的方法是什么呢?使用原型链!
何为继承?学过java的朋友都知道,子类把父类的方法拿来自己用。
举个栗子:动物会吃饭睡觉,狗继承自动物,于是狗也会了吃饭睡觉;狗会吠叫,旺财和大黄继承自狗,于是旺财也会吠叫
于是旺财—->狗—->动物就形成了一条链,js原型也有一条类似的链叫做原型链
这条链的原则是:A是B的构造函数 那么B.__proto__ == A.prototype
如上诉的代码:Dog是wc的构造函数 那么wc.__proto__ == Dog.prototype,wc可以使用Dog.prototype里的属性和方法
我们再往上找找原型链吧!

Dog.prototype.__proto__ 是什么? 因为Dog.prototype是Obejct对象,所以Dog.prototype.__proto__ == Object.prototype
Dog.__proto__ 是什么?因为Dog是构造函数,是Function对象,所以Dog.__proto__ == Function.prototype
我们不妨画一下上面的原型链示意图:
这里写图片描述
现在你对原型链有了一个较为清晰的认识了吧!
还有最后一个问题,这也堪称是原型里最绕最乱的问题了,不过我会帮你理的清清楚楚!

Function和Object

这两位都是大佬级的角色了:Function是一切函数的起始,Object是一切对象的起始,他们怎么干一架呢?

  1. Object也是一个构造函数,只要是函数都是Function的实例,所以:Object.__proto__ == Function.prototype
  2. Function也是一个构造函数,所以: Function.__proto__ == Function.prototype
  3. Function.prototype是一个”对象?”,所以:Function.prototype.__proto__ == Object.prototype
  4. Object.prototype.__proto__ == null
    这里有一个巨坑:typeof Function.prototype 结果是function 我也不知道为什么,知道原因的大神请不吝赐教^-^

最后附上一张原型链总结图吧:
这里写图片描述

通过写博客的方式,我想把对前端编程的感悟记录下来,回顾自己走过的路会对以后要走的路有所启示的。
如果你和我一样是初窥前端的一个怀抱梦想的初学者,我希望我走过的这段路能对你有些许的启示,让你对前端收获一点感悟。
如果你是一个前端的大牛,也由衷的希望你能留下些许的指教。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值