不求甚解 - - liao一下prototype

如果你爱我,就干了这碗热热的毒鸡汤!
在父母的期望面前我们不敢说不行,我们总是用行动告诉他们我们是真的不行。欧耶!

关于prototype,怎么说呢,以前的前端开发是经常用的,但是最近忽然发现,好像很久都没用过这个属性了。因为现在封装好的主流框架和插件很多,用着方便,底层的东西都不怎么用了,也用不太到了。

最近自己在开发一款插件,突然发现这个东西我好像忘的差不多了。我的良心受到了深深的谴责,我怎么可能是这么不求甚解的人呢?(来自灵魂的拷问:难道不是吗?)所以写点东西,记录一下。哼哼~

什么是prototype?

prototype [ˈprəʊtətaɪp] : 原型

大家知道JavaScript是面向对象的,还有很多语言也是面向对象的,比如C++,Java等,但是JavaScript厉害了,官方定义:在JavaScript中一切皆为对象:字符串、数字、数组、日期,等等。(这么多对象,单身狗是不是很心动?咳咳)

话说JavaScript的面向对象和其它的语言的一样吗?首先,两者间面向对象的原理肯定是一样的。但是呢,又存在着一些细微的差别,在我的感觉上呢:一个是恪守成规的书香门第,一个是随遇而安的江湖浪子。毕竟当时Brendan Eich开发JavaScript的时候也是比较随性的,怎么简单好用怎么来,也方便了现在很多的初学者。拿Java和JavaScript举例吧,毕竟JavaScript是在Java盛行时的大环境下开发出来的:

在Java的面向对象中,类和实例是不同的实体,但是JavaScript中,一切皆为对象。
在Java的面向对象中,类不能当做函数去运行,但是JavaScript可以。
在Java的面向对象中,有丰富的继承机制,但是JavaScript仅通过原型链继承。
在Java的面向对象中,良好的成员作用域支持(private,protected,public等),但是JavaScript中全继承。

还有很多哈,在这就不一一列举了(下面会有,感兴趣的去看),JavaScript和Java一样是面向对象,但是JavaScript比Java的面向对象更加的灵活,方便。

所以JS中对象的定义: 拥有属性和方法的数据。

定义的非常的简单大气:一个对象,就是一个独立的个体,比如定义一个字符串,它很独立的,它有自己的长度属性:str.length;还有它的方法,比如subString(),indexOf() 等等,所以它就是一个字符串对象。

跑题了?没有没有,因为在JavaScript中,prototype对象是实现面向对象的一个重要机制。上面埋了伏笔,JavaScript是通过原型链来实现继承的。

先看一下prototype的官方定义: prototype 属性使您有能力向对象添加属性和方法。

从这两个定义中是不是看出了点什么,没错,prototype就是为了实现面向对象的继承被弄出来的。因为Brendan Eich大神不想讲Java上那套完整的继承机制全搬到JavaScript上,所以他决定为构造函数设置一个prototype属性,指向一个原型对象,而所有的对象都会继承原型对象的属性和方法。

所以原型链继承方式就这么形成了:

  • 创建构造函数
  • 通过new创建构造函数的实例,即对象
  • 通过构造函数自带的prototype对象,即原型对象,来修改对象的属性和方法

举个栗子:

function beauty () { // 构造函数this.skin = 'white';
}let my = new beauty(); // 实例化对象
let your = new beauty()
console.log(my.skin); // 继承原生属性beauty.beautiful = 'yes'; // 直接为构造函数添加属性
console.log(my.beautiful); // 没继承到beauty.prototype.realBeautiful = 'yes'; // 通过原型对象给构造函数添加属性
console.log(my.realBeautiful); // 继承到了
console.log(your.realBeautiful); // 其它实例也继承到共享属性

执行结果:

总结一下:

prototype是构造函数创建完成时自动生成的一个prototype 属性。该属性是个指针,指向了一个对象,这个对象我们称之为原型对象。我们可以在任意时刻和位置,通过修改该原型对象的属性或方法,为实例添加共享属性或方法。

用白话说,就是你的项目中大量的需要一个共享属性的时候,用prototype就对了。
再举个栗子,比如说我的项目中每个日期实例都需要一个返回去年年份的函数。可以这么做:

Date.prototype.getLastYear = () => {let now = new Date();return now.getFullYear() - 1;
};
let d = new Date();
console.log(d.getLastYear());

结果:

就是随便举个栗子,大概意思就是这么回事,理解了就好。

但是一般我们开发的过程中是不会这么用的,毕竟改了一些公共的类会导致很多问题的,在实际开发过程中,我们一般都这么搞:

function myDate () {}
myDate.prototype = new Date(); // 用一个新的构造函数去封装我们要继承的类
myDate.prototype.getLastYear = () => {let now = new Date();return now.getFullYear() - 1;
};
let d = new myDate(); // 创建这个新的构造函数的实例,继承原型对象的方法
console.log(d.getLastYear());

创建一个新的构造函数,原型指向我们需要的类,这样我们给构造函数原型添加公共属性时就很安全,方便了。(实现这种继承有很多方式,下一篇会写。)

prototype、constructor、__proto__

了解了原型,再了解一下原型链吧。

JavaScript 对象是动态的属性“包”(指其自己的属性)。JavaScript 对象有一个指向一个原型对象的链。当试图访问一个对象的属性时,它不仅仅在该对象上搜寻,还会搜寻该对象的原型,以及该对象的原型的原型,依次层层向上搜索,直到找到一个名字匹配的属性或到达原型链的末尾。

比如我写一个继承关系:

function beauty () { // 构造函数this.skin ='white';this.stature = '170';
}
beauty.prototype.realBeautiful = 'yes';
let my = new beauty(); // 实例化对象
console.log(beauty.prototype);
console.log(my);

看控制台上会怎么显示:

可以发现,原型对象里面,除了我们设置的两个属性外,还有两个属性(constructor,__proto__)。
而继承的对象上也多出了一个属性(__proto__)。

接下来,我们先看一下constructor属性吧:

诶,很神奇哦,它也有prototype属性,而且跟外层我们打出来啊的prototype属性一毛一样,如果再打开里边的prototype的constructor,就是个死循环了。
所以我们发现了beauty.prototype.constructor指向的是beauty函数,即该原型的所属构造函数。

再接再厉,打开__proto__属性,看里面有什么:

不难发现,原来__proto__指向的是该对象的原型对象。即my.__proto__ === beauty.prototype
beauty.__proto__ === Object.prototype。
也就明白了,为啥对象都可以使用上图中的hasOwnProperty,toString之类的方法了。

了解了这几个属性后,我们再看下属性到底是怎么继承的:
还是这个例子:

function beauty () { // 构造函数this.skin ='white';this.stature = '170';
}
beauty.prototype.skin = 'green';
beauty.prototype.realBeautiful = 'yes'; // 通过远行对象给构造函数添加属性
// 不要在beauty函数的原型上直接定义beauty.prototype = {realBeautiful:'yes'};这样会直接打破原型链
let my = new beauty(); // 实例化对象
my.stature = '160';console.log(my.skin);
console.log(my.realBeautiful);
console.log(my.stature);
console.log(my.face);

执行结果:

从执行结果可以看到:

对于JS对象的属性继承,是存在一个顺序的:

  • 首先,自身的属性:像上例中的 my.stature,my.skin,自身属性的值是可以任意修改的。(虽然beauty的原型中也设置了skin属性,但是还没到查找原型属性的那一步,这就是传说中的“属性遮蔽”)
  • 其次,原型的属性:像上例中的 my.realBeautiful,自身的属性中没有找到,会去原型中(my.__proto__中)找。
  • 最后,原型的原型的属性:比如上例中的my.face,自身的属性中没有,去原型中找,发现原型中(my.__proto__中,即beauty.prototype中)也没有,那就去原型的原型中找(my.__proto__.__proto__中,即Object.prototype中),知道返回的prototype值为null为止,返回属性值为undefined。

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

到此,JS的继承关系,所谓原型链,以及prototype,、__proto__,constructor之间的关系,应该都比较清楚了。

拓展(有兴趣的看)

1、Java和JavaScript面向对象的区别

该表格摘抄自: https://www.iteye.com/blog/jobar-2015021

Java JavaScript
静态类型 动态类型
自定义类型可以是类,接口或枚举定义 自定义类型由函数或原型定义
类型不可在运行时改变 类型可在运行时改变
定义变量需要声明具体类型(强类型) 定义变量不需要声明具体类型(弱类型)
构造器是具体的方法 构造器只是一个函数,构造器与函数之间无区别
类和实例是不同的实体 一切均为对象,构造器函数和原型也是对象
支持静态和实例成员 不直接支持静态和实例成员
由抽象类和接口支持抽象类型 不直接支持抽象类型
良好的成员作用域支持(private, package, protected,public) 仅支持public的成员
丰富的继承机制 仅通过原型继承机制
支持方法重载和方法重写 不直接支持方法重载和方法重写
丰富的反射机制 有反射特性
由包来支持模块化 无直接的包或模块化支持

2、关于为啥new一个对象的时候,会被自动添加一个__proto__ 属性呢?

let my = new beauty();

当你在执行这样的语句时,JavaScript实际执行的语句是:

var my = new Object();
my.__proto__ = beauty.prototype;
beauty.call(my);

3、如何判断某个对象是否为数组。(说白了就是判断继承关系)

有两种方法可以确定实例和构造函数之间的关系: instanceofisPrototypeOf
①、使用 instanceof 方法,判断某实例是否为某构造函数的实例。(凡是在二者的原型链中出现过的构造函数,都会返回true)

let arr = [1, 2, 3];
console.log(arr instanceof Array); // 返回true

②、使用 isPrototypeOf 方法,判断某原型是否为某实例原型链中出现过的。(凡是在原型链中出现过的原型,都会返回true)

let arr = [1, 2, 3];
console.log(Array.prototype.isPrototypeOf(arr)); // 返回true

4、我在测试第3题的过程中,发现个现象,觉得也要提一下。

看个例子,我定义了三个字符串。

let str = 'happy';
let str1 = String('happy');
let str2 = new String('happy');
console.log('str:', str);
console.log('str1:', str);
console.log('str2:', str);
console.log(str instanceof String);
console.log(str1 instanceof String);
console.log(str2 instanceof String);

执行结果:

于是我又这么干了一下:

let str = 'happy';
let str1 = String('happy');
let str2 = new String('happy');
console.log('str:', typeof str);
console.log('str1:', typeof str1);
console.log('str2:', typeof str2);

结果是这样的:

发现只有第三种定义方式才是对象,不是说JavaScript中一切皆为对象吗?所以继续看:

console.log(str.__proto__);

结果:

又确实是个对象,而且和str2.__proto__完全相同。

脑子感觉又乱了哈,没事,从头捋一捋。

首先呢,看一下JS的数据类型有哪些?

值类型(基本类型):字符串(String)、数字(Number)、布尔(Boolean)、对空(Null)、未定义(Undefined)、Symbol。

注:Symbol 是 ES6 引入了一种新的原始数据类型,表示独一无二的值。

引用数据类型:对象(Object)、数组(Array)、函数(Function)。

那么上边所谓JS的一切皆为对象的话,到底对不对呢?

来细细的扒一下。

我们在上边文章开始的位置提过JS对对象的定义:拥有属性和方法的数据。
那么基本类型是否拥有属性和方法?

很显然,有!

比如这些我们经常会用到属性:

let str = '123'; // 定义一个基本类型的字符串
console.log(str.length); // 字符串长度属性
console.log(str.substring(1,2)); // 截取字符串的方法

那它就是对象,没跑了。但是基本类型的对象,和我们上面说的Object对象还是不大一样的。

比如字符串,数值还有布尔型的数据。他们拥有对应的类String,Number,Boolean所拥有的的全部属性和方法。但是又跟new出来的String,Number,Boolean类的对象不一样:基本类型的对象无法修改或增加属性,但是new出来的可以。

举例:

let str = 'happy';
let str2 = new String('happy');
str.shuxing = '1alala';
str2.shuxing = '1alala';
console.log(str.shuxing);
console.log(str2.shuxing);

结果:

所以可以从这个方向理解,就是字符型,数值型以及布尔型,不是String类,Number类,以及Boolean类直接创建的对象,但是却继承了对应的类的所有的属性和方法。

为啥? JS就是这么规定的。

简单的说,就是需要自定义一些属性或方法的时候,可以new,不需要的话,基本类型足矣。

如果对堆栈比较了解的同学们,估计这么说会了解的更透彻:
String、Number、Boolean基本类型是存储在栈(stack)内存中的,数据大小确定,内存空间大小可以分配。而引用类型是存储在堆(heap)内存中的,例如对象。栈中存在的仅仅是一个堆的指针。

较真的同学又问了:那null和undefined呢?也是对象?

null 类型概念上说是一个空对象,即是一个不存在的对象的占位符,或者说是指向空对象的一个指针。使用typeof运算得到 “object”,所以说它是一个特殊的对象不为过。

undefined是从null派生出来的,即当声明的变量还未被初始化时,变量的默认值为undefined。
我个人理解大概就是:两个变量,undefined表示未初始化,null表示初始化为空对象。
官方的意思是希望undefined表示出错了,忘记赋值的意思,而null表示一种空对象的正常现象。意会意会!

差不多了,我已经到极限了。

来,跟我一起喊:在JS中一切皆为对象!

傲娇大少之---【JS的原型,prototype、__proto__、constructor】相关推荐

  1. 傲娇大少之——【面试总问的ES6】

    最近心里只有一句话: 减肥真特么难!虽然我已经收了十多斤了.但是看着还是不瘦,程序员是不是都有发胖的苦恼? 细说ES6 追本溯源,谈下ES6啥意思 大家都知道ES6新增了很多功能,但是这么多年过去了, ...

  2. 傲娇大少之——【GET请求和POST请求】

    若有来生,不婚不嫁,不孕不养,不做谁的妻,不为谁的娘. 只做自家女,养父母终老. 孑然一身,我行我素,随心所欲! ~诶,我好像不用等到来生... 关于http协议的get和post请求 get和pos ...

  3. js 的原型 (简单快速理解原型链)

    /*** 1.原型对象 XXX.prototype* 2.构造函数的原型对象获取 XXX.__proto__;* 3.使用 函数的方法或属性时,先从自身函数查找,找不到就像上查找 XXX.__prot ...

  4. JS中关于构造函数、原型链、prototype、constructor、instanceof、__proto__属性

    在Javascript不存在类(Class)的概念,javascript中不是基于类的,而是通过构造函数(constructor)和原型链(prototype chains)实现的.但是在ES6中引入 ...

  5. js 原型prototype继承模式

    js中利用原型prototype的方式实现继承是最常见的继承模式,如果让a的实例继承b,原型prototype的继承方式如下: function A(){} function B(){} A.prot ...

  6. JS中的prototype、__proto__与constructor,原型和原型链

    理解原型的几个关键点: 1.所有的引用类型(数组.函数.对象)可以自由扩展属性(除null以外); 2.所有的引用类型(对象)都有一个'_ _ proto_ _'属性(也叫隐式原型,它是一个普通的对象 ...

  7. 一张图理解JS的原型(prototype、_proto_、constructor的三角关系)

    注意:前方高能预警,请认真仔细看完,阅读完后自己再次画下原型图,相信你一定会有更深刻的认识.(推荐炒鸡好用的画流程图的软件ProcessOn) 构造函数:function Foo ( ) { }; 实 ...

  8. JS中的prototype、__proto__与constructor

    作为一名前端工程师,必须搞懂JS中的prototype.__proto__与constructor属性,相信很多初学者对这些属性存在许多困惑,容易把它们混淆,本文旨在帮助大家理清它们之间的关系并彻底搞 ...

  9. JavaScript中的原型(prototype)与继承

    在JavaScript中,原型是用来模仿其他「类」语言继承机制的基础.原型并不复杂,原型只是一个对象. 一.原型对象 1.1 什么是原型对象 每当我们创建了一个函数后,这个函数就拥有了一个protot ...

最新文章

  1. “几何深度学习”受爱因斯坦启示:让AI摆脱平面看到更高的维度
  2. java和python的web自动化有什么区别-Python和Java哪个更适合做自动化测试?
  3. [deviceone开发]-do_Dialog的基本使用示例
  4. 一文读懂区块链以及一个区块链的实现
  5. vue加跨域代理静态文件404_解决vue-router history模式和跨域代理 部署到IIS时404的一些问题...
  6. cocos2d-x 2.x版本接入bugly的总结
  7. oracle 备份恢复 12oracle逻辑备份恢复补充
  8. ADB登录验证暴力破解工具
  9. 怎么用python下载网易云_使用Python实现下载网易云音乐的高清MV
  10. java开发的格式与书写规范
  11. Scala基础:类和构造器
  12. Latex、如何将word中的表格转换为Latex代码
  13. 电视root工具_TapTap | 无需Root,成功移植 IOS14,拿下!!!
  14. 运城计算机学校耻 中学校,山西这几所中学,“一本”上线率过半,“名校收割机”实至名归...
  15. 视频显著性检测----《Flow Guided Recurrent Neural Encoder for Video Salient Object Detection》
  16. 负反馈分析方法的本质
  17. IDC评述网:11月份海外域名主机服务商TOP10
  18. thinksnsv4.6运行php,SNS开源社交系统ThinkSNS V4.6.0
  19. Springboot中设置response直接在线打开文件
  20. zz一个研究生毕业以后的人生规划(转自天涯虚拟社区)

热门文章

  1. 移动开发的罗曼蒂克消亡史?不存在的。
  2. 极化强度矢量和磁化强度矢量
  3. 本金50000元,能不能全职炒股养家?
  4. 小学生搞了自己学校的网站!
  5. 【罗德岛人事处】明日方舟模拟寻访PHP网站搭建
  6. 租赁行业提供固定资产管理的解决方案
  7. Hbuilder打包app---kalrry
  8. 逗塔战争TD新人入门图文攻略
  9. DNSPod十问巩书凯:制造业小工厂上云是个伪需求吗?
  10. 计算机cpu散热方式,常见的CPU散热方法