1.prototypeçå«ä¹
2.js原生语法之prototype,原型源码__proto__和constructor
3.请问babel能将es5转成es6吗?
prototypeçå«ä¹
æ们ç¥éJScriptä¸å¯¹è±¡çprototypeå±æ§ï¼æ¯ç¨æ¥è¿å对象类åååçå¼ç¨çãæ们使ç¨prototypeå±æ§æä¾å¯¹è±¡çç±»çä¸ç»åºæ¬åè½ã并ä¸å¯¹è±¡çæ°å®ä¾ä¼"继æ¿"èµäºè¯¥å¯¹è±¡ååçæä½ãä½æ¯è¿ä¸ªprototypeå°åºæ¯æä¹å®ç°å被管ççå¢ï¼
对äºå¯¹è±¡çprototypeå±æ§ç说æï¼JScriptæåä¸å¦æ¯è¯´ï¼ææ JScript å é¨å¯¹è±¡é½æåªè¯»ç prototype å±æ§ãå¯ä»¥åå ¶ååä¸å¨ææ·»å åè½(å±æ§åæ¹æ³)ï¼ä½è¯¥å¯¹è±¡ä¸è½è¢«èµäºä¸åçååãç¶èï¼ç¨æ·å®ä¹ç对象å¯ä»¥è¢«èµç»æ°çååã
ä¸é¢æ们çä¸ä¸ªç»å ¸çprototypeå±æ§ç使ç¨ç¤ºä¾ã
1ã为èæ¬ç¯å¢å 建对象添å æ¹æ³ï¼
ç¨åºä»£ç
Array.prototype.max = function()
{
var i, max = this[0];
for (i = 1; i < this.length; i++)
{
if (max < this[i])
max = this[i];
}
return max;
};
2ã为ç¨æ·èªå®ä¹ç±»æ·»å æ¹æ³ï¼
ç¨åºä»£ç
function TestObject(name)
{
this.m_Name = name;
}
TestObject.prototype.ShowName = function()
{
alert(this.m_Name);
};
3ãæ´æ°èªå®ä¹ç±»çprototypeï¼
ç¨åºä»£ç
function TestObjectA()
{
this.MethodA = function()
{
alert('TestObjectA.MethodA()');
}
}
function TestObjectB()
{
this.MethodB = function()
{
alert('TestObjectB.MethodB()');
}
}
TestObjectB.prototype = new TestObjectA();
第ä¸ä¸ªå¾ç¼çå§ï¼å¯¹åï¼å®å°±æ¯æ们åé¢ä»ç»çåå继æ¿æ³å~~ ä¸è¿ä»å¤©æ们ä¸æ¯ç 究"继æ¿"ï¼ä¹æ以å¯ä»¥è¿æ ·æ¥å®ç°ä¸ç§ç»§æ¿ï¼åªæ¯å©ç¨äºprototypeå±æ§çä¸ä¸ªå¯ä½ç¨èå·²ã
prototypeè¿æä¸ä¸ªé»è®¤çå±æ§ï¼constructorï¼æ¯ç¨æ¥è¡¨ç¤ºå建对象çå½æ°ç(å³æ们OOPé说çæé å½æ°)ãconstructorå±æ§æ¯ææå ·æprototypeå±æ§ç对象çæåãå®ä»¬å æ¬é¤GlobalåMath对象以å¤çææJScriptå é¨å¯¹è±¡ãconstructorå±æ§ä¿åäºå¯¹æé ç¹å®å¯¹è±¡å®ä¾çå½æ°çå¼ç¨ã
å¼æ¸ æ¥äºJScriptä¸prototypeå±æ§æä¹ä½¿ç¨åï¼ä¸é¢æ们åæ¥æ·±å ¥çç 究å®ã
ä¸é¢çæç« ä¸æç½åäºä¸ä¸prototypeå±æ§å¨JScriptä¸çåç§ç¨æ³ï¼ä½æ¯prototypeè¿ä¸ªä¸è¥¿å´ä¸æ¯JScriptåé åºæ¥çï¼JScriptå®é ä¸æ¯ä½¿ç¨äºæ们设计模å¼ä¸prototype patternçä¸ç§è¡çå½¢å¼ãä¸é¢æå ç®åç说ä¸ä¸prototype patternï¼ç¶ååæ¥çå°åºJScriptä¸çprototypeæ¯æä¹åäº?!
What's prototype pattern?
Specify the kinds of objects to create using a prototypical instance, and create new objects by copying this prototype.
ç¨ååå®ä¾æå®å建对象çç§ç±»ï¼å¹¶ä¸éè¿æ·è´è¿äºååå建æ°ç对象ã
åå模å¼å 许ä¸ä¸ªå¯¹è±¡åå建å¦å¤ä¸ä¸ªå¯å®å¶ç对象ï¼æ ¹æ¬æ éç¥éä»»ä½å¦ä½å建çç»èï¼å·¥ä½åçæ¯ï¼éè¿å°ä¸ä¸ªååå¯¹è±¡ä¼ ç»é£ä¸ªè¦åå¨å建ç对象ï¼è¿ä¸ªè¦åå¨å建ç对象éè¿è¯·æ±åå对象æ·è´å®ä»¬èªå·±æ¥å®æ½å建ã
继ç»äºè§£å°åºä»ä¹æ¯prototype patternï¼å¯ä»¥åç'设计模å¼ä¹Prototype(åå)'è¿ç¯æç« ï¼å³ä½¿ä¸æJavaä¹æ²¡æå ³ç³»ï¼æå®ç代ç é½å½C#çå°±è¡äºã
ææ¸ æ¥ä»ä¹æ¯ååäºå§ï¼åæ£è®°çä¸ç¹ï¼prototype patternæ¯çå®ç°æ¯ä¾èµäºcloneè¿ä¸ªæä½çï¼å½ç¶è¦shallow copyè¿æ¯deep copyçcloneçèªå·±çéè¦äºã
ä¸é¢æ们继ç»è¯´JScriptéçprototypeï¼ä¸ºä»ä¹æ们说å®åprototype patternéçprototypeä¸ä¸æ ·å¢?! è¿ä¸ªä¸æ¯æ说就说åºæ¥çï¼ä¹ä¸æ¯æå¹åºæ¥çï¼ççè¿ä¸ªç¤ºä¾ï¼ä½ å°±è½å¤§æ¦ç³æ¶ï¼
ç¨åºä»£ç
<script language="javascript">
function RP()
{
RP.PropertyA = 1;
RP.MethodA = function()
{
alert("RP.MethodA ");
};
this.PropertyA = ;
this.MethodA = function()
{
alert("this.MethodA");
};
}
RP.prototype.PropertyA = ;
RP.prototype.MethodA = function()
{
alert("RP.prototype.MethodA");
};
</script>
ä¸è¦çæ¥ï¼è¿æ²¡æå¼å§å示ä¾ï¼åªæ¯ç»åºäºæ们ç¨æ¥æ¼ç¤ºçä¸ä¸ªç±»ãRPæ¯ä»ä¹ï¼rpwtåï¼å½ç¶ä¸æ¯äºï¼RPæ¯ResearchPrototypeäºã好äºä¸åºè¯äºï¼ç示ä¾åç»æåæã
ç¨åºä»£ç
<script language="javascript">
rp = new RP();
alert(RP.PropertyA);
RP.MethodA();
alert(rp.PropertyA);
rp.MethodA();
</script>
è¿è¡ç»æéªäº®ç»åºï¼
1
RP.MethodA
this.MethodA
è¿ä¸ª%$@#^$%&^...ï¼ä¸è¦çæ¥ï¼ç»§ç»çå¦ï¼
ç¨åºä»£ç
<script language="javascript">
rp = new RP();
delete RP.PropertyA;
alert(RP.PropertyA);
delete RP.MethodA;
RP.MethodA();
delete rp.PropertyA;
alert(rp.PropertyA);
delete rp.MethodA;
rp.MethodA();
</script>
è¿è¡ç»æå次ç»åºï¼
ç¨åºä»£ç
undefined
A Runtime Error has occurred.
Do you wish to Debug?
Line:
Error: Object doesn't support this property or method
RP.prototype.MethodA
好ç©å§ï¼çåºæ¥ä»ä¹åå äºåï¼è¿éçRP.PropertyAåRP.MethodAåªæ¯ç¨æ¥ååç §çï¼å¯æ¯æä¹æthis.PropertyAåthis.MethodAé½deleteäºï¼è¿è½åºæ¥ç»æï¼èä¸è¿æ¯prototypeå¯¼å ¥çå±æ§åæ¹æ³å¢ï¼
è¿å°±æ¯JScriptçprototypeåprototype patternä¸prototypeæ大çä¸åäºï¼JScriptä¸çè¿ä¸ªæè°çprototypeå±æ§å ¶å®æ¯ä¸ªè¯è¨æ¬èº«æ¯æçç¹æ§ï¼è¿é没æåçä»»ä½çcopyï¼ä¸ç®¡shallowè¿æ¯deepçã对äºJScriptç解éå¼æï¼å®å¨å¤ç"."æ"[keyName]"å¼ç¨ç对象çå±æ§åæ¹æ³æ¶ï¼å å¨å¯¹è±¡æ¬èº«çå®ä¾(this)ä¸æ¥æ¾ï¼å¦ææ¾å°å°±è¿åææ§è¡ãå¦æ没ææ¥æ¾å°ï¼å°±æ¥æ¾å¯¹è±¡çprototype(this.constructor.prototype)éæ¯å¦å®ä¹äºè¢«æ¥æ¾ç对象åæ¹æ³ï¼å¦ææ¾å°å°±è¿åææ§è¡ï¼å¦æ没ææ¥æ¾å°ï¼å°±è¿åundefined(对äºå±æ§)æruntime error(对äºæ¹æ³)ã
æ£å 为prototypeå¯¼å ¥ç±»å®ä¾çå±æ§ææ¹æ³æ¯å¨ææ¥æ¾çï¼æ以æ们æè½å¯¹ç³»ç»å é¨å¯¹è±¡æ·»å prototypeå±æ§åæ¹æ³ï¼æ¯å¦ç»String对象添å trimæ¹æ³ï¼
ç¨åºä»£ç
<script lanuage="javascript">
String.prototype.trim()
{
return this.replace(/(^\s+)|(\s+$)/g, "");
}
</scritp>
æ¾ç¶JScriptä¸çè¿ç§ç¨æ³ä¹æ¯prototype patternä¸çprototypeä¸è½è§£éåæ¯æçã
è¿ä¸å¯¹äºJScript OOPä¸åå继æ¿æ³çç解å 该没æä»»ä½çéç¢äºå§ï¼åæ¶ä¹åºè¯¥æç½ä¸ºä»ä¹åå继æ¿æ³æé£ä¹å¤§ç天ç缺é·äºå§ï¼å½ç¶å¦ææä»»ä½é®é¢ï¼æ¬¢è¿ç»§ç»è®¨è®ºã
éæ¼ç¤ºç¤ºä¾æºä»£ç ï¼
ç¨åºä»£ç
<html>
<head>
<meta name="author" content="birdshome@å客å">
<title>JScript Prototype Research</title>
</head>
<body>
<script language="javascript">
function RP()
{
RP.PropertyA = 1;
RP.MethodA = function()
{
alert("RP.MethodA ");
};
this.PropertyA = ;
this.MethodA = function()
{
alert("this.MethodA");
};
}
RP.prototype.PropertyA = ;
RP.prototype.MethodA = function()
{
alert("RP.prototype.MethodA");
};
</script>
<script language="javascript">
rp = new RP();
delete RP.PropertyA;
alert(RP.PropertyA);
delete RP.MethodA;
RP.MethodA();
delete rp.PropertyA;
alert(rp.PropertyA);
delete rp.MethodA;
rp.MethodA();
</script>
</body>
</html>
js原生语法之prototype,__proto__和constructor
1前言
写了几篇vue的源码注释(并不算解析...),感觉到了对原型的理解是不够的,在js中,原型是非常重要的,只要你想在js这座山上往上爬,它就会嘲笑你,你把我搞会了么?如果没有,它就给你加个十倍重力.如果搞懂了,那肯定是能月薪过万,赢取白富美,走向人生巅峰的啦~~~
这篇文章讲的都是我自己的理解,应该是原创的(我有%把握,除非是我之前看过文章记到脑子里了,没法给到引用了,联系我可以加上),但是如果有人借鉴我的这篇文章,希望给到一个这篇文章的链接.其实我只是想我的文章能有更多的阅读量,我想月薪过万,赢取白富美,走向人生巅峰~~~
2前置知识点2.1数据类型js共有7种数据类型
从可不可以读取属性,可以分为两类
可以读取属性:
自身可以有属性:object
自身不可以有属性:string,number,boolean,symbol
不可以读取属性:null,undefined
null,undefined类型,读取和设置属性都是非法的,直接报错.
只有object能有自有属性,可以读取属性和设置属性
string,number,boolean,symbol类型可以读取属性,其实是先构造成包装对象,再读取属性,设置属性也是一样,可以理解设置到了会立即销毁的包装对象上,就是可以设置,但是没有任何实质效果.
2.2判断是否是自身属性(hasOwnProperty)hasOwnProperty方法是继承来的,用来判断该对象自身上是否有这个属性,有就行,不管是什么值
constobj={ a:1}consto=Object.create(obj)o.b=1o.c=void0console.log('a',o.a,o.hasOwnProperty('a'))//可以读取到值,继承而来,但不是自身属性console.log('b',o.b,o.hasOwnProperty('b'))//可以读取到值,自身属性console.log('c',o.c,o.hasOwnProperty('c'))//读取到undefined,自身属性console.log('d',o.d,o.hasOwnProperty('d'))//读取到undefined,不是自身属性,也没有继承到这个属性3一点小思考程序就是数据结构与算法,好的程序最好是使用最小的内存存储数据,使用最快的时间完成运行得到结果.
复用数据可以达到减少内存使用的目的,例如a和b需要完成一样的功能,就可以复用同一个方法(属性).
那么就需要解决一个问题,这个复用的方法存在哪里,a和b怎样能找到它.
在js中的解决方案是,a和b都由函数(这里先叫Function吧)构造而来,复用的方法存放在函数身上(prototype属性里).
(因为只有构造函数身上需要存放复用的方法,所以prototype只有可构造的函数上才有,箭头函数不是用来构造的,它就没有,其它对象,如果连函数都不是,就更不会有这个属性了)
那么需要给a,b和Function建立起联系,因为a,b需要到Function身上找它们可以用的复用方法
在js中的实现是通过constructor属性,即a.constructor,b.constructor可以找到Function
所以通过a.constructor.prototype可以找到它可以复用的方法的存放地址,为了快速找到js提供了一种快捷方法a.__proto__一步到位找到,即a.constructor.prototype和a.__proto__找到的是同一个对象,当然它俩是全等的啦.
//它俩都不是自有属性,我也不知道怎么从这俩属性上找到原型对象的了,肯定是魔法.....constobj={ }console.log(obj.hasOwnProperty('__proto__'))//falseconsole.log(obj.hasOwnProperty('constructor'))//false(所以,如果手动修改了constructor,prototype,__proto__的指向,那么你得清楚你在干什么)
(我不知道js的设计者是不是这样想的,哈哈,我就这样认为,这样好理解多了)
(这个过程称之为继承,而且是一个链式过程,即可以a.constructor.prototype.constructor.prototype.constructor.prototype这样查找,直到找到最顶端,这个过程可以由a.__proto__.__proto__.__proto__加速,这个就叫做原型链,js的继承只有这一种实现方式.)
(上面只是引导思考过程,其实查找原型对象并不会通过a.constructor.prototype去找,而是直接通过__proto__查找)
3.1修改constructorconstDog=function(){ }constdog=newDog()dog.constructor=0console.log(dog.hasOwnProperty('constructor'))//trueconsole.log(dog.constructor)//0console.log(dog.__proto__.constructor)//[Function:Dog]总结,修改了这个属性,增加了找到构造它的构造函数的难度,不能直接获取了,需要到原型对象上去读取.
如果它自身的这个属性和原型上的这个属性都被修改了,那么也只是找不到它的构造函数了而已,不会有别的影响.
3.1.1instanceofinstanceof关心的是原型链,跟constructor没有关系
印证上面的点,修改constructor属性,除了让实例找不到构造它的构造函数,没有别的影响了.如果需要通过实例找到它的构造函数,就需要维护好它俩的关系.
//语法是//ainstanceofb//这个操作符是判断a的原型链上是否有b.prototype,因为要判断b.prototype所以b必需是一个可构造的函数,否则会报错constfn=function(){ }consto=Object.create(fn.prototype)//此时o的原型链上有fn.prototype,因为o.__proto__===fn.prototypeconsole.log(oinstanceoffn)//trueconstemptyObj={ }fn.prototype=emptyObj//此时o的原型链上已经没有fn.prototype了,因为此时o.__proto__已经不再和fn.prototype相等了console.log(oinstanceoffn)//falseo.__proto__=emptyObj//修正了o.__proto__就好了console.log(oinstanceoffn)//true3.1.2isPrototypeOf现在有个新的api,实现的功能和instanceof一致,但是更加语义化一些,直接判断对象是否在另一个对象的原型链上
constfn=function(){ }consto=Object.create(fn.prototype)console.log(fn.prototype.isPrototypeOf(o))//true3.2修改__proto__|prototype先说一个总结,在构造实例的时候,会将这个实例的__proto__指向此时的构造函数的prototype,然后实例实际是继承的是__proto__.(为什么强调此时,因为构造函数的prototype可能会被修改指向,修改之后只会影响修改之后构造的实例,修改之前构造的实例还会使用修改之前的prototype)
所以,就可以理解到修改__proto__和prototype会有哪些影响了
修改__proto__的指向
只会影响它自己的继承
constDog=function(){ }constdog=newDog()constd=newDog()Dog.prototype.name='Dog'dog.__proto__={ name:'__proto__',}console.log(d.name)//Dogconsole.log(dog.name)//__proto__修改__proto__的属性
会影响这一波段构造的实例
constDog=function(){ }constdog=newDog()constd=newDog()Dog.prototype.name='Dog'console.log(d.name)//Dogconsole.log(dog.name)//DogDog.prototype={ name:'after',}constdog1=newDog()constd1=newDog()console.log(d1.name)//afterconsole.log(dog1.name)//afterdog1.__proto__.name='__proto__'//可以看到只影响了当前这一段构造的实例,之前和之后的都不会被影响到,因为这一段内的是同一个Dog.prototype,它们的__proto__都是指向它的console.log(d1.name)//__proto__console.log(dog1.name)//__proto__Dog.prototype={ name:'new',}constdog2=newDog()constd2=newDog()console.log(d2.name)//newconsole.log(dog2.name)//new修改prototype的指向
会影响这一波段构造的实例
修改prototype的属性
会影响这一波段构造的实例,同修改__proto__的属性
4修改和获取原型对象的方式4.1修改上面已经讲了修改prototype和__proto__
4.1.1Object.createconstobj={ name:'objName',}consto=Object.create(obj)//它相当于o.__proto__=obj,但是推荐使用`Object.create`console.log(o.name)//objNameconsole.log(o.__proto__===obj)//true4.1.2Object.setPrototypeOfconstobj={ name:'objName',}consto={ }Object.setPrototypeOf(o,obj)//它相当于o.__proto__=obj,但是推荐使用`Object.setPrototypeOf`constproto=Object.getPrototypeOf(o)console.log(proto===obj&&proto===o.__proto__)//trueconstobj1={ }o.__proto__=obj1constproto1=Object.getPrototypeOf(o)console.log(proto1===obj1&&proto1===o.__proto__)//true总结,在什么时候使用Object.create,在什么时候使用Object.setPrototypeOf呢,首先它俩都是标准api,都是建议使用的,在创建对象的时候就要指定原型时使用Object.create,需要动态修改原型对象时,使用Object.setPrototypeOf
4.2获取之前已经讲了,通过constructor.prototype和__proto__获取了
4.2.1Object.getPrototypeOfconstobj={ name:'objName',}consto={ }Object.setPrototypeOf(o,obj)constproto=Object.getPrototypeOf(o)console.log(proto===obj&&proto===o.__proto__)//true5js内置原生构造函数这些原生的构造函数的prototype属性是不可写,不可枚举,不可配置的
//它俩都不是自有属性,我也不知道怎么从这俩属性上找到原型对象的了,肯定是魔法.....constobj={ }console.log(obj.hasOwnProperty('__proto__'))//falseconsole.log(obj.hasOwnProperty('constructor'))//false.1js继承的最顶端是什么null,必须是这家伙,不然只能无限套娃了
然后其它所有对象都是从Object构造而来,所以所有的对象都可以继承到Object.prototype.
//它俩都不是自有属性,我也不知道怎么从这俩属性上找到原型对象的了,肯定是魔法.....constobj={ }console.log(obj.hasOwnProperty('__proto__'))//falseconsole.log(obj.hasOwnProperty('constructor'))//false.2js继承的二等公民(Function)在上面的小思考中,说到,js对象都是函数构造而来,所以包括Object也是由Function构造来的,甚至它自己都是由自己构造而来
//它俩都不是自有属性,我也不知道怎么从这俩属性上找到原型对象的了,肯定是魔法.....constobj={ }console.log(obj.hasOwnProperty('__proto__'))//falseconsole.log(obj.hasOwnProperty('constructor'))//false2我再来一点小理解,可能是在js内部做了小处理,第一个Function是凭空变出来的....然后这个Function构造出了Object,然后这个Object构造出了第一个原型对象Object.prototype,然后再去修改一些引用关系.
其实最复杂的是Object和Function的关系
//它俩都不是自有属性,我也不知道怎么从这俩属性上找到原型对象的了,肯定是魔法.....constobj={ }console.log(obj.hasOwnProperty('__proto__'))//falseconsole.log(obj.hasOwnProperty('constructor'))//false.3js继承的三等公民(内置的其他构造函数)//它俩都不是自有属性,我也不知道怎么从这俩属性上找到原型对象的了,肯定是魔法.....constobj={ }console.log(obj.hasOwnProperty('__proto__'))//falseconsole.log(obj.hasOwnProperty('constructor'))//false用户定义的特定公民构造函数这个才是重点,根据上面的理解,我会再开一篇文章写一下我理解的js的继承,这里就先留个坑
7.总结这篇文章跟网上大多讲constructor,prototype,__proto__的文章都有所不同,我的立足点是从给定的一个可以读取属性的值开始,在js中,除了null和undefined,其它所有的值都可以成为立足点.从这个立足点开始,它的__proto__属性记录了它的原型对象,这个原型对象是构造它时,它的构造函数的prototype属性的值。
//它俩都不是继承自有属性,我也不知道怎么从这俩属性上找到原型对象的了,肯定是魔法.....constobj={ }console.log(obj.hasOwnProperty('__proto__'))//falseconsole.log(obj.hasOwnProperty('constructor'))//false5读取一个值的属性的值时,如果它自身有这个属性,那么直接返回这个属性的值,否则就会到它的__proto__对象上去找,一直递归下去,直到找到顶部null,找到就返回它的值,没找到就返回undefined
这篇文章有三个理解点,让我茅塞顿开,都是在我试验了好久突然得到的结论:
以一个值为立足点开始分析
在构造实例的时候,会将这个实例__proto__指向此时的构造函数的prototype
查找原型对象时,以__proto__为准
请问babel能将es5转成es6吗?
反转技能在前端圈中大放异彩,Deno 的原型源码诞生即是对 Node 的一次正面反转。别忘了,继承Babel 本名 6to5,原型源码逆向运行即成为 5to6,继承源码冒险小说满足了问题的原型源码全部需求。
在前端工程师的继承技能库中,Babel 的原型源码编译原理是人尽皆知的知识点。作为 AST 结构转换工具,继承Babel 的原型源码输出标准并非固定,可以灵活调整以适应不同版本的继承代码需求。此领域代表项目包括 jscodeshift、原型源码java serializable 源码recast 等。继承通过 Babel,原型源码可以自动优化老项目中的 lodash 的 require,替换为按需 import,并且替换 let 和箭头函数等。其核心功能在于对源码进行自动化优化。ntp源码安装
这些优化基本上在保持语言表达力的基础上进行,主要涉及语法结构的转换,如从 prototype 转向 class。尽管 prototype 继承的动态性很强,但通过静态分析在编译期确定原型链的难度较大,难以自动化迁移到 ES6 的lede 稳定 源码 class 语法。
转换 prototype 到 class 难以实现,反映了类式继承(classical inheritance)和原型继承(prototypal inheritance)的内在差异。前者在编译期即可确定继承链,后者则在运行时动态组装。原型继承的灵活性虽强,但也带来复杂的mybatis batch源码语法结构,难以通过反向生成更高层的抽象。
经验表明,从高级抽象到低级抽象的转换相对容易,反之则困难重重。现今,"可视化自动生成代码"领域虽有进展,但将修改过的逻辑代码重新进行可视化编辑仍非易事。
5to6 并未成为热门话题,ES5 时代已逝,各大公司自然无需从 ES5 升级至 ES6。不过,这种技能的未来仍然充满希望。比如,可以将 ES6 中的每个 VariableDeclaration 加上 "any",实现从 ES6 平滑升级至 AnyScript(夸张表述)。
总而言之,反转技能虽然实际用途有限,但其独特的实用价值不容忽视。在适当情况下,敢于并善于反转,能为生活增添无尽乐趣。