国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

Class:向傳統(tǒng)類模式轉(zhuǎn)變的構(gòu)造函數(shù)

jone5679 / 1468人閱讀

摘要:所以的類等同于構(gòu)造函數(shù)。只能存在于子類的構(gòu)造方法中,這時(shí)它指代父類構(gòu)造函數(shù)。指代父類的構(gòu)造函數(shù)此時(shí)指向,打印出。改變了構(gòu)造函數(shù)的行為,一旦發(fā)現(xiàn)其不是通過這種形式調(diào)用的,構(gòu)造函數(shù)會(huì)忽略傳入的參數(shù)。

前言

JS基于原型的‘類’,一直被轉(zhuǎn)行前端的碼僚們大呼驚奇,但接近傳統(tǒng)模式使用class關(guān)鍵字定義的出現(xiàn),卻使得一些前端同行深感遺憾而紛紛留言:“還我獨(dú)特的JS”、“凈搞些沒實(shí)質(zhì)的東西”、“自己沒有類還非要往別家的類上靠”,甚至是“已轉(zhuǎn)行”等等。有情緒很正常,畢竟新知識(shí)意味著更多時(shí)間與精力的開銷,又不是簡(jiǎn)單的閉眼享受。

然而歷史的軸印前行依舊,對(duì)于class可以肯定的一點(diǎn)是你不能對(duì)面試官說:“拜托,不是小弟不懂,僅僅是不愿意了解,您換個(gè)問題唄!”一方面雖然class只是個(gè)語法糖,但extends對(duì)繼承的改進(jìn)還是不錯(cuò)的。另一方面今后可能在‘類’上出現(xiàn)的新特性應(yīng)該是由class而不是構(gòu)造函數(shù)承載,誰也不確定它將來會(huì)出落得怎樣標(biāo)致。因此,來來來,慢慢的喝下這碗熱氣騰騰的紅糖姜湯。

1 class

ECMAScript中沒有類的概念,我們的實(shí)例是基于原型由構(gòu)造函數(shù)生成具有動(dòng)態(tài)屬性和方法的對(duì)象。不過為了與國際接軌,描述的更為簡(jiǎn)便和高大上,依然會(huì)使用‘類’這一詞。所以JS的類等同于構(gòu)造函數(shù)。ES6的class只是個(gè)語法糖,其定義生成的對(duì)象依然構(gòu)造函數(shù)。不過為了與構(gòu)造函數(shù)模式區(qū)分開,我們稱其為類模式。學(xué)習(xí)class需要有構(gòu)造函數(shù)和原型對(duì)象的知識(shí),具體可以自行百度。

// ---使用構(gòu)造函數(shù)
function C () {
  console.log("New someone.");
}

C.a = function () { return "a"; }; // 靜態(tài)方法

C.prototype.b = function () { return "b"; }; // 原型方法


// ---使用class
class C {
  static a() { return "a"; } // 靜態(tài)方法
  
  constructor() { console.log("New someone."); } // 構(gòu)造方法
  
  b() { return "b"; } // 原型方法
};
1.1 與變量對(duì)比

關(guān)鍵字class類似定義函數(shù)的關(guān)鍵字function,其定義的方式有聲明式和表達(dá)式(匿名式和命名式)兩種。通過聲明式定義的變量的性質(zhì)與function不同,更為類似letconst,不會(huì)提前解析,不存在變量提升,不與全局作用域掛鉤和擁有暫時(shí)性死區(qū)等。class定義生成的變量就是一個(gè)構(gòu)造函數(shù),也因此,類可以寫成立即執(zhí)行的模式。

// ---聲明式
class C {}
function F() {}

// ---匿名表達(dá)式
let C = class {};
let F = function () {};

// ---命名表達(dá)式
let C = class CC {};
let F = function FF() {};

// ---本質(zhì)是個(gè)函數(shù)
class C {}
console.log(typeof C); // function
console.log(Object.prototype.toString.call(C)); // [object Function]
console.log(C.hasOwnProperty("prototype")); // true

// ---不存在變量提升
C; // 報(bào)錯(cuò),不存在C。
class C {}
// 存在提前解析和變量提升
F; // 不報(bào)錯(cuò),F(xiàn)已被聲明和賦值。
function F() {}

// ---自執(zhí)行模式
let c = new (class {
})();
let f = new (function () {
})();
1.2 與對(duì)象對(duì)比

類內(nèi)容({}里面)的形式與對(duì)象字面量相似。不過類內(nèi)容里面只能定義方法不能定義屬性,方法的形式只能是函數(shù)簡(jiǎn)寫式,方法間不用也不能用逗號(hào)分隔。方法名可以是帶括號(hào)的表達(dá)式,也可以為Symbol值。方法分為三類,構(gòu)造方法(constructor方法)、原型方法(存在于構(gòu)造函數(shù)的prototype屬性上)和靜態(tài)方法(存在于構(gòu)造函數(shù)本身上)

class C {
  // 原型方法a
  a() { console.log("a"); }
  // 構(gòu)造方法,每次生成實(shí)例時(shí)都會(huì)被調(diào)用并返回新實(shí)例。
  constructor() {}
  // 靜態(tài)方法b,帶static關(guān)鍵字。
  static b() { console.log("b"); }
  // 原型方法,帶括號(hào)的表達(dá)式
  ["a" + "b"]() { console.log("ab"); }
  // 原型方法,使用Symbol值
  [Symbol.for("s")]() { console.log("symbol s"); }
}

C.b(); // b

let c = new C();
c.a(); // a
c.ab(); // ab
c[Symbol.for("s")](); // symbol s

不能直接定義屬性,并不表示類不能有原型或靜態(tài)屬性。解析class會(huì)形成一個(gè)構(gòu)造函數(shù),因此只需像為構(gòu)造函數(shù)添加屬性一樣為類添加即可。更為直接也是推薦的是只使用getter函數(shù)定義只讀屬性。為什么不能直接設(shè)置屬性?是技術(shù)不成熟?是官方希望傳遞某種思想?抑或僅僅是筆者隨意拋出的一個(gè)問題?

// ---直接在C類(構(gòu)造函數(shù))上修改
class C {}
C.a = "a";
C.b = function () { return "b"; };
C.prototype.c = "c";
C.prototype.d = function () { return "d"; };

let c = new C();
c.c; // c
c.d(); // d

// ---使用setter和getter
// 定義只能獲取不能修改的原型或靜態(tài)屬性
class C {
  get a() { return "a"; }
  static get b() { return "b"; }
}

let c = new C();
c.a; // a
c.a = "1"; // 賦值沒用,只有g(shù)et沒有set無法修改。
1.3 與構(gòu)造函數(shù)對(duì)比

下面是使用構(gòu)造函數(shù)和類實(shí)現(xiàn)相同功能的代碼。直觀上,class簡(jiǎn)化了代碼,使得內(nèi)容更為聚合。constructor方法體等同構(gòu)造函數(shù)的函數(shù)體,如果沒有顯式定義此方法,一個(gè)空的constructor方法會(huì)被默認(rèn)添加用于返回新的實(shí)例。與ES5一樣,也可以自定義返回另一個(gè)對(duì)象而不是新實(shí)例。

// ---構(gòu)造函數(shù)
function C(a) {
  this.a = a;
}

// 靜態(tài)屬性和方法
C.b = "b";
C.c = function () { return "c"; };

// 原型屬性和方法
C.prototype.d = "d";
C.prototype.e = function () { return "e"; };
Object.defineProperty(C.prototype, "f", { // 只讀屬性
  get() {
    return "f";
  }
});

// ---類
class C {
  static c() { return "c"; }
  
  constructor(a) {
    this.a = a;
  }
  
  e() { return "e"; }
  get f() { return "f"; }
}

C.b = "b";
C.prototype.d = "d";

類雖然是個(gè)函數(shù),但只能通過new生成實(shí)例而不能直接調(diào)用。類內(nèi)部所定義的全部方法是不可枚舉的,在構(gòu)造函數(shù)本身和prototype上添加的屬性和方法是可枚舉的。類內(nèi)部定義的方法默認(rèn)是嚴(yán)格模式,無需顯式聲明。以上三點(diǎn)增加了類的嚴(yán)謹(jǐn)性,比較遺憾的是,依然還沒有直接定義私有屬性和方法的方式。

// ---能否直接調(diào)用
class C {}
C(); // 報(bào)錯(cuò)

function C() {}
C(); // 可以


// ---是否可枚舉
class C {
  static a() {} // 不可枚舉
  b() {} // 不可枚舉
}

C.c = function () {}; // 可枚舉
C.prototype.d = function () {}; // 可枚舉

isEnumerable(C, ["a", "c"]); // a false, c true
isEnumerable(C.prototype, ["b", "d"]); // b false, d true

function isEnumerable(target, keys) {
  let obj = Object.getOwnPropertyDescriptors(target);
  keys.forEach(k => {
    console.log(k, obj[k].enumerable);
  });
}


// ---是否為嚴(yán)格模式
class C {
  a() {
    let is = false;
    try {
      n = 1;
    } catch (e) {
      is = true;
    }
    console.log(is ? "true" : "false");
  }
}

C.prototype.b = function () {
  let is = false;
  try {
    n = 1;
  } catch (e) {
    is = true;
  }
  console.log(is ? "true" : "false");
};

let c = new C();
c.a(); // true,是嚴(yán)格模式。
c.b(); // false,不是嚴(yán)格模式。

在方法前加上static關(guān)鍵字表示此方法為靜態(tài)方法,它存在于類本身,不能被實(shí)例直接訪問。靜態(tài)方法中的this指向類本身。因?yàn)樘幱诓煌瑢?duì)象上,靜態(tài)方法和原型方法可以重名。ES6新增了一個(gè)命令new.target,指代new后面的構(gòu)造函數(shù)或class,該命令的使用有某些限制,具體請(qǐng)看下面示例。

// ---static
class C {
  static a() { console.log(this === C); }
  a() { console.log(this instanceof C); }
}

let c = new C();
C.a(); // true
c.a(); // true


// ---new.target
// 構(gòu)造函數(shù)
function C() {
  console.log(new.target);
}

C.prototype.a = function () { console.log(new.target); };

let c = new C(); // 打印出C
c.a(); // 在普通方法中為undefined。

// ---類
class C {
  constructor() { console.log(new.target); }
  a() { console.log(new.target); }
}

let c = new C(); // 打印出C
c.a(); // 在普通方法中為undefined。

// ---在函數(shù)外部使用會(huì)報(bào)錯(cuò)
new.target; // 報(bào)錯(cuò)
2 extends

ES5中的經(jīng)典繼承方法是寄生組合式繼承,子類會(huì)分別繼承父類實(shí)例和原型上的屬性和方法。ES6中的繼承本質(zhì)也是如此,不過實(shí)現(xiàn)方式有所改變,具體如下面的代碼。可以看到,原型上的繼承是使用extends關(guān)鍵字這一更接近傳統(tǒng)語言的形式,實(shí)例上的繼承是通過調(diào)用super完成子類this塑造。表面上看,方式更為的統(tǒng)一和簡(jiǎn)潔。

class C1 {
  constructor(a) { this.a = a; }
  b() { console.log("b"); }
}

class C extends C1 { // 繼承原型數(shù)據(jù)
  constructor() {
    super("a"); // 繼承實(shí)例數(shù)據(jù)
  }
}
2.1 與構(gòu)造函數(shù)對(duì)比

使用extends繼承,不僅僅會(huì)將子類的prototype屬性的原型對(duì)象(__proto__)設(shè)置為父類的prototype,還會(huì)將子類本身的原型對(duì)象(__proto__)設(shè)置為父類本身。這意味著子類不單單會(huì)繼承父類的原型數(shù)據(jù),也會(huì)繼承父類本身擁有的靜態(tài)屬性和方法。而ES5的經(jīng)典繼承只會(huì)繼承父類的原型數(shù)據(jù)。不單單是財(cái)富,連老爸的名氣也要獲得,不錯(cuò)不錯(cuò)。

class C1 {
  static get a() { console.log("a"); }
  static b() { console.log("b"); }
}

class C extends C1 {
}
// 等價(jià),沒有構(gòu)造方法會(huì)默認(rèn)添加。
class C extends C1 {
  constructor(...args) {
    super(...args);
  }
}

let c = new C();
C.a; // a,繼承了父類的靜態(tài)屬性。
C.b(); // b,繼承了父類的靜態(tài)方法。
console.log(Object.getPrototypeOf(C) === C1); // true,C的原型對(duì)象為C1
console.log(Object.getPrototypeOf(C.prototype) === C1.prototype); // true,C的prototype屬性的原型對(duì)象為C1的prototype

ES5中的實(shí)例繼承,是先創(chuàng)造子類的實(shí)例對(duì)象this,再通過callapply方法,在this上添加父類的實(shí)例屬性和方法。當(dāng)然也可以選擇不繼承父類的實(shí)例數(shù)據(jù)。而ES6不同,它的設(shè)計(jì)使得實(shí)例繼承更為優(yōu)秀和嚴(yán)謹(jǐn)。

在ES6的實(shí)例繼承中,是先調(diào)用super方法創(chuàng)建父類的this(依舊指向子類)和添加父類的實(shí)例數(shù)據(jù),再通過子類的構(gòu)造函數(shù)修飾this,與ES5正好相反。ES6規(guī)定在子類的constructor方法里,在使用到this之前,必須先調(diào)用super方法得到子類的this。不調(diào)用super方法,意味著子類得不到this對(duì)象。

class C1 {
  constructor() {
    console.log("C1", this instanceof C);
  }
}

class C extends C1 {
  constructor() {
    super(); // 在super()之前不能使用this,否則報(bào)錯(cuò)。
    console.log("C");
  }
}

new C(); // 先打印出C1 true,再打印C。
2.2 super

關(guān)鍵字super比較奇葩,在不同的環(huán)境和使用方式下,它會(huì)指代不同的東西(總的說可以指代對(duì)象或方法兩種)。而且在不顯式的指明是作為對(duì)象或方法使用時(shí),比如console.log(super),會(huì)直接報(bào)錯(cuò)。

作為函數(shù)時(shí)。super只能存在于子類的構(gòu)造方法中,這時(shí)它指代父類構(gòu)造函數(shù)。

作為對(duì)象時(shí)。super在靜態(tài)方法中指代父類本身,在構(gòu)造方法和原型方法中指代父類的prototype屬性。不過通過super調(diào)用父類方法時(shí),方法的this依舊指向子類。即是說,通過super調(diào)用父類的靜態(tài)方法時(shí),該方法的this指向子類本身;調(diào)用父類的原型方法時(shí),該方法的this指向該(子類的)實(shí)例。而且通過super對(duì)某屬性賦值時(shí),在子類的原型方法里指代該實(shí)例,在子類的靜態(tài)方法里指代子類本身,畢竟直接在子類中通過super修改父類是很危險(xiǎn)的。

很迷糊對(duì)吧,瘋瘋癲癲的,還是結(jié)合著代碼看吧!

class C1 {
  static a() {
    console.log(this === C);
  }
  b() {
    console.log(this instanceof C);
  }
}

class C extends C1 {
  static c() {
    console.log(super.a); // 此時(shí)super指向C1,打印出function a。
    
    this.x = 2; // this等于C。
    super.x = 3; // 此時(shí)super等于this,即C。
    console.log(super.x); // 此時(shí)super指向C1,打印出undefined。
    console.log(this.x); // 值已改為3。

    super.a(); // 打印出true,a方法的this指向C。
  }

  constructor() {
    super(); // 指代父類的構(gòu)造函數(shù)
    
    console.log(super.c); // 此時(shí)super指向C1.prototype,打印出function c。

    this.x = 2; // this等于新實(shí)例。
    super.x = 3; // 此時(shí)super等于this,即實(shí)例本身。
    console.log(super.x); // 此時(shí)super指向C1.prototype,打印出undefined。
    console.log(this.x); // 值已改為3。

    super.b(); // 打印出true,b方法的this指向?qū)嵗旧怼?  }
}
2.3 繼承原生構(gòu)造函數(shù)

使用構(gòu)造函數(shù)模式,構(gòu)建繼承了原生數(shù)據(jù)結(jié)構(gòu)(比如Array)的子類,有許多缺陷的。一方面由上文可知,原始繼承是先創(chuàng)建子類this,再通過父類構(gòu)造函數(shù)進(jìn)行修飾,因此無法獲取到父類的內(nèi)部屬性(隱藏屬性)。另一方面,原生構(gòu)造函數(shù)會(huì)直接忽略callapply方法傳入的this,導(dǎo)致子類根本無法獲取到父類的實(shí)例屬性和方法。

function MyArray(...args) {
  Array.apply(this, args);
}

MyArray.prototype = Array.prototype;
// MyArray.prototype.constructor = MyArray;

let arr = new MyArray(1, 2, 3); // arr為對(duì)象,沒有儲(chǔ)存值。
arr.push(4, 5); // 在arr上新增了0,1和length屬性。
arr.map(d => d); // 返回?cái)?shù)組[4, 5]
arr.length = 1; // arr并沒有更新,依舊有0,1屬性,且arr[1]為5。

創(chuàng)建類的過程,是先構(gòu)造一個(gè)屬于父類卻指向子類的this(繞口),再通過父類和子類的構(gòu)造函數(shù)進(jìn)行修飾。因此可以規(guī)避構(gòu)造函數(shù)的問題,獲取到父類的實(shí)例屬性和方法,包括內(nèi)部屬性。進(jìn)而真正的創(chuàng)建原生數(shù)據(jù)結(jié)構(gòu)的子類,從而簡(jiǎn)單的擴(kuò)展原生數(shù)據(jù)類型。另外還可以通過設(shè)置Symbol.species屬性,使得衍生對(duì)象為原生類而不是自定義子類的實(shí)例。

class MyArray extends Array { // 實(shí)現(xiàn)是如此的簡(jiǎn)單
  static get [Symbol.species]() { return Array; }
}

let arr = new MyArray(1, 2, 3); // arr為數(shù)組,儲(chǔ)存有1,2,3。
arr.map(d => d); // 返回?cái)?shù)組[1, 2, 3]
arr.length = 1; // arr正常更新,已包含必要的內(nèi)部屬性。

需要注意的是繼承Object的子類。ES6改變了Object構(gòu)造函數(shù)的行為,一旦發(fā)現(xiàn)其不是通過new Object()這種形式調(diào)用的,構(gòu)造函數(shù)會(huì)忽略傳入的參數(shù)。由此導(dǎo)致Object子類無法正常初始化,但這不是個(gè)大問題。

class MyObject extends Object {
  static get [Symbol.species]() { return Object; }
}

let o = new MyObject({ id: 1 });
console.log(o.hasOwnPropoty("id")); // false,沒有被正確初始化
推薦

ES6精華:Symbol
ES6精華:Promise
Async:簡(jiǎn)潔優(yōu)雅的異步之道
Generator:JS執(zhí)行權(quán)的真實(shí)操作者

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/108526.html

相關(guān)文章

  • ES6—class與面對(duì)象編程(8)

    摘要:接下來我們看下類的寫法,這個(gè)就很接近于傳統(tǒng)面向?qū)ο笳Z言了。如果你想了解傳統(tǒng)面向?qū)ο笳Z言,這里是一個(gè)好切入點(diǎn)。作為對(duì)象時(shí),指向父類的原型對(duì)象。這些就是為將來在中支持面向?qū)ο蟮念悪C(jī)制而預(yù)留的。 在ES5中,我們經(jīng)常使用方法或者對(duì)象去模擬類的使用,并基于原型實(shí)現(xiàn)繼承,雖然可以實(shí)現(xiàn)功能,但是代碼并不優(yōu)雅,很多人還是傾向于用 class 來組織代碼,很多類庫、框架創(chuàng)造了自己的 API 來實(shí)現(xiàn) c...

    wangjuntytl 評(píng)論0 收藏0
  • Java虛擬機(jī) :Java字節(jié)碼編譯生成和運(yùn)行優(yōu)化

    摘要:字節(jié)碼生成把語法樹定義的抽象的語法結(jié)構(gòu)按照二進(jìn)制字節(jié)碼的規(guī)則排布成字節(jié)碼,最終我們可以看到滿足虛擬機(jī)運(yùn)行要求的二進(jìn)制字節(jié)碼被轉(zhuǎn)換出來。上面的過程完成后,命令扮演的編譯器就將源代碼轉(zhuǎn)成了結(jié)構(gòu)化的二進(jìn)制字節(jié)碼。 這篇文章的素材來自周志明的《深入理解Java虛擬機(jī)》。 作為Java開發(fā)人員,一定程度了解JVM虛擬機(jī)的的運(yùn)作方式非常重要,本文就一些簡(jiǎn)單的虛擬機(jī)的相關(guān)概念和運(yùn)作機(jī)制展開我自己的學(xué)...

    Hwg 評(píng)論0 收藏0
  • 談?wù)勎宜斫?em>的面對(duì)象

    摘要:眾多面向?qū)ο蟮木幊趟枷腚m不盡一致,但是無論哪種面向?qū)ο缶幊陶Z言都具有以下的共通功能。原型編程以類為中心的傳統(tǒng)面向?qū)ο缶幊蹋且灶悶榛A(chǔ)生成新對(duì)象。而原型模式的面向?qū)ο缶幊陶Z言沒有類這樣一個(gè)概念。 什么是面向?qū)ο螅窟@個(gè)問題往往會(huì)問到剛畢業(yè)的新手or實(shí)習(xí)生上,也是往往作為一個(gè)技術(shù)面試的開頭題。在這里我們不去談如何答(fu)好(yan)問(guo)題(qu),僅談?wù)勎宜斫獾拿嫦驅(qū)ο蟆?從歷...

    avwu 評(píng)論0 收藏0
  • JavaScript設(shè)計(jì)模式

    摘要:依賴于接口的設(shè)計(jì)模式下面列出的設(shè)計(jì)模式,尤其依賴接口工廠模式。這些私用的靜態(tài)成員可以從構(gòu)造器內(nèi)部訪問,這意味著所有私用函數(shù)和特權(quán)函數(shù)都能訪問它們。構(gòu)造器靜態(tài)特權(quán)方法封裝之弊私用方法很難進(jìn)行單元測(cè)試。 1.弱類型語言 在JavaScript中,定義變量時(shí)不必聲明其類型。但這并不意味著變量沒有類型。一個(gè)變量可以屬于幾種類型之一,這取決于其包含的數(shù)據(jù)。JavaScript中有三種原始類型:...

    mingzhong 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<