新一代標(biāo)準(zhǔn)已經(jīng)提出有些日子了轮听,最近在寫(xiě)react的時(shí)候剥哑,react用了大量的class的語(yǔ)法去寫(xiě)組件别垮,對(duì)于class的理解不是很深刻盗舰,于是這里把從babel轉(zhuǎn)換的代碼分享給大家晶府。
類=構(gòu)造函數(shù)+原型
es6標(biāo)準(zhǔn)的類和其他語(yǔ)言的類很相似,但是這只是一種語(yǔ)法糖钻趋,底層還是通過(guò)原型繼承實(shí)現(xiàn)的川陆,首先看一個(gè)簡(jiǎn)單的類的形式
class A {
constructor(){
this.name='xiaoming'
}
sayHello(){
console.log('hello')
}
}
包含一個(gè)構(gòu)造函數(shù),和一個(gè)類里面的函數(shù)蛮位,如果大家對(duì)原型繼承有所了解的話较沪,這種形式可以近似寫(xiě)成
function A(){
this.name='xiaoming';
}
A.prototype={
sayHello:function(){
console.log('hello')
}
}
這就是類的雛形,但是實(shí)際操作上還是有些不同的失仁,下面就是babel翻譯的es5的語(yǔ)法
'use strict';
var _createClass = function () {
function defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor)
descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
return function (Constructor, protoProps, staticProps) {
if (protoProps)
defineProperties(Constructor.prototype, protoProps);
if (staticProps)
defineProperties(Constructor, staticProps);
return Constructor;
}}();
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
var A = function () {
function A() {
_classCallCheck(this, A);
this.name = 'xiaoming';
}
_createClass(A, [{
key: 'sayHello',
value: function sayHello() {
console.log('hello');
}
}]);
return A;
}();
這個(gè)代碼感興趣大家可以看看尸曼,其中有幾個(gè)地方需要注意,這個(gè)類A不能當(dāng)成函數(shù)去調(diào)用萄焦,A()
這種方法調(diào)用會(huì)報(bào)錯(cuò)控轿,可以
let a = new A();
這樣去實(shí)例化一個(gè)類,當(dāng)然類都是需要繼承的拂封,新版本中繼承用extends來(lái)實(shí)現(xiàn)茬射,考慮這樣一個(gè)類B繼承類A
class A {
constructor(){
this.name='xiaoming'
}
sayHello(){
console.log('hello')
}
}
class B extends A{
constructor(){
super()
this.age=12
}
sayHello(){
console.log('hello')
}
}
這是加完繼承后的代碼
'use strict';
var _createClass = function () {
function defineProperties(target, props) {
...//和前面一樣
}();
function _possibleConstructorReturn(self, call) { if (!self) {
throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); }
return call && (typeof call === "object" || typeof call === "function") ? call : self;
}
function _inherits(subClass, superClass) {
if (typeof superClass !== "function" && superClass !== null) {
throw new TypeError("Super expression must either be null or a function,not " + typeof superClass);
}
subClass.prototype = Object.create(superClass && superClass.prototype, {
constructor: {
value: subClass,
enumerable: false,
writable: true,
configurable: true
}
});
if (superClass)
Object.setPrototypeOf ?
Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
var A = function () {
function A() {
_classCallCheck(this, A);
this.name = 'xiaoming';
this.say = function () {
console.log("123");
};
}
_createClass(A, [{
key: 'sayHello',
value: function sayHello() {
console.log('hello');
}
}]);
return A;
}();
var B = function (_A) {
_inherits(B, _A);
function B() {
_classCallCheck(this, B);
var _this = _possibleConstructorReturn(this, (B.__proto__ || Object.getPrototypeOf(B)).call(this));
_this.age =12;
return _this;
}
_createClass(B, [{
key: 'sayHello',
value: function sayHello() {
console.log('hello');
}
}]);
return B;
}(A);
代碼很長(zhǎng),都不用看,只看_inherits這個(gè)函數(shù)冒签,從這一句
subClass.prototype = Object.create(superClass && superClass.prototype, {
constructor: {
value: subClass,
enumerable: false,
writable: true,
configurable: true
}
});
這里有個(gè)小技巧在抛,&&當(dāng)計(jì)算前面為false時(shí),就不計(jì)算后面的表達(dá)式了萧恕,當(dāng)然返回的是false或者最后一個(gè)值刚梭,這是從左向右計(jì)算的肠阱,這里意思就是如果 superClass 存在,那就計(jì)算 superClass.prototype朴读,當(dāng)然也就是存在的屹徘,這一句就是將B的原型設(shè)為一個(gè)以 A 的 prototype 為原型的對(duì)象,也就是說(shuō)
B.prototype.__proto__=A.prototype
proto VS prototype
這里__proto__
這個(gè)屬性是每個(gè)對(duì)象都有的磨德,就是因?yàn)檫@個(gè)屬性的存在缘回,對(duì)象可以繼承很多不是他自己的屬性或方法,比如toString()
典挑。雖然toString()
不是這個(gè)對(duì)象自己的方法,但是去調(diào)用一個(gè)對(duì)象的這個(gè)方法時(shí)啦吧,這個(gè)對(duì)象自己沒(méi)有您觉,就會(huì)去找這個(gè)對(duì)象的__proto__
,在它的__proto__
所指向的對(duì)象中去找授滓,如果找不到琳水,就會(huì)繼續(xù)去在這個(gè)__proto__
的__proto__
中去找,這就形成了一個(gè)原型鏈般堆,直到找到為止在孝,找不到就會(huì)報(bào)錯(cuò)。
而prototype
和__proto__
之間的區(qū)別很明顯淮摔,prototype
是函數(shù)對(duì)象所特有的私沮,他作為一個(gè)屬性指向另一個(gè)對(duì)象,即這個(gè)函數(shù)的原型對(duì)象和橙,它存在的目的只是為了生產(chǎn)對(duì)象仔燕,通過(guò)這個(gè)函數(shù)new出來(lái)的對(duì)象都有一個(gè)__proto__
屬性指向這個(gè)函數(shù)的原型對(duì)象,從下面代碼就可以看出來(lái)
function A(){
this.name='xiaoming'
}
A.prototype={
sayhi:function(){
console.log('hi')
}
}
var a=new A();
console.log(a.__proto__===A.prototype)
// true
也就是說(shuō)魔招,對(duì)象的__proto__
屬性指向那個(gè)制造這個(gè)對(duì)象的構(gòu)造函數(shù)的原型對(duì)象晰搀,通過(guò)對(duì)象字面量形式創(chuàng)建的對(duì)象的__proto__
就是Object.prototype
,
o = {};// 以字面量方式創(chuàng)建的空對(duì)象就相當(dāng)于:
o = Object.create(Object.prototype);
那么繼續(xù)之前的話題,_inherits
函數(shù)中有這一句
if (superClass)
Object.setPrototypeOf ?
Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
這樣子類的__proto__
就指向了父類办斑,就將原型鏈頭指向了父類外恕,也就是在B中可以使用A的方法和屬性,注意是在B這個(gè)構(gòu)造函數(shù)內(nèi)乡翅,B的原型對(duì)象在之前的代碼已經(jīng)解釋了鳞疲,通過(guò)Object.create
方法把B的原型對(duì)象綁定到了A的原型上,B的原型對(duì)象可以通過(guò)原型鏈原型繼承使用A的原型對(duì)象的屬性和方法峦朗。
總之最后的情況是這樣的
B.__proto__=A
B.prototype=對(duì)象xx
對(duì)象xx:{
__proto__:A.prototype
constructor:B
...
}
當(dāng)使用new去創(chuàng)建一個(gè)B的實(shí)例b時(shí)會(huì)發(fā)生這樣的過(guò)程
在constructor中會(huì)得到以this.xx建丧,比如this.age this.age...
等等還有一個(gè)不能忘了在B的構(gòu)造函數(shù)中會(huì)有個(gè)super(),這樣A的構(gòu)造函數(shù)也會(huì)執(zhí)行了波势,不然沒(méi)有name屬性
然后就是將這個(gè)對(duì)象的__proto__
指向那個(gè)對(duì)象xx翎朱,也就是B這個(gè)構(gòu)造函數(shù)的原型對(duì)象橄维,這樣就能訪問(wèn)這個(gè)原型鏈上的屬性和方法了。
總結(jié)
新版本的類也是基于原型繼承的拴曲,所以只要把基礎(chǔ)打好了争舞,遇到新的東西也理解的比較清楚,class中constructor對(duì)應(yīng)的還是以前的構(gòu)造函數(shù)澈灼,整個(gè)類里面的內(nèi)容就是這個(gè)構(gòu)造函數(shù)的原型對(duì)象的內(nèi)容竞川,如果有繼承還要加上繼承的對(duì)象的內(nèi)容,我們依然可以用類名xx
來(lái)指代以前的構(gòu)造函數(shù)叁熔,xx.prototype
來(lái)指代原型對(duì)象委乌。新的語(yǔ)法形式,對(duì)外隱藏了實(shí)現(xiàn)的細(xì)節(jié)荣回,寫(xiě)起來(lái)更加簡(jiǎn)潔遭贸,還有會(huì)在不正當(dāng)時(shí)使用時(shí)的錯(cuò)誤提示。