成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

this、call、apply、bind、繼承、原型鏈

DevTalking / 1770人閱讀

摘要:因?yàn)閷傩圆檎沂前凑赵玩湶檎?,先查找自身再查找原型鏈,找到為止。用了等?hào),先給自身賦值,所以自身賦值成功了也不會(huì)繼續(xù)去原型鏈查找。因?yàn)槎加懈髯缘娜毕?,所以就有一種組合繼承,將構(gòu)造函數(shù)繼承和繼承混合起來,方法寫在父類的上,是比較常見的方法。

本文來自我的github 0.前言

這些都是js基礎(chǔ)進(jìn)階的必備了,有時(shí)候可能一下子想不起來是什么,時(shí)不時(shí)就回頭看看基礎(chǔ),增強(qiáng)硬實(shí)力。

1.this 1.1this指向

誰最后調(diào)用,就指向誰
先簡單復(fù)習(xí)一次,this指向就那么幾種:

new 關(guān)鍵字

指向new 創(chuàng)建的對(duì)象

function F() {
        this.name = 1
}
var f = new F()

call、apply、bind

指向傳入函數(shù)的第一個(gè)參數(shù)。a.call(b),函數(shù)a內(nèi)部如果是要用到this。則這個(gè)this指向b

對(duì)象的方法

對(duì)象內(nèi)部的方法指向?qū)ο蟊旧?/p>

 var obj = {
                value: 5,
                printThis: function () {
                    console.log(this);
                }
            };

按值傳遞

指向全局

 var obj = {
                value: 5,
                printThis: function () {
                    console.log(this);
                }
            };
var f = obj.printThis
f()
如果出現(xiàn)上面對(duì)條規(guī)則的累加情況,則優(yōu)先級(jí)自1至4遞減,this的指向按照優(yōu)先級(jí)最高的規(guī)則判斷。

5.箭頭函數(shù)
指向箭頭函數(shù)定義時(shí)外層上下文

var obj = {
                value: 5,
                printThis: function () {
                        return function(){ console.log(this)}
                   }
            };
obj.printThis()//window

 var obj = {
                value: 5,
                printThis: function () {
                                      return () => console.log(this)
                                 }
            };
obj.printThis()()//obj
2.call、apply、bind

前兩者都是一樣,只是參數(shù)表現(xiàn)形式不同,bind表示的是靜態(tài)的前兩者,需要手動(dòng)調(diào)用
a.call(b,args)讓函數(shù)a執(zhí)行上下文指向b,也就是b的屬性就算沒有a函數(shù),也能像b.a(args)這樣子調(diào)用

方法大家都知道,我們不妨來自己實(shí)現(xiàn)一下這三個(gè):

2.1 call實(shí)現(xiàn)

再看一次概念,b沒有a方法,也就是沒有b.a,如果想要這個(gè)效果,那就利用這三個(gè)函數(shù)來改變執(zhí)行上下文。于是我們就可以想到,要是自己實(shí)現(xiàn)一個(gè),大概就是,給b強(qiáng)行加上這個(gè)a 的方法,然后拿到argument去調(diào)用:

Function.prototype.mycall = function(){
    var ctx = arguments[0]||window||global//獲取上下文,call的第一個(gè)參數(shù)
    var len = arguments.length
    var hash = new Date().getTime()//避免名字重復(fù)
    ctx[hash] = this//將this緩存,this就是那個(gè)想在另一個(gè)上下文利用的函數(shù)
    var result
    if(len === 1){
        result = ctx[hash]()//如果后面沒有其他參數(shù)直接運(yùn)行
    } else{
        var i = 1
        var args = []
        for(;i

apply也是同理,而且少了數(shù)組這一步,更加簡單接下來我們看一下bind怎么實(shí)現(xiàn):

Function.prototype.mybind = function(){
    var ctx = arguments[0]||window||global
    var f = this
    var args1 = []
    if(arguments.length>1){//預(yù)先填入的參數(shù)
        var i = 1
        for(;i < arguments.length;i++){
            args1.push(arguments[i])
        }
    }
    return function(){
        var args2 = Array.prototype.slice.call(arguments)//call和apply我們都可以實(shí)現(xiàn),這里就不再重復(fù)
        return f.apply(ctx,args1.concat(args2))//將預(yù)先填入的參數(shù)和執(zhí)行時(shí)的參數(shù)合并
    }
}

此外,需要注意的,一個(gè)函數(shù)被bind后,以后無論怎么用call、apply、bind,this指向都不會(huì)變,都是第一次bind的上下文

3.從call到繼承

首先,js沒有嚴(yán)格意義上的子類父類,實(shí)現(xiàn)繼承是依靠原型鏈來實(shí)現(xiàn)類似于所謂的類的效果。

3.1 call繼承(構(gòu)造函數(shù)繼承)

我們希望G繼承F,或者是說,開發(fā)的時(shí)候,由于G有很多屬性繼承F我們想偷懶,那么就可以這樣

function F(name,age){
  this.name = name 
  this.age = age
}
function G(name,age,a) {
  F.call(this,...arguments)
  this.a = a
}
var g = new G("a",12,1) //G?{name: "a", age: 12, a: 1}

這個(gè)方法特別之處是,子類可以向父類構(gòu)造函數(shù)傳參。但是,無法獲取F的原型上的屬性。
另外,方法也是寫在內(nèi)部

this.f = function(){}

也注定無法實(shí)現(xiàn)函數(shù)復(fù)用了,每一個(gè)實(shí)例都有一個(gè)函數(shù),浪費(fèi)內(nèi)存。

3.2 prototype繼承

要想子類獲得父類的屬性,如果是通過原型來實(shí)現(xiàn)繼承,那么就是父類的一個(gè)實(shí)例是子類的原型:

function F(){
  this.a = [1,2,3,4]
  this.b = 2
}
var f = new F()
function G(){}
G.prototype = f
var g = new G()
var h = new G()
g.a //[1,2,3,4]
g.b //2
//對(duì)于引用類型,如果我們修改g.a(不是用=賦值,用=不會(huì)操作到原型鏈)
g.a.push(123)
g.a//[1,2,3,4,123]
//而且其他的實(shí)例也會(huì)變化
h.a //[1,2,3,4,123]
g.b = 666 //只是在實(shí)例里面對(duì)b屬性進(jìn)行改寫,不會(huì)影響原形鏈

可以看見,對(duì)于父類的引用類型,某個(gè)值是引用類型的屬性被改寫后,子類的所有的實(shí)例繼承過來的屬性都會(huì)變,主要的是,子類都可以改變父類。但是=賦值操作相當(dāng)于直接在某一個(gè)實(shí)例上面改寫。因?yàn)閷傩圆檎沂前凑赵玩湶檎?,先查找自身再查找原型鏈,找到為止。用了等?hào),先給自身賦值,所以自身賦值成功了也不會(huì)繼續(xù)去原型鏈查找。

因?yàn)槎加懈髯缘娜毕?,所以就有一種組合繼承,將構(gòu)造函數(shù)繼承和prototype繼承混合起來,方法寫在父類的prototype上,是比較常見的方法。但是實(shí)例化都會(huì)調(diào)用兩次構(gòu)造函數(shù),new和call

3.3Object.create繼承(原型繼承)

這樣子,可以在兩個(gè)prototype中間加上一個(gè)中介F類,使得子類不會(huì)污染父類,子類A是父類B繼承而來,而且還可以在中間給他定義屬性

function A() {}? 
function B() {}? 
A.prototype = Object.create(B.prototype,{father:{value:[1,2,3]}});

//Object.create的hack
Object.create =Object.create|| function (o) {
????var F = function () {};
????F.prototype = o;
????return new F();
}
//其實(shí)create函數(shù)內(nèi)部的原理就是這樣子,看回去上面的A和B,這些操作相當(dāng)于
var F = function () {};
F.prototype = B.prototype;//原型被重寫,a.__proto__.constructor是B而不是F
A.prototype = new F()

//create方法,第二個(gè)參數(shù)類似于defineProperty,而且定義的屬性可以自行配置,默認(rèn)是不可以重新賦值
var a = new A()
a.father //[1,2,3]
a.father = 1
a.father //[1,2,3]

在不需要?jiǎng)佑脴?gòu)造函數(shù)的時(shí)候,只是想看到讓子類父類這種繼承關(guān)系,create基本上是完美選擇

3.4 寄生式繼承

利用一個(gè)封裝好繼承過程的函數(shù)來實(shí)現(xiàn)繼承,不需要另外定義一個(gè)子類,直接把子類的方法寫在函數(shù)里面

function createobj (obj) {
    var temp = Object.create(obj)
    temp.f = function () {
        console.log("this is father")
    }
    return temp
}
function B() {}? 
var b = createobj (B.prototype)
b.f() //this is father

但是,不能做到函數(shù)復(fù)用,每一個(gè)實(shí)例都要寫一份,而且寫了一個(gè)createobj就是寫死了,也不能獲取B類的內(nèi)部屬性

3.5 寄生組合式繼承

對(duì)于上面的僅僅依靠Object.create繼承,a.__proto__原型對(duì)象被重寫,他的構(gòu)造函數(shù)是B,而不是中間量F,對(duì)于這種中間類F無意義,而且只是依靠中間原型對(duì)象,我們可以用比較完美的寄生組合式繼承:

function A() {}? 
function B() {}? 
var prototype = Object.create(B.prototype)//創(chuàng)建
prototype.constructor = A//增強(qiáng)
A.prototype = prototype//指定,這下a.__proto__.constructor 就是A了
var a = new A()

不用創(chuàng)建中間類F,而且構(gòu)造函數(shù)A的確是造出a的(a.__proto__.constructor == A),而不是像create那樣改寫原型鏈,構(gòu)造函數(shù)是B

附上原型鏈圖解:(注意終點(diǎn)是null,中間的都是正常new構(gòu)造,沒有改寫prototype)

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

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/94360.html

相關(guān)文章

  • JS之函數(shù)(1)

    摘要:前言這段時(shí)間突然發(fā)現(xiàn)原生好多東西都忘記了但有些東西確實(shí)很重要所以又重新再梳理一次。 showImg(https://segmentfault.com/img/bVbqqkr?w=874&h=382); 前言 這段時(shí)間突然發(fā)現(xiàn)JS原生好多東西都忘記了,但有些東西確實(shí)很重要,所以又重新再梳理一次。主要有函數(shù)的3種定義方法,ES5函數(shù)this指向,call與appl用法,JS常見的4種設(shè)計(jì)模...

    宋華 評(píng)論0 收藏0
  • javascript原生一步步實(shí)現(xiàn)bind分析

    摘要:綁定函數(shù)被調(diào)用時(shí),也接受預(yù)設(shè)的參數(shù)提供給原函數(shù)。原型鏈官方文檔上有一句話說明綁定過后的函數(shù)被實(shí)例化之后,需要繼承原函數(shù)的原型鏈方法,且綁定過程中提供的被忽略繼承原函數(shù)的對(duì)象,但是參數(shù)還是會(huì)使用。 bind 官方描述 bind() 函數(shù)會(huì)創(chuàng)建一個(gè)新函數(shù)(稱為綁定函數(shù)),新函數(shù)與被調(diào)函數(shù)(綁定函數(shù)的目標(biāo)函數(shù))具有相同的函數(shù)體(在 ECMAScript 5 規(guī)范中內(nèi)置的call屬性)。當(dāng)目標(biāo)...

    coordinate35 評(píng)論0 收藏0
  • 溫故知新之javascript面向?qū)ο?/b>

    摘要:應(yīng)該非常小心,避免出現(xiàn)不使用命令直接調(diào)用構(gòu)造函數(shù)的情況。上面代碼表示,使用屬性,確定實(shí)例對(duì)象的構(gòu)造函數(shù)是,而不是。當(dāng)然,從繼承鏈來看,只有一個(gè)父類,但是由于在的實(shí)例上,同時(shí)執(zhí)行和的構(gòu)造函數(shù),所以它同時(shí)繼承了這兩個(gè)類的方法。 基本概念 類和實(shí)例是大多數(shù)面向?qū)ο缶幊陶Z言的基本概念 類:類是對(duì)象的類型模板 實(shí)例:實(shí)例是根據(jù)類創(chuàng)建的對(duì)象但是,JavaScript語言的對(duì)象體系,不是基于類的,...

    趙連江 評(píng)論0 收藏0
  • JavaScript中bind方法的實(shí)現(xiàn)

    摘要:新函數(shù)也能使用操作符創(chuàng)建對(duì)象這種行為就像把原函數(shù)當(dāng)成構(gòu)造器,提供的值被忽略。說明綁定后的新函數(shù)被實(shí)例化之后,需要繼承原函數(shù)的原型鏈方法,且綁定過程中提供的被忽略繼承原函數(shù)的對(duì)象,但是參數(shù)還是會(huì)使用。 在討論bind方法前,我們可以先看一個(gè)例子: var getElementsByTagName = document.getElementsByTagName; getElementsBy...

    dackel 評(píng)論0 收藏0
  • JS原型

    摘要:指向原型對(duì)象的構(gòu)造函數(shù)二原型鏈?zhǔn)裁词窃玩溤玩溇褪菍?shí)例對(duì)象和原型對(duì)象之間的關(guān)系,他們使用來關(guān)聯(lián)。改變指向?qū)崿F(xiàn)繼承創(chuàng)建類創(chuàng)建原型對(duì)象每天堅(jiān)持鍛煉創(chuàng)建構(gòu)造函數(shù)函數(shù)就是改變執(zhí)行代碼片段中指向。 一. JS原型的簡單理解 1.1 prototype prototype:是一個(gè)函數(shù)的屬性,每個(gè)函數(shù)中都會(huì)有一個(gè)prototype屬性,這個(gè)屬性是一個(gè)指針,指向一個(gè)對(duì)象。在JavaScript...

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

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

0條評(píng)論

DevTalking

|高級(jí)講師

TA的文章

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