译者:T5450LX1
两个著眼于web控制技术的80后
你不必美天傻子,你只须要美天这些喽 你就一定会胜过绝大部分人!
具体来说他们介绍呵呵一般第一类 与 表达式第一类
他们在自学蓝本以后具体来说介绍呵呵javascript之中的一般第一类 和 表达式第一类,
如图1
javascript中什么样情形归属于一般第一类 如下表所示标识符:
//一般第一类 function Test() { } var obj1 = new Test(); varobj2 =new Object(); var obj3 = {}; console.log(typeof obj1); //Object console.log(typeof obj2); //Object console.log(typeof obj3); //Object表达式第一类:凡是用Function()创建的都是表达式第一类。 比如:自定义表达式, 事件表达式 系统的Object、Array、Date、String、RegEx 以上都归属于表达式第一类 ==小提示:== 以上的都是Function的实例第一类, 那么也只有实例才会有_ _ proto_ _ 那个特性特别注意:这里的Function是比较特殊的表达式第一类, 因为Function.prototype本身它应该对准是蓝本第一类, 但Function的prototype却是表达式第一类 上图已经有表明!
javascript中什么样情形归属于表达式第一类 如下表所示标识符:
//表达式第一类 function F1(){ } var F2 = function(){ } var F3 = function(a,b){ } window.onload=function () { var div1=document.getElementById(div1); div1.onclick=function () { alert(1); } console.log(typeof div1.onclick); //function } console.log(typeof F1); //function console.log(typeof F2); //function console.log(typeof F3); //function console.log(typeof Object); //function console.log(typeof Array); //function console.log(typeof String); //function console.log(typeof Date); //function console.log(typeof RegEx); //function console.log(typeof Function); //function表达式第一类都是Function的实例第一类
就如同Array是通过Function创建出来的。
因为Array是Function的实例,是实例就会有._ _ proto_ _ 那个特性, 从上面的流程图上看 两个表达式第一类的_ _ proto_ _属性值是: ƒ () { [native code] }
而特殊的Function.prototype的值也是两个: ƒ () { [native code] }
所以他们可以推断出两个条件: 表达式第一类._ _ proto_ _ === Function.prototype 是成立的! 回到true
由此引出上面判断条件:
Array._ _proto_ _ == Function.prototype //true String._ _proto_ _ == Function.prototype //true RegExp._ _proto_ _== Function.prototype //true Date._ _proto_ _ ==Function.prototype //trueƒ () { [native code] } 是什么?
native code 的意思是它是程序自带的,是二进制编译得无法显示出来标识符, native code是本地标识符, 这里他们就简单地解释呵呵即可!
以上内容作为自学蓝本以后的铺垫介绍即可!! 接下来他们就慢慢地拆解图1的细节内容!!
引出两个问题
问题1 性能方面
假如创建两个第一类 在内存的堆区中就会开辟两个空间来保存第一类,假如每个第一类里面有相同的方法也会被创建出来
这样就存在两个问题,就是公共的方法或者特性会存在内存之中n份.. , 大量的占用了内存开销!
如图2
每两个第一类都生成了同样的say()方法, 这种标识符中假如每个第一类都有公共一样的方法 就显得很占据内存空间!
上图的标识符如下表所示
function Person(name,age){ this.name=name; this,age=age;this.say=function () { console(输出结果); } } var obj1=new Person(); var obj2=new Person(); varobj3=new Person(); var obj4=new Person(); var obj5=new Person();问题2 特性 方法 不能共享!
有时候他们希望两个方法能够被多个相同第一类类型都可以公共的进行使用!
例如: 定义两个数组私有方法, 而另外两个数组第一类是不能访问那个私有方法的
标识符案例:
var arr=[5,5,10]; //arr数组第一类的sum方法 arr.sum=function () { varresult=0; for(var i=0;i<this.length;i++){ result+=this[i]; } return result; } console.log(arr.sum()); //结果: 20 var arr2=[10,10,10]; console.log(arr2.sum()); //结果: Uncaught TypeError: arr2.sum is not a function解答: 这里报错是因为 arr2 那个数组第一类 根本就不存在sum() 那个方法, 它是归属于arr数组第一类私有的两个方法!
所以有时候他们希望两个方法能够被多个相同第一类类型都可以公共的来使用!
以上两个问题 , 问题1是性能优化不足,问题2是私有方法不能被相同类型的第一类初始化 , 所以解决上述问题方法或特性不能共享的办法 就要用到: 蓝本[提高性能] 也就是通常说的:蓝本模式
接下来他们就来探讨呵呵蓝本是什么!
蓝本第一类是什么!
根据图1 表达式第一类都有两个prototype特性对准的是两个蓝本第一类, 那么他们可以推出以下概念:
每创建两个表达式都会有两个prototype特性,那个特性是两个指针,对准两个第一类(蓝本第一类)
蓝本第一类,也就是(构造表达式.prototype), 之中含有两个constructor 特性 那个特性(对准的就是当前蓝本第一类的构造表达式)
如下表所示图:
蓝本第一类作用:是包含特定类型的所有实例共享的特性和方法, 就是说你把特性和方法定义在蓝本第一类里面之后,那么那个类型的实例就都会共享这些特性和方法!
蓝本第一类优点: 就是可以让所有实例第一类共享它所包含的特性和方法。
蓝本第一类的语法基础
要特定类型的所有实例都共享的特性和方法, 就要把它们定义在蓝本第一类的上面!
蓝本: 要使用prototype那个关键字, 要写在构造表达式的上面:
//语法如下表所示构造表达式名.prototype.特性=值; 构造表达式名.prototype.方法=function(){ ..标识符段.. }如下表所示图: 所以用: 构造表达式名.prototype 你就可以把特性和方法定义在蓝本第一类之中
案例标识符:
function createPerson(name,age) { this.name=name; this,age=age; } createPerson.prototype.say=function () { console.log(我的名字叫+this.name); } var a=new createPerson(张三,33); var b=new createPerson(李四,55); var c=new createPerson(王武,66); a.say(); b.say(); c.say();为了方便理解 我画了一张图, 以上标识符的流程图分析如下表所示图:
特殊的Function.prototype
Function.prototype是个例外,为什么说它是两个例外呢? 按道理来
作为两个表达式第一类,它又没prototype特性。 从图1中就可以看出来那个道理!
ƒ () { [native code] } 那个东西是什么上面已经解释过了!
如下表所示图:
蓝本知识点
为了节省内存,他们把第一类的方法都放在蓝本里面。为什么呢?
因为改写第一类上面公用的方法或者特性、让公用的方法或者特性在内存中只存在一份
在他们通过new实例化第一类的时候,在内存中,会自动拷贝构造表达式中的所有特性和方法,用来给实例第一类赋值,而不管他们实例化多少次,蓝本里面的特性和方法只生成一次,所以会节省内存。
一般定义方式与蓝本定义的优先级高低
如下表所示标识符:
function createPerson(name,age) { this.name=name; this,age=age; } createPerson.prototype.say=function () { console.log(我的名字叫+this.name); } var a=newcreatePerson(张三,33); var b=new createPerson(李四,55); var c=new createPerson(王武,66); a.say(); b.say(); c.say();//一般定义的优先级高于蓝本prototype c.say=function(){ console.log(输出ok); } c.say();所以以上的一般定义的方式要比蓝本定义的方式的优先级高!,但这并不是把蓝本覆盖了 只是优先初始化一般定义的方法
如图:
蓝本的中的 _ _ proto_ _
具体来说回顾呵呵, 实例化new的时候,系统会在内存中创建了两个空的第一类,就像是这样 var p = {} , 拷贝构造表达式中的特性和方法到空第一类中。
重点的是: 每个实例化第一类都会有两个 _ _ proto_ _ 特性, 那个特性是自动生成的, _ _ proto_ _ 特性对准类的蓝本第一类。
构造表达式、实例化第一类、蓝本第一类的之间的关系
先来看一段标识符案例
function createPerson(name,age) { this.name=name;this,age=age; } createPerson.prototype.say=function () { console.log(我的名字叫+this.name); }var obj=new createPerson(张三,33); console.log(obj.__proto__); console.log(createPerson.prototype); console.log(obj.__proto__.constructor); //构造表达式、实例化第一类、蓝本第一类的基本关系图分析
当然你也可以通过 实例化第一类的_ _ proto_ _特性 和 构造表达式的prototype特性进行比较可以验证结果, 他们可以通过打印来验证
console.log(实例化第一类._ _proto_ _ === 构造表达式.prototype); //true定义在实例和定义在蓝本下的区别总结:
先看一段代码案例:
function Test(){ } //定义特性 Test.prototype.name = “张三”; Test.prototype.age = 33; //定义方法 Test.prototype.getAge = function(){ return this.age; } var t1 = new Test(); var t2 = new Test(); var t3 = new Test(); t3.name = “李四”; console.log(t1.name); // 张三 来自蓝本 console.log(t2.name); // 张三 来自蓝本 console.log(t3.name); // 李四 来自实例 //打印实例看下图结果 console.log(t1); console.log(t2); console.log(t3);以上图就解释了为什么定义在蓝本中 特性和方法是公用的, 而单独定义在实例中是归属于独立的特性和方法不共有!
所以 他们也推断出 在实例中定义特性和方法 会覆盖 或者说 会实现初始化实例中定义的特性和方法 假如没才会到蓝本中去寻找! 这里其实就是他们一会要讲到的蓝本链!
_ _ proto_ _与 prototype的详细认识
1.所有的引用类型,比如数组、第一类、都有两个_ _ proto_ _特性(也叫隐式蓝本,它来对准自己的原型第一类)
重点再次提醒: 所有的第一类引用 都有_ _ proto_ _ 那个特性! 记住了!
通过上面的测试他们不难发现,其中它们赋值的引用第一类中 打印出来看到都有两个 _ _ proto_ _的特性 都是对准自己的蓝本第一类
标识符如下表所示:
function createPerson(name,age) { this.name=name; this,age=age; } createPerson.prototype.say=function () { console.log(我的名字叫+this.name); } var obj=new createPerson(测试,33); //第一类引用打印 console.log(obj); var arr=[1,2,3]; //数组引用的打印 console.log(arr); var arr2=new Array(2,2,2); //数组引用的打印 console.log(arr2);2.再一次重点特别注意: 所有引用类型,它的_ _ proto_ _特性对准那个引用本身的蓝本第一类 而构造表达式的prototype特性的值也就是对准的蓝本第一类
所以在各自相应引用类型的_ _ proto_ _特性 和 构造表达式的 prototype特性 彼此它们两个是相等的! 上面的图中也可以表明这一点!
_ _ proto_ _特性 和 构造表达式的 prototype特性比较, 案例标识符如下表所示:
//案例1 function createPerson(name,age) { this.name=name; this,age=age; } createPerson.prototype.say=function () { console.log(我的名字叫+this.name); } var obj=new createPerson(张三,33); console.log(obj.__proto__);//打印出obj第一类引用的蓝本第一类 console.log(createPerson.prototype); //打印出createPerson构造表达式的蓝本第一类 console.log(obj.__proto__===createPerson.prototype);//而且它们是相等的,对准同两个蓝本第一类 //案例2 var arr=new Array(2,2,2); console.log(arr.__proto__);console.log(Array.prototype); console.log(arr.__proto__ === Array.prototype);3.所有的构造函数 或者 一般表达式都有两个prototype特性 (这也叫显式蓝本,它也对准自己的蓝本第一类)。
案例标识符:
//一般表达式 function Test() { } //打印一般表达式的prototype特性 console.log(Test.prototype); //构造表达式 function createPerson(name,age) { this.name=name; this,age=age; } createPerson.prototype.say=function () { console.log(我的名字叫+this.name); } console.log(createPerson.prototype);图解如下表所示:
_ _proto _ _和 prototype区别
prototype是每个表达式都会具备的两个特性,它是两个指针,对准蓝本第一类,只有一般表达式或 构造函数才有。
_ _ proto_ _特性 是主流浏览器上在除null第一类以外的每个引用第一类上都支持存在的两个特性,它能够对准当前该引用第一类的:蓝本第一类 其实_ _ proto_ _就是用来将引用第一类与蓝本相连的特性
小结: 两个只有表达式才有的特性(prototype),两个是引用第一类才有的特性(_ _ proto_ _ ),
特别注意: 你用两个表达式去初始化特性(_ _ proto_ _ ), 会得到两个: ƒ () { [native code] }
蓝本中批量添加特性与方法
使用prototype那个关键字, 要批量的把特性和方法写入蓝本 就在构造表达式的上面写两个JSON格式 如下表所示标识符, 这样比单一的两个个写方便!
//语法 构造表达式.prototype={ 特性名: 值, 方法名:function(){ //方法体… 这里的this是什么要看执行的时候谁初始化了那个表达式 }, 方法名:function(){ //方法体… 这里的this是什么要看执行的时候谁初始化了那个表达式 } }案例标识符:
createPerson.prototype={aaa:123, // prototype第一类里面又有其他的特性 showName:function(){ //this是什么要看执行的时候谁初始化了那个表达式 console.log(“我的名字叫:”+this.name); }, showAge:function(){ //this是什么要看执行的时候谁初始化了那个表达式 console.log(“我的年龄是:”+this.age); } }function createPerson(name,age) { this.name=name; this.age=age; } var obj= newcreatePerson(张三,33); console.log(obj); obj.showName(); obj.showAge(); console.log(obj.aaa);以上蓝本标识符 图解
蓝本特别注意事项
重点特别注意
假如是自定义构造表达式,并且使用{ }这种方式批量的在prototype中定义特性和方法, 会改变蓝本中constructor对构造表达式的对准!
也就是说使用{ }这种方式批量在prototype中定义特性和方法, 那么constructor的对准就是两个表达式第一类
测试标识符如下表所示
function createPerson(name,age) { this.name=name; this,age=age; }/* createPerson.prototype.say=function(){ console.log(我的名字叫+this.name); }*/ createPerson.prototype={ say:function () { console.log(我的名字叫+this.name); } } varobj=new createPerson(张三,33); console.log(obj.__proto__);在控制台输出会看到 _ _proto _ _的值, constructor那个特性就没了!
蓝本基本小结
让相同方法在内存中存在一份
蓝本定义方式要比一般定义方式的优先级要低
在项目之中公共相同的特性和方法可以加载在蓝本上
蓝本链 核心原理
具体来说这里要提出一点的是 在JS中实现继承主要是依靠蓝本链来实现! ,所以他们才须要自学蓝本链的原理!
原型链核心概念
蓝本链: 当试图初始化或想得到两个第一类实例中的特性 或 方法时,假如那个第一类本身不存在那个特性 或 方法 也就是说构造表达式中没定义你想要的特性或方法,那么就会通过构造表达式的prototype特性到蓝本中去 寻找那个特性 或者 方法 (也就是它的构造表达式的’prototype’特性会到蓝本第一类中去寻找) , 假如有就回到,假如没就会到顶层的Object去找 , 假如有就回到, 假如还是找不到就回到undefined!
蓝本链流程图
上图可以用以下例子来表明:当构造表达式 Test 存在 getName 那个方法时,就不必到构造表达式的蓝本之中去找 getName那个方法;反之,就到构造表达式的蓝本之中去找getName那个方法;假如构造表达式的蓝本中也不存在 getName那个方法时,就要到顶层第一类的蓝本中去找 getName那个方法。
上图测试案例标识符如下表所示:
function Test(name){ this.name=name;this.getName=function(){ return this.name+“我在构造表达式中”; } } Test.prototype.getName=function(){ return this.name+“我在蓝本第一类中”; } Object.prototype.getName=function(){ return this.name+“我在顶层第一类中”; } var t1=new Test(小红); console.log(t1.getName());蓝本链案例标识符2 如下表所示:
Array.prototype.aaa=123; //把那个自定义特性定义到蓝本第一类下 var arr=new Array(1,2,3); console.log(arr.aaa); console.log(Array.prototype); var arr2=[重庆,上海,北京]; console.log(arr2.aaa);如下表所示图:
小结: arr 和 arr2都能够找到aaa那个特性, 并且那个特性是数组蓝本第一类下的特性,是公共的
只要是数组就可以初始化那个特性, 同理方法也是一样,
所以创建很多很多个相同类型第一类的时候, 创建出来的每两个第一类,假如里面都有一些公共的方法,这样就会占用很多的资源,
而通过蓝本来实现的话,只须要在构造表达式里面给特性赋值,而把方法写在prototype特性,当然特性也是可以写在蓝本之中的,
这样每个第一类引用都可以使用prototype特性里面的方法 或 特性,并且节省了不少的资源
这就是他们为什么要使用蓝本的原因!
蓝本链总体结构图解小结
当试图初始化或想得到两个第一类实例中的特性 或 方法时,假如那个第一类本身不存在那个特性 或 方法 那么就会通过构造表达式的prototype特性到蓝本中去 寻找那个特性 或者 方法 假如有就回到,假如没就会到顶层的Object去找 , 假如有就回到, 假如还是找不到就回到undefined!
但又因为构造表达式中的prototype特性值本身又是两个第一类(蓝本第一类), 所以这里它也有两个_ _ proto
如下表所示图:
小结:
当obj初始化test()方法,JS发现Fn中没那个方法,于是它就去Fn.prototype中去找,发现还是没那个方法,然后就去Object.prototype中去找,找到了,就初始化Object.prototype中的test()方法。
这就是蓝本链查找,而则一层一层的链接的关系就是:蓝本链。
obj能够初始化Object.prototype中的方法正是因为存在蓝本链关系的机制!
另外,在使用蓝本的时候,一般推荐将须要扩展的方法写在 构造表达式.prototype特性中,
而不要写在: 构造表达式.prototype._ _ proto _ _中, 因为这里也就是Object顶层第一类,定义到这里的特性和方法 所有JS第一类都可以初始化,在这里面定义多了就会影响整体性能,所以不建议定义到这里!
T5450LX1@今日头条,首发原创控制技术文章
假如喜欢话请 “点赞 评论 收藏” 一键三连
大家的支持就是我坚持下去的动力!
