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

資訊專欄INFORMATION COLUMN

js溫故而知新7(面向?qū)ο缶幊?——學(xué)習(xí)廖雪峰的js教程

Jaden / 2603人閱讀

摘要:不區(qū)分類和實例的概念,而是通過原型來實現(xiàn)面向?qū)ο缶幊獭P聞?chuàng)建的的原型鏈?zhǔn)且簿褪钦f,的原型指向函數(shù)的原型。最后,創(chuàng)建一個對象代碼和前面章節(jié)完全一樣小明繼承用定義對象的另一個巨大的好處是繼承更方便了。

JavaScript不區(qū)分類和實例的概念,而是通過原型(prototype)來實現(xiàn)面向?qū)ο缶幊獭?/p>

原型是指當(dāng)我們想要創(chuàng)建xiaoming這個具體的學(xué)生時,我們并沒有一個Student類型可用。那怎么辦?恰好有這么一個現(xiàn)成的對象:

var robot = {
    name: "Robot",
    height: 1.6,
    run: function () {
        console.log(this.name + " is running...");
    }
};

我們看這個robot對象有名字,有身高,還會跑,有點像小明,干脆就根據(jù)它來“創(chuàng)建”小明得了!

于是我們把它改名為Student,然后創(chuàng)建出xiaoming:

var Student = {
    name: "Robot",
    height: 1.2,
    run: function () {
        console.log(this.name + " is running...");
    }
};

var xiaoming = {
    name: "小明"
};

xiaoming.__proto__ = Student;

注意最后一行代碼把xiaoming的原型指向了對象Student,看上去xiaoming仿佛是從Student繼承下來的:

xiaoming.name; // "小明"
xiaoming.run(); // 小明 is running...

xiaoming有自己的name屬性,但并沒有定義run()方法。不過,由于小明是從Student繼承而來,只要Student有run()方法,xiaoming也可以調(diào)用:

JavaScript的原型鏈和Java的Class區(qū)別就在,它沒有“Class”的概念所有對象都是實例,所謂繼承關(guān)系不過是把一個對象的原型指向另一個對象而已

如果你把xiaoming的原型指向其他對象:

var Bird = {
    fly: function () {
        console.log(this.name + " is flying...");
    }
};

xiaoming.__proto__ = Bird;

現(xiàn)在xiaoming已經(jīng)無法run()了,他已經(jīng)變成了一只鳥:

xiaoming.fly(); // 小明 is flying...

在JavaScrip代碼運行時期,你可以把xiaoming從Student變成Bird,或者變成任何對象。

請注意,上述代碼僅用于演示目的。在編寫JavaScript代碼時,不要直接用obj.__proto__去改變一個對象的原型,并且,低版本的IE也無法使用__proto__。Object.create()方法可以傳入一個原型對象,并創(chuàng)建一個基于該原型的新對象,但是新對象什么屬性都沒有,因此,我們可以編寫一個函數(shù)來創(chuàng)建xiaoming:

// 原型對象:
var Student = {
    name: "Robot",
    height: 1.2,
    run: function () {
        console.log(this.name + " is running...");
    }
};

function createStudent(name) {
    // 基于Student原型創(chuàng)建一個新對象:
    var s = Object.create(Student);
    // 初始化新對象:
    s.name = name;
    return s;
}

var xiaoming = createStudent("小明");
xiaoming.run(); // 小明 is running...
xiaoming.__proto__ === Student; // true
創(chuàng)建對象

JavaScript對每個創(chuàng)建的對象都會設(shè)置一個原型,指向它的原型對象

當(dāng)我們用obj.xxx訪問一個對象的屬性時,JavaScript引擎先在當(dāng)前對象上查找該屬性,如果沒有找到,就到其原型對象上找,如果還沒有找到,就一直上溯到Object.prototype對象,最后,如果還沒有找到,就只能返回undefined。

例如,創(chuàng)建一個Array對象:

var arr = [1, 2, 3];

其原型鏈?zhǔn)牵?/p>

arr ----> Array.prototype ----> Object.prototype ----> null

Array.prototype定義了indexOf()、shift()等方法,因此你可以在所有的Array對象上直接調(diào)用這些方法。

當(dāng)我們創(chuàng)建一個函數(shù)時:

function foo() {
    return 0;
}

函數(shù)也是一個對象,它的原型鏈?zhǔn)牵?/p>

foo ----> Function.prototype ----> Object.prototype ----> null

由于Function.prototype定義了apply()等方法,因此,所有函數(shù)都可以調(diào)用apply()方法。

很容易想到,如果原型鏈很長,那么訪問一個對象的屬性就會因為花更多的時間查找而變得更慢,因此要注意不要把原型鏈搞得太長。

構(gòu)造函數(shù)

除了直接用{ ... }創(chuàng)建一個對象外,JavaScript還可以用一種構(gòu)造函數(shù)的方法來創(chuàng)建對象。它的用法是,先定義一個構(gòu)造函數(shù):

function Student(name) {
    this.name = name;
    this.hello = function () {
        alert("Hello, " + this.name + "!");
    }
}

這確實是一個普通函數(shù),但是在JavaScript中,可以用關(guān)鍵字new來調(diào)用這個函數(shù),并返回一個對象:

var xiaoming = new Student("小明");
xiaoming.name; // "小明"
xiaoming.hello(); // Hello, 小明!

注意,如果不寫new,這就是一個普通函數(shù),它返回undefined。但是,如果寫了new,它就變成了一個構(gòu)造函數(shù),它綁定的this指向新創(chuàng)建的對象,并默認(rèn)返回this,也就是說,不需要在最后寫return this;。

新創(chuàng)建的xiaoming的原型鏈?zhǔn)牵?/p>

xiaoming ----> Student.prototype ----> Object.prototype ----> null

也就是說,xiaoming的原型指向函數(shù)Student的原型。如果你又創(chuàng)建了xiaohong、xiaojun,那么這些對象的原型與xiaoming是一樣的:

xiaoming ↘
xiaohong -→ Student.prototype ----> Object.prototype ----> null
xiaojun  ↗

用new Student()創(chuàng)建的對象還從原型上獲得了一個constructor屬性,它指向函數(shù)Student本身:

xiaoming.constructor === Student.prototype.constructor; // true
Student.prototype.constructor === Student; // true

Object.getPrototypeOf(xiaoming) === Student.prototype; // true

xiaoming instanceof Student; // true

看暈了吧?用一張圖來表示這些亂七八糟的關(guān)系就是:

紅色箭頭是原型鏈。注意,Student.prototype指向的對象就是xiaoming、xiaohong的原型對象,這個原型對象自己還有個屬性constructor,指向Student函數(shù)本身。
另外,函數(shù)Student恰好有個屬性prototype指向xiaoming、xiaohong的原型對象,但是xiaoming、xiaohong這些對象可沒有prototype這個屬性,不過可以用__proto__這個非標(biāo)準(zhǔn)用法來查看。

現(xiàn)在我們就認(rèn)為xiaoming、xiaohong這些對象“繼承”自Student。

不過還有一個小問題,注意觀察:

xiaoming.name; // "小明"
xiaohong.name; // "小紅"
xiaoming.hello; // function: Student.hello()
xiaohong.hello; // function: Student.hello()
xiaoming.hello === xiaohong.hello; // false

xiaoming和xiaohong各自的name不同,這是對的,否則我們無法區(qū)分誰是誰了。

xiaoming和xiaohong各自的hello是一個函數(shù),但它們是兩個不同的函數(shù),雖然函數(shù)名稱和代碼都是相同的!

如果我們通過new Student()創(chuàng)建了很多對象,這些對象的hello函數(shù)實際上只需要共享同一個函數(shù)就可以了,這樣可以節(jié)省很多內(nèi)存。

要讓創(chuàng)建的對象共享一個hello函數(shù),根據(jù)對象的屬性查找原則,我們只要把hello函數(shù)移動到xiaoming、xiaohong這些對象共同的原型上就可以了,也就是Student.prototype:

修改代碼如下:

function Student(name) {
    this.name = name;
}

Student.prototype.hello = function () {
    alert("Hello, " + this.name + "!");
};

用new創(chuàng)建基于原型的JavaScript的對象就是這么簡單!

忘記寫new怎么辦

如果一個函數(shù)被定義為用于創(chuàng)建對象的構(gòu)造函數(shù),但是調(diào)用時忘記了寫new怎么辦?

在strict模式下,this.name =name將報錯,因為this綁定為undefined,在非strict模式下,this.name =name不報錯,因為this綁定為window,于是無意間創(chuàng)建了全局變量name,并且返回undefined,這個結(jié)果更糟糕。

所以,調(diào)用構(gòu)造函數(shù)千萬不要忘記寫new。為了區(qū)分普通函數(shù)和構(gòu)造函數(shù),按照約定,構(gòu)造函數(shù)首字母應(yīng)當(dāng)大寫,而普通函數(shù)首字母應(yīng)當(dāng)小寫,這樣,一些語法檢查工具如jslint將可以幫你檢測到漏寫的new。
最后,我們還可以編寫一個createStudent()函數(shù),在內(nèi)部封裝所有的new操作。一個常用的編程模式像這樣:

function Student(props) {
    this.name = props.name || "匿名"; // 默認(rèn)值為"匿名"
    this.grade = props.grade || 1; // 默認(rèn)值為1
}

Student.prototype.hello = function () {
    alert("Hello, " + this.name + "!");
};

function createStudent(props) {
    return new Student(props || {})
}

這個createStudent()函數(shù)有幾個巨大的優(yōu)點:一是不需要new來調(diào)用,二是參數(shù)非常靈活,可以不傳,也可以這么傳

var xiaoming = createStudent({
    name: "小明"
});

xiaoming.grade; // 1

如果創(chuàng)建的對象有很多屬性,我們只需要傳遞需要的某些屬性,剩下的屬性可以用默認(rèn)值。由于參數(shù)是一個Object,我們無需記憶參數(shù)的順序。如果恰好從JSON拿到了一個對象,就可以直接創(chuàng)建出xiaoming。

原型繼承

先回顧Student構(gòu)造函數(shù):

function Student(props) {
    this.name = props.name || "Unnamed";
}

Student.prototype.hello = function () {
    alert("Hello, " + this.name + "!");
}

現(xiàn)在,我們要基于Student擴展出PrimaryStudent,可以先定義出PrimaryStudent:

function PrimaryStudent(props) {
    // 調(diào)用Student構(gòu)造函數(shù),綁定this變量:
    Student.call(this, props);
    this.grade = props.grade || 1;
}

但是,調(diào)用了Student構(gòu)造函數(shù)不等于繼承了Student,PrimaryStudent創(chuàng)建的對象的原型是:

new PrimaryStudent() ----> PrimaryStudent.prototype ----> Object.prototype ----> null

必須想辦法把原型鏈修改為:

new PrimaryStudent() ----> PrimaryStudent.prototype ----> Student.prototype ----> Object.prototype ----> null

這樣,原型鏈對了,繼承關(guān)系就對了。新的基于PrimaryStudent創(chuàng)建的對象不但能調(diào)用PrimaryStudent.prototype定義的方法,也可以調(diào)用Student.prototype定義的方法。

如果你想用最簡單粗暴的方法這么干:

PrimaryStudent.prototype = Student.prototype;

是不行的!如果這樣的話,PrimaryStudent和Student共享一個原型對象,那還要定義PrimaryStudent干啥?
我們必須借助一個中間對象來實現(xiàn)正確的原型鏈,這個中間對象的原型要指向Student.prototype。為了實現(xiàn)這一點,參考道爺(就是發(fā)明JSON的那個道格拉斯)的代碼,中間對象可以用一個空函數(shù)F來實現(xiàn):

// PrimaryStudent構(gòu)造函數(shù):
function PrimaryStudent(props) {
    Student.call(this, props);
    this.grade = props.grade || 1;
}

// 空函數(shù)F:
function F() {
}

// 把F的原型指向Student.prototype:
F.prototype = Student.prototype;

// 把PrimaryStudent的原型指向一個新的F對象,F(xiàn)對象的原型正好指向Student.prototype:
PrimaryStudent.prototype = new F();

// 把PrimaryStudent原型的構(gòu)造函數(shù)修復(fù)為PrimaryStudent:
PrimaryStudent.prototype.constructor = PrimaryStudent;

// 繼續(xù)在PrimaryStudent原型(就是new F()對象)上定義方法:
PrimaryStudent.prototype.getGrade = function () {
    return this.grade;
};

// 創(chuàng)建xiaoming:
var xiaoming = new PrimaryStudent({
    name: "小明",
    grade: 2
});
xiaoming.name; // "小明"
xiaoming.grade; // 2

// 驗證原型:
xiaoming.__proto__ === PrimaryStudent.prototype; // true
xiaoming.__proto__.__proto__ === Student.prototype; // true

// 驗證繼承關(guān)系:
xiaoming instanceof PrimaryStudent; // true
xiaoming instanceof Student; // true

instanceof

instanceof 用于判斷一個變量是否是某個對象的實例,如 var a=new Array();alert(a instanceof Array); 會返回 true,同時 alert(a instanceof Object) 也會返回 true;這是因為 Array 是 object 的子類。再如:function test(){};var a=new test();alert(a instanceof test) 會返回true.

談到 instanceof 我們要多插入一個問題,就是 function 的 arguments,我們大家也許都認(rèn)為 arguments 是一個 Array,但如果使用 instaceof 去測試會發(fā)現(xiàn) arguments 不是一個 Array 對象,盡管看起來很像。

另外:

測試 var a=new Array();if (a instanceof Object) alert("Y");else alert("N");

得"Y’.

但 if (window instanceof Object) alert("Y");else alert("N");

得"N".

所以,這里的 instanceof 測試的 object 是指 js 語法中的 object,不是指 dom 模型對象。

使用 typeof 會有些區(qū)別.

alert(typeof(window)) 會得 object.

注意,函數(shù)F僅用于橋接,我們僅創(chuàng)建了一個new F()實例,而且,沒有改變原有的Student定義的原型鏈。

如果把繼承這個動作用一個inherits()函數(shù)封裝起來,還可以隱藏F的定義,并簡化代碼:

    function inherits(Child, Parent) {
        var F = function () {};
        F.prototype = Parent.prototype;
        Child.prototype = new F();
        Child.prototype.constructor = Child;
    }
function Student(props) {
    this.name = props.name || "Unnamed";
}

Student.prototype.hello = function () {
    alert("Hello, " + this.name + "!");
}

function PrimaryStudent(props) {
    Student.call(this, props);
    this.grade = props.grade || 1;
}

// 實現(xiàn)原型繼承鏈:
inherits(PrimaryStudent, Student);

// 綁定其他方法到PrimaryStudent原型:
PrimaryStudent.prototype.getGrade = function () {
    return this.grade;
};

小結(jié)

JavaScript的原型繼承實現(xiàn)方式就是:

定義新的構(gòu)造函數(shù),并在內(nèi)部用call()調(diào)用希望“繼承”的構(gòu)造函數(shù),并綁定this;
借助中間函數(shù)F實現(xiàn)原型鏈繼承,最好通過封裝的inherits函數(shù)完成;
繼續(xù)在新的構(gòu)造函數(shù)的原型上定義新方法。

class繼承

在上面的章節(jié)中我們看到了JavaScript的對象模型是基于原型實現(xiàn)的,特點是簡單,缺點是理解起來比傳統(tǒng)的類-實例模型要困難,最大的缺點是繼承的實現(xiàn)需要編寫大量代碼,并且需要正確實現(xiàn)原型鏈。
有沒有更簡單的寫法?有!

新的關(guān)鍵字class從ES6開始正式被引入到JavaScript中。class的目的就是讓定義類更簡單。

我們先回顧用函數(shù)實現(xiàn)Student的方法:

function Student(name) {
    this.name = name;
}

Student.prototype.hello = function () {
    alert("Hello, " + this.name + "!");
}

如果用新的class關(guān)鍵字來編寫Student,可以這樣寫:

class Student {
    constructor(name) {
        this.name = name;
    }

    hello() {
        alert("Hello, " + this.name + "!");
    }
}

比較一下就可以發(fā)現(xiàn),class的定義包含了構(gòu)造函數(shù)constructor和定義在原型對象上的函數(shù)hello()(注意沒有function關(guān)鍵字),這樣就避免了Student.prototype.hello = function () {...}這樣分散的代碼。

最后,創(chuàng)建一個Student對象代碼和前面章節(jié)完全一樣:

var xiaoming = new Student("小明");
xiaoming.hello();

class繼承

用class定義對象的另一個巨大的好處是繼承更方便了。想一想我們從Student派生一個PrimaryStudent需要編寫的代碼量。現(xiàn)在,原型繼承的中間對象,原型對象的構(gòu)造函數(shù)等等都不需要考慮了,直接通過extends來實現(xiàn):

class PrimaryStudent extends Student {
    constructor(name, grade) {
        super(name); // 記得用super調(diào)用父類的構(gòu)造方法!
        this.grade = grade;
    }

    myGrade() {
        alert("I am at grade " + this.grade);
    }
}

注意PrimaryStudent的定義也是class關(guān)鍵字實現(xiàn)的,而extends則表示原型鏈對象來自Student。子類的構(gòu)造函數(shù)可能會與父類不太相同,例如,PrimaryStudent需要name和grade兩個參數(shù),并且需要通過super(name)來調(diào)用父類的構(gòu)造函數(shù),否則父類的name屬性無法正常初始化。

PrimaryStudent已經(jīng)自動獲得了父類Student的hello方法,我們又在子類中定義了新的myGrade方法。

ES6引入的class和原有的JavaScript原型繼承有什么區(qū)別呢?實際上它們沒有任何區(qū)別,class的作用就是讓JavaScript引擎去實現(xiàn)原來需要我們自己編寫的原型鏈代碼。簡而言之,用class的好處就是極大地簡化了原型鏈代碼。

你一定會問,class這么好用,能不能現(xiàn)在就用上?

現(xiàn)在用還早了點,因為不是所有的主流瀏覽器都支持ES6的class。如果一定要現(xiàn)在就用上,就需要一個工具把class代碼轉(zhuǎn)換為傳統(tǒng)的prototype代碼,可以試試Babel這個工具。

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

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

相關(guān)文章

  • js溫故知新3——學(xué)習(xí)雪峰js教程

    摘要:例如,在一個中,刪掉偶數(shù),只保留奇數(shù),可以這么寫把一個中的空字符串刪掉,可以這么寫注意以下的版本沒有方法可見用這個高階函數(shù),關(guān)鍵在于正確實現(xiàn)一個篩選函數(shù)。回調(diào)函數(shù)接收的回調(diào)函數(shù),其實可以有多個參數(shù)。 1.map 由于map()方法定義在JavaScript的Array中,我們調(diào)用Array的map()方法,傳入我們自己的函數(shù),就得到了一個新的Array作為結(jié)果: function po...

    zhaot 評論0 收藏0
  • js溫故知新8(瀏覽器)——學(xué)習(xí)雪峰js教程

    摘要:對象不但充當(dāng)全局作用域,而且表示瀏覽器窗口。對象有和屬性,可以獲取瀏覽器窗口的內(nèi)部寬度和高度。對象表示當(dāng)前頁面的信息。由于在瀏覽器中以形式表示為樹形結(jié)構(gòu),對象就是整個樹的根節(jié)點。這個行為由瀏覽器實現(xiàn),主流瀏覽器均支持選項,從開始支持。 瀏覽器 目前主流的瀏覽器: IE 6~11:從IE10開始支持ES6標(biāo)準(zhǔn); Chrome:基于Webkit內(nèi)核,內(nèi)置了非常強悍的JavaScript引...

    charles_paul 評論0 收藏0
  • js溫故知新6(JSON)——學(xué)習(xí)雪峰js教程

    JSON JSON是JavaScript Object Notation的縮寫,它是一種數(shù)據(jù)交換格式。 道格拉斯·克羅克福特(Douglas Crockford)--雅虎的高級架構(gòu)師--發(fā)明了JSON這種超輕量級的數(shù)據(jù)交換格式. 序列化 讓我們先把小明這個對象序列化成JSON格式的字符串: var xiaoming = { name: 小明, age: 14, gender...

    20171112 評論0 收藏0
  • js溫故知新——學(xué)習(xí)雪峰js教程

    摘要:在設(shè)計時,有兩種比較運算符第一種是比較,它會自動轉(zhuǎn)換數(shù)據(jù)類型再比較,很多時候,會得到非常詭異的結(jié)果第二種是比較,它不會自動轉(zhuǎn)換數(shù)據(jù)類型,如果數(shù)據(jù)類型不一致,返回,如果一致,再比較。 數(shù)據(jù)類型和變量 數(shù)據(jù)類型計算機顧名思義就是可以做數(shù)學(xué)計算的機器,因此,計算機程序理所當(dāng)然地可以處理各種數(shù)值。但是,計算機能處理的遠(yuǎn)不止數(shù)值,還可以處理文本、圖形、音頻、視頻、網(wǎng)頁等各種各樣的數(shù)據(jù),不同的數(shù)據(jù)...

    taohonghui 評論0 收藏0
  • js溫故知新2——學(xué)習(xí)雪峰js教程

    摘要:讓我們拆開寫小明正常結(jié)果單獨調(diào)用函數(shù)怎么返回了請注意,我們已經(jīng)進入到了的一個大坑里。如果單獨調(diào)用函數(shù),比如,此時,該函數(shù)的指向全局對象,也就是。 函數(shù) 1. arguments JavaScript還有一個免費贈送的關(guān)鍵字arguments,它只在函數(shù)內(nèi)部起作用,并且永遠(yuǎn)指向當(dāng)前函數(shù)的調(diào)用者傳入的所有參數(shù)。arguments類似Array但它不是一個Array: function fo...

    Caizhenhao 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<